| //===---------------------- RetireControlUnit.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 |
| // |
| //===----------------------------------------------------------------------===// |
| /// \file |
| /// |
| /// This file simulates the hardware responsible for retiring instructions. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/MCA/HardwareUnits/RetireControlUnit.h" |
| #include "llvm/Support/Debug.h" |
| |
| #define DEBUG_TYPE "llvm-mca" |
| |
| namespace llvm { |
| namespace mca { |
| |
| RetireControlUnit::RetireControlUnit(const MCSchedModel &SM) |
| : NextAvailableSlotIdx(0), CurrentInstructionSlotIdx(0), |
| NumROBEntries(SM.MicroOpBufferSize), |
| AvailableEntries(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) |
| AvailableEntries = EPI.ReorderBufferSize; |
| MaxRetirePerCycle = EPI.MaxRetirePerCycle; |
| } |
| NumROBEntries = AvailableEntries; |
| assert(NumROBEntries && "Invalid reorder buffer size!"); |
| Queue.resize(2 * NumROBEntries); |
| } |
| |
| // Reserves a number of slots, and returns a new token. |
| unsigned RetireControlUnit::dispatch(const InstRef &IR) { |
| const Instruction &Inst = *IR.getInstruction(); |
| unsigned Entries = normalizeQuantity(Inst.getNumMicroOps()); |
| assert((AvailableEntries >= Entries) && "Reorder Buffer unavailable!"); |
| |
| unsigned TokenID = NextAvailableSlotIdx; |
| Queue[NextAvailableSlotIdx] = {IR, Entries, false}; |
| NextAvailableSlotIdx += std::max(1U, Entries); |
| NextAvailableSlotIdx %= Queue.size(); |
| |
| AvailableEntries -= Entries; |
| return TokenID; |
| } |
| |
| const RetireControlUnit::RUToken &RetireControlUnit::getCurrentToken() const { |
| const RetireControlUnit::RUToken &Current = Queue[CurrentInstructionSlotIdx]; |
| #ifndef NDEBUG |
| const Instruction *Inst = Current.IR.getInstruction(); |
| assert(Inst && "Invalid RUToken in the RCU queue."); |
| #endif |
| return Current; |
| } |
| |
| unsigned RetireControlUnit::computeNextSlotIdx() const { |
| const RetireControlUnit::RUToken &Current = getCurrentToken(); |
| unsigned NextSlotIdx = CurrentInstructionSlotIdx + std::max(1U, Current.NumSlots); |
| return NextSlotIdx % Queue.size(); |
| } |
| |
| const RetireControlUnit::RUToken &RetireControlUnit::peekNextToken() const { |
| return Queue[computeNextSlotIdx()]; |
| } |
| |
| void RetireControlUnit::consumeCurrentToken() { |
| RetireControlUnit::RUToken &Current = Queue[CurrentInstructionSlotIdx]; |
| Current.IR.getInstruction()->retire(); |
| |
| // Update the slot index to be the next item in the circular queue. |
| CurrentInstructionSlotIdx += std::max(1U, Current.NumSlots); |
| CurrentInstructionSlotIdx %= Queue.size(); |
| AvailableEntries += Current.NumSlots; |
| Current = { InstRef(), 0U, false }; |
| } |
| |
| void RetireControlUnit::onInstructionExecuted(unsigned TokenID) { |
| assert(Queue.size() > TokenID); |
| assert(Queue[TokenID].IR.getInstruction() && "Instruction was not dispatched!"); |
| assert(Queue[TokenID].Executed == false && "Instruction already executed!"); |
| Queue[TokenID].Executed = true; |
| } |
| |
| #ifndef NDEBUG |
| void RetireControlUnit::dump() const { |
| dbgs() << "Retire Unit: { Total ROB Entries =" << NumROBEntries |
| << ", Available ROB entries=" << AvailableEntries << " }\n"; |
| } |
| #endif |
| |
| } // namespace mca |
| } // namespace llvm |