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) {}
219 bool runOnMachineFunction(MachineFunction &MF)
override;
221 void getAnalysisUsage(AnalysisUsage &AU)
const override {
226 MachineFunctionProperties getRequiredProperties()
const override {
227 return MachineFunctionProperties().setNoVRegs();
234 if (
MI->isCall() ||
MI->mayRaiseFPException() ||
235 MI->hasUnmodeledSideEffects())
237 auto IsRegMask = [](
const MachineOperand &MO) {
return MO.isRegMask(); };
241 "Calls were filtered out above!");
243 auto IsUnordered = [](MachineMemOperand *MMO) {
return MMO->isUnordered(); };
247ImplicitNullChecks::DependenceResult
248ImplicitNullChecks::computeDependence(
const MachineInstr *
MI,
253 std::optional<ArrayRef<MachineInstr *>::iterator> Dep;
256 if (canReorder(*
I,
MI))
259 if (Dep == std::nullopt) {
264 return {
false, std::nullopt};
271bool ImplicitNullChecks::canReorder(
const MachineInstr *
A,
272 const MachineInstr *
B) {
273 assert(canHandle(
A) && canHandle(
B) &&
"Precondition!");
279 for (
const auto &MOA :
A->operands()) {
280 if (!(MOA.isReg() && MOA.getReg()))
284 for (
const auto &MOB :
B->operands()) {
285 if (!(MOB.isReg() && MOB.getReg()))
290 if (
TRI->regsOverlap(RegA, RegB) && (MOA.isDef() || MOB.isDef()))
298bool ImplicitNullChecks::runOnMachineFunction(MachineFunction &MF) {
302 AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
307 analyzeBlockForNullChecks(
MBB, NullCheckList);
309 if (!NullCheckList.
empty())
310 rewriteNullChecks(NullCheckList);
312 return !NullCheckList.
empty();
320 if (
MBB->isLiveIn(*AR))
325ImplicitNullChecks::AliasResult
326ImplicitNullChecks::areMemoryOpsAliased(
const MachineInstr &
MI,
327 const MachineInstr *PrevMI)
const {
336 if (
MI.memoperands_empty())
337 return MI.mayStore() ? AR_WillAliasEverything : AR_MayAlias;
339 return PrevMI->
mayStore() ? AR_WillAliasEverything : AR_MayAlias;
341 for (MachineMemOperand *MMO1 :
MI.memoperands()) {
344 assert(MMO1->getValue() &&
"MMO1 should have a Value!");
345 for (MachineMemOperand *MMO2 : PrevMI->
memoperands()) {
346 if (
const PseudoSourceValue *PSV = MMO2->getPseudoValue()) {
347 if (PSV->mayAlias(MFI))
360ImplicitNullChecks::SuitabilityResult
361ImplicitNullChecks::isSuitableMemoryOp(
const MachineInstr &
MI,
366 if (
MI.getDesc().getNumDefs() > 1)
367 return SR_Unsuitable;
369 if (!
MI.mayLoadOrStore() ||
MI.isPredicable())
370 return SR_Unsuitable;
371 auto AM =
TII->getAddrModeFromMemoryOp(
MI,
TRI);
372 if (!AM || AM->Form != ExtAddrMode::Formula::Basic)
373 return SR_Unsuitable;
376 int64_t Displacement =
AddrMode.Displacement;
380 if (BaseReg != PointerReg && ScaledReg != PointerReg)
381 return SR_Unsuitable;
382 const MachineRegisterInfo &
MRI =
MI.getMF()->getRegInfo();
383 unsigned PointerRegSizeInBits =
TRI->getRegSizeInBits(PointerReg,
MRI);
387 TRI->getRegSizeInBits(BaseReg,
MRI) != PointerRegSizeInBits) ||
389 TRI->getRegSizeInBits(ScaledReg,
MRI) != PointerRegSizeInBits))
390 return SR_Unsuitable;
394 auto CalculateDisplacementFromAddrMode = [&](
Register RegUsedInAddr,
395 int64_t Multiplier) {
401 assert(Multiplier &&
"expected to be non-zero!");
402 MachineInstr *ModifyingMI =
nullptr;
404 It !=
MI.getParent()->
rend(); It++) {
405 const MachineInstr *CurrMI = &*It;
407 ModifyingMI =
const_cast<MachineInstr *
>(CurrMI);
416 if (!
TII->getConstValDefinedInReg(*ModifyingMI, RegUsedInAddr, ImmVal))
423 assert(MultiplierC.isStrictlyPositive() &&
424 "expected to be a positive value!");
428 APInt Product = ImmValC.smul_ov(MultiplierC, IsOverflow);
431 APInt DisplacementC(64, Displacement,
true );
432 DisplacementC = Product.
sadd_ov(DisplacementC, IsOverflow);
437 if (DisplacementC.getActiveBits() > 64)
439 Displacement = DisplacementC.getSExtValue();
445 bool BaseRegIsConstVal =
false, ScaledRegIsConstVal =
false;
446 if (CalculateDisplacementFromAddrMode(BaseReg, 1))
447 BaseRegIsConstVal =
true;
448 if (CalculateDisplacementFromAddrMode(ScaledReg,
AddrMode.Scale))
449 ScaledRegIsConstVal =
true;
456 if ((BaseReg && BaseReg != PointerReg && !BaseRegIsConstVal) ||
457 (ScaledReg && ScaledReg != PointerReg && !ScaledRegIsConstVal))
458 return SR_Unsuitable;
463 return SR_Unsuitable;
466 for (
auto *PrevMI : PrevInsts) {
467 AliasResult AR = areMemoryOpsAliased(
MI, PrevMI);
468 if (AR == AR_WillAliasEverything)
469 return SR_Impossible;
470 if (AR == AR_MayAlias)
471 return SR_Unsuitable;
476bool ImplicitNullChecks::canDependenceHoistingClobberLiveIns(
477 MachineInstr *DependenceMI, MachineBasicBlock *NullSucc) {
478 for (
const auto &DependenceMO : DependenceMI->
operands()) {
479 if (!(DependenceMO.isReg() && DependenceMO.getReg()))
508bool ImplicitNullChecks::canHoistInst(MachineInstr *FaultingMI,
510 MachineBasicBlock *NullSucc,
511 MachineInstr *&Dependence) {
512 auto DepResult = computeDependence(FaultingMI, InstsSeenSoFar);
513 if (!DepResult.CanReorder)
516 if (!DepResult.PotentialDependence) {
517 Dependence =
nullptr;
521 auto DependenceItr = *DepResult.PotentialDependence;
522 auto *DependenceMI = *DependenceItr;
529 assert(canHandle(DependenceMI) &&
"Should never have reached here!");
533 if (canDependenceHoistingClobberLiveIns(DependenceMI, NullSucc))
537 computeDependence(DependenceMI, {InstsSeenSoFar.
begin(), DependenceItr});
539 if (!DepDepResult.CanReorder || DepDepResult.PotentialDependence)
542 Dependence = DependenceMI;
549bool ImplicitNullChecks::analyzeBlockForNullChecks(
550 MachineBasicBlock &
MBB, SmallVectorImpl<NullCheck> &NullCheckList) {
551 using MachineBranchPredicate = TargetInstrInfo::MachineBranchPredicate;
553 MDNode *BranchMD =
nullptr;
555 BranchMD = BB->getTerminator()->getMetadata(LLVMContext::MD_make_implicit);
560 MachineBranchPredicate MBP;
562 if (
TII->analyzeBranchPredicate(
MBB, MBP,
true))
566 if (!(MBP.LHS.isReg() && MBP.RHS.isImm() && MBP.RHS.getImm() == 0 &&
567 (MBP.Predicate == MachineBranchPredicate::PRED_NE ||
568 MBP.Predicate == MachineBranchPredicate::PRED_EQ)))
573 if (MBP.ConditionDef && !MBP.SingleUseCondition)
576 MachineBasicBlock *NotNullSucc, *NullSucc;
578 if (MBP.Predicate == MachineBranchPredicate::PRED_NE) {
579 NotNullSucc = MBP.TrueDest;
580 NullSucc = MBP.FalseDest;
582 NotNullSucc = MBP.FalseDest;
583 NullSucc = MBP.TrueDest;
591 const Register PointerReg = MBP.LHS.getReg();
593 if (MBP.ConditionDef) {
612 assert(MBP.ConditionDef->getParent() == &
MBB &&
613 "Should be in basic block");
615 for (
auto I =
MBB.
rbegin(); MBP.ConditionDef != &*
I; ++
I)
616 if (
I->modifiesRegister(PointerReg,
TRI))
673 SmallVector<MachineInstr *, 8> InstsSeenSoFar;
675 for (
auto &
MI : *NotNullSucc) {
679 MachineInstr *Dependence;
680 SuitabilityResult SR = isSuitableMemoryOp(
MI, PointerReg, InstsSeenSoFar);
681 if (SR == SR_Impossible)
683 if (SR == SR_Suitable &&
684 canHoistInst(&
MI, InstsSeenSoFar, NullSucc, Dependence)) {
686 NullSucc, Dependence);
692 if (!
TII->preservesZeroValueInReg(&
MI, PointerReg,
TRI))
704MachineInstr *ImplicitNullChecks::insertFaultingInstr(
705 MachineInstr *
MI, MachineBasicBlock *
MBB, MachineBasicBlock *HandlerMBB) {
707 unsigned NumDefs =
MI->getDesc().getNumDefs();
708 assert(NumDefs <= 1 &&
"other cases unhandled!");
712 DefReg =
MI->getOperand(0).getReg();
713 assert(NumDefs == 1 &&
"expected exactly one def!");
723 auto MIB =
BuildMI(
MBB,
DL,
TII->get(TargetOpcode::FAULTING_OP), DefReg)
728 for (
auto &MO :
MI->uses()) {
730 MachineOperand NewMO = MO;
734 assert(MO.isDef() &&
"Expected def or use");
749void ImplicitNullChecks::rewriteNullChecks(
753 for (
const auto &
NC : NullCheckList) {
756 (void)BranchesRemoved;
757 assert(BranchesRemoved > 0 &&
"expected at least one branch!");
759 if (
auto *DepMI =
NC.getOnlyDependency()) {
760 DepMI->removeFromParent();
761 NC.getCheckBlock()->insert(
NC.getCheckBlock()->end(), DepMI);
768 MachineInstr *FaultingInstr = insertFaultingInstr(
769 NC.getMemOperation(),
NC.getCheckBlock(),
NC.getNullSucc());
775 for (
const MachineOperand &MO : FaultingInstr->
all_defs()) {
782 if (
auto *DepMI =
NC.getOnlyDependency()) {
783 for (
auto &MO : DepMI->all_defs()) {
784 if (!MO.getReg() || MO.isDead())
786 if (!
NC.getNotNullSucc()->isLiveIn(MO.getReg()))
787 NC.getNotNullSucc()->addLiveIn(MO.getReg());
791 NC.getMemOperation()->eraseFromParent();
792 if (
auto *CheckOp =
NC.getCheckOperation())
793 CheckOp->eraseFromParent();
800 NumImplicitNullChecks++;
804char ImplicitNullChecks::ID = 0;
809 "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...
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.
AAResults AliasAnalysis
Temporary typedef for legacy code that uses a generic AliasAnalysis pointer or reference.