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,
230 MachineFunctionProperties::Property::NoVRegs);
237 if (
MI->isCall() ||
MI->mayRaiseFPException() ||
238 MI->hasUnmodeledSideEffects())
240 auto IsRegMask = [](
const MachineOperand &MO) {
return MO.isRegMask(); };
244 "Calls were filtered out above!");
250ImplicitNullChecks::DependenceResult
256 std::optional<ArrayRef<MachineInstr *>::iterator> Dep;
259 if (canReorder(*
I,
MI))
262 if (Dep == std::nullopt) {
267 return {
false, std::nullopt};
276 assert(canHandle(
A) && canHandle(
B) &&
"Precondition!");
282 for (
const auto &MOA :
A->operands()) {
283 if (!(MOA.isReg() && MOA.getReg()))
287 for (
const auto &MOB :
B->operands()) {
288 if (!(MOB.isReg() && MOB.getReg()))
293 if (
TRI->regsOverlap(RegA, RegB) && (MOA.isDef() || MOB.isDef()))
305 AA = &getAnalysis<AAResultsWrapperPass>().getAAResults();
310 analyzeBlockForNullChecks(
MBB, NullCheckList);
312 if (!NullCheckList.
empty())
313 rewriteNullChecks(NullCheckList);
315 return !NullCheckList.
empty();
328ImplicitNullChecks::AliasResult
339 if (
MI.memoperands_empty())
340 return MI.mayStore() ? AR_WillAliasEverything : AR_MayAlias;
342 return PrevMI->
mayStore() ? AR_WillAliasEverything : AR_MayAlias;
347 assert(MMO1->getValue() &&
"MMO1 should have a Value!");
350 if (PSV->mayAlias(MFI))
363ImplicitNullChecks::SuitabilityResult
369 if (
MI.getDesc().getNumDefs() > 1)
370 return SR_Unsuitable;
372 if (!
MI.mayLoadOrStore() ||
MI.isPredicable())
373 return SR_Unsuitable;
374 auto AM =
TII->getAddrModeFromMemoryOp(
MI,
TRI);
375 if (!AM || AM->Form != ExtAddrMode::Formula::Basic)
376 return SR_Unsuitable;
379 int64_t Displacement =
AddrMode.Displacement;
383 if (BaseReg != PointerReg && ScaledReg != PointerReg)
384 return SR_Unsuitable;
386 unsigned PointerRegSizeInBits =
TRI->getRegSizeInBits(PointerReg,
MRI);
390 TRI->getRegSizeInBits(BaseReg,
MRI) != PointerRegSizeInBits) ||
392 TRI->getRegSizeInBits(ScaledReg,
MRI) != PointerRegSizeInBits))
393 return SR_Unsuitable;
397 auto CalculateDisplacementFromAddrMode = [&](
Register RegUsedInAddr,
398 int64_t Multiplier) {
404 assert(Multiplier &&
"expected to be non-zero!");
407 It !=
MI.getParent()->
rend(); It++) {
419 if (!
TII->getConstValDefinedInReg(*ModifyingMI, RegUsedInAddr, ImmVal))
423 int32_t RegSizeInBits =
TRI->getRegSizeInBits(RegUsedInAddr,
MRI);
424 APInt ImmValC(RegSizeInBits, ImmVal,
true );
425 APInt MultiplierC(RegSizeInBits, Multiplier);
426 assert(MultiplierC.isStrictlyPositive() &&
427 "expected to be a positive value!");
431 APInt Product = ImmValC.
smul_ov(MultiplierC, IsOverflow);
434 APInt DisplacementC(64, Displacement,
true );
435 DisplacementC = Product.
sadd_ov(DisplacementC, IsOverflow);
440 if (DisplacementC.getActiveBits() > 64)
448 bool BaseRegIsConstVal =
false, ScaledRegIsConstVal =
false;
449 if (CalculateDisplacementFromAddrMode(BaseReg, 1))
450 BaseRegIsConstVal =
true;
451 if (CalculateDisplacementFromAddrMode(ScaledReg,
AddrMode.Scale))
452 ScaledRegIsConstVal =
true;
459 if ((BaseReg && BaseReg != PointerReg && !BaseRegIsConstVal) ||
460 (ScaledReg && ScaledReg != PointerReg && !ScaledRegIsConstVal))
461 return SR_Unsuitable;
466 return SR_Unsuitable;
469 for (
auto *PrevMI : PrevInsts) {
471 if (AR == AR_WillAliasEverything)
472 return SR_Impossible;
473 if (AR == AR_MayAlias)
474 return SR_Unsuitable;
479bool ImplicitNullChecks::canDependenceHoistingClobberLiveIns(
481 for (
const auto &DependenceMO : DependenceMI->
operands()) {
482 if (!(DependenceMO.isReg() && DependenceMO.getReg()))
511bool ImplicitNullChecks::canHoistInst(
MachineInstr *FaultingMI,
515 auto DepResult = computeDependence(FaultingMI, InstsSeenSoFar);
516 if (!DepResult.CanReorder)
519 if (!DepResult.PotentialDependence) {
524 auto DependenceItr = *DepResult.PotentialDependence;
525 auto *DependenceMI = *DependenceItr;
532 assert(canHandle(DependenceMI) &&
"Should never have reached here!");
536 if (canDependenceHoistingClobberLiveIns(DependenceMI, NullSucc))
540 computeDependence(DependenceMI, {InstsSeenSoFar.
begin(), DependenceItr});
542 if (!DepDepResult.CanReorder || DepDepResult.PotentialDependence)
552bool ImplicitNullChecks::analyzeBlockForNullChecks(
556 MDNode *BranchMD =
nullptr;
558 BranchMD = BB->getTerminator()->getMetadata(LLVMContext::MD_make_implicit);
563 MachineBranchPredicate MBP;
565 if (
TII->analyzeBranchPredicate(
MBB, MBP,
true))
569 if (!(MBP.LHS.isReg() && MBP.RHS.isImm() && MBP.RHS.getImm() == 0 &&
570 (MBP.Predicate == MachineBranchPredicate::PRED_NE ||
571 MBP.Predicate == MachineBranchPredicate::PRED_EQ)))
576 if (MBP.ConditionDef && !MBP.SingleUseCondition)
581 if (MBP.Predicate == MachineBranchPredicate::PRED_NE) {
582 NotNullSucc = MBP.TrueDest;
583 NullSucc = MBP.FalseDest;
585 NotNullSucc = MBP.FalseDest;
586 NullSucc = MBP.TrueDest;
594 const Register PointerReg = MBP.LHS.getReg();
596 if (MBP.ConditionDef) {
615 assert(MBP.ConditionDef->getParent() == &
MBB &&
616 "Should be in basic block");
618 for (
auto I =
MBB.
rbegin(); MBP.ConditionDef != &*
I; ++
I)
619 if (
I->modifiesRegister(PointerReg,
TRI))
678 for (
auto &
MI : *NotNullSucc) {
683 SuitabilityResult SR = isSuitableMemoryOp(
MI, PointerReg, InstsSeenSoFar);
684 if (SR == SR_Impossible)
686 if (SR == SR_Suitable &&
687 canHoistInst(&
MI, InstsSeenSoFar, NullSucc,
Dependence)) {
695 if (!
TII->preservesZeroValueInReg(&
MI, PointerReg,
TRI))
709 const unsigned NoRegister = 0;
713 unsigned NumDefs =
MI->getDesc().getNumDefs();
714 assert(NumDefs <= 1 &&
"other cases unhandled!");
716 unsigned DefReg = NoRegister;
718 DefReg =
MI->getOperand(0).getReg();
719 assert(NumDefs == 1 &&
"expected exactly one def!");
729 auto MIB =
BuildMI(
MBB,
DL,
TII->get(TargetOpcode::FAULTING_OP), DefReg)
734 for (
auto &MO :
MI->uses()) {
740 assert(MO.isDef() &&
"Expected def or use");
749 MIB.setMemRefs(
MI->memoperands());
755void ImplicitNullChecks::rewriteNullChecks(
759 for (
const auto &
NC : NullCheckList) {
762 (void)BranchesRemoved;
763 assert(BranchesRemoved > 0 &&
"expected at least one branch!");
765 if (
auto *DepMI =
NC.getOnlyDependency()) {
766 DepMI->removeFromParent();
767 NC.getCheckBlock()->insert(
NC.getCheckBlock()->end(), DepMI);
775 NC.getMemOperation(),
NC.getCheckBlock(),
NC.getNullSucc());
788 if (
auto *DepMI =
NC.getOnlyDependency()) {
789 for (
auto &MO : DepMI->all_defs()) {
790 if (!MO.getReg() || MO.isDead())
792 if (!
NC.getNotNullSucc()->isLiveIn(MO.getReg()))
793 NC.getNotNullSucc()->addLiveIn(MO.getReg());
797 NC.getMemOperation()->eraseFromParent();
798 if (
auto *CheckOp =
NC.getCheckOperation())
799 CheckOp->eraseFromParent();
806 NumImplicitNullChecks++;
810char ImplicitNullChecks::ID = 0;
815 "Implicit null checks",
false,
false)
unsigned const MachineRegisterInfo * MRI
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, unsigned 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))
unsigned 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)
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
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.
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.
APInt sadd_ov(const APInt &RHS, bool &Overflow) const
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.
bool isLiveIn(MCPhysReg Reg, LaneBitmask LaneMask=LaneBitmask::getAll()) const
Return true if the specified register is in the live in set.
void addLiveIn(MCRegister PhysReg, LaneBitmask LaneMask=LaneBitmask::getAll())
Adds the specified register as a live in.
reverse_iterator rbegin()
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.
MachineFunctionProperties & set(Property P)
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.
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.
iterator_range< mop_iterator > operands()
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.
iterator_range< filtered_mop_iterator > all_defs()
Returns an iterator range over all operands that are (explicit or implicit) register defs.
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 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)
reverse_iterator rend(StringRef path)
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.
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.
void initializeImplicitNullChecksPass(PassRegistry &)
Represents a predicate at the MachineFunction level.