bool runOnMachineFunction(MachineFunction &MF) override;
};
+
+/// \brief Detect re-ordering hazards and dependencies.
+///
+/// This class keeps track of defs and uses, and can be queried if a given
+/// machine instruction can be re-ordered from after the machine instructions
+/// seen so far to before them.
+class HazardDetector {
+ DenseSet<unsigned> RegDefs;
+ DenseSet<unsigned> RegUses;
+ const TargetRegisterInfo &TRI;
+ bool hasSeenClobber;
+
+public:
+ explicit HazardDetector(const TargetRegisterInfo &TRI) :
+ TRI(TRI), hasSeenClobber(false) {}
+
+ /// \brief Make a note of \p MI for later queries to isSafeToHoist.
+ ///
+ /// May clobber this HazardDetector instance. \see isClobbered.
+ void rememberInstruction(MachineInstr *MI);
+
+ /// \brief Return true if it is safe to hoist \p MI from after all the
+ /// instructions seen so far (via rememberInstruction) to before it.
+ bool isSafeToHoist(MachineInstr *MI);
+
+ /// \brief Return true if this instance of HazardDetector has been clobbered
+ /// (i.e. has no more useful information).
+ ///
+ /// A HazardDetecter is clobbered when it sees a construct it cannot
+ /// understand, and it would have to return a conservative answer for all
+ /// future queries. Having a separate clobbered state lets the client code
+ /// bail early, without making queries about all of the future instructions
+ /// (which would have returned the most conservative answer anyway).
+ ///
+ /// Calling rememberInstruction or isSafeToHoist on a clobbered HazardDetector
+ /// is an error.
+ bool isClobbered() { return hasSeenClobber; }
+};
+}
+
+
+void HazardDetector::rememberInstruction(MachineInstr *MI) {
+ assert(!isClobbered() &&
+ "Don't add instructions to a clobbered hazard detector");
+
+ if (MI->mayStore() || MI->hasUnmodeledSideEffects()) {
+ hasSeenClobber = true;
+ return;
+ }
+
+ for (auto *MMO : MI->memoperands()) {
+ // Right now we don't want to worry about LLVM's memory model.
+ if (!MMO->isUnordered()) {
+ hasSeenClobber = true;
+ return;
+ }
+ }
+
+ for (auto &MO : MI->operands()) {
+ if (!MO.isReg() || !MO.getReg())
+ continue;
+
+ if (MO.isDef())
+ RegDefs.insert(MO.getReg());
+ else
+ RegUses.insert(MO.getReg());
+ }
+}
+
+bool HazardDetector::isSafeToHoist(MachineInstr *MI) {
+ assert(!isClobbered() && "isSafeToHoist cannot do anything useful!");
+
+ // Right now we don't want to worry about LLVM's memory model. This can be
+ // made more precise later.
+ for (auto *MMO : MI->memoperands())
+ if (!MMO->isUnordered())
+ return false;
+
+ for (auto &MO : MI->operands()) {
+ if (MO.isReg() && MO.getReg()) {
+ for (unsigned Reg : RegDefs)
+ if (TRI.regsOverlap(Reg, MO.getReg()))
+ return false; // We found a write-after-write or read-after-write
+
+ if (MO.isDef())
+ for (unsigned Reg : RegUses)
+ if (TRI.regsOverlap(Reg, MO.getReg()))
+ return false; // We found a write-after-read
+ }
+ }
+
+ return true;
}
bool ImplicitNullChecks::runOnMachineFunction(MachineFunction &MF) {
unsigned PointerReg = MBP.LHS.getReg();
- // As we scan NotNullSucc for a suitable load instruction, we keep track of
- // the registers defined and used by the instructions we scan past. This bit
- // of information lets us decide if it is legal to hoist the load instruction
- // we find (if we do find such an instruction) to before NotNullSucc.
- DenseSet<unsigned> RegDefs, RegUses;
-
- // Returns true if it is safe to reorder MI to before NotNullSucc.
- auto IsSafeToHoist = [&](MachineInstr *MI) {
- // Right now we don't want to worry about LLVM's memory model. This can be
- // made more precise later.
- for (auto *MMO : MI->memoperands())
- if (!MMO->isUnordered())
- return false;
-
- for (auto &MO : MI->operands()) {
- if (MO.isReg() && MO.getReg()) {
- for (unsigned Reg : RegDefs)
- if (TRI->regsOverlap(Reg, MO.getReg()))
- return false; // We found a write-after-write or read-after-write
-
- if (MO.isDef())
- for (unsigned Reg : RegUses)
- if (TRI->regsOverlap(Reg, MO.getReg()))
- return false; // We found a write-after-read
- }
- }
-
- return true;
- };
+ HazardDetector HD(*TRI);
for (auto MII = NotNullSucc->begin(), MIE = NotNullSucc->end(); MII != MIE;
++MII) {
if (TII->getMemOpBaseRegImmOfs(MI, BaseReg, Offset, TRI))
if (MI->mayLoad() && !MI->isPredicable() && BaseReg == PointerReg &&
Offset < PageSize && MI->getDesc().getNumDefs() <= 1 &&
- IsSafeToHoist(MI)) {
+ HD.isSafeToHoist(MI)) {
NullCheckList.emplace_back(MI, MBP.ConditionDef, &MBB, NotNullSucc,
NullSucc);
return true;
}
- // MI did not match our criteria for conversion to a trapping load. Check
- // if we can continue looking.
-
- if (MI->mayStore() || MI->hasUnmodeledSideEffects())
+ HD.rememberInstruction(MI);
+ if (HD.isClobbered())
return false;
-
- for (auto *MMO : MI->memoperands())
- // Right now we don't want to worry about LLVM's memory model.
- if (!MMO->isUnordered())
- return false;
-
- // It _may_ be okay to reorder a later load instruction across MI. Make a
- // note of its operands so that we can make the legality check if we find a
- // suitable load instruction:
-
- for (auto &MO : MI->operands()) {
- if (!MO.isReg() || !MO.getReg())
- continue;
-
- if (MO.isDef())
- RegDefs.insert(MO.getReg());
- else
- RegUses.insert(MO.getReg());
- }
}
return false;