From 9f9cdd41ccb90ed75fa8903307214f16e56ed51a Mon Sep 17 00:00:00 2001 From: Andrea Di Biagio Date: Tue, 18 Sep 2018 15:00:06 +0000 Subject: [PATCH] [llvm-mca] Add the ability to mark register reads/writes associated with dep-breaking instructions. NFCI This patch adds two new boolean fields: - Field `ReadState::IndependentFromDef`. - Field `WriteState::WritesZero`. Field `IndependentFromDef` is set for ReadState objects associated with dependency-breaking instructions. It is used by the simulator when updating data dependencies between registers. Field `WritesZero` is set by WriteState objects associated with dependency breaking zero-idiom instructions. It helps the PRF identify which writes don't consume any physical registers. llvm-svn: 342483 --- .../llvm-mca/include/HardwareUnits/RegisterFile.h | 9 +++---- llvm/tools/llvm-mca/include/Instruction.h | 30 ++++++++++++++-------- .../llvm-mca/lib/HardwareUnits/RegisterFile.cpp | 10 ++++---- llvm/tools/llvm-mca/lib/InstrBuilder.cpp | 18 ++++++++----- llvm/tools/llvm-mca/lib/Stages/DispatchStage.cpp | 17 +++++------- llvm/tools/llvm-mca/lib/Stages/RetireStage.cpp | 4 +-- 6 files changed, 47 insertions(+), 41 deletions(-) diff --git a/llvm/tools/llvm-mca/include/HardwareUnits/RegisterFile.h b/llvm/tools/llvm-mca/include/HardwareUnits/RegisterFile.h index 17272d6..bae8840 100644 --- a/llvm/tools/llvm-mca/include/HardwareUnits/RegisterFile.h +++ b/llvm/tools/llvm-mca/include/HardwareUnits/RegisterFile.h @@ -136,16 +136,15 @@ public: // This method updates the register mappings inserting a new register // definition. This method is also responsible for updating the number of // allocated physical registers in each register file modified by the write. - // No physical regiser is allocated when flag ShouldAllocatePhysRegs is set. + // No physical regiser is allocated if this write is from a zero-idiom. void addRegisterWrite(WriteRef Write, - llvm::MutableArrayRef UsedPhysRegs, - bool ShouldAllocatePhysRegs = true); + llvm::MutableArrayRef UsedPhysRegs); // Removes write \param WS from the register mappings. // Physical registers may be released to reflect this update. + // No registers are released if this write is from a zero-idiom. void removeRegisterWrite(const WriteState &WS, - llvm::MutableArrayRef FreedPhysRegs, - bool ShouldFreePhysRegs = true); + llvm::MutableArrayRef FreedPhysRegs); // Checks if there are enough physical registers in the register files. // Returns a "response mask" where each bit represents the response from a diff --git a/llvm/tools/llvm-mca/include/Instruction.h b/llvm/tools/llvm-mca/include/Instruction.h index 653d7af..b6c0f9b 100644 --- a/llvm/tools/llvm-mca/include/Instruction.h +++ b/llvm/tools/llvm-mca/include/Instruction.h @@ -102,6 +102,9 @@ class WriteState { // super-registers. bool ClearsSuperRegs; + // True if this write is from a dependency breaking zero-idiom instruction. + bool WritesZero; + // This field is set if this is a partial register write, and it has a false // dependency on any previous write of the same register (or a portion of it). // DependentWrite must be able to complete before this write completes, so @@ -121,10 +124,10 @@ class WriteState { public: WriteState(const WriteDescriptor &Desc, unsigned RegID, - bool clearsSuperRegs = false) + bool clearsSuperRegs = false, bool writesZero = false) : WD(Desc), CyclesLeft(UNKNOWN_CYCLES), RegisterID(RegID), - ClearsSuperRegs(clearsSuperRegs), DependentWrite(nullptr), - NumWriteUsers(0U) {} + ClearsSuperRegs(clearsSuperRegs), WritesZero(writesZero), + DependentWrite(nullptr), NumWriteUsers(0U) {} WriteState(const WriteState &Other) = delete; WriteState &operator=(const WriteState &Other) = delete; @@ -137,6 +140,7 @@ public: unsigned getNumUsers() const { return Users.size() + NumWriteUsers; } bool clearsSuperRegisters() const { return ClearsSuperRegs; } + bool isWriteZero() const { return WritesZero; } const WriteState *getDependentWrite() const { return DependentWrite; } void setDependentWrite(WriteState *Other) { @@ -177,11 +181,14 @@ class ReadState { // This field is set to true only if there are no dependent writes, and // there are no `CyclesLeft' to wait. bool IsReady; + // True if this register read is from a dependency-breaking instruction. + bool IndependentFromDef; public: ReadState(const ReadDescriptor &Desc, unsigned RegID) : RD(Desc), RegisterID(RegID), DependentWrites(0), - CyclesLeft(UNKNOWN_CYCLES), TotalCycles(0), IsReady(true) {} + CyclesLeft(UNKNOWN_CYCLES), TotalCycles(0), IsReady(true), + IndependentFromDef(false) {} ReadState(const ReadState &Other) = delete; ReadState &operator=(const ReadState &Other) = delete; @@ -192,6 +199,9 @@ public: bool isReady() const { return IsReady; } bool isImplicitRead() const { return RD.isImplicitRead(); } + bool isIndependentFromDef() const { return IndependentFromDef; } + void setIndependentFromDef() { IndependentFromDef = true; } + void cycleEvent(); void writeStartEvent(unsigned Cycles); void setDependentWrites(unsigned Writes) { @@ -281,6 +291,10 @@ struct InstrDesc { // A zero latency instruction doesn't consume any scheduler resources. bool isZeroLatency() const { return !MaxLatency && Resources.empty(); } + + InstrDesc() = default; + InstrDesc(const InstrDesc &Other) = delete; + InstrDesc &operator=(const InstrDesc &Other) = delete; }; /// An instruction propagated through the simulated instruction pipeline. @@ -309,8 +323,6 @@ class Instruction { // Retire Unit token ID for this instruction. unsigned RCUTokenID; - bool IsDepBreaking; - using UniqueDef = std::unique_ptr; using UniqueUse = std::unique_ptr; using VecDefs = std::vector; @@ -326,8 +338,7 @@ class Instruction { public: Instruction(const InstrDesc &D) - : Desc(D), Stage(IS_INVALID), CyclesLeft(UNKNOWN_CYCLES), RCUTokenID(0), - IsDepBreaking(false) {} + : Desc(D), Stage(IS_INVALID), CyclesLeft(UNKNOWN_CYCLES), RCUTokenID(0) {} Instruction(const Instruction &Other) = delete; Instruction &operator=(const Instruction &Other) = delete; @@ -345,9 +356,6 @@ public: }); } - bool isDependencyBreaking() const { return IsDepBreaking; } - void setDependencyBreaking() { IsDepBreaking = true; } - unsigned getNumUsers() const { unsigned NumUsers = 0; for (const UniqueDef &Def : Defs) diff --git a/llvm/tools/llvm-mca/lib/HardwareUnits/RegisterFile.cpp b/llvm/tools/llvm-mca/lib/HardwareUnits/RegisterFile.cpp index 0f90028..bd66c71 100644 --- a/llvm/tools/llvm-mca/lib/HardwareUnits/RegisterFile.cpp +++ b/llvm/tools/llvm-mca/lib/HardwareUnits/RegisterFile.cpp @@ -139,8 +139,7 @@ void RegisterFile::freePhysRegs(const RegisterRenamingInfo &Entry, } void RegisterFile::addRegisterWrite(WriteRef Write, - MutableArrayRef UsedPhysRegs, - bool ShouldAllocatePhysRegs) { + MutableArrayRef UsedPhysRegs) { WriteState &WS = *Write.getWriteState(); unsigned RegID = WS.getRegisterID(); assert(RegID && "Adding an invalid register definition?"); @@ -163,6 +162,7 @@ void RegisterFile::addRegisterWrite(WriteRef Write, // a false dependency on RenameAs. The only exception is for when the write // implicitly clears the upper portion of the underlying register. // If a write clears its super-registers, then it is renamed as `RenameAs`. + bool ShouldAllocatePhysRegs = !WS.isWriteZero(); const RegisterRenamingInfo &RRI = RegisterMappings[RegID].second; if (RRI.RenameAs && RRI.RenameAs != RegID) { RegID = RRI.RenameAs; @@ -200,9 +200,8 @@ void RegisterFile::addRegisterWrite(WriteRef Write, RegisterMappings[*I].first = Write; } -void RegisterFile::removeRegisterWrite(const WriteState &WS, - MutableArrayRef FreedPhysRegs, - bool ShouldFreePhysRegs) { +void RegisterFile::removeRegisterWrite( + const WriteState &WS, MutableArrayRef FreedPhysRegs) { unsigned RegID = WS.getRegisterID(); assert(RegID != 0 && "Invalidating an already invalid register?"); @@ -210,6 +209,7 @@ void RegisterFile::removeRegisterWrite(const WriteState &WS, "Invalidating a write of unknown cycles!"); assert(WS.getCyclesLeft() <= 0 && "Invalid cycles left for this write!"); + bool ShouldFreePhysRegs = !WS.isWriteZero(); unsigned RenameAs = RegisterMappings[RegID].second.RenameAs; if (RenameAs && RenameAs != RegID) { RegID = RenameAs; diff --git a/llvm/tools/llvm-mca/lib/InstrBuilder.cpp b/llvm/tools/llvm-mca/lib/InstrBuilder.cpp index 1688a56..989051c 100644 --- a/llvm/tools/llvm-mca/lib/InstrBuilder.cpp +++ b/llvm/tools/llvm-mca/lib/InstrBuilder.cpp @@ -423,6 +423,11 @@ InstrBuilder::createInstruction(const MCInst &MCI) { const InstrDesc &D = *DescOrErr; std::unique_ptr NewIS = llvm::make_unique(D); + // Check if this is a dependency breaking instruction. + bool IsDepBreaking = MCIA.isDependencyBreaking(STI, MCI); + // FIXME: this is a temporary hack to identify zero-idioms. + bool IsZeroIdiom = D.isZeroLatency() && IsDepBreaking; + // Initialize Reads first. for (const ReadDescriptor &RD : D.Reads) { int RegID = -1; @@ -444,7 +449,11 @@ InstrBuilder::createInstruction(const MCInst &MCI) { // Okay, this is a register operand. Create a ReadState for it. assert(RegID > 0 && "Invalid register ID found!"); - NewIS->getUses().emplace_back(llvm::make_unique(RD, RegID)); + auto RS = llvm::make_unique(RD, RegID); + + if (IsDepBreaking && !RD.isImplicitRead()) + RS->setIndependentFromDef(); + NewIS->getUses().emplace_back(std::move(RS)); } // Early exit if there are no writes. @@ -459,10 +468,6 @@ InstrBuilder::createInstruction(const MCInst &MCI) { // register writes implicitly clear the upper portion of a super-register. MCIA.clearsSuperRegisters(MRI, MCI, WriteMask); - // Check if this is a dependency breaking instruction. - if (MCIA.isDependencyBreaking(STI, MCI)) - NewIS->setDependencyBreaking(); - // Initialize writes. unsigned WriteIndex = 0; for (const WriteDescriptor &WD : D.Writes) { @@ -476,7 +481,8 @@ InstrBuilder::createInstruction(const MCInst &MCI) { assert(RegID && "Expected a valid register ID!"); NewIS->getDefs().emplace_back(llvm::make_unique( - WD, RegID, /* ClearsSuperRegs */ WriteMask[WriteIndex])); + WD, RegID, /* ClearsSuperRegs */ WriteMask[WriteIndex], + /* WritesZero */ IsZeroIdiom)); ++WriteIndex; } diff --git a/llvm/tools/llvm-mca/lib/Stages/DispatchStage.cpp b/llvm/tools/llvm-mca/lib/Stages/DispatchStage.cpp index 52fdfed..dc39a3a 100644 --- a/llvm/tools/llvm-mca/lib/Stages/DispatchStage.cpp +++ b/llvm/tools/llvm-mca/lib/Stages/DispatchStage.cpp @@ -106,21 +106,16 @@ llvm::Error DispatchStage::dispatch(InstRef IR) { // 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. - bool IsDependencyBreaking = IS.isDependencyBreaking(); for (std::unique_ptr &RS : IS.getUses()) - if (RS->isImplicitRead() || !IsDependencyBreaking) + if (!RS->isIndependentFromDef()) updateRAWDependencies(*RS, STI); - // By default, a dependency-breaking zero-latency instruction is expected to - // be optimized at register renaming stage. That means, no physical register - // is allocated to the instruction. - bool ShouldAllocateRegisters = - !(Desc.isZeroLatency() && IsDependencyBreaking); + // By default, a dependency-breaking zero-idiom is expected to be optimized + // at register renaming stage. That means, no physical register is allocated + // to the instruction. SmallVector RegisterFiles(PRF.getNumRegisterFiles()); - for (std::unique_ptr &WS : IS.getDefs()) { - PRF.addRegisterWrite(WriteRef(IR.getSourceIndex(), WS.get()), RegisterFiles, - ShouldAllocateRegisters); - } + for (std::unique_ptr &WS : IS.getDefs()) + PRF.addRegisterWrite(WriteRef(IR.getSourceIndex(), WS.get()), RegisterFiles); // Reserve slots in the RCU, and notify the instruction that it has been // dispatched to the schedulers for execution. diff --git a/llvm/tools/llvm-mca/lib/Stages/RetireStage.cpp b/llvm/tools/llvm-mca/lib/Stages/RetireStage.cpp index 768a68e..9e392e3 100644 --- a/llvm/tools/llvm-mca/lib/Stages/RetireStage.cpp +++ b/llvm/tools/llvm-mca/lib/Stages/RetireStage.cpp @@ -51,11 +51,9 @@ void RetireStage::notifyInstructionRetired(const InstRef &IR) { LLVM_DEBUG(llvm::dbgs() << "[E] Instruction Retired: #" << IR << '\n'); llvm::SmallVector FreedRegs(PRF.getNumRegisterFiles()); const Instruction &Inst = *IR.getInstruction(); - const InstrDesc &Desc = Inst.getDesc(); - bool ShouldFreeRegs = !(Desc.isZeroLatency() && Inst.isDependencyBreaking()); for (const std::unique_ptr &WS : Inst.getDefs()) - PRF.removeRegisterWrite(*WS.get(), FreedRegs, ShouldFreeRegs); + PRF.removeRegisterWrite(*WS.get(), FreedRegs); notifyEvent(HWInstructionRetiredEvent(IR, FreedRegs)); } -- 2.7.4