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!");
115 DependenceResult computeDependence(
const MachineInstr *
MI,
121 MachineInstr *MemOperation;
124 MachineInstr *CheckOperation;
127 MachineBasicBlock *CheckBlock;
130 MachineBasicBlock *NotNullSucc;
133 MachineBasicBlock *NullSucc;
137 MachineInstr *OnlyDependency;
140 explicit NullCheck(MachineInstr *memOperation, MachineInstr *checkOperation,
141 MachineBasicBlock *checkBlock,
142 MachineBasicBlock *notNullSucc,
143 MachineBasicBlock *nullSucc,
144 MachineInstr *onlyDependency)
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; }
153 MachineBasicBlock *getCheckBlock()
const {
return CheckBlock; }
155 MachineBasicBlock *getNotNullSucc()
const {
return NotNullSucc; }
157 MachineBasicBlock *getNullSucc()
const {
return NullSucc; }
159 MachineInstr *getOnlyDependency()
const {
return OnlyDependency; }
162 const TargetInstrInfo *TII =
nullptr;
163 const TargetRegisterInfo *TRI =
nullptr;
165 MachineFrameInfo *MFI =
nullptr;
167 bool analyzeBlockForNullChecks(MachineBasicBlock &
MBB,
168 SmallVectorImpl<NullCheck> &NullCheckList);
169 MachineInstr *insertFaultingInstr(MachineInstr *
MI, MachineBasicBlock *
MBB,
170 MachineBasicBlock *HandlerMBB);
176 AR_WillAliasEverything
182 AliasResult areMemoryOpsAliased(
const MachineInstr &
MI,
183 const MachineInstr *PrevMI)
const;
185 enum SuitabilityResult {
197 SuitabilityResult isSuitableMemoryOp(
const MachineInstr &
MI,
204 bool canDependenceHoistingClobberLiveIns(MachineInstr *DependenceMI,
205 MachineBasicBlock *NullSucc);
210 bool canHoistInst(MachineInstr *FaultingMI,
212 MachineBasicBlock *NullSucc, MachineInstr *&Dependence);
217 ImplicitNullChecks() : MachineFunctionPass(ID) {
221 bool runOnMachineFunction(MachineFunction &MF)
override;
223 void getAnalysisUsage(AnalysisUsage &AU)
const override {
228 MachineFunctionProperties getRequiredProperties()
const override {
229 return MachineFunctionProperties().setNoVRegs();
236 if (
MI->isCall() ||
MI->mayRaiseFPException() ||
237 MI->hasUnmodeledSideEffects())
239 auto IsRegMask = [](
const MachineOperand &MO) {
return MO.isRegMask(); };
243 "Calls were filtered out above!");
245 auto IsUnordered = [](MachineMemOperand *MMO) {
return MMO->isUnordered(); };
249ImplicitNullChecks::DependenceResult
250ImplicitNullChecks::computeDependence(
const MachineInstr *
MI,
255 std::optional<ArrayRef<MachineInstr *>::iterator> Dep;
258 if (canReorder(*
I,
MI))
261 if (Dep == std::nullopt) {
266 return {
false, std::nullopt};
273bool ImplicitNullChecks::canReorder(
const MachineInstr *
A,
274 const MachineInstr *
B) {
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()))
300bool ImplicitNullChecks::runOnMachineFunction(MachineFunction &MF) {
304 AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
309 analyzeBlockForNullChecks(
MBB, NullCheckList);
311 if (!NullCheckList.
empty())
312 rewriteNullChecks(NullCheckList);
314 return !NullCheckList.
empty();
322 if (
MBB->isLiveIn(*AR))
327ImplicitNullChecks::AliasResult
328ImplicitNullChecks::areMemoryOpsAliased(
const MachineInstr &
MI,
329 const MachineInstr *PrevMI)
const {
338 if (
MI.memoperands_empty())
339 return MI.mayStore() ? AR_WillAliasEverything : AR_MayAlias;
341 return PrevMI->
mayStore() ? AR_WillAliasEverything : AR_MayAlias;
343 for (MachineMemOperand *MMO1 :
MI.memoperands()) {
346 assert(MMO1->getValue() &&
"MMO1 should have a Value!");
347 for (MachineMemOperand *MMO2 : PrevMI->
memoperands()) {
348 if (
const PseudoSourceValue *PSV = MMO2->getPseudoValue()) {
349 if (PSV->mayAlias(MFI))
362ImplicitNullChecks::SuitabilityResult
363ImplicitNullChecks::isSuitableMemoryOp(
const MachineInstr &
MI,
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;
384 const MachineRegisterInfo &
MRI =
MI.getMF()->getRegInfo();
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!");
404 MachineInstr *ModifyingMI =
nullptr;
406 It !=
MI.getParent()->
rend(); It++) {
407 const MachineInstr *CurrMI = &*It;
409 ModifyingMI =
const_cast<MachineInstr *
>(CurrMI);
418 if (!
TII->getConstValDefinedInReg(*ModifyingMI, RegUsedInAddr, ImmVal))
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)
441 Displacement = DisplacementC.getSExtValue();
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) {
469 AliasResult AR = areMemoryOpsAliased(
MI, PrevMI);
470 if (AR == AR_WillAliasEverything)
471 return SR_Impossible;
472 if (AR == AR_MayAlias)
473 return SR_Unsuitable;
478bool ImplicitNullChecks::canDependenceHoistingClobberLiveIns(
479 MachineInstr *DependenceMI, MachineBasicBlock *NullSucc) {
480 for (
const auto &DependenceMO : DependenceMI->
operands()) {
481 if (!(DependenceMO.isReg() && DependenceMO.getReg()))
510bool ImplicitNullChecks::canHoistInst(MachineInstr *FaultingMI,
512 MachineBasicBlock *NullSucc,
513 MachineInstr *&Dependence) {
514 auto DepResult = computeDependence(FaultingMI, InstsSeenSoFar);
515 if (!DepResult.CanReorder)
518 if (!DepResult.PotentialDependence) {
519 Dependence =
nullptr;
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)
544 Dependence = DependenceMI;
551bool ImplicitNullChecks::analyzeBlockForNullChecks(
552 MachineBasicBlock &
MBB, SmallVectorImpl<NullCheck> &NullCheckList) {
553 using MachineBranchPredicate = TargetInstrInfo::MachineBranchPredicate;
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)
578 MachineBasicBlock *NotNullSucc, *NullSucc;
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))
675 SmallVector<MachineInstr *, 8> InstsSeenSoFar;
677 for (
auto &
MI : *NotNullSucc) {
681 MachineInstr *Dependence;
682 SuitabilityResult SR = isSuitableMemoryOp(
MI, PointerReg, InstsSeenSoFar);
683 if (SR == SR_Impossible)
685 if (SR == SR_Suitable &&
686 canHoistInst(&
MI, InstsSeenSoFar, NullSucc, Dependence)) {
688 NullSucc, Dependence);
694 if (!
TII->preservesZeroValueInReg(&
MI, PointerReg,
TRI))
706MachineInstr *ImplicitNullChecks::insertFaultingInstr(
707 MachineInstr *
MI, MachineBasicBlock *
MBB, MachineBasicBlock *HandlerMBB) {
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()) {
732 MachineOperand NewMO = MO;
736 assert(MO.isDef() &&
"Expected def or use");
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);
770 MachineInstr *FaultingInstr = insertFaultingInstr(
771 NC.getMemOperation(),
NC.getCheckBlock(),
NC.getNullSucc());
777 for (
const MachineOperand &MO : FaultingInstr->
all_defs()) {
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< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
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
Promote Memory to Register
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)
uint16_t RegSizeInBits(const MCRegisterInfo &MRI, MCRegister RegNo)
A wrapper pass to provide the legacy pass manager access to a suitably prepared AAResults object.
bool isNoAlias(const MemoryLocation &LocA, const MemoryLocation &LocB)
A trivial helper function to check to see if the specified pointers are no-alias.
LLVM_ABI APInt sadd_ov(const APInt &RHS, bool &Overflow) const
AnalysisUsage & addRequired()
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.
const MachineFunction * getParent() const
Return the MachineFunction containing this basic block.
reverse_iterator rbegin()
MachineInstrBundleIterator< const MachineInstr, true > const_reverse_iterator
LLVM_ABI bool isLiveIn(MCRegister Reg, LaneBitmask LaneMask=LaneBitmask::getAll()) const
Return true if the specified register is in the live in set.
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.
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 & setMemRefs(ArrayRef< MachineMemOperand * > MMOs) const
const MachineInstrBuilder & addImm(int64_t Val) const
Add a new immediate operand.
const MachineInstrBuilder & addMBB(MachineBasicBlock *MBB, unsigned TargetFlags=0) const
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.
void setIsDead(bool Val=true)
void setIsKill(bool Val=true)
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...
Wrapper class representing virtual and physical registers.
reference emplace_back(ArgTypes &&... Args)
void push_back(const T &Elt)
TargetRegisterInfo base class - We assume that the target defines a static array of TargetRegisterDes...
virtual const TargetInstrInfo * getInstrInfo() const
initializer< Ty > init(const Ty &Val)
std::reverse_iterator< iterator > rend() const
BaseReg
Stack frame base register. Bit 0 of FREInfo.Info.
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.
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
ArrayRef(const T &OneElt) -> ArrayRef< T >
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
LLVM_ABI void initializeImplicitNullChecksPass(PassRegistry &)
AAResults AliasAnalysis
Temporary typedef for legacy code that uses a generic AliasAnalysis pointer or reference.