| //===---------------------- RetireControlUnit.cpp ---------------*- C++ -*-===// | |
| // | |
| // The LLVM Compiler Infrastructure | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// \file | |
| /// | |
| /// This file simulates the hardware responsible for retiring instructions. | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #include "RetireControlUnit.h" | |
| #include "llvm/Support/Debug.h" | |
| using namespace llvm; | |
| #define DEBUG_TYPE "llvm-mca" | |
| namespace mca { | |
| RetireControlUnit::RetireControlUnit(const llvm::MCSchedModel &SM) | |
| : NextAvailableSlotIdx(0), CurrentInstructionSlotIdx(0), | |
| AvailableSlots(SM.MicroOpBufferSize), MaxRetirePerCycle(0) { | |
| // Check if the scheduling model provides extra information about the machine | |
| // processor. If so, then use that information to set the reorder buffer size | |
| // and the maximum number of instructions retired per cycle. | |
| if (SM.hasExtraProcessorInfo()) { | |
| const MCExtraProcessorInfo &EPI = SM.getExtraProcessorInfo(); | |
| if (EPI.ReorderBufferSize) | |
| AvailableSlots = EPI.ReorderBufferSize; | |
| MaxRetirePerCycle = EPI.MaxRetirePerCycle; | |
| } | |
| assert(AvailableSlots && "Invalid reorder buffer size!"); | |
| Queue.resize(AvailableSlots); | |
| } | |
| // Reserves a number of slots, and returns a new token. | |
| unsigned RetireControlUnit::reserveSlot(const InstRef &IR, | |
| unsigned NumMicroOps) { | |
| assert(isAvailable(NumMicroOps)); | |
| unsigned NormalizedQuantity = | |
| std::min(NumMicroOps, static_cast<unsigned>(Queue.size())); | |
| // Zero latency instructions may have zero mOps. Artificially bump this | |
| // value to 1. Although zero latency instructions don't consume scheduler | |
| // resources, they still consume one slot in the retire queue. | |
| NormalizedQuantity = std::max(NormalizedQuantity, 1U); | |
| unsigned TokenID = NextAvailableSlotIdx; | |
| Queue[NextAvailableSlotIdx] = {IR, NormalizedQuantity, false}; | |
| NextAvailableSlotIdx += NormalizedQuantity; | |
| NextAvailableSlotIdx %= Queue.size(); | |
| AvailableSlots -= NormalizedQuantity; | |
| return TokenID; | |
| } | |
| const RetireControlUnit::RUToken &RetireControlUnit::peekCurrentToken() const { | |
| return Queue[CurrentInstructionSlotIdx]; | |
| } | |
| void RetireControlUnit::consumeCurrentToken() { | |
| const RetireControlUnit::RUToken &Current = peekCurrentToken(); | |
| assert(Current.NumSlots && "Reserved zero slots?"); | |
| assert(Current.IR.isValid() && "Invalid RUToken in the RCU queue."); | |
| // Update the slot index to be the next item in the circular queue. | |
| CurrentInstructionSlotIdx += Current.NumSlots; | |
| CurrentInstructionSlotIdx %= Queue.size(); | |
| AvailableSlots += Current.NumSlots; | |
| } | |
| void RetireControlUnit::onInstructionExecuted(unsigned TokenID) { | |
| assert(Queue.size() > TokenID); | |
| assert(Queue[TokenID].Executed == false && Queue[TokenID].IR.isValid()); | |
| Queue[TokenID].Executed = true; | |
| } | |
| #ifndef NDEBUG | |
| void RetireControlUnit::dump() const { | |
| dbgs() << "Retire Unit: { Total Slots=" << Queue.size() | |
| << ", Available Slots=" << AvailableSlots << " }\n"; | |
| } | |
| #endif | |
| } // namespace mca |