diff options
Diffstat (limited to 'contrib/llvm-project/llvm/lib/MCA/Instruction.cpp')
| -rw-r--r-- | contrib/llvm-project/llvm/lib/MCA/Instruction.cpp | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/contrib/llvm-project/llvm/lib/MCA/Instruction.cpp b/contrib/llvm-project/llvm/lib/MCA/Instruction.cpp new file mode 100644 index 000000000000..001842bca318 --- /dev/null +++ b/contrib/llvm-project/llvm/lib/MCA/Instruction.cpp @@ -0,0 +1,254 @@ +//===--------------------- Instruction.cpp ----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file defines abstractions used by the Pipeline to model register reads, +// register writes and instructions. +// +//===----------------------------------------------------------------------===// + +#include "llvm/MCA/Instruction.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/raw_ostream.h" + +namespace llvm { +namespace mca { + +void WriteState::writeStartEvent(unsigned IID, unsigned RegID, + unsigned Cycles) { + CRD.IID = IID; + CRD.RegID = RegID; + CRD.Cycles = Cycles; + DependentWriteCyclesLeft = Cycles; + DependentWrite = nullptr; +} + +void ReadState::writeStartEvent(unsigned IID, unsigned RegID, unsigned Cycles) { + assert(DependentWrites); + assert(CyclesLeft == UNKNOWN_CYCLES); + + // This read may be dependent on more than one write. This typically occurs + // when a definition is the result of multiple writes where at least one + // write does a partial register update. + // The HW is forced to do some extra bookkeeping to track of all the + // dependent writes, and implement a merging scheme for the partial writes. + --DependentWrites; + if (TotalCycles < Cycles) { + CRD.IID = IID; + CRD.RegID = RegID; + CRD.Cycles = Cycles; + TotalCycles = Cycles; + } + + if (!DependentWrites) { + CyclesLeft = TotalCycles; + IsReady = !CyclesLeft; + } +} + +void WriteState::onInstructionIssued(unsigned IID) { + assert(CyclesLeft == UNKNOWN_CYCLES); + // Update the number of cycles left based on the WriteDescriptor info. + CyclesLeft = getLatency(); + + // Now that the time left before write-back is known, notify + // all the users. + for (const std::pair<ReadState *, int> &User : Users) { + ReadState *RS = User.first; + unsigned ReadCycles = std::max(0, CyclesLeft - User.second); + RS->writeStartEvent(IID, RegisterID, ReadCycles); + } + + // Notify any writes that are in a false dependency with this write. + if (PartialWrite) + PartialWrite->writeStartEvent(IID, RegisterID, CyclesLeft); +} + +void WriteState::addUser(unsigned IID, ReadState *User, int ReadAdvance) { + // If CyclesLeft is different than -1, then we don't need to + // update the list of users. We can just notify the user with + // the actual number of cycles left (which may be zero). + if (CyclesLeft != UNKNOWN_CYCLES) { + unsigned ReadCycles = std::max(0, CyclesLeft - ReadAdvance); + User->writeStartEvent(IID, RegisterID, ReadCycles); + return; + } + + Users.emplace_back(User, ReadAdvance); +} + +void WriteState::addUser(unsigned IID, WriteState *User) { + if (CyclesLeft != UNKNOWN_CYCLES) { + User->writeStartEvent(IID, RegisterID, std::max(0, CyclesLeft)); + return; + } + + assert(!PartialWrite && "PartialWrite already set!"); + PartialWrite = User; + User->setDependentWrite(this); +} + +void WriteState::cycleEvent() { + // Note: CyclesLeft can be a negative number. It is an error to + // make it an unsigned quantity because users of this write may + // specify a negative ReadAdvance. + if (CyclesLeft != UNKNOWN_CYCLES) + CyclesLeft--; + + if (DependentWriteCyclesLeft) + DependentWriteCyclesLeft--; +} + +void ReadState::cycleEvent() { + // Update the total number of cycles. + if (DependentWrites && TotalCycles) { + --TotalCycles; + return; + } + + // Bail out immediately if we don't know how many cycles are left. + if (CyclesLeft == UNKNOWN_CYCLES) + return; + + if (CyclesLeft) { + --CyclesLeft; + IsReady = !CyclesLeft; + } +} + +#ifndef NDEBUG +void WriteState::dump() const { + dbgs() << "{ OpIdx=" << WD->OpIndex << ", Lat=" << getLatency() << ", RegID " + << getRegisterID() << ", Cycles Left=" << getCyclesLeft() << " }"; +} + +void WriteRef::dump() const { + dbgs() << "IID=" << getSourceIndex() << ' '; + if (isValid()) + getWriteState()->dump(); + else + dbgs() << "(null)"; +} +#endif + +const CriticalDependency &Instruction::computeCriticalRegDep() { + if (CriticalRegDep.Cycles) + return CriticalRegDep; + + unsigned MaxLatency = 0; + for (const WriteState &WS : getDefs()) { + const CriticalDependency &WriteCRD = WS.getCriticalRegDep(); + if (WriteCRD.Cycles > MaxLatency) + CriticalRegDep = WriteCRD; + } + + for (const ReadState &RS : getUses()) { + const CriticalDependency &ReadCRD = RS.getCriticalRegDep(); + if (ReadCRD.Cycles > MaxLatency) + CriticalRegDep = ReadCRD; + } + + return CriticalRegDep; +} + +void Instruction::dispatch(unsigned RCUToken) { + assert(Stage == IS_INVALID); + Stage = IS_DISPATCHED; + RCUTokenID = RCUToken; + + // Check if input operands are already available. + if (updateDispatched()) + updatePending(); +} + +void Instruction::execute(unsigned IID) { + assert(Stage == IS_READY); + Stage = IS_EXECUTING; + + // Set the cycles left before the write-back stage. + CyclesLeft = getLatency(); + + for (WriteState &WS : getDefs()) + WS.onInstructionIssued(IID); + + // Transition to the "executed" stage if this is a zero-latency instruction. + if (!CyclesLeft) + Stage = IS_EXECUTED; +} + +void Instruction::forceExecuted() { + assert(Stage == IS_READY && "Invalid internal state!"); + CyclesLeft = 0; + Stage = IS_EXECUTED; +} + +bool Instruction::updatePending() { + assert(isPending() && "Unexpected instruction stage found!"); + + if (!all_of(getUses(), [](const ReadState &Use) { return Use.isReady(); })) + return false; + + // A partial register write cannot complete before a dependent write. + if (!all_of(getDefs(), [](const WriteState &Def) { return Def.isReady(); })) + return false; + + Stage = IS_READY; + return true; +} + +bool Instruction::updateDispatched() { + assert(isDispatched() && "Unexpected instruction stage found!"); + + if (!all_of(getUses(), [](const ReadState &Use) { + return Use.isPending() || Use.isReady(); + })) + return false; + + // A partial register write cannot complete before a dependent write. + if (!all_of(getDefs(), + [](const WriteState &Def) { return !Def.getDependentWrite(); })) + return false; + + Stage = IS_PENDING; + return true; +} + +void Instruction::update() { + if (isDispatched()) + updateDispatched(); + if (isPending()) + updatePending(); +} + +void Instruction::cycleEvent() { + if (isReady()) + return; + + if (isDispatched() || isPending()) { + for (ReadState &Use : getUses()) + Use.cycleEvent(); + + for (WriteState &Def : getDefs()) + Def.cycleEvent(); + + update(); + return; + } + + assert(isExecuting() && "Instruction not in-flight?"); + assert(CyclesLeft && "Instruction already executed?"); + for (WriteState &Def : getDefs()) + Def.cycleEvent(); + CyclesLeft--; + if (!CyclesLeft) + Stage = IS_EXECUTED; +} + +const unsigned WriteRef::INVALID_IID = std::numeric_limits<unsigned>::max(); + +} // namespace mca +} // namespace llvm |
