//
// Field `AllowMoveElimination` is set for registers that are used as
// destination by optimizable register moves.
+ //
+ // Field `AliasRegID` is set by writes from register moves that have been
+ // eliminated at register renaming stage. A move eliminated at register
+ // renaming stage is effectively bypassed, and its write aliases the source
+ // register definition.
struct RegisterRenamingInfo {
IndexPlusCostPairTy IndexPlusCost;
llvm::MCPhysReg RenameAs;
+ llvm::MCPhysReg AliasRegID;
bool AllowMoveElimination;
RegisterRenamingInfo()
- : IndexPlusCost(std::make_pair(0U, 1U)), RenameAs(0U),
+ : IndexPlusCost(std::make_pair(0U, 1U)), RenameAs(0U), AliasRegID(0U),
AllowMoveElimination(false) {}
};
// implicitly clears the upper portion of the underlying register.
// If a write clears its super-registers, then it is renamed as `RenameAs`.
bool IsWriteZero = WS.isWriteZero();
- bool ShouldAllocatePhysRegs = !IsWriteZero;
+ bool IsEliminated = WS.isEliminated();
+ bool ShouldAllocatePhysRegs = !IsWriteZero && !IsEliminated;
const RegisterRenamingInfo &RRI = RegisterMappings[RegID].second;
if (RRI.RenameAs && RRI.RenameAs != RegID) {
if (OtherWrite.getWriteState() &&
(OtherWrite.getSourceIndex() != Write.getSourceIndex())) {
// This partial write has a false dependency on RenameAs.
+ assert(!IsEliminated && "Unexpected partial update!");
WS.setDependentWrite(OtherWrite.getWriteState());
}
}
ZeroRegisters.clearBit(*I);
}
- // Update the mapping for register RegID including its sub-registers.
- RegisterMappings[RegID].first = Write;
- for (MCSubRegIterator I(RegID, &MRI); I.isValid(); ++I)
- RegisterMappings[*I].first = Write;
+ // If this is move has been eliminated, then the call to tryEliminateMove
+ // should have already updated all the register mappings.
+ if (!IsEliminated) {
+ // Update the mapping for register RegID including its sub-registers.
+ RegisterMappings[RegID].first = Write;
+ RegisterMappings[RegID].second.AliasRegID = 0U;
+ for (MCSubRegIterator I(RegID, &MRI); I.isValid(); ++I) {
+ RegisterMappings[*I].first = Write;
+ RegisterMappings[*I].second.AliasRegID = 0U;
+ }
- // No physical registers are allocated for instructions that are optimized in
- // hardware. For example, zero-latency data-dependency breaking instructions
- // don't consume physical registers.
- if (ShouldAllocatePhysRegs)
- allocatePhysRegs(RegisterMappings[RegID].second, UsedPhysRegs);
+ // No physical registers are allocated for instructions that are optimized in
+ // hardware. For example, zero-latency data-dependency breaking instructions
+ // don't consume physical registers.
+ if (ShouldAllocatePhysRegs)
+ allocatePhysRegs(RegisterMappings[RegID].second, UsedPhysRegs);
+ }
if (!WS.clearsSuperRegisters())
return;
for (MCSuperRegIterator I(RegID, &MRI); I.isValid(); ++I) {
- RegisterMappings[*I].first = Write;
+ if (!IsEliminated) {
+ RegisterMappings[*I].first = Write;
+ RegisterMappings[*I].second.AliasRegID = 0U;
+ }
+
if (IsWriteZero)
ZeroRegisters.setBit(*I);
else
void RegisterFile::removeRegisterWrite(
const WriteState &WS, MutableArrayRef<unsigned> FreedPhysRegs) {
+ // Early exit if this write was eliminated. A write eliminated at register
+ // renaming stage generates an alias, and it is not added to the PRF.
+ if (WS.isEliminated())
+ return;
+
unsigned RegID = WS.getRegisterID();
assert(RegID != 0 && "Invalidating an already invalid register?");
if (RMT.AllowZeroMoveEliminationOnly && !IsZeroMove)
return false;
+ MCPhysReg FromReg = RS.getRegisterID();
+ MCPhysReg ToReg = WS.getRegisterID();
+
+ // Construct an alias.
+ MCPhysReg AliasReg = FromReg;
+ if (RRIFrom.RenameAs)
+ AliasReg = RRIFrom.RenameAs;
+
+ const RegisterRenamingInfo &RMAlias = RegisterMappings[AliasReg].second;
+ if (RMAlias.AliasRegID)
+ AliasReg = RMAlias.AliasRegID;
+
+ if (AliasReg != ToReg) {
+ RegisterMappings[ToReg].second.AliasRegID = AliasReg;
+ for (MCSubRegIterator I(ToReg, &MRI); I.isValid(); ++I)
+ RegisterMappings[*I].second.AliasRegID = AliasReg;
+ }
+
RMT.NumMoveEliminated++;
if (IsZeroMove)
WS.setWriteZero();
WS.setEliminated();
+
return true;
}
assert(RegID && RegID < RegisterMappings.size());
LLVM_DEBUG(dbgs() << "RegisterFile: collecting writes for register "
<< MRI.getName(RegID) << '\n');
+
+ // Check if this is an alias.
+ const RegisterRenamingInfo &RRI = RegisterMappings[RegID].second;
+ if (RRI.AliasRegID)
+ RegID = RRI.AliasRegID;
+
const WriteRef &WR = RegisterMappings[RegID].first;
if (WR.isValid())
Writes.push_back(WR);
}
// Check if this is an optimizable reg-reg move.
+ bool IsEliminated = false;
if (IS.isOptimizableMove()) {
assert(IS.getDefs().size() == 1 && "Expected a single input!");
assert(IS.getUses().size() == 1 && "Expected a single output!");
- PRF.tryEliminateMove(*IS.getDefs()[0], *IS.getUses()[0]);
+ IsEliminated = PRF.tryEliminateMove(*IS.getDefs()[0], *IS.getUses()[0]);
}
// A dependency-breaking instruction doesn't have to wait on the register
// instruction. A dependency-breaking instruction is a zero-latency
// instruction that doesn't consume hardware resources.
// An example of dependency-breaking instruction on X86 is a zero-idiom XOR.
- for (std::unique_ptr<ReadState> &RS : IS.getUses())
- if (!RS->isIndependentFromDef())
- updateRAWDependencies(*RS, STI);
+ //
+ // We also don't update data dependencies for instructions that have been
+ // eliminated at register renaming stage.
+ if (!IsEliminated) {
+ for (std::unique_ptr<ReadState> &RS : IS.getUses()) {
+ if (!RS->isIndependentFromDef())
+ updateRAWDependencies(*RS, STI);
+ }
+ }
// By default, a dependency-breaking zero-idiom is expected to be optimized
// at register renaming stage. That means, no physical register is allocated