106f32e7eSjoerg //===--------------------- DispatchStage.cpp --------------------*- C++ -*-===//
206f32e7eSjoerg //
306f32e7eSjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
406f32e7eSjoerg // See https://llvm.org/LICENSE.txt for license information.
506f32e7eSjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
606f32e7eSjoerg //
706f32e7eSjoerg //===----------------------------------------------------------------------===//
806f32e7eSjoerg /// \file
906f32e7eSjoerg ///
1006f32e7eSjoerg /// This file models the dispatch component of an instruction pipeline.
1106f32e7eSjoerg ///
1206f32e7eSjoerg /// The DispatchStage is responsible for updating instruction dependencies
1306f32e7eSjoerg /// and communicating to the simulated instruction scheduler that an instruction
1406f32e7eSjoerg /// is ready to be scheduled for execution.
1506f32e7eSjoerg ///
1606f32e7eSjoerg //===----------------------------------------------------------------------===//
1706f32e7eSjoerg 
1806f32e7eSjoerg #include "llvm/MCA/Stages/DispatchStage.h"
1906f32e7eSjoerg #include "llvm/MCA/HWEventListener.h"
2006f32e7eSjoerg #include "llvm/MCA/HardwareUnits/Scheduler.h"
2106f32e7eSjoerg #include "llvm/Support/Debug.h"
2206f32e7eSjoerg 
2306f32e7eSjoerg #define DEBUG_TYPE "llvm-mca"
2406f32e7eSjoerg 
2506f32e7eSjoerg namespace llvm {
2606f32e7eSjoerg namespace mca {
2706f32e7eSjoerg 
DispatchStage(const MCSubtargetInfo & Subtarget,const MCRegisterInfo & MRI,unsigned MaxDispatchWidth,RetireControlUnit & R,RegisterFile & F)2806f32e7eSjoerg DispatchStage::DispatchStage(const MCSubtargetInfo &Subtarget,
2906f32e7eSjoerg                              const MCRegisterInfo &MRI,
3006f32e7eSjoerg                              unsigned MaxDispatchWidth, RetireControlUnit &R,
3106f32e7eSjoerg                              RegisterFile &F)
3206f32e7eSjoerg     : DispatchWidth(MaxDispatchWidth), AvailableEntries(MaxDispatchWidth),
3306f32e7eSjoerg       CarryOver(0U), CarriedOver(), STI(Subtarget), RCU(R), PRF(F) {
3406f32e7eSjoerg   if (!DispatchWidth)
3506f32e7eSjoerg     DispatchWidth = Subtarget.getSchedModel().IssueWidth;
3606f32e7eSjoerg }
3706f32e7eSjoerg 
notifyInstructionDispatched(const InstRef & IR,ArrayRef<unsigned> UsedRegs,unsigned UOps) const3806f32e7eSjoerg void DispatchStage::notifyInstructionDispatched(const InstRef &IR,
3906f32e7eSjoerg                                                 ArrayRef<unsigned> UsedRegs,
4006f32e7eSjoerg                                                 unsigned UOps) const {
4106f32e7eSjoerg   LLVM_DEBUG(dbgs() << "[E] Instruction Dispatched: #" << IR << '\n');
4206f32e7eSjoerg   notifyEvent<HWInstructionEvent>(
4306f32e7eSjoerg       HWInstructionDispatchedEvent(IR, UsedRegs, UOps));
4406f32e7eSjoerg }
4506f32e7eSjoerg 
checkPRF(const InstRef & IR) const4606f32e7eSjoerg bool DispatchStage::checkPRF(const InstRef &IR) const {
4706f32e7eSjoerg   SmallVector<MCPhysReg, 4> RegDefs;
4806f32e7eSjoerg   for (const WriteState &RegDef : IR.getInstruction()->getDefs())
4906f32e7eSjoerg     RegDefs.emplace_back(RegDef.getRegisterID());
5006f32e7eSjoerg 
5106f32e7eSjoerg   const unsigned RegisterMask = PRF.isAvailable(RegDefs);
5206f32e7eSjoerg   // A mask with all zeroes means: register files are available.
5306f32e7eSjoerg   if (RegisterMask) {
5406f32e7eSjoerg     notifyEvent<HWStallEvent>(
5506f32e7eSjoerg         HWStallEvent(HWStallEvent::RegisterFileStall, IR));
5606f32e7eSjoerg     return false;
5706f32e7eSjoerg   }
5806f32e7eSjoerg 
5906f32e7eSjoerg   return true;
6006f32e7eSjoerg }
6106f32e7eSjoerg 
checkRCU(const InstRef & IR) const6206f32e7eSjoerg bool DispatchStage::checkRCU(const InstRef &IR) const {
6306f32e7eSjoerg   const unsigned NumMicroOps = IR.getInstruction()->getNumMicroOps();
6406f32e7eSjoerg   if (RCU.isAvailable(NumMicroOps))
6506f32e7eSjoerg     return true;
6606f32e7eSjoerg   notifyEvent<HWStallEvent>(
6706f32e7eSjoerg       HWStallEvent(HWStallEvent::RetireControlUnitStall, IR));
6806f32e7eSjoerg   return false;
6906f32e7eSjoerg }
7006f32e7eSjoerg 
canDispatch(const InstRef & IR) const7106f32e7eSjoerg bool DispatchStage::canDispatch(const InstRef &IR) const {
7206f32e7eSjoerg   bool CanDispatch = checkRCU(IR);
7306f32e7eSjoerg   CanDispatch &= checkPRF(IR);
7406f32e7eSjoerg   CanDispatch &= checkNextStage(IR);
7506f32e7eSjoerg   return CanDispatch;
7606f32e7eSjoerg }
7706f32e7eSjoerg 
dispatch(InstRef IR)7806f32e7eSjoerg Error DispatchStage::dispatch(InstRef IR) {
7906f32e7eSjoerg   assert(!CarryOver && "Cannot dispatch another instruction!");
8006f32e7eSjoerg   Instruction &IS = *IR.getInstruction();
8106f32e7eSjoerg   const InstrDesc &Desc = IS.getDesc();
8206f32e7eSjoerg   const unsigned NumMicroOps = IS.getNumMicroOps();
8306f32e7eSjoerg   if (NumMicroOps > DispatchWidth) {
8406f32e7eSjoerg     assert(AvailableEntries == DispatchWidth);
8506f32e7eSjoerg     AvailableEntries = 0;
8606f32e7eSjoerg     CarryOver = NumMicroOps - DispatchWidth;
8706f32e7eSjoerg     CarriedOver = IR;
8806f32e7eSjoerg   } else {
8906f32e7eSjoerg     assert(AvailableEntries >= NumMicroOps);
9006f32e7eSjoerg     AvailableEntries -= NumMicroOps;
9106f32e7eSjoerg   }
9206f32e7eSjoerg 
9306f32e7eSjoerg   // Check if this instructions ends the dispatch group.
9406f32e7eSjoerg   if (Desc.EndGroup)
9506f32e7eSjoerg     AvailableEntries = 0;
9606f32e7eSjoerg 
97*da58b97aSjoerg   // Check if this is an optimizable reg-reg move or an XCHG-like instruction.
98*da58b97aSjoerg   if (IS.isOptimizableMove())
99*da58b97aSjoerg     if (PRF.tryEliminateMoveOrSwap(IS.getDefs(), IS.getUses()))
10006f32e7eSjoerg       IS.setEliminated();
10106f32e7eSjoerg 
10206f32e7eSjoerg   // A dependency-breaking instruction doesn't have to wait on the register
10306f32e7eSjoerg   // input operands, and it is often optimized at register renaming stage.
10406f32e7eSjoerg   // Update RAW dependencies if this instruction is not a dependency-breaking
10506f32e7eSjoerg   // instruction. A dependency-breaking instruction is a zero-latency
10606f32e7eSjoerg   // instruction that doesn't consume hardware resources.
10706f32e7eSjoerg   // An example of dependency-breaking instruction on X86 is a zero-idiom XOR.
10806f32e7eSjoerg   //
10906f32e7eSjoerg   // We also don't update data dependencies for instructions that have been
11006f32e7eSjoerg   // eliminated at register renaming stage.
11106f32e7eSjoerg   if (!IS.isEliminated()) {
11206f32e7eSjoerg     for (ReadState &RS : IS.getUses())
11306f32e7eSjoerg       PRF.addRegisterRead(RS, STI);
11406f32e7eSjoerg   }
11506f32e7eSjoerg 
11606f32e7eSjoerg   // By default, a dependency-breaking zero-idiom is expected to be optimized
11706f32e7eSjoerg   // at register renaming stage. That means, no physical register is allocated
11806f32e7eSjoerg   // to the instruction.
11906f32e7eSjoerg   SmallVector<unsigned, 4> RegisterFiles(PRF.getNumRegisterFiles());
12006f32e7eSjoerg   for (WriteState &WS : IS.getDefs())
12106f32e7eSjoerg     PRF.addRegisterWrite(WriteRef(IR.getSourceIndex(), &WS), RegisterFiles);
12206f32e7eSjoerg 
12306f32e7eSjoerg   // Reserve entries in the reorder buffer.
12406f32e7eSjoerg   unsigned RCUTokenID = RCU.dispatch(IR);
12506f32e7eSjoerg   // Notify the instruction that it has been dispatched.
12606f32e7eSjoerg   IS.dispatch(RCUTokenID);
12706f32e7eSjoerg 
12806f32e7eSjoerg   // Notify listeners of the "instruction dispatched" event,
12906f32e7eSjoerg   // and move IR to the next stage.
13006f32e7eSjoerg   notifyInstructionDispatched(IR, RegisterFiles,
13106f32e7eSjoerg                               std::min(DispatchWidth, NumMicroOps));
13206f32e7eSjoerg   return moveToTheNextStage(IR);
13306f32e7eSjoerg }
13406f32e7eSjoerg 
cycleStart()13506f32e7eSjoerg Error DispatchStage::cycleStart() {
136*da58b97aSjoerg   // The retire stage is responsible for calling method `cycleStart`
137*da58b97aSjoerg   // on the PRF.
13806f32e7eSjoerg   if (!CarryOver) {
13906f32e7eSjoerg     AvailableEntries = DispatchWidth;
14006f32e7eSjoerg     return ErrorSuccess();
14106f32e7eSjoerg   }
14206f32e7eSjoerg 
14306f32e7eSjoerg   AvailableEntries = CarryOver >= DispatchWidth ? 0 : DispatchWidth - CarryOver;
14406f32e7eSjoerg   unsigned DispatchedOpcodes = DispatchWidth - AvailableEntries;
14506f32e7eSjoerg   CarryOver -= DispatchedOpcodes;
14606f32e7eSjoerg   assert(CarriedOver && "Invalid dispatched instruction");
14706f32e7eSjoerg 
14806f32e7eSjoerg   SmallVector<unsigned, 8> RegisterFiles(PRF.getNumRegisterFiles(), 0U);
14906f32e7eSjoerg   notifyInstructionDispatched(CarriedOver, RegisterFiles, DispatchedOpcodes);
15006f32e7eSjoerg   if (!CarryOver)
15106f32e7eSjoerg     CarriedOver = InstRef();
15206f32e7eSjoerg   return ErrorSuccess();
15306f32e7eSjoerg }
15406f32e7eSjoerg 
isAvailable(const InstRef & IR) const15506f32e7eSjoerg bool DispatchStage::isAvailable(const InstRef &IR) const {
156*da58b97aSjoerg   // Conservatively bail out if there are no available dispatch entries.
157*da58b97aSjoerg   if (!AvailableEntries)
158*da58b97aSjoerg     return false;
159*da58b97aSjoerg 
16006f32e7eSjoerg   const Instruction &Inst = *IR.getInstruction();
16106f32e7eSjoerg   unsigned NumMicroOps = Inst.getNumMicroOps();
16206f32e7eSjoerg   const InstrDesc &Desc = Inst.getDesc();
16306f32e7eSjoerg   unsigned Required = std::min(NumMicroOps, DispatchWidth);
16406f32e7eSjoerg   if (Required > AvailableEntries)
16506f32e7eSjoerg     return false;
16606f32e7eSjoerg 
16706f32e7eSjoerg   if (Desc.BeginGroup && AvailableEntries != DispatchWidth)
16806f32e7eSjoerg     return false;
16906f32e7eSjoerg 
17006f32e7eSjoerg   // The dispatch logic doesn't internally buffer instructions.  It only accepts
17106f32e7eSjoerg   // instructions that can be successfully moved to the next stage during this
17206f32e7eSjoerg   // same cycle.
17306f32e7eSjoerg   return canDispatch(IR);
17406f32e7eSjoerg }
17506f32e7eSjoerg 
execute(InstRef & IR)17606f32e7eSjoerg Error DispatchStage::execute(InstRef &IR) {
17706f32e7eSjoerg   assert(canDispatch(IR) && "Cannot dispatch another instruction!");
17806f32e7eSjoerg   return dispatch(IR);
17906f32e7eSjoerg }
18006f32e7eSjoerg 
18106f32e7eSjoerg #ifndef NDEBUG
dump() const18206f32e7eSjoerg void DispatchStage::dump() const {
18306f32e7eSjoerg   PRF.dump();
18406f32e7eSjoerg   RCU.dump();
18506f32e7eSjoerg }
18606f32e7eSjoerg #endif
18706f32e7eSjoerg } // namespace mca
18806f32e7eSjoerg } // namespace llvm
189