64 cl::desc(
"The page size of the target in bytes"),
68 "imp-null-max-insts-to-consider",
69 cl::desc(
"The max number of instructions to consider hoisting loads over "
70 "(the algorithm is quadratic over this number)"),
73#define DEBUG_TYPE "implicit-null-checks"
76 "Number of explicit null checks made implicit");
80class ImplicitNullChecksImpl {
93 struct DependenceResult {
100 std::optional<ArrayRef<MachineInstr *>::iterator> PotentialDependence;
105 : CanReorder(CanReorder), PotentialDependence(PotentialDependence) {
106 assert((!PotentialDependence || CanReorder) &&
107 "!CanReorder && PotentialDependence.hasValue() not allowed!");
116 DependenceResult computeDependence(
const MachineInstr *
MI,
122 MachineInstr *MemOperation;
125 MachineInstr *CheckOperation;
128 MachineBasicBlock *CheckBlock;
131 MachineBasicBlock *NotNullSucc;
134 MachineBasicBlock *NullSucc;
138 MachineInstr *OnlyDependency;
141 explicit NullCheck(MachineInstr *memOperation, MachineInstr *checkOperation,
142 MachineBasicBlock *checkBlock,
143 MachineBasicBlock *notNullSucc,
144 MachineBasicBlock *nullSucc,
145 MachineInstr *onlyDependency)
146 : MemOperation(memOperation), CheckOperation(checkOperation),
147 CheckBlock(checkBlock), NotNullSucc(notNullSucc), NullSucc(nullSucc),
148 OnlyDependency(onlyDependency) {}
150 MachineInstr *getMemOperation()
const {
return MemOperation; }
152 MachineInstr *getCheckOperation()
const {
return CheckOperation; }
154 MachineBasicBlock *getCheckBlock()
const {
return CheckBlock; }
156 MachineBasicBlock *getNotNullSucc()
const {
return NotNullSucc; }
158 MachineBasicBlock *getNullSucc()
const {
return NullSucc; }
160 MachineInstr *getOnlyDependency()
const {
return OnlyDependency; }
163 const TargetInstrInfo *TII =
nullptr;
164 const TargetRegisterInfo *TRI =
nullptr;
166 MachineFrameInfo *MFI =
nullptr;
168 bool analyzeBlockForNullChecks(MachineBasicBlock &
MBB,
169 SmallVectorImpl<NullCheck> &NullCheckList);
170 MachineInstr *insertFaultingInstr(MachineInstr *
MI, MachineBasicBlock *
MBB,
171 MachineBasicBlock *HandlerMBB);
177 AR_WillAliasEverything
183 AliasResult areMemoryOpsAliased(
const MachineInstr &
MI,
184 const MachineInstr *PrevMI)
const;
186 enum SuitabilityResult {
198 SuitabilityResult isSuitableMemoryOp(
const MachineInstr &
MI,
205 bool canDependenceHoistingClobberLiveIns(MachineInstr *DependenceMI,
206 MachineBasicBlock *NullSucc);
211 bool canHoistInst(MachineInstr *FaultingMI,
213 MachineBasicBlock *NullSucc, MachineInstr *&Dependence);
217 : TII(MF.getSubtarget().getInstrInfo()),
218 TRI(MF.getRegInfo().getTargetRegisterInfo()), AA(AA),
219 MFI(&MF.getFrameInfo()) {}
228 ImplicitNullChecksLegacy() : MachineFunctionPass(ID) {}
233 auto *AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
234 return ImplicitNullChecksImpl(MF, AA).run(MF);
237 void getAnalysisUsage(AnalysisUsage &AU)
const override {
242 MachineFunctionProperties getRequiredProperties()
const override {
243 return MachineFunctionProperties().setNoVRegs();
250 if (
MI->isCall() ||
MI->mayRaiseFPException() ||
251 MI->hasUnmodeledSideEffects())
253 auto IsRegMask = [](
const MachineOperand &MO) {
return MO.isRegMask(); };
257 "Calls were filtered out above!");
259 auto IsUnordered = [](MachineMemOperand *MMO) {
return MMO->isUnordered(); };
263ImplicitNullChecksImpl::DependenceResult
264ImplicitNullChecksImpl::computeDependence(
const MachineInstr *
MI,
269 std::optional<ArrayRef<MachineInstr *>::iterator> Dep;
272 if (canReorder(*
I,
MI))
275 if (Dep == std::nullopt) {
280 return {
false, std::nullopt};
287bool ImplicitNullChecksImpl::canReorder(
const MachineInstr *
A,
288 const MachineInstr *
B) {
289 assert(canHandle(
A) && canHandle(
B) &&
"Precondition!");
295 for (
const auto &MOA :
A->operands()) {
296 if (!(MOA.isReg() && MOA.getReg()))
300 for (
const auto &MOB :
B->operands()) {
301 if (!(MOB.isReg() && MOB.getReg()))
306 if (
TRI->regsOverlap(RegA, RegB) && (MOA.isDef() || MOB.isDef()))
319 analyzeBlockForNullChecks(
MBB, NullCheckList);
321 if (!NullCheckList.
empty())
322 rewriteNullChecks(NullCheckList);
324 return !NullCheckList.
empty();
332 if (
MBB->isLiveIn(*AR))
337ImplicitNullChecksImpl::AliasResult
338ImplicitNullChecksImpl::areMemoryOpsAliased(
const MachineInstr &
MI,
339 const MachineInstr *PrevMI)
const {
348 if (
MI.memoperands_empty())
349 return MI.mayStore() ? AR_WillAliasEverything : AR_MayAlias;
351 return PrevMI->
mayStore() ? AR_WillAliasEverything : AR_MayAlias;
353 for (MachineMemOperand *MMO1 :
MI.memoperands()) {
356 assert(MMO1->getValue() &&
"MMO1 should have a Value!");
357 for (MachineMemOperand *MMO2 : PrevMI->
memoperands()) {
358 if (
const PseudoSourceValue *PSV = MMO2->getPseudoValue()) {
359 if (PSV->mayAlias(MFI))
372ImplicitNullChecksImpl::SuitabilityResult
373ImplicitNullChecksImpl::isSuitableMemoryOp(
const MachineInstr &
MI,
378 if (
MI.getDesc().getNumDefs() > 1)
379 return SR_Unsuitable;
381 if (!
MI.mayLoadOrStore() ||
MI.isPredicable())
382 return SR_Unsuitable;
383 auto AM =
TII->getAddrModeFromMemoryOp(
MI,
TRI);
384 if (!AM || AM->Form != ExtAddrMode::Formula::Basic)
385 return SR_Unsuitable;
388 int64_t Displacement =
AddrMode.Displacement;
392 if (BaseReg != PointerReg && ScaledReg != PointerReg)
393 return SR_Unsuitable;
394 const MachineRegisterInfo &MRI =
MI.getMF()->getRegInfo();
395 unsigned PointerRegSizeInBits =
TRI->getRegSizeInBits(PointerReg, MRI);
399 TRI->getRegSizeInBits(BaseReg, MRI) != PointerRegSizeInBits) ||
401 TRI->getRegSizeInBits(ScaledReg, MRI) != PointerRegSizeInBits))
402 return SR_Unsuitable;
406 auto CalculateDisplacementFromAddrMode = [&](
Register RegUsedInAddr,
407 int64_t Multiplier) {
413 assert(Multiplier &&
"expected to be non-zero!");
414 MachineInstr *ModifyingMI =
nullptr;
416 It !=
MI.getParent()->
rend(); It++) {
417 const MachineInstr *CurrMI = &*It;
419 ModifyingMI =
const_cast<MachineInstr *
>(CurrMI);
428 if (!
TII->getConstValDefinedInReg(*ModifyingMI, RegUsedInAddr, ImmVal))
435 assert(MultiplierC.isStrictlyPositive() &&
436 "expected to be a positive value!");
440 APInt Product = ImmValC.smul_ov(MultiplierC, IsOverflow);
443 APInt DisplacementC(64, Displacement,
true );
444 DisplacementC = Product.
sadd_ov(DisplacementC, IsOverflow);
449 if (DisplacementC.getActiveBits() > 64)
451 Displacement = DisplacementC.getSExtValue();
457 bool BaseRegIsConstVal =
false, ScaledRegIsConstVal =
false;
458 if (CalculateDisplacementFromAddrMode(BaseReg, 1))
459 BaseRegIsConstVal =
true;
460 if (CalculateDisplacementFromAddrMode(ScaledReg,
AddrMode.Scale))
461 ScaledRegIsConstVal =
true;
468 if ((BaseReg && BaseReg != PointerReg && !BaseRegIsConstVal) ||
469 (ScaledReg && ScaledReg != PointerReg && !ScaledRegIsConstVal))
470 return SR_Unsuitable;
475 return SR_Unsuitable;
478 for (
auto *PrevMI : PrevInsts) {
479 AliasResult AR = areMemoryOpsAliased(
MI, PrevMI);
480 if (AR == AR_WillAliasEverything)
481 return SR_Impossible;
482 if (AR == AR_MayAlias)
483 return SR_Unsuitable;
488bool ImplicitNullChecksImpl::canDependenceHoistingClobberLiveIns(
489 MachineInstr *DependenceMI, MachineBasicBlock *NullSucc) {
490 for (
const auto &DependenceMO : DependenceMI->
operands()) {
491 if (!(DependenceMO.isReg() && DependenceMO.getReg()))
520bool ImplicitNullChecksImpl::canHoistInst(
522 MachineBasicBlock *NullSucc, MachineInstr *&Dependence) {
523 auto DepResult = computeDependence(FaultingMI, InstsSeenSoFar);
524 if (!DepResult.CanReorder)
527 if (!DepResult.PotentialDependence) {
528 Dependence =
nullptr;
532 auto DependenceItr = *DepResult.PotentialDependence;
533 auto *DependenceMI = *DependenceItr;
540 assert(canHandle(DependenceMI) &&
"Should never have reached here!");
544 if (canDependenceHoistingClobberLiveIns(DependenceMI, NullSucc))
548 computeDependence(DependenceMI, {InstsSeenSoFar.
begin(), DependenceItr});
550 if (!DepDepResult.CanReorder || DepDepResult.PotentialDependence)
553 Dependence = DependenceMI;
560bool ImplicitNullChecksImpl::analyzeBlockForNullChecks(
561 MachineBasicBlock &
MBB, SmallVectorImpl<NullCheck> &NullCheckList) {
562 using MachineBranchPredicate = TargetInstrInfo::MachineBranchPredicate;
564 MDNode *BranchMD =
nullptr;
566 BranchMD = BB->getTerminator()->getMetadata(LLVMContext::MD_make_implicit);
571 MachineBranchPredicate MBP;
573 if (
TII->analyzeBranchPredicate(
MBB, MBP,
true))
577 if (!(MBP.LHS.isReg() && MBP.RHS.isImm() && MBP.RHS.getImm() == 0 &&
578 (MBP.Predicate == MachineBranchPredicate::PRED_NE ||
579 MBP.Predicate == MachineBranchPredicate::PRED_EQ)))
584 if (MBP.ConditionDef && !MBP.SingleUseCondition)
587 MachineBasicBlock *NotNullSucc, *NullSucc;
589 if (MBP.Predicate == MachineBranchPredicate::PRED_NE) {
590 NotNullSucc = MBP.TrueDest;
591 NullSucc = MBP.FalseDest;
593 NotNullSucc = MBP.FalseDest;
594 NullSucc = MBP.TrueDest;
602 const Register PointerReg = MBP.LHS.getReg();
604 if (MBP.ConditionDef) {
623 assert(MBP.ConditionDef->getParent() == &
MBB &&
624 "Should be in basic block");
626 for (
auto I =
MBB.
rbegin(); MBP.ConditionDef != &*
I; ++
I)
627 if (
I->modifiesRegister(PointerReg,
TRI))
684 SmallVector<MachineInstr *, 8> InstsSeenSoFar;
686 for (
auto &
MI : *NotNullSucc) {
690 MachineInstr *Dependence;
691 SuitabilityResult SR = isSuitableMemoryOp(
MI, PointerReg, InstsSeenSoFar);
692 if (SR == SR_Impossible)
694 if (SR == SR_Suitable &&
695 canHoistInst(&
MI, InstsSeenSoFar, NullSucc, Dependence)) {
697 NullSucc, Dependence);
703 if (!
TII->preservesZeroValueInReg(&
MI, PointerReg,
TRI))
715MachineInstr *ImplicitNullChecksImpl::insertFaultingInstr(
716 MachineInstr *
MI, MachineBasicBlock *
MBB, MachineBasicBlock *HandlerMBB) {
717 unsigned NumDefs =
MI->getDesc().getNumDefs();
718 assert(NumDefs <= 1 &&
"other cases unhandled!");
722 DefReg =
MI->getOperand(0).getReg();
723 assert(NumDefs == 1 &&
"expected exactly one def!");
734 TII->get(TargetOpcode::FAULTING_OP), DefReg)
739 for (
auto &MO :
MI->uses()) {
741 MachineOperand NewMO = MO;
745 assert(MO.isDef() &&
"Expected def or use");
760void ImplicitNullChecksImpl::rewriteNullChecks(
764 for (
const auto &
NC : NullCheckList) {
767 (void)BranchesRemoved;
768 assert(BranchesRemoved > 0 &&
"expected at least one branch!");
770 if (
auto *DepMI =
NC.getOnlyDependency()) {
771 DepMI->removeFromParent();
772 NC.getCheckBlock()->insert(
NC.getCheckBlock()->end(), DepMI);
779 MachineInstr *FaultingInstr = insertFaultingInstr(
780 NC.getMemOperation(),
NC.getCheckBlock(),
NC.getNullSucc());
786 for (
const MachineOperand &MO : FaultingInstr->
all_defs()) {
793 if (
auto *DepMI =
NC.getOnlyDependency()) {
794 for (
auto &MO : DepMI->all_defs()) {
795 if (!MO.getReg() || MO.isDead())
797 if (!
NC.getNotNullSucc()->isLiveIn(MO.getReg()))
798 NC.getNotNullSucc()->addLiveIn(MO.getReg());
802 NC.getMemOperation()->eraseFromParent();
803 if (
auto *CheckOp =
NC.getCheckOperation())
804 CheckOp->eraseFromParent();
811 NumImplicitNullChecks++;
815char ImplicitNullChecksLegacy::ID = 0;
820 "Implicit null checks",
false,
false)
832 bool Changed = ImplicitNullChecksImpl(MF, &
AA).run(MF);
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.
FunctionAnalysisManager FAM
#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 manager for alias analyses.
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.
LLVM_ABI PreservedAnalyses run(MachineFunction &MF, MachineFunctionAnalysisManager &MFAM)
MCRegAliasIterator enumerates all registers aliasing Reg.
An RAII based helper class to modify MachineFunctionProperties when running pass.
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.
Function & getFunction()
Return the LLVM function that this machine code represents.
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)
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...
A set of analyses that are preserved following a run of a transformation pass.
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
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...
Abstract Attribute helper functions.
initializer< Ty > init(const Ty &Val)
DXILDebugInfoMap run(Module &M)
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.
AnalysisManager< MachineFunction > MachineFunctionAnalysisManager
LLVM_ABI PreservedAnalyses getMachineFunctionPassPreservedAnalyses()
Returns the minimum set of Analyses that all machine function passes must preserve.
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.
AAResults AliasAnalysis
Temporary typedef for legacy code that uses a generic AliasAnalysis pointer or reference.