[ImplicitNull] Extract out a HazardDetector class, NFC

This will make later functional changes easier to follow.

llvm-svn: 252946
This commit is contained in:
Sanjoy Das 2015-11-12 20:51:44 +00:00
parent 2d4a5edb9c
commit 3c4432b86c

View File

@ -108,6 +108,98 @@ public:
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) {
@ -203,35 +295,7 @@ bool ImplicitNullChecks::analyzeBlockForNullChecks(
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) {
@ -240,36 +304,15 @@ bool ImplicitNullChecks::analyzeBlockForNullChecks(
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;