63 cl::desc(
"The page size of the target in bytes"),
67 "imp-null-max-insts-to-consider",
68 cl::desc(
"The max number of instructions to consider hoisting loads over "
69 "(the algorithm is quadratic over this number)"),
72#define DEBUG_TYPE "implicit-null-checks"
75 "Number of explicit null checks made implicit");
92 struct DependenceResult {
99 std::optional<ArrayRef<MachineInstr *>::iterator> PotentialDependence;
104 : CanReorder(CanReorder), PotentialDependence(PotentialDependence) {
105 assert((!PotentialDependence || CanReorder) &&
106 "!CanReorder && PotentialDependence.hasValue() not allowed!");
145 : MemOperation(memOperation), CheckOperation(checkOperation),
146 CheckBlock(checkBlock), NotNullSucc(notNullSucc), NullSucc(nullSucc),
147 OnlyDependency(onlyDependency) {}
149 MachineInstr *getMemOperation()
const {
return MemOperation; }
151 MachineInstr *getCheckOperation()
const {
return CheckOperation; }
159 MachineInstr *getOnlyDependency()
const {
return OnlyDependency; }
176 AR_WillAliasEverything
185 enum SuitabilityResult {
204 bool canDependenceHoistingClobberLiveIns(
MachineInstr *DependenceMI,
236 if (
MI->isCall() ||
MI->mayRaiseFPException() ||
237 MI->hasUnmodeledSideEffects())
239 auto IsRegMask = [](
const MachineOperand &MO) {
return MO.isRegMask(); };
243 "Calls were filtered out above!");
249ImplicitNullChecks::DependenceResult
255 std::optional<ArrayRef<MachineInstr *>::iterator> Dep;
258 if (canReorder(*
I,
MI))
261 if (Dep == std::nullopt) {
266 return {
false, std::nullopt};
275 assert(canHandle(
A) && canHandle(
B) &&
"Precondition!");
281 for (
const auto &MOA :
A->operands()) {
282 if (!(MOA.isReg() && MOA.getReg()))
286 for (
const auto &MOB :
B->operands()) {
287 if (!(MOB.isReg() && MOB.getReg()))
292 if (
TRI->regsOverlap(RegA, RegB) && (MOA.isDef() || MOB.isDef()))
304 AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
309 analyzeBlockForNullChecks(
MBB, NullCheckList);
311 if (!NullCheckList.
empty())
312 rewriteNullChecks(NullCheckList);
314 return !NullCheckList.
empty();
327ImplicitNullChecks::AliasResult
338 if (
MI.memoperands_empty())
339 return MI.mayStore() ? AR_WillAliasEverything : AR_MayAlias;
341 return PrevMI->
mayStore() ? AR_WillAliasEverything : AR_MayAlias;
346 assert(MMO1->getValue() &&
"MMO1 should have a Value!");
349 if (PSV->mayAlias(MFI))
362ImplicitNullChecks::SuitabilityResult
368 if (
MI.getDesc().getNumDefs() > 1)
369 return SR_Unsuitable;
371 if (!
MI.mayLoadOrStore() ||
MI.isPredicable())
372 return SR_Unsuitable;
373 auto AM =
TII->getAddrModeFromMemoryOp(
MI,
TRI);
374 if (!AM || AM->Form != ExtAddrMode::Formula::Basic)
375 return SR_Unsuitable;
378 int64_t Displacement =
AddrMode.Displacement;
382 if (BaseReg != PointerReg && ScaledReg != PointerReg)
383 return SR_Unsuitable;
385 unsigned PointerRegSizeInBits =
TRI->getRegSizeInBits(PointerReg,
MRI);
389 TRI->getRegSizeInBits(BaseReg,
MRI) != PointerRegSizeInBits) ||
391 TRI->getRegSizeInBits(ScaledReg,
MRI) != PointerRegSizeInBits))
392 return SR_Unsuitable;
396 auto CalculateDisplacementFromAddrMode = [&](
Register RegUsedInAddr,
397 int64_t Multiplier) {
403 assert(Multiplier &&
"expected to be non-zero!");
406 It !=
MI.getParent()->
rend(); It++) {
418 if (!
TII->getConstValDefinedInReg(*ModifyingMI, RegUsedInAddr, ImmVal))
422 int32_t RegSizeInBits =
TRI->getRegSizeInBits(RegUsedInAddr,
MRI);
423 APInt ImmValC(RegSizeInBits, ImmVal,
true );
424 APInt MultiplierC(RegSizeInBits, Multiplier);
425 assert(MultiplierC.isStrictlyPositive() &&
426 "expected to be a positive value!");
430 APInt Product = ImmValC.
smul_ov(MultiplierC, IsOverflow);
433 APInt DisplacementC(64, Displacement,
true );
434 DisplacementC = Product.
sadd_ov(DisplacementC, IsOverflow);
439 if (DisplacementC.getActiveBits() > 64)
447 bool BaseRegIsConstVal =
false, ScaledRegIsConstVal =
false;
448 if (CalculateDisplacementFromAddrMode(BaseReg, 1))
449 BaseRegIsConstVal =
true;
450 if (CalculateDisplacementFromAddrMode(ScaledReg,
AddrMode.Scale))
451 ScaledRegIsConstVal =
true;
458 if ((BaseReg && BaseReg != PointerReg && !BaseRegIsConstVal) ||
459 (ScaledReg && ScaledReg != PointerReg && !ScaledRegIsConstVal))
460 return SR_Unsuitable;
465 return SR_Unsuitable;
468 for (
auto *PrevMI : PrevInsts) {
470 if (AR == AR_WillAliasEverything)
471 return SR_Impossible;
472 if (AR == AR_MayAlias)
473 return SR_Unsuitable;
478bool ImplicitNullChecks::canDependenceHoistingClobberLiveIns(
480 for (
const auto &DependenceMO : DependenceMI->
operands()) {
481 if (!(DependenceMO.isReg() && DependenceMO.getReg()))
510bool ImplicitNullChecks::canHoistInst(
MachineInstr *FaultingMI,
514 auto DepResult = computeDependence(FaultingMI, InstsSeenSoFar);
515 if (!DepResult.CanReorder)
518 if (!DepResult.PotentialDependence) {
523 auto DependenceItr = *DepResult.PotentialDependence;
524 auto *DependenceMI = *DependenceItr;
531 assert(canHandle(DependenceMI) &&
"Should never have reached here!");
535 if (canDependenceHoistingClobberLiveIns(DependenceMI, NullSucc))
539 computeDependence(DependenceMI, {InstsSeenSoFar.
begin(), DependenceItr});
541 if (!DepDepResult.CanReorder || DepDepResult.PotentialDependence)
551bool ImplicitNullChecks::analyzeBlockForNullChecks(
555 MDNode *BranchMD =
nullptr;
557 BranchMD = BB->getTerminator()->getMetadata(LLVMContext::MD_make_implicit);
562 MachineBranchPredicate MBP;
564 if (
TII->analyzeBranchPredicate(
MBB, MBP,
true))
568 if (!(MBP.LHS.isReg() && MBP.RHS.isImm() && MBP.RHS.getImm() == 0 &&
569 (MBP.Predicate == MachineBranchPredicate::PRED_NE ||
570 MBP.Predicate == MachineBranchPredicate::PRED_EQ)))
575 if (MBP.ConditionDef && !MBP.SingleUseCondition)
580 if (MBP.Predicate == MachineBranchPredicate::PRED_NE) {
581 NotNullSucc = MBP.TrueDest;
582 NullSucc = MBP.FalseDest;
584 NotNullSucc = MBP.FalseDest;
585 NullSucc = MBP.TrueDest;
593 const Register PointerReg = MBP.LHS.getReg();
595 if (MBP.ConditionDef) {
614 assert(MBP.ConditionDef->getParent() == &
MBB &&
615 "Should be in basic block");
617 for (
auto I =
MBB.
rbegin(); MBP.ConditionDef != &*
I; ++
I)
618 if (
I->modifiesRegister(PointerReg,
TRI))
677 for (
auto &
MI : *NotNullSucc) {
682 SuitabilityResult SR = isSuitableMemoryOp(
MI, PointerReg, InstsSeenSoFar);
683 if (SR == SR_Impossible)
685 if (SR == SR_Suitable &&
686 canHoistInst(&
MI, InstsSeenSoFar, NullSucc,
Dependence)) {
694 if (!
TII->preservesZeroValueInReg(&
MI, PointerReg,
TRI))
709 unsigned NumDefs =
MI->getDesc().getNumDefs();
710 assert(NumDefs <= 1 &&
"other cases unhandled!");
714 DefReg =
MI->getOperand(0).getReg();
715 assert(NumDefs == 1 &&
"expected exactly one def!");
725 auto MIB =
BuildMI(
MBB,
DL,
TII->get(TargetOpcode::FAULTING_OP), DefReg)
730 for (
auto &MO :
MI->uses()) {
736 assert(MO.isDef() &&
"Expected def or use");
745 MIB.setMemRefs(
MI->memoperands());
751void ImplicitNullChecks::rewriteNullChecks(
755 for (
const auto &
NC : NullCheckList) {
758 (void)BranchesRemoved;
759 assert(BranchesRemoved > 0 &&
"expected at least one branch!");
761 if (
auto *DepMI =
NC.getOnlyDependency()) {
762 DepMI->removeFromParent();
763 NC.getCheckBlock()->insert(
NC.getCheckBlock()->end(), DepMI);
771 NC.getMemOperation(),
NC.getCheckBlock(),
NC.getNullSucc());
784 if (
auto *DepMI =
NC.getOnlyDependency()) {
785 for (
auto &MO : DepMI->all_defs()) {
786 if (!MO.getReg() || MO.isDead())
788 if (!
NC.getNotNullSucc()->isLiveIn(MO.getReg()))
789 NC.getNotNullSucc()->addLiveIn(MO.getReg());
793 NC.getMemOperation()->eraseFromParent();
794 if (
auto *CheckOp =
NC.getCheckOperation())
795 CheckOp->eraseFromParent();
802 NumImplicitNullChecks++;
806char ImplicitNullChecks::ID = 0;
811 "Implicit null checks",
false,
false)
unsigned const MachineRegisterInfo * MRI
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
const HexagonInstrInfo * TII
static bool AnyAliasLiveIn(const TargetRegisterInfo *TRI, MachineBasicBlock *MBB, Register Reg)
static cl::opt< int > PageSize("imp-null-check-page-size", cl::desc("The page size of the target in bytes"), cl::init(4096), cl::Hidden)
static cl::opt< unsigned > MaxInstsToConsider("imp-null-max-insts-to-consider", cl::desc("The max number of instructions to consider hoisting loads over " "(the algorithm is quadratic over this number)"), cl::Hidden, cl::init(8))
Register const TargetRegisterInfo * TRI
This file provides utility analysis objects describing memory locations.
#define INITIALIZE_PASS_DEPENDENCY(depName)
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
This file defines the SmallVector class.
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
#define STATISTIC(VARNAME, DESC)
A wrapper pass to provide the legacy pass manager access to a suitably prepared AAResults object.
A private abstract base class describing the concept of an individual alias analysis implementation.
bool isNoAlias(const MemoryLocation &LocA, const MemoryLocation &LocB)
A trivial helper function to check to see if the specified pointers are no-alias.
Class for arbitrary precision integers.
LLVM_ABI APInt sadd_ov(const APInt &RHS, bool &Overflow) const
LLVM_ABI APInt smul_ov(const APInt &RHS, bool &Overflow) const
int64_t getSExtValue() const
Get sign extended value.
The possible results of an alias query.
Represent the analysis usage information of a pass.
AnalysisUsage & addRequired()
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Dependence - This class represents a dependence between two memory memory references in a function.
unsigned removeBranch(MachineBasicBlock &MBB, int *BytesRemoved=nullptr) const override
Remove the branching code at the end of the specific MBB.
unsigned insertBranch(MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB, ArrayRef< MachineOperand > Cond, const DebugLoc &DL, int *BytesAdded=nullptr) const override
Insert branch code into the end of the specified MachineBasicBlock.
MCRegAliasIterator enumerates all registers aliasing Reg.
unsigned pred_size() const
const BasicBlock * getBasicBlock() const
Return the LLVM basic block that this instance corresponded to originally.
void addLiveIn(MCRegister PhysReg, LaneBitmask LaneMask=LaneBitmask::getAll())
Adds the specified register as a live in.
reverse_iterator rbegin()
LLVM_ABI bool isLiveIn(MCRegister Reg, LaneBitmask LaneMask=LaneBitmask::getAll()) const
Return true if the specified register is in the live in set.
The MachineFrameInfo class represents an abstract stack frame until prolog/epilog code is inserted.
MachineFunctionPass - This class adapts the FunctionPass interface to allow convenient creation of pa...
void getAnalysisUsage(AnalysisUsage &AU) const override
getAnalysisUsage - Subclasses that override getAnalysisUsage must call this.
virtual bool runOnMachineFunction(MachineFunction &MF)=0
runOnMachineFunction - This method must be overloaded to perform the desired machine code transformat...
virtual MachineFunctionProperties getRequiredProperties() const
Properties which a MachineFunction may have at a given point in time.
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
MachineFrameInfo & getFrameInfo()
getFrameInfo - Return the frame info object for the current function.
MachineRegisterInfo & getRegInfo()
getRegInfo - Return information about the registers currently in use.
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
const MachineInstrBuilder & addMBB(MachineBasicBlock *MBB, unsigned TargetFlags=0) const
MachineBasicBlock iterator that automatically skips over MIs that are inside bundles (i....
Representation of each machine instruction.
bool mayLoadOrStore(QueryType Type=AnyInBundle) const
Return true if this instruction could possibly read or modify memory.
filtered_mop_range all_defs()
Returns an iterator range over all operands that are (explicit or implicit) register defs.
bool memoperands_empty() const
Return true if we don't have any memory operands which described the memory access done by this instr...
bool modifiesRegister(Register Reg, const TargetRegisterInfo *TRI) const
Return true if the MachineInstr modifies (fully define or partially define) the specified register.
bool mayLoad(QueryType Type=AnyInBundle) const
Return true if this instruction could possibly read memory.
ArrayRef< MachineMemOperand * > memoperands() const
Access to memory operands of the instruction.
bool mayStore(QueryType Type=AnyInBundle) const
Return true if this instruction could possibly modify memory.
A description of a memory reference used in the backend.
MachineOperand class - Representation of each machine instruction operand.
void setIsDead(bool Val=true)
void setIsKill(bool Val=true)
MachineRegisterInfo - Keep track of information for virtual and physical registers,...
const TargetRegisterInfo * getTargetRegisterInfo() const
static MemoryLocation getAfter(const Value *Ptr, const AAMDNodes &AATags=AAMDNodes())
Return a location that may access any location after Ptr, while remaining within the underlying objec...
static LLVM_ABI PassRegistry * getPassRegistry()
getPassRegistry - Access the global registry object, which is automatically initialized at applicatio...
Special value supplied for machine level alias analysis.
Wrapper class representing virtual and physical registers.
This class consists of common code factored out of the SmallVector class to reduce code duplication b...
reference emplace_back(ArgTypes &&... Args)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
TargetInstrInfo - Interface to description of machine instruction set.
TargetRegisterInfo base class - We assume that the target defines a static array of TargetRegisterDes...
virtual const TargetInstrInfo * getInstrInfo() const
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Reg
All possible values of the reg field in the ModR/M byte.
initializer< Ty > init(const Ty &Val)
BaseReg
Stack frame base register. Bit 0 of FREInfo.Info.
LLVM_ABI reverse_iterator rend(StringRef path LLVM_LIFETIME_BOUND)
Get reverse end iterator over path.
This is an optimization pass for GlobalISel generic memory operations.
bool all_of(R &&range, UnaryPredicate P)
Provide wrappers to std::all_of which take ranges instead of having to pass begin/end explicitly.
MachineInstrBuilder BuildMI(MachineFunction &MF, const MIMetadata &MIMD, const MCInstrDesc &MCID)
Builder interface. Specify how to create the initial instruction itself.
LLVM_ABI char & ImplicitNullChecksID
ImplicitNullChecks - This pass folds null pointer checks into nearby memory operations.
bool none_of(R &&Range, UnaryPredicate P)
Provide wrappers to std::none_of which take ranges instead of having to pass begin/end explicitly.
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
LLVM_ABI void initializeImplicitNullChecksPass(PassRegistry &)
Represents a predicate at the MachineFunction level.