diff options
Diffstat (limited to 'include/llvm/MCA')
| -rw-r--r-- | include/llvm/MCA/Context.h | 21 | ||||
| -rw-r--r-- | include/llvm/MCA/HWEventListener.h | 38 | ||||
| -rw-r--r-- | include/llvm/MCA/HardwareUnits/HardwareUnit.h | 7 | ||||
| -rw-r--r-- | include/llvm/MCA/HardwareUnits/LSUnit.h | 393 | ||||
| -rw-r--r-- | include/llvm/MCA/HardwareUnits/RegisterFile.h | 10 | ||||
| -rw-r--r-- | include/llvm/MCA/HardwareUnits/ResourceManager.h | 31 | ||||
| -rw-r--r-- | include/llvm/MCA/HardwareUnits/RetireControlUnit.h | 7 | ||||
| -rw-r--r-- | include/llvm/MCA/HardwareUnits/Scheduler.h | 138 | ||||
| -rw-r--r-- | include/llvm/MCA/InstrBuilder.h | 7 | ||||
| -rw-r--r-- | include/llvm/MCA/Instruction.h | 161 | ||||
| -rw-r--r-- | include/llvm/MCA/Pipeline.h | 7 | ||||
| -rw-r--r-- | include/llvm/MCA/SourceMgr.h | 7 | ||||
| -rw-r--r-- | include/llvm/MCA/Stages/DispatchStage.h | 13 | ||||
| -rw-r--r-- | include/llvm/MCA/Stages/EntryStage.h | 7 | ||||
| -rw-r--r-- | include/llvm/MCA/Stages/ExecuteStage.h | 20 | ||||
| -rw-r--r-- | include/llvm/MCA/Stages/InstructionTables.h | 7 | ||||
| -rw-r--r-- | include/llvm/MCA/Stages/MicroOpQueueStage.h | 88 | ||||
| -rw-r--r-- | include/llvm/MCA/Stages/RetireStage.h | 7 | ||||
| -rw-r--r-- | include/llvm/MCA/Stages/Stage.h | 7 | ||||
| -rw-r--r-- | include/llvm/MCA/Support.h | 33 |
20 files changed, 758 insertions, 251 deletions
diff --git a/include/llvm/MCA/Context.h b/include/llvm/MCA/Context.h index 6b2bee0fdc42..503d780d4947 100644 --- a/include/llvm/MCA/Context.h +++ b/include/llvm/MCA/Context.h @@ -1,9 +1,8 @@ //===---------------------------- Context.h ---------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 @@ -32,15 +31,21 @@ namespace mca { /// This is a convenience struct to hold the parameters necessary for creating /// the pre-built "default" out-of-order pipeline. struct PipelineOptions { - PipelineOptions(unsigned DW, unsigned RFS, unsigned LQS, unsigned SQS, - bool NoAlias) - : DispatchWidth(DW), RegisterFileSize(RFS), LoadQueueSize(LQS), - StoreQueueSize(SQS), AssumeNoAlias(NoAlias) {} + PipelineOptions(unsigned UOPQSize, unsigned DecThr, unsigned DW, unsigned RFS, + unsigned LQS, unsigned SQS, bool NoAlias, + bool ShouldEnableBottleneckAnalysis = false) + : MicroOpQueueSize(UOPQSize), DecodersThroughput(DecThr), + DispatchWidth(DW), RegisterFileSize(RFS), LoadQueueSize(LQS), + StoreQueueSize(SQS), AssumeNoAlias(NoAlias), + EnableBottleneckAnalysis(ShouldEnableBottleneckAnalysis) {} + unsigned MicroOpQueueSize; + unsigned DecodersThroughput; // Instructions per cycle. unsigned DispatchWidth; unsigned RegisterFileSize; unsigned LoadQueueSize; unsigned StoreQueueSize; bool AssumeNoAlias; + bool EnableBottleneckAnalysis; }; class Context { diff --git a/include/llvm/MCA/HWEventListener.h b/include/llvm/MCA/HWEventListener.h index 3b32b2cd6577..e11d06de2b2e 100644 --- a/include/llvm/MCA/HWEventListener.h +++ b/include/llvm/MCA/HWEventListener.h @@ -1,9 +1,8 @@ //===----------------------- HWEventListener.h ------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 @@ -40,6 +39,7 @@ public: // Events generated by the Retire Control Unit. Retired, // Events generated by the Scheduler. + Pending, Ready, Issued, Executed, @@ -126,6 +126,35 @@ public: const InstRef &IR; }; +// A HWPressureEvent describes an increase in backend pressure caused by +// the presence of data dependencies or unavailability of pipeline resources. +class HWPressureEvent { +public: + enum GenericReason { + INVALID = 0, + // Scheduler was unable to issue all the ready instructions because some + // pipeline resources were unavailable. + RESOURCES, + // Instructions could not be issued because of register data dependencies. + REGISTER_DEPS, + // Instructions could not be issued because of memory dependencies. + MEMORY_DEPS + }; + + HWPressureEvent(GenericReason reason, ArrayRef<InstRef> Insts, + uint64_t Mask = 0) + : Reason(reason), AffectedInstructions(Insts), ResourceMask(Mask) {} + + // Reason for this increase in backend pressure. + GenericReason Reason; + + // Instructions affected (i.e. delayed) by this increase in backend pressure. + ArrayRef<InstRef> AffectedInstructions; + + // A mask of unavailable processor resources. + const uint64_t ResourceMask; +}; + class HWEventListener { public: // Generic events generated by the pipeline. @@ -134,6 +163,7 @@ public: virtual void onEvent(const HWInstructionEvent &Event) {} virtual void onEvent(const HWStallEvent &Event) {} + virtual void onEvent(const HWPressureEvent &Event) {} using ResourceRef = std::pair<uint64_t, uint64_t>; virtual void onResourceAvailable(const ResourceRef &RRef) {} diff --git a/include/llvm/MCA/HardwareUnits/HardwareUnit.h b/include/llvm/MCA/HardwareUnits/HardwareUnit.h index 104a2009f219..f6e178bcff10 100644 --- a/include/llvm/MCA/HardwareUnits/HardwareUnit.h +++ b/include/llvm/MCA/HardwareUnits/HardwareUnit.h @@ -1,9 +1,8 @@ //===-------------------------- HardwareUnit.h ------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/include/llvm/MCA/HardwareUnits/LSUnit.h b/include/llvm/MCA/HardwareUnits/LSUnit.h index e217fc50f780..ae9a49c64855 100644 --- a/include/llvm/MCA/HardwareUnits/LSUnit.h +++ b/include/llvm/MCA/HardwareUnits/LSUnit.h @@ -1,9 +1,8 @@ //===------------------------- LSUnit.h --------------------------*- C++-*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 @@ -16,21 +15,298 @@ #ifndef LLVM_MCA_LSUNIT_H #define LLVM_MCA_LSUNIT_H -#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/MC/MCSchedule.h" #include "llvm/MCA/HardwareUnits/HardwareUnit.h" +#include "llvm/MCA/Instruction.h" namespace llvm { namespace mca { -class InstRef; class Scheduler; -/// A Load/Store Unit implementing a load and store queues. +/// A node of a memory dependency graph. A MemoryGroup describes a set of +/// instructions with same memory dependencies. /// -/// This class implements a load queue and a store queue to emulate the -/// out-of-order execution of memory operations. -/// Each load (or store) consumes an entry in the load (or store) queue. +/// By construction, instructions of a MemoryGroup don't depend on each other. +/// At dispatch stage, instructions are mapped by the LSUnit to MemoryGroups. +/// A Memory group identifier is then stored as a "token" in field +/// Instruction::LSUTokenID of each dispatched instructions. That token is used +/// internally by the LSUnit to track memory dependencies. +class MemoryGroup { + unsigned NumPredecessors; + unsigned NumExecutingPredecessors; + unsigned NumExecutedPredecessors; + + unsigned NumInstructions; + unsigned NumExecuting; + unsigned NumExecuted; + SmallVector<MemoryGroup *, 4> Succ; + + CriticalDependency CriticalPredecessor; + InstRef CriticalMemoryInstruction; + + MemoryGroup(const MemoryGroup &) = delete; + MemoryGroup &operator=(const MemoryGroup &) = delete; + +public: + MemoryGroup() + : NumPredecessors(0), NumExecutingPredecessors(0), + NumExecutedPredecessors(0), NumInstructions(0), NumExecuting(0), + NumExecuted(0), CriticalPredecessor(), CriticalMemoryInstruction() {} + MemoryGroup(MemoryGroup &&) = default; + + ArrayRef<MemoryGroup *> getSuccessors() const { return Succ; } + unsigned getNumSuccessors() const { return Succ.size(); } + unsigned getNumPredecessors() const { return NumPredecessors; } + unsigned getNumExecutingPredecessors() const { + return NumExecutingPredecessors; + } + unsigned getNumExecutedPredecessors() const { + return NumExecutedPredecessors; + } + unsigned getNumInstructions() const { return NumInstructions; } + unsigned getNumExecuting() const { return NumExecuting; } + unsigned getNumExecuted() const { return NumExecuted; } + + const InstRef &getCriticalMemoryInstruction() const { + return CriticalMemoryInstruction; + } + const CriticalDependency &getCriticalPredecessor() const { + return CriticalPredecessor; + } + + void addSuccessor(MemoryGroup *Group) { + Group->NumPredecessors++; + assert(!isExecuted() && "Should have been removed!"); + if (isExecuting()) + Group->onGroupIssued(CriticalMemoryInstruction); + Succ.emplace_back(Group); + } + + bool isWaiting() const { + return NumPredecessors > + (NumExecutingPredecessors + NumExecutedPredecessors); + } + bool isPending() const { + return NumExecutingPredecessors && + ((NumExecutedPredecessors + NumExecutingPredecessors) == + NumPredecessors); + } + bool isReady() const { return NumExecutedPredecessors == NumPredecessors; } + bool isExecuting() const { + return NumExecuting && (NumExecuting == (NumInstructions - NumExecuted)); + } + bool isExecuted() const { return NumInstructions == NumExecuted; } + + void onGroupIssued(const InstRef &IR) { + assert(!isReady() && "Unexpected group-start event!"); + NumExecutingPredecessors++; + + unsigned Cycles = IR.getInstruction()->getCyclesLeft(); + if (CriticalPredecessor.Cycles < Cycles) { + CriticalPredecessor.IID = IR.getSourceIndex(); + CriticalPredecessor.Cycles = Cycles; + } + } + + void onGroupExecuted() { + assert(!isReady() && "Inconsistent state found!"); + NumExecutingPredecessors--; + NumExecutedPredecessors++; + } + + void onInstructionIssued(const InstRef &IR) { + assert(!isExecuting() && "Invalid internal state!"); + ++NumExecuting; + + // update the CriticalMemDep. + const Instruction &IS = *IR.getInstruction(); + if ((bool)CriticalMemoryInstruction) { + const Instruction &OtherIS = *CriticalMemoryInstruction.getInstruction(); + if (OtherIS.getCyclesLeft() < IS.getCyclesLeft()) + CriticalMemoryInstruction = IR; + } else { + CriticalMemoryInstruction = IR; + } + + if (!isExecuting()) + return; + + // Notify successors that this group started execution. + for (MemoryGroup *MG : Succ) + MG->onGroupIssued(CriticalMemoryInstruction); + } + + void onInstructionExecuted() { + assert(isReady() && !isExecuted() && "Invalid internal state!"); + --NumExecuting; + ++NumExecuted; + + if (!isExecuted()) + return; + + // Notify successors that this group has finished execution. + for (MemoryGroup *MG : Succ) + MG->onGroupExecuted(); + } + + void addInstruction() { + assert(!getNumSuccessors() && "Cannot add instructions to this group!"); + ++NumInstructions; + } + + void cycleEvent() { + if (isWaiting() && CriticalPredecessor.Cycles) + CriticalPredecessor.Cycles--; + } +}; + +/// Abstract base interface for LS (load/store) units in llvm-mca. +class LSUnitBase : public HardwareUnit { + /// Load queue size. + /// + /// A value of zero for this field means that the load queue is unbounded. + /// Processor models can declare the size of a load queue via tablegen (see + /// the definition of tablegen class LoadQueue in + /// llvm/Target/TargetSchedule.td). + unsigned LQSize; + + /// Load queue size. + /// + /// A value of zero for this field means that the store queue is unbounded. + /// Processor models can declare the size of a store queue via tablegen (see + /// the definition of tablegen class StoreQueue in + /// llvm/Target/TargetSchedule.td). + unsigned SQSize; + + unsigned UsedLQEntries; + unsigned UsedSQEntries; + + /// True if loads don't alias with stores. + /// + /// By default, the LS unit assumes that loads and stores don't alias with + /// eachother. If this field is set to false, then loads are always assumed to + /// alias with stores. + const bool NoAlias; + + /// Used to map group identifiers to MemoryGroups. + DenseMap<unsigned, std::unique_ptr<MemoryGroup>> Groups; + unsigned NextGroupID; + +public: + LSUnitBase(const MCSchedModel &SM, unsigned LoadQueueSize, + unsigned StoreQueueSize, bool AssumeNoAlias); + + virtual ~LSUnitBase(); + + /// Returns the total number of entries in the load queue. + unsigned getLoadQueueSize() const { return LQSize; } + + /// Returns the total number of entries in the store queue. + unsigned getStoreQueueSize() const { return SQSize; } + + unsigned getUsedLQEntries() const { return UsedLQEntries; } + unsigned getUsedSQEntries() const { return UsedSQEntries; } + unsigned assignLQSlot() { return UsedLQEntries++; } + unsigned assignSQSlot() { return UsedSQEntries++; } + + bool assumeNoAlias() const { return NoAlias; } + + enum Status { + LSU_AVAILABLE = 0, + LSU_LQUEUE_FULL, // Load Queue unavailable + LSU_SQUEUE_FULL // Store Queue unavailable + }; + + /// This method checks the availability of the load/store buffers. + /// + /// Returns LSU_AVAILABLE if there are enough load/store queue entries to + /// accomodate instruction IR. By default, LSU_AVAILABLE is returned if IR is + /// not a memory operation. + virtual Status isAvailable(const InstRef &IR) const = 0; + + /// Allocates LS resources for instruction IR. + /// + /// This method assumes that a previous call to `isAvailable(IR)` succeeded + /// with a LSUnitBase::Status value of LSU_AVAILABLE. + /// Returns the GroupID associated with this instruction. That value will be + /// used to set the LSUTokenID field in class Instruction. + virtual unsigned dispatch(const InstRef &IR) = 0; + + bool isSQEmpty() const { return !UsedSQEntries; } + bool isLQEmpty() const { return !UsedLQEntries; } + bool isSQFull() const { return SQSize && SQSize == UsedSQEntries; } + bool isLQFull() const { return LQSize && LQSize == UsedLQEntries; } + + bool isValidGroupID(unsigned Index) const { + return Index && (Groups.find(Index) != Groups.end()); + } + + /// Check if a peviously dispatched instruction IR is now ready for execution. + bool isReady(const InstRef &IR) const { + unsigned GroupID = IR.getInstruction()->getLSUTokenID(); + const MemoryGroup &Group = getGroup(GroupID); + return Group.isReady(); + } + + /// Check if instruction IR only depends on memory instructions that are + /// currently executing. + bool isPending(const InstRef &IR) const { + unsigned GroupID = IR.getInstruction()->getLSUTokenID(); + const MemoryGroup &Group = getGroup(GroupID); + return Group.isPending(); + } + + /// Check if instruction IR is still waiting on memory operations, and the + /// wait time is still unknown. + bool isWaiting(const InstRef &IR) const { + unsigned GroupID = IR.getInstruction()->getLSUTokenID(); + const MemoryGroup &Group = getGroup(GroupID); + return Group.isWaiting(); + } + + bool hasDependentUsers(const InstRef &IR) const { + unsigned GroupID = IR.getInstruction()->getLSUTokenID(); + const MemoryGroup &Group = getGroup(GroupID); + return !Group.isExecuted() && Group.getNumSuccessors(); + } + + const MemoryGroup &getGroup(unsigned Index) const { + assert(isValidGroupID(Index) && "Group doesn't exist!"); + return *Groups.find(Index)->second; + } + + MemoryGroup &getGroup(unsigned Index) { + assert(isValidGroupID(Index) && "Group doesn't exist!"); + return *Groups.find(Index)->second; + } + + unsigned createMemoryGroup() { + Groups.insert( + std::make_pair(NextGroupID, llvm::make_unique<MemoryGroup>())); + return NextGroupID++; + } + + // Instruction executed event handlers. + virtual void onInstructionExecuted(const InstRef &IR); + + virtual void onInstructionIssued(const InstRef &IR) { + unsigned GroupID = IR.getInstruction()->getLSUTokenID(); + Groups[GroupID]->onInstructionIssued(IR); + } + + virtual void cycleEvent(); + +#ifndef NDEBUG + void dump() const; +#endif +}; + +/// Default Load/Store Unit (LS Unit) for simulated processors. +/// +/// Each load (or store) consumes one entry in the load (or store) queue. /// /// Rules are: /// 1) A younger load is allowed to pass an older load only if there are no @@ -89,26 +365,7 @@ class Scheduler; /// A load/store barrier is "executed" when it becomes the oldest entry in /// the load/store queue(s). That also means, all the older loads/stores have /// already been executed. -class LSUnit : public HardwareUnit { - // Load queue size. - // LQ_Size == 0 means that there are infinite slots in the load queue. - unsigned LQ_Size; - - // Store queue size. - // SQ_Size == 0 means that there are infinite slots in the store queue. - unsigned SQ_Size; - - // If true, loads will never alias with stores. This is the default. - bool NoAlias; - - // When a `MayLoad` instruction is dispatched to the schedulers for execution, - // the LSUnit reserves an entry in the `LoadQueue` for it. - // - // LoadQueue keeps track of all the loads that are in-flight. A load - // instruction is eventually removed from the LoadQueue when it reaches - // completion stage. That means, a load leaves the queue whe it is 'executed', - // and its value can be forwarded on the data path to outside units. - // +class LSUnit : public LSUnitBase { // This class doesn't know about the latency of a load instruction. So, it // conservatively/pessimistically assumes that the latency of a load opcode // matches the instruction latency. @@ -139,66 +396,50 @@ class LSUnit : public HardwareUnit { // alternative approaches that let instructions specify the number of // load/store queue entries which they consume at dispatch stage (See // PR39830). - SmallSet<unsigned, 16> LoadQueue; - SmallSet<unsigned, 16> StoreQueue; - - void assignLQSlot(unsigned Index); - void assignSQSlot(unsigned Index); - bool isReadyNoAlias(unsigned Index) const; - + // // An instruction that both 'mayStore' and 'HasUnmodeledSideEffects' is // conservatively treated as a store barrier. It forces older store to be // executed before newer stores are issued. - SmallSet<unsigned, 8> StoreBarriers; - + // // An instruction that both 'MayLoad' and 'HasUnmodeledSideEffects' is // conservatively treated as a load barrier. It forces older loads to execute // before newer loads are issued. - SmallSet<unsigned, 8> LoadBarriers; - - bool isSQEmpty() const { return StoreQueue.empty(); } - bool isLQEmpty() const { return LoadQueue.empty(); } - bool isSQFull() const { return SQ_Size != 0 && StoreQueue.size() == SQ_Size; } - bool isLQFull() const { return LQ_Size != 0 && LoadQueue.size() == LQ_Size; } + unsigned CurrentLoadGroupID; + unsigned CurrentLoadBarrierGroupID; + unsigned CurrentStoreGroupID; public: - LSUnit(const MCSchedModel &SM, unsigned LQ = 0, unsigned SQ = 0, - bool AssumeNoAlias = false); + LSUnit(const MCSchedModel &SM) + : LSUnit(SM, /* LQSize */ 0, /* SQSize */ 0, /* NoAlias */ false) {} + LSUnit(const MCSchedModel &SM, unsigned LQ, unsigned SQ) + : LSUnit(SM, LQ, SQ, /* NoAlias */ false) {} + LSUnit(const MCSchedModel &SM, unsigned LQ, unsigned SQ, bool AssumeNoAlias) + : LSUnitBase(SM, LQ, SQ, AssumeNoAlias), CurrentLoadGroupID(0), + CurrentLoadBarrierGroupID(0), CurrentStoreGroupID(0) {} -#ifndef NDEBUG - void dump() const; -#endif - - enum Status { LSU_AVAILABLE = 0, LSU_LQUEUE_FULL, LSU_SQUEUE_FULL }; - - // Returns LSU_AVAILABLE if there are enough load/store queue entries to serve - // IR. It also returns LSU_AVAILABLE if IR is not a memory operation. - Status isAvailable(const InstRef &IR) const; - - // Allocates load/store queue resources for IR. - // - // This method assumes that a previous call to `isAvailable(IR)` returned - // LSU_AVAILABLE, and that IR is a memory operation. - void dispatch(const InstRef &IR); + /// Returns LSU_AVAILABLE if there are enough load/store queue entries to + /// accomodate instruction IR. + Status isAvailable(const InstRef &IR) const override; - // By default, rules are: - // 1. A store may not pass a previous store. - // 2. A load may not pass a previous store unless flag 'NoAlias' is set. - // 3. A load may pass a previous load. - // 4. A store may not pass a previous load (regardless of flag 'NoAlias'). - // 5. A load has to wait until an older load barrier is fully executed. - // 6. A store has to wait until an older store barrier is fully executed. - virtual bool isReady(const InstRef &IR) const; + /// Allocates LS resources for instruction IR. + /// + /// This method assumes that a previous call to `isAvailable(IR)` succeeded + /// returning LSU_AVAILABLE. + /// + /// Rules are: + /// By default, rules are: + /// 1. A store may not pass a previous store. + /// 2. A load may not pass a previous store unless flag 'NoAlias' is set. + /// 3. A load may pass a previous load. + /// 4. A store may not pass a previous load (regardless of flag 'NoAlias'). + /// 5. A load has to wait until an older load barrier is fully executed. + /// 6. A store has to wait until an older store barrier is fully executed. + unsigned dispatch(const InstRef &IR) override; - // Load and store instructions are tracked by their corresponding queues from - // dispatch until the "instruction executed" event. - // Only when a load instruction reaches the 'Executed' stage, its value - // becomes available to the users. At that point, the load no longer needs to - // be tracked by the load queue. // FIXME: For simplicity, we optimistically assume a similar behavior for // store instructions. In practice, store operations don't tend to leave the // store queue until they reach the 'Retired' stage (See PR39830). - void onInstructionExecuted(const InstRef &IR); + void onInstructionExecuted(const InstRef &IR) override; }; } // namespace mca diff --git a/include/llvm/MCA/HardwareUnits/RegisterFile.h b/include/llvm/MCA/HardwareUnits/RegisterFile.h index c23ab0389234..36506327bd29 100644 --- a/include/llvm/MCA/HardwareUnits/RegisterFile.h +++ b/include/llvm/MCA/HardwareUnits/RegisterFile.h @@ -1,9 +1,8 @@ //===--------------------- RegisterFile.h -----------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 @@ -21,6 +20,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSchedule.h" +#include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MCA/HardwareUnits/HardwareUnit.h" #include "llvm/Support/Error.h" @@ -196,7 +196,7 @@ public: // Collect writes that are in a data dependency with RS, and update RS // internal state. - void addRegisterRead(ReadState &RS, SmallVectorImpl<WriteRef> &Writes) const; + void addRegisterRead(ReadState &RS, const MCSubtargetInfo &STI) const; // Removes write \param WS from the register mappings. // Physical registers may be released to reflect this update. diff --git a/include/llvm/MCA/HardwareUnits/ResourceManager.h b/include/llvm/MCA/HardwareUnits/ResourceManager.h index 549a46c247fe..2f91185516fb 100644 --- a/include/llvm/MCA/HardwareUnits/ResourceManager.h +++ b/include/llvm/MCA/HardwareUnits/ResourceManager.h @@ -1,9 +1,8 @@ //===--------------------- ResourceManager.h --------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 @@ -335,13 +334,26 @@ class ResourceManager { // Used to quickly identify groups that own a particular resource unit. std::vector<uint64_t> Resource2Groups; - // A table to map processor resource IDs to processor resource masks. + // A table that maps processor resource IDs to processor resource masks. SmallVector<uint64_t, 8> ProcResID2Mask; + // A table that maps resource indices to actual processor resource IDs in the + // scheduling model. + SmallVector<unsigned, 8> ResIndex2ProcResID; + // Keeps track of which resources are busy, and how many cycles are left // before those become usable again. SmallDenseMap<ResourceRef, unsigned> BusyResources; + // Set of processor resource units available on the target. + uint64_t ProcResUnitMask; + + // Set of processor resource units that are available during this cycle. + uint64_t AvailableProcResUnits; + + // Set of processor resource groups that are currently reserved. + uint64_t ReservedResourceGroups; + // Returns the actual resource unit that will be used. ResourceRef selectPipe(uint64_t ResourceID); @@ -389,7 +401,14 @@ public: // Release a previously reserved processor resource. void releaseResource(uint64_t ResourceID); - bool canBeIssued(const InstrDesc &Desc) const; + // Returns a zero mask if resources requested by Desc are all available during + // this cycle. It returns a non-zero mask value only if there are unavailable + // processor resources; each bit set in the mask represents a busy processor + // resource unit or a reserved processor resource group. + uint64_t checkAvailability(const InstrDesc &Desc) const; + + uint64_t getProcResUnitMask() const { return ProcResUnitMask; } + uint64_t getAvailableProcResUnits() const { return AvailableProcResUnits; } void issueInstruction( const InstrDesc &Desc, diff --git a/include/llvm/MCA/HardwareUnits/RetireControlUnit.h b/include/llvm/MCA/HardwareUnits/RetireControlUnit.h index 71360e984ade..06290141739e 100644 --- a/include/llvm/MCA/HardwareUnits/RetireControlUnit.h +++ b/include/llvm/MCA/HardwareUnits/RetireControlUnit.h @@ -1,9 +1,8 @@ //===---------------------- RetireControlUnit.h -----------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/include/llvm/MCA/HardwareUnits/Scheduler.h b/include/llvm/MCA/HardwareUnits/Scheduler.h index 351ea4827df9..27beb842dfd2 100644 --- a/include/llvm/MCA/HardwareUnits/Scheduler.h +++ b/include/llvm/MCA/HardwareUnits/Scheduler.h @@ -1,9 +1,8 @@ //===--------------------- Scheduler.h ------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 @@ -68,22 +67,6 @@ public: /// resources. This class is also responsible for tracking the progress of /// instructions from the dispatch stage, until the write-back stage. /// -/// An instruction dispatched to the Scheduler is initially placed into either -/// the 'WaitSet' or the 'ReadySet' depending on the availability of the input -/// operands. -/// -/// An instruction is moved from the WaitSet to the ReadySet when register -/// operands become available, and all memory dependencies are met. -/// Instructions that are moved from the WaitSet to the ReadySet transition -/// in state from 'IS_AVAILABLE' to 'IS_READY'. -/// -/// On every cycle, the Scheduler checks if it can promote instructions from the -/// WaitSet to the ReadySet. -/// -/// An Instruction is moved from the ReadySet the `IssuedSet` when it is issued -/// to a (one or more) pipeline(s). This event also causes an instruction state -/// transition (i.e. from state IS_READY, to state IS_EXECUTING). An Instruction -/// leaves the IssuedSet when it reaches the write-back stage. class Scheduler : public HardwareUnit { LSUnit &LSU; @@ -93,10 +76,58 @@ class Scheduler : public HardwareUnit { // Hardware resources that are managed by this scheduler. std::unique_ptr<ResourceManager> Resources; + // Instructions dispatched to the Scheduler are internally classified based on + // the instruction stage (see Instruction::InstrStage). + // + // An Instruction dispatched to the Scheduler is added to the WaitSet if not + // all its register operands are available, and at least one latency is + // unknown. By construction, the WaitSet only contains instructions that are + // in the IS_DISPATCHED stage. + // + // An Instruction transitions from the WaitSet to the PendingSet if the + // instruction is not ready yet, but the latency of every register read is + // known. Instructions in the PendingSet can only be in the IS_PENDING or + // IS_READY stage. Only IS_READY instructions that are waiting on memory + // dependencies can be added to the PendingSet. + // + // Instructions in the PendingSet are immediately dominated only by + // instructions that have already been issued to the underlying pipelines. In + // the presence of bottlenecks caused by data dependencies, the PendingSet can + // be inspected to identify problematic data dependencies between + // instructions. + // + // An instruction is moved to the ReadySet when all register operands become + // available, and all memory dependencies are met. Instructions that are + // moved from the PendingSet to the ReadySet must transition to the 'IS_READY' + // stage. + // + // On every cycle, the Scheduler checks if it can promote instructions from the + // PendingSet to the ReadySet. + // + // An Instruction is moved from the ReadySet to the `IssuedSet` when it starts + // exection. This event also causes an instruction state transition (i.e. from + // state IS_READY, to state IS_EXECUTING). An Instruction leaves the IssuedSet + // only when it reaches the write-back stage. std::vector<InstRef> WaitSet; + std::vector<InstRef> PendingSet; std::vector<InstRef> ReadySet; std::vector<InstRef> IssuedSet; + // A mask of busy resource units. It defaults to the empty set (i.e. a zero + // mask), and it is cleared at the beginning of every cycle. + // It is updated every time the scheduler fails to issue an instruction from + // the ready set due to unavailable pipeline resources. + // Each bit of the mask represents an unavailable resource. + uint64_t BusyResourceUnits; + + // Counts the number of instructions in the pending set that were dispatched + // during this cycle. + unsigned NumDispatchedToThePendingSet; + + // True if the previous pipeline Stage was unable to dispatch a full group of + // opcodes because scheduler buffers (or LS queues) were unavailable. + bool HadTokenStall; + /// Verify the given selection strategy and set the Strategy member /// accordingly. If no strategy is provided, the DefaultSchedulerStrategy is /// used. @@ -112,9 +143,15 @@ class Scheduler : public HardwareUnit { // vector 'Executed'. void updateIssuedSet(SmallVectorImpl<InstRef> &Executed); - // Try to promote instructions from WaitSet to ReadySet. + // Try to promote instructions from the PendingSet to the ReadySet. // Add promoted instructions to the 'Ready' vector in input. - void promoteToReadySet(SmallVectorImpl<InstRef> &Ready); + // Returns true if at least one instruction was promoted. + bool promoteToReadySet(SmallVectorImpl<InstRef> &Ready); + + // Try to promote instructions from the WaitSet to the PendingSet. + // Add promoted instructions to the 'Pending' vector in input. + // Returns true if at least one instruction was promoted. + bool promoteToPendingSet(SmallVectorImpl<InstRef> &Pending); public: Scheduler(const MCSchedModel &Model, LSUnit &Lsu) @@ -127,7 +164,8 @@ public: Scheduler(std::unique_ptr<ResourceManager> RM, LSUnit &Lsu, std::unique_ptr<SchedulerStrategy> SelectStrategy) - : LSU(Lsu), Resources(std::move(RM)) { + : LSU(Lsu), Resources(std::move(RM)), BusyResourceUnits(0), + NumDispatchedToThePendingSet(0), HadTokenStall(false) { initializeStrategy(std::move(SelectStrategy)); } @@ -140,15 +178,12 @@ public: SC_DISPATCH_GROUP_STALL, }; - /// Check if the instruction in 'IR' can be dispatched and returns an answer - /// in the form of a Status value. + /// Check if the instruction in 'IR' can be dispatched during this cycle. + /// Return SC_AVAILABLE if both scheduler and LS resources are available. /// - /// The DispatchStage is responsible for querying the Scheduler before - /// dispatching new instructions. This routine is used for performing such - /// a query. If the instruction 'IR' can be dispatched, then true is - /// returned, otherwise false is returned with Event set to the stall type. - /// Internally, it also checks if the load/store unit is available. - Status isAvailable(const InstRef &IR) const; + /// This method is also responsible for setting field HadTokenStall if + /// IR cannot be dispatched to the Scheduler due to unavailable resources. + Status isAvailable(const InstRef &IR); /// Reserves buffer and LSUnit queue resources that are necessary to issue /// this instruction. @@ -156,11 +191,11 @@ public: /// Returns true if instruction IR is ready to be issued to the underlying /// pipelines. Note that this operation cannot fail; it assumes that a /// previous call to method `isAvailable(IR)` returned `SC_AVAILABLE`. - void dispatch(const InstRef &IR); - - /// Returns true if IR is ready to be executed by the underlying pipelines. - /// This method assumes that IR has been previously dispatched. - bool isReady(const InstRef &IR) const; + /// + /// If IR is a memory operation, then the Scheduler queries the LS unit to + /// obtain a LS token. An LS token is used internally to track memory + /// dependencies. + bool dispatch(InstRef &IR); /// Issue an instruction and populates a vector of used pipeline resources, /// and a vector of instructions that transitioned to the ready state as a @@ -168,6 +203,7 @@ public: void issueInstruction( InstRef &IR, SmallVectorImpl<std::pair<ResourceRef, ResourceCycles>> &Used, + SmallVectorImpl<InstRef> &Pending, SmallVectorImpl<InstRef> &Ready); /// Returns true if IR has to be issued immediately, or if IR is a zero @@ -181,9 +217,15 @@ public: /// have changed in state, and that are now available to new instructions. /// Instructions executed are added to vector Executed, while vector Ready is /// populated with instructions that have become ready in this new cycle. + /// Vector Pending is popluated by instructions that have transitioned through + /// the pending stat during this cycle. The Pending and Ready sets may not be + /// disjoint. An instruction is allowed to transition from the WAIT state to + /// the READY state (going through the PENDING state) within a single cycle. + /// That means, instructions may appear in both the Pending and Ready set. void cycleEvent(SmallVectorImpl<ResourceRef> &Freed, - SmallVectorImpl<InstRef> &Ready, - SmallVectorImpl<InstRef> &Executed); + SmallVectorImpl<InstRef> &Executed, + SmallVectorImpl<InstRef> &Pending, + SmallVectorImpl<InstRef> &Ready); /// Convert a resource mask into a valid llvm processor resource identifier. unsigned getResourceID(uint64_t Mask) const { @@ -195,6 +237,26 @@ public: /// resources are not available. InstRef select(); + bool isReadySetEmpty() const { return ReadySet.empty(); } + bool isWaitSetEmpty() const { return WaitSet.empty(); } + + /// This method is called by the ExecuteStage at the end of each cycle to + /// identify bottlenecks caused by data dependencies. Vector RegDeps is + /// populated by instructions that were not issued because of unsolved + /// register dependencies. Vector MemDeps is populated by instructions that + /// were not issued because of unsolved memory dependencies. + void analyzeDataDependencies(SmallVectorImpl<InstRef> &RegDeps, + SmallVectorImpl<InstRef> &MemDeps); + + /// Returns a mask of busy resources, and populates vector Insts with + /// instructions that could not be issued to the underlying pipelines because + /// not all pipeline resources were available. + uint64_t analyzeResourcePressure(SmallVectorImpl<InstRef> &Insts); + + // Returns true if the dispatch logic couldn't dispatch a full group due to + // unavailable scheduler and/or LS resources. + bool hadTokenStall() const { return HadTokenStall; } + #ifndef NDEBUG // Update the ready queues. void dump() const; diff --git a/include/llvm/MCA/InstrBuilder.h b/include/llvm/MCA/InstrBuilder.h index 5f998db5e4ce..690016354f7a 100644 --- a/include/llvm/MCA/InstrBuilder.h +++ b/include/llvm/MCA/InstrBuilder.h @@ -1,9 +1,8 @@ //===--------------------- InstrBuilder.h -----------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/include/llvm/MCA/Instruction.h b/include/llvm/MCA/Instruction.h index b91610c64d85..d4d3f22797f7 100644 --- a/include/llvm/MCA/Instruction.h +++ b/include/llvm/MCA/Instruction.h @@ -1,9 +1,8 @@ //===--------------------- Instruction.h ------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 @@ -81,6 +80,15 @@ struct ReadDescriptor { class ReadState; +/// A critical data dependency descriptor. +/// +/// Field RegID is set to the invalid register for memory dependencies. +struct CriticalDependency { + unsigned IID; + unsigned RegID; + unsigned Cycles; +}; + /// Tracks uses of a register definition (e.g. register write). /// /// Each implicit/explicit register write is associated with an instance of @@ -124,9 +132,11 @@ class WriteState { // A partial write that is in a false dependency with this write. WriteState *PartialWrite; - unsigned DependentWriteCyclesLeft; + // Critical register dependency for this write. + CriticalDependency CRD; + // A list of dependent reads. Users is a set of dependent // reads. A dependent read is added to the set only if CyclesLeft // is "unknown". As soon as CyclesLeft is 'known', each user in the set @@ -141,7 +151,7 @@ public: : WD(&Desc), CyclesLeft(UNKNOWN_CYCLES), RegisterID(RegID), PRFID(0), ClearsSuperRegs(clearsSuperRegs), WritesZero(writesZero), IsEliminated(false), DependentWrite(nullptr), PartialWrite(nullptr), - DependentWriteCyclesLeft(0) {} + DependentWriteCyclesLeft(0), CRD() {} WriteState(const WriteState &Other) = default; WriteState &operator=(const WriteState &Other) = default; @@ -151,13 +161,21 @@ public: unsigned getRegisterID() const { return RegisterID; } unsigned getRegisterFileID() const { return PRFID; } unsigned getLatency() const { return WD->Latency; } - - void addUser(ReadState *Use, int ReadAdvance); - void addUser(WriteState *Use); - unsigned getDependentWriteCyclesLeft() const { return DependentWriteCyclesLeft; } + const WriteState *getDependentWrite() const { return DependentWrite; } + const CriticalDependency &getCriticalRegDep() const { return CRD; } + + // This method adds Use to the set of data dependent reads. IID is the + // instruction identifier associated with this write. ReadAdvance is the + // number of cycles to subtract from the latency of this data dependency. + // Use is in a RAW dependency with this write. + void addUser(unsigned IID, ReadState *Use, int ReadAdvance); + + // Use is a younger register write that is in a false dependency with this + // write. IID is the instruction identifier associated with this write. + void addUser(unsigned IID, WriteState *Use); unsigned getNumUsers() const { unsigned NumUsers = Users.size(); @@ -169,17 +187,20 @@ public: bool clearsSuperRegisters() const { return ClearsSuperRegs; } bool isWriteZero() const { return WritesZero; } bool isEliminated() const { return IsEliminated; } - bool isExecuted() const { - return CyclesLeft != UNKNOWN_CYCLES && CyclesLeft <= 0; + + bool isReady() const { + if (DependentWrite) + return false; + unsigned CyclesLeft = getDependentWriteCyclesLeft(); + return !CyclesLeft || CyclesLeft < getLatency(); } - const WriteState *getDependentWrite() const { return DependentWrite; } - void setDependentWrite(WriteState *Other) { DependentWrite = Other; } - void writeStartEvent(unsigned Cycles) { - DependentWriteCyclesLeft = Cycles; - DependentWrite = nullptr; + bool isExecuted() const { + return CyclesLeft != UNKNOWN_CYCLES && CyclesLeft <= 0; } + void setDependentWrite(const WriteState *Other) { DependentWrite = Other; } + void writeStartEvent(unsigned IID, unsigned RegID, unsigned Cycles); void setWriteZero() { WritesZero = true; } void setEliminated() { assert(Users.empty() && "Write is in an inconsistent state."); @@ -191,7 +212,7 @@ public: // On every cycle, update CyclesLeft and notify dependent users. void cycleEvent(); - void onInstructionIssued(); + void onInstructionIssued(unsigned IID); #ifndef NDEBUG void dump() const; @@ -221,6 +242,8 @@ class ReadState { // dependent writes (i.e. field DependentWrite) is zero, this value is // propagated to field CyclesLeft. unsigned TotalCycles; + // Longest register dependency. + CriticalDependency CRD; // This field is set to true only if there are no dependent writes, and // there are no `CyclesLeft' to wait. bool IsReady; @@ -232,14 +255,16 @@ class ReadState { public: ReadState(const ReadDescriptor &Desc, unsigned RegID) : RD(&Desc), RegisterID(RegID), PRFID(0), DependentWrites(0), - CyclesLeft(UNKNOWN_CYCLES), TotalCycles(0), IsReady(true), + CyclesLeft(UNKNOWN_CYCLES), TotalCycles(0), CRD(), IsReady(true), IsZero(false), IndependentFromDef(false) {} const ReadDescriptor &getDescriptor() const { return *RD; } unsigned getSchedClass() const { return RD->SchedClassID; } unsigned getRegisterID() const { return RegisterID; } unsigned getRegisterFileID() const { return PRFID; } + const CriticalDependency &getCriticalRegDep() const { return CRD; } + bool isPending() const { return !IndependentFromDef && CyclesLeft > 0; } bool isReady() const { return IsReady; } bool isImplicitRead() const { return RD->isImplicitRead(); } @@ -247,7 +272,7 @@ public: void setIndependentFromDef() { IndependentFromDef = true; } void cycleEvent(); - void writeStartEvent(unsigned Cycles); + void writeStartEvent(unsigned IID, unsigned RegID, unsigned Cycles); void setDependentWrites(unsigned Writes) { DependentWrites = Writes; IsReady = !Writes; @@ -330,9 +355,16 @@ struct InstrDesc { // A list of buffered resources consumed by this instruction. SmallVector<uint64_t, 4> Buffers; + unsigned UsedProcResUnits; + unsigned UsedProcResGroups; + unsigned MaxLatency; // Number of MicroOps for this instruction. unsigned NumMicroOps; + // SchedClassID used to construct this InstrDesc. + // This information is currently used by views to do fast queries on the + // subtarget when computing the reciprocal throughput. + unsigned SchedClassID; bool MayLoad; bool MayStore; @@ -398,6 +430,7 @@ public: // Returns true if this instruction is a candidate for move elimination. bool isOptimizableMove() const { return IsOptimizableMove; } void setOptimizableMove() { IsOptimizableMove = true; } + bool isMemOp() const { return Desc.MayLoad || Desc.MayStore; } }; /// An instruction propagated through the simulated instruction pipeline. @@ -406,12 +439,13 @@ public: /// that are sent to the various components of the simulated hardware pipeline. class Instruction : public InstructionBase { enum InstrStage { - IS_INVALID, // Instruction in an invalid state. - IS_AVAILABLE, // Instruction dispatched but operands are not ready. - IS_READY, // Instruction dispatched and operands ready. - IS_EXECUTING, // Instruction issued. - IS_EXECUTED, // Instruction executed. Values are written back. - IS_RETIRED // Instruction retired. + IS_INVALID, // Instruction in an invalid state. + IS_DISPATCHED, // Instruction dispatched but operands are not ready. + IS_PENDING, // Instruction is not ready, but operand latency is known. + IS_READY, // Instruction dispatched and operands ready. + IS_EXECUTING, // Instruction issued. + IS_EXECUTED, // Instruction executed. Values are written back. + IS_RETIRED // Instruction retired. }; // The current instruction stage. @@ -424,12 +458,34 @@ class Instruction : public InstructionBase { // Retire Unit token ID for this instruction. unsigned RCUTokenID; + // LS token ID for this instruction. + // This field is set to the invalid null token if this is not a memory + // operation. + unsigned LSUTokenID; + + // Critical register dependency. + CriticalDependency CriticalRegDep; + + // Critical memory dependency. + CriticalDependency CriticalMemDep; + + // A bitmask of busy processor resource units. + // This field is set to zero only if execution is not delayed during this + // cycle because of unavailable pipeline resources. + uint64_t CriticalResourceMask; + + // True if this instruction has been optimized at register renaming stage. + bool IsEliminated; + public: Instruction(const InstrDesc &D) : InstructionBase(D), Stage(IS_INVALID), CyclesLeft(UNKNOWN_CYCLES), - RCUTokenID(0) {} + RCUTokenID(0), LSUTokenID(0), CriticalRegDep(), CriticalMemDep(), + CriticalResourceMask(0), IsEliminated(false) {} unsigned getRCUTokenID() const { return RCUTokenID; } + unsigned getLSUTokenID() const { return LSUTokenID; } + void setLSUTokenID(unsigned LSUTok) { LSUTokenID = LSUTok; } int getCyclesLeft() const { return CyclesLeft; } // Transition to the dispatch stage, and assign a RCUToken to this @@ -438,37 +494,48 @@ public: void dispatch(unsigned RCUTokenID); // Instruction issued. Transition to the IS_EXECUTING state, and update - // all the definitions. - void execute(); + // all the register definitions. + void execute(unsigned IID); - // Force a transition from the IS_AVAILABLE state to the IS_READY state if - // input operands are all ready. State transitions normally occur at the - // beginning of a new cycle (see method cycleEvent()). However, the scheduler - // may decide to promote instructions from the wait queue to the ready queue - // as the result of another issue event. This method is called every time the - // instruction might have changed in state. + // Force a transition from the IS_DISPATCHED state to the IS_READY or + // IS_PENDING state. State transitions normally occur either at the beginning + // of a new cycle (see method cycleEvent()), or as a result of another issue + // event. This method is called every time the instruction might have changed + // in state. It internally delegates to method updateDispatched() and + // updateWaiting(). void update(); + bool updateDispatched(); + bool updatePending(); - bool isDispatched() const { return Stage == IS_AVAILABLE; } + bool isDispatched() const { return Stage == IS_DISPATCHED; } + bool isPending() const { return Stage == IS_PENDING; } bool isReady() const { return Stage == IS_READY; } bool isExecuting() const { return Stage == IS_EXECUTING; } bool isExecuted() const { return Stage == IS_EXECUTED; } bool isRetired() const { return Stage == IS_RETIRED; } + bool isEliminated() const { return IsEliminated; } - bool isEliminated() const { - return isReady() && getDefs().size() && - all_of(getDefs(), - [](const WriteState &W) { return W.isEliminated(); }); - } - - // Forces a transition from state IS_AVAILABLE to state IS_EXECUTED. + // Forces a transition from state IS_DISPATCHED to state IS_EXECUTED. void forceExecuted(); + void setEliminated() { IsEliminated = true; } void retire() { assert(isExecuted() && "Instruction is in an invalid state!"); Stage = IS_RETIRED; } + const CriticalDependency &getCriticalRegDep() const { return CriticalRegDep; } + const CriticalDependency &getCriticalMemDep() const { return CriticalMemDep; } + const CriticalDependency &computeCriticalRegDep(); + void setCriticalMemDep(const CriticalDependency &MemDep) { + CriticalMemDep = MemDep; + } + + uint64_t getCriticalResourceMask() const { return CriticalResourceMask; } + void setCriticalResourceMask(uint64_t ResourceMask) { + CriticalResourceMask = ResourceMask; + } + void cycleEvent(); }; @@ -483,13 +550,17 @@ public: InstRef(unsigned Index, Instruction *I) : Data(std::make_pair(Index, I)) {} bool operator==(const InstRef &Other) const { return Data == Other.Data; } + bool operator!=(const InstRef &Other) const { return Data != Other.Data; } + bool operator<(const InstRef &Other) const { + return Data.first < Other.Data.first; + } unsigned getSourceIndex() const { return Data.first; } Instruction *getInstruction() { return Data.second; } const Instruction *getInstruction() const { return Data.second; } /// Returns true if this references a valid instruction. - operator bool() const { return Data.second != nullptr; } + explicit operator bool() const { return Data.second != nullptr; } /// Invalidate this reference. void invalidate() { Data.second = nullptr; } @@ -537,7 +608,7 @@ public: return !WS || WS->isExecuted(); } - bool isValid() const { return Data.first != INVALID_IID && Data.second; } + bool isValid() const { return Data.second && Data.first != INVALID_IID; } bool operator==(const WriteRef &Other) const { return Data == Other.Data; } #ifndef NDEBUG diff --git a/include/llvm/MCA/Pipeline.h b/include/llvm/MCA/Pipeline.h index acd256060bdd..935033f67f8b 100644 --- a/include/llvm/MCA/Pipeline.h +++ b/include/llvm/MCA/Pipeline.h @@ -1,9 +1,8 @@ //===--------------------- Pipeline.h ---------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/include/llvm/MCA/SourceMgr.h b/include/llvm/MCA/SourceMgr.h index 5e0ca6419f5d..dbe31db1b1dd 100644 --- a/include/llvm/MCA/SourceMgr.h +++ b/include/llvm/MCA/SourceMgr.h @@ -1,9 +1,8 @@ //===--------------------- SourceMgr.h --------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/include/llvm/MCA/Stages/DispatchStage.h b/include/llvm/MCA/Stages/DispatchStage.h index f015cd7522eb..d80ededeaca1 100644 --- a/include/llvm/MCA/Stages/DispatchStage.h +++ b/include/llvm/MCA/Stages/DispatchStage.h @@ -1,9 +1,8 @@ //===----------------------- DispatchStage.h --------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 @@ -62,8 +61,6 @@ class DispatchStage final : public Stage { bool canDispatch(const InstRef &IR) const; Error dispatch(InstRef IR); - void updateRAWDependencies(ReadState &RS, const MCSubtargetInfo &STI); - void notifyInstructionDispatched(const InstRef &IR, ArrayRef<unsigned> UsedPhysRegs, unsigned uOps) const; @@ -71,9 +68,7 @@ class DispatchStage final : public Stage { public: DispatchStage(const MCSubtargetInfo &Subtarget, const MCRegisterInfo &MRI, unsigned MaxDispatchWidth, RetireControlUnit &R, - RegisterFile &F) - : DispatchWidth(MaxDispatchWidth), AvailableEntries(MaxDispatchWidth), - CarryOver(0U), CarriedOver(), STI(Subtarget), RCU(R), PRF(F) {} + RegisterFile &F); bool isAvailable(const InstRef &IR) const override; diff --git a/include/llvm/MCA/Stages/EntryStage.h b/include/llvm/MCA/Stages/EntryStage.h index cd9a65b8cc2b..59a2daff886e 100644 --- a/include/llvm/MCA/Stages/EntryStage.h +++ b/include/llvm/MCA/Stages/EntryStage.h @@ -1,9 +1,8 @@ //===---------------------- EntryStage.h ------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/include/llvm/MCA/Stages/ExecuteStage.h b/include/llvm/MCA/Stages/ExecuteStage.h index 8cb287e06d9f..03737e0220eb 100644 --- a/include/llvm/MCA/Stages/ExecuteStage.h +++ b/include/llvm/MCA/Stages/ExecuteStage.h @@ -1,9 +1,8 @@ //===---------------------- ExecuteStage.h ----------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 @@ -29,6 +28,12 @@ namespace mca { class ExecuteStage final : public Stage { Scheduler &HWS; + unsigned NumDispatchedOpcodes; + unsigned NumIssuedOpcodes; + + // True if this stage should notify listeners of HWPressureEvents. + bool EnablePressureEvents; + Error issueInstruction(InstRef &IR); // Called at the beginning of each cycle to issue already dispatched @@ -42,7 +47,10 @@ class ExecuteStage final : public Stage { ExecuteStage &operator=(const ExecuteStage &Other) = delete; public: - ExecuteStage(Scheduler &S) : Stage(), HWS(S) {} + ExecuteStage(Scheduler &S) : ExecuteStage(S, false) {} + ExecuteStage(Scheduler &S, bool ShouldPerformBottleneckAnalysis) + : Stage(), HWS(S), NumDispatchedOpcodes(0), NumIssuedOpcodes(0), + EnablePressureEvents(ShouldPerformBottleneckAnalysis) {} // This stage works under the assumption that the Pipeline will eventually // execute a retire stage. We don't need to check if pipelines and/or @@ -61,12 +69,14 @@ public: // Instructions that transitioned to the 'Executed' state are automatically // moved to the next stage (i.e. RetireStage). Error cycleStart() override; + Error cycleEnd() override; Error execute(InstRef &IR) override; void notifyInstructionIssued( const InstRef &IR, MutableArrayRef<std::pair<ResourceRef, ResourceCycles>> Used) const; void notifyInstructionExecuted(const InstRef &IR) const; + void notifyInstructionPending(const InstRef &IR) const; void notifyInstructionReady(const InstRef &IR) const; void notifyResourceAvailable(const ResourceRef &RR) const; diff --git a/include/llvm/MCA/Stages/InstructionTables.h b/include/llvm/MCA/Stages/InstructionTables.h index 34e338f0ce6b..4b463c9b51c1 100644 --- a/include/llvm/MCA/Stages/InstructionTables.h +++ b/include/llvm/MCA/Stages/InstructionTables.h @@ -1,9 +1,8 @@ //===--------------------- InstructionTables.h ------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/include/llvm/MCA/Stages/MicroOpQueueStage.h b/include/llvm/MCA/Stages/MicroOpQueueStage.h new file mode 100644 index 000000000000..50a5ef87b2d2 --- /dev/null +++ b/include/llvm/MCA/Stages/MicroOpQueueStage.h @@ -0,0 +1,88 @@ +//===---------------------- MicroOpQueueStage.h -----------------*- 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 defines a stage that implements a queue of micro opcodes. +/// It can be used to simulate a hardware micro-op queue that serves opcodes to +/// the out of order backend. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_MCA_MICRO_OP_QUEUE_STAGE_H +#define LLVM_MCA_MICRO_OP_QUEUE_STAGE_H + +#include "llvm/ADT/SmallVector.h" +#include "llvm/MCA/Stages/Stage.h" + +namespace llvm { +namespace mca { + +/// A stage that simulates a queue of instruction opcodes. +class MicroOpQueueStage : public Stage { + SmallVector<InstRef, 8> Buffer; + unsigned NextAvailableSlotIdx; + unsigned CurrentInstructionSlotIdx; + + // Limits the number of instructions that can be written to this buffer every + // cycle. A value of zero means that there is no limit to the instruction + // throughput in input. + const unsigned MaxIPC; + unsigned CurrentIPC; + + // Number of entries that are available during this cycle. + unsigned AvailableEntries; + + // True if instructions dispatched to this stage don't need to wait for the + // next cycle before moving to the next stage. + // False if this buffer acts as a one cycle delay in the execution pipeline. + bool IsZeroLatencyStage; + + MicroOpQueueStage(const MicroOpQueueStage &Other) = delete; + MicroOpQueueStage &operator=(const MicroOpQueueStage &Other) = delete; + + // By default, an instruction consumes a number of buffer entries equal to its + // number of micro opcodes (see field `InstrDesc::NumMicroOpcodes`). The + // number of entries consumed by an instruction is normalized to the + // minimum value between NumMicroOpcodes and the buffer size. This is to avoid + // problems with (microcoded) instructions that generate a number of micro + // opcodes than doesn't fit in the buffer. + unsigned getNormalizedOpcodes(const InstRef &IR) const { + unsigned NormalizedOpcodes = + std::min(static_cast<unsigned>(Buffer.size()), + IR.getInstruction()->getDesc().NumMicroOps); + return NormalizedOpcodes ? NormalizedOpcodes : 1U; + } + + Error moveInstructions(); + +public: + MicroOpQueueStage(unsigned Size, unsigned IPC = 0, + bool ZeroLatencyStage = true); + + bool isAvailable(const InstRef &IR) const override { + if (MaxIPC && CurrentIPC == MaxIPC) + return false; + unsigned NormalizedOpcodes = getNormalizedOpcodes(IR); + if (NormalizedOpcodes > AvailableEntries) + return false; + return true; + } + + bool hasWorkToComplete() const override { + return AvailableEntries != Buffer.size(); + } + + Error execute(InstRef &IR) override; + Error cycleStart() override; + Error cycleEnd() override; +}; + +} // namespace mca +} // namespace llvm + +#endif // LLVM_MCA_MICRO_OP_QUEUE_STAGE_H diff --git a/include/llvm/MCA/Stages/RetireStage.h b/include/llvm/MCA/Stages/RetireStage.h index 2051ce5c86ad..08c216ac7bf4 100644 --- a/include/llvm/MCA/Stages/RetireStage.h +++ b/include/llvm/MCA/Stages/RetireStage.h @@ -1,9 +1,8 @@ //===---------------------- RetireStage.h -----------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/include/llvm/MCA/Stages/Stage.h b/include/llvm/MCA/Stages/Stage.h index fc7ab569bb0f..46b242caa6cf 100644 --- a/include/llvm/MCA/Stages/Stage.h +++ b/include/llvm/MCA/Stages/Stage.h @@ -1,9 +1,8 @@ //===---------------------- Stage.h -----------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 diff --git a/include/llvm/MCA/Support.h b/include/llvm/MCA/Support.h index 7b0c5bf3a486..1da097c90922 100644 --- a/include/llvm/MCA/Support.h +++ b/include/llvm/MCA/Support.h @@ -1,9 +1,8 @@ //===--------------------- Support.h ----------------------------*- C++ -*-===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// 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 @@ -61,24 +60,13 @@ public: return (Denominator == 1) ? Numerator : (double)Numerator / Denominator; } + unsigned getNumerator() const { return Numerator; } + unsigned getDenominator() const { return Denominator; } + // Add the components of RHS to this instance. Instead of calculating // the final value here, we keep track of the numerator and denominator // separately, to reduce floating point error. - ResourceCycles &operator+=(const ResourceCycles &RHS) { - if (Denominator == RHS.Denominator) - Numerator += RHS.Numerator; - else { - // Create a common denominator for LHS and RHS by calculating the least - // common multiple from the GCD. - unsigned GCD = GreatestCommonDivisor64(Denominator, RHS.Denominator); - unsigned LCM = (Denominator * RHS.Denominator) / GCD; - unsigned LHSNumerator = Numerator * (LCM / Denominator); - unsigned RHSNumerator = RHS.Numerator * (LCM / RHS.Denominator); - Numerator = LHSNumerator + RHSNumerator; - Denominator = LCM; - } - return *this; - } + ResourceCycles &operator+=(const ResourceCycles &RHS); }; /// Populates vector Masks with processor resource masks. @@ -106,6 +94,13 @@ public: void computeProcResourceMasks(const MCSchedModel &SM, MutableArrayRef<uint64_t> Masks); +// Returns the index of the highest bit set. For resource masks, the position of +// the highest bit set can be used to construct a resource mask identifier. +inline unsigned getResourceStateIndex(uint64_t Mask) { + assert(Mask && "Processor Resource Mask cannot be zero!"); + return (std::numeric_limits<uint64_t>::digits - countLeadingZeros(Mask)) - 1; +} + /// Compute the reciprocal block throughput from a set of processor resource /// cycles. The reciprocal block throughput is computed as the MAX between: /// - NumMicroOps / DispatchWidth |
