This patch changes how instruction execution is orchestrated by the Pipeline.
In particular, this patch makes it more explicit how instructions transition through the various pipeline stages during execution.
The main goal is to simplify the stage API and simpify the Pipeline execution. At the same time, this patch fixes some issues which are currently latent, but that are likely to cause problems in future if people define custom pipelines.
The new design assumes that each pipeline stage knows the "next-in-sequence".
The Stage API has gained two new methods:
- isAvailable(IR)
- checkNextStage(IR)
- moveToTheNextStage(IR).
An instruction IR can be executed by a Stage if method Stage::isAvailable(IR) returns true.
Stage may accept a limited number of instructions per cycle. So, method isAvailable(IR) is responsible for checking if instruction IR can execute within the current cycle.
Instructions can move to next stages using method moveToTheNextStage(IR).
An instruction cannot be moved to the next stage if method checkNextStage(IR) (called on the current stage) returns false.
Instruction IR may be allowed to transition through multiple stages in a single cycle, provided that stages are available, and that checkNextStage(IR) always returns true.
Since now stages know about their "next-in-sequence", some methods in the Stage interface have become now redundant. For example, this patch gets rid of Stage::preExecute() and Stage::postExecute().
Method Pipeline::runCycle() is now much simpler, and it correctly visits stages in post-order at the beginning/end of a cycle. Before, this caused an issue with the sequence of stages in the default pipeline; that issue is now fixed.
Other changes:
- DispatchStage no longer requires a reference to the Scheduler.
- ExecuteStage no longer needs to directly interact with the RetireControlUnit. Instead, executed instructions are now directly moved to the next stage (i.e. the retire stage).
- RetireStage gained an execute method. This allowed us to remove the dependency with the RCU in ExecuteStage.
- FecthStage now updates the "program counter" during cycleBegin() (i.e. before we start executing new instructions).
- We no longer need Stage::Status to be returned by method execute(). It has been dropped in favor of a more lightweight llvm::Error.
With this change, the Pipeline logic is simplified. We no longer generate extra calls to pre/postExecute() every instruction. We also avoid redundant execute(IR) calls on stages that are not reached by IR (that was an oddity of the previous design).
Overally, when testing a debug build of llvm-mca, I measured a ~11% performance gain w.r.t. the previous design. I also think that the Stage interface is probably easier to read now. That being said, code comments have to be improved (I plan to do it in a follow-up patch).
Please let me know if okay to commit.
-Andrea
I plan to change that variable name to "StagePipeline".
Just to be consistent with the other naming convention.