2018-06-25 18:53:00 +02:00
|
|
|
//===--------------------- Pipeline.cpp -------------------------*- C++ -*-===//
|
2018-03-08 14:05:02 +01:00
|
|
|
//
|
2019-01-19 09:50:56 +01:00
|
|
|
// 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
|
2018-03-08 14:05:02 +01:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// \file
|
|
|
|
///
|
2018-06-25 18:53:00 +02:00
|
|
|
/// This file implements an ordered container of stages that simulate the
|
|
|
|
/// pipeline of a hardware backend.
|
2018-03-08 14:05:02 +01:00
|
|
|
///
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2018-12-17 09:08:31 +01:00
|
|
|
#include "llvm/MCA/Pipeline.h"
|
|
|
|
#include "llvm/MCA/HWEventListener.h"
|
2018-03-08 14:05:02 +01:00
|
|
|
#include "llvm/Support/Debug.h"
|
|
|
|
|
2018-10-30 16:56:08 +01:00
|
|
|
namespace llvm {
|
2018-03-08 14:05:02 +01:00
|
|
|
namespace mca {
|
|
|
|
|
|
|
|
#define DEBUG_TYPE "llvm-mca"
|
|
|
|
|
2018-06-25 18:53:00 +02:00
|
|
|
void Pipeline::addEventListener(HWEventListener *Listener) {
|
2018-03-08 14:05:02 +01:00
|
|
|
if (Listener)
|
|
|
|
Listeners.insert(Listener);
|
2018-06-27 18:09:33 +02:00
|
|
|
for (auto &S : Stages)
|
|
|
|
S->addListener(Listener);
|
2018-03-08 14:05:02 +01:00
|
|
|
}
|
|
|
|
|
2018-06-25 18:53:00 +02:00
|
|
|
bool Pipeline::hasWorkToProcess() {
|
2018-09-28 12:47:24 +02:00
|
|
|
return any_of(Stages, [](const std::unique_ptr<Stage> &S) {
|
[llvm-mca] Introduce a sequential container of Stages
Summary:
Remove explicit stages and introduce a list of stages.
A pipeline should be composed of an arbitrary list of stages, and not any
predefined list of stages in the Backend. The Backend should not know of any
particular stage, rather it should only be concerned that it has a list of
stages, and that those stages will fulfill the contract of what it means to be
a Stage (namely pre/post/execute a given instruction).
For now, we leave the original set of stages defined in the Backend ctor;
however, I imagine these will be moved out at a later time.
This patch makes an adjustment to the semantics of Stage::isReady.
Specifically, what the Backend really needs to know is if a Stage has
unfinished work. With that said, it is more appropriately renamed
Stage::hasWorkToComplete(). This change will clean up the check in
Backend::run(), allowing us to query each stage to see if there is unfinished
work, regardless of what subclass a stage might be. I feel that this change
simplifies the semantics too, but that's a subjective statement.
Given how RetireStage and ExecuteStage handle data in their preExecute(), I've
had to change the order of Retire and Execute in our stage list. Retire must
complete any of its preExecute actions before ExecuteStage's preExecute can
take control. This is mainly because both stages utilize the RCU. In the
meantime, I want to see if I can adjust that or remove that coupling.
Reviewers: andreadb, RKSimon, courbet
Reviewed By: andreadb
Subscribers: tschuett, gbedwell, llvm-commits
Differential Revision: https://reviews.llvm.org/D46907
llvm-svn: 335361
2018-06-22 18:17:26 +02:00
|
|
|
return S->hasWorkToComplete();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-11-28 20:31:19 +01:00
|
|
|
Expected<unsigned> Pipeline::run() {
|
2018-08-16 17:43:09 +02:00
|
|
|
assert(!Stages.empty() && "Unexpected empty pipeline found!");
|
|
|
|
|
2018-10-24 21:37:45 +02:00
|
|
|
do {
|
2018-07-13 11:27:34 +02:00
|
|
|
notifyCycleBegin();
|
2018-09-28 12:47:24 +02:00
|
|
|
if (Error Err = runCycle())
|
2018-11-28 20:31:19 +01:00
|
|
|
return std::move(Err);
|
2018-07-13 11:27:34 +02:00
|
|
|
notifyCycleEnd();
|
|
|
|
++Cycles;
|
2018-10-24 21:37:45 +02:00
|
|
|
} while (hasWorkToProcess());
|
|
|
|
|
2018-11-28 20:31:19 +01:00
|
|
|
return Cycles;
|
[llvm-mca] Introduce a pipeline Stage class and FetchStage.
Summary:
This is just an idea, really two ideas. I expect some push-back,
but I realize that posting a diff is the most comprehensive way to express
these concepts.
This patch introduces a Stage class which represents the
various stages of an instruction pipeline. As a start, I have created a simple
FetchStage that is based on existing logic for how MCA produces
instructions, but now encapsulated in a Stage. The idea should become more concrete
once we introduce additional stages. The idea being, that when a stage completes,
the next stage in the pipeline will be executed. Stages are chained together
as a singly linked list to closely model a real pipeline. For now there is only one stage,
so the stage-to-stage flow of instructions isn't immediately obvious.
Eventually, Stage will also handle event notifications, but that functionality
is not complete, and not destined for this patch. Ideally, an interested party
can register for notifications from a particular stage. Callbacks will be issued to
these listeners at various points in the execution of the stage.
For now, eventing functionality remains similar to what it has been in mca::Backend.
We will be building-up the Stage class as we move on, such as adding debug output.
This patch also removes the unique_ptr<Instruction> return value from
InstrBuilder::createInstruction. An Instruction pointer is still produced,
but now it's up to the caller to decide how that item should be managed post-allocation
(e.g., smart pointer). This allows the Fetch stage to create instructions and
manage the lifetime of those instructions as it wishes, and not have to be bound to any
specific managed pointer type. Other callers of createInstruction might have different
requirements, and thus can manage the pointer to fit their needs. Another idea would be to push the
ownership to the RCU.
Currently, the FetchStage will wrap the Instruction
pointer in a shared_ptr. This allows us to remove the Instruction container in
Backend, which was probably going to disappear, or move, at some point anyways.
Note that I did run these changes through valgrind, to make sure we are not leaking
memory. While the shared_ptr comes with some additional overhead it relieves us
from having to manage a list of generated instructions, and/or make lookup calls
to remove the instructions.
I realize that both the Stage class and the Instruction pointer management
(mentioned directly above) are separate but related ideas, and probably should
land as separate patches; I am happy to do that if either idea is decent.
The main reason these two ideas are together is that
Stage::execute() can mutate an InstRef. For the fetch stage, the InstRef is populated
as the primary action of that stage (execute()). I didn't want to change the Stage interface
to support the idea of generating an instruction. Ideally, instructions are to
be pushed through the pipeline. I didn't want to draw too much of a
specialization just for the fetch stage. Excuse the word-salad.
Reviewers: andreadb, courbet, RKSimon
Reviewed By: andreadb
Subscribers: llvm-commits, mgorny, javed.absar, tschuett, gbedwell
Differential Revision: https://reviews.llvm.org/D46741
llvm-svn: 332390
2018-05-15 22:21:04 +02:00
|
|
|
}
|
|
|
|
|
2018-09-28 12:47:24 +02:00
|
|
|
Error Pipeline::runCycle() {
|
|
|
|
Error Err = ErrorSuccess();
|
[llvm-mca] Refactor how execution is orchestrated by the Pipeline.
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 both the stage API and the Pipeline execution. At
the same time, this patch fixes some design issues which are currently latent,
but that are likely to cause problems in future if people start defining custom
pipelines.
The new design assumes that each pipeline stage knows the "next-in-sequence".
The Stage API has gained three new methods:
- isAvailable(IR)
- checkNextStage(IR)
- moveToTheNextStage(IR).
An instruction IR can be executed by a Stage if method `Stage::isAvailable(IR)`
returns true.
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.
Stages are now responsible for moving instructions to the next stage in sequence
if necessary.
Instructions are allowed to transition through multiple stages during a single
cycle (as long as stages are available, and as long as all the calls to
`checkNextStage(IR)` returns true).
Methods `Stage::preExecute()` and `Stage::postExecute()` have now become
redundant, and those are removed by this patch.
Method Pipeline::runCycle() is now simpler, and it correctly visits stages
on every begin/end of cycle.
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.
Overally, 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, and I plan to do it in a follow-up patch.
Differential revision: https://reviews.llvm.org/D50849
llvm-svn: 339923
2018-08-16 21:00:48 +02:00
|
|
|
// Update stages before we start processing new instructions.
|
|
|
|
for (auto I = Stages.rbegin(), E = Stages.rend(); I != E && !Err; ++I) {
|
2018-08-16 17:43:09 +02:00
|
|
|
const std::unique_ptr<Stage> &S = *I;
|
|
|
|
Err = S->cycleStart();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now fetch and execute new instructions.
|
|
|
|
InstRef IR;
|
[llvm-mca] Refactor how execution is orchestrated by the Pipeline.
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 both the stage API and the Pipeline execution. At
the same time, this patch fixes some design issues which are currently latent,
but that are likely to cause problems in future if people start defining custom
pipelines.
The new design assumes that each pipeline stage knows the "next-in-sequence".
The Stage API has gained three new methods:
- isAvailable(IR)
- checkNextStage(IR)
- moveToTheNextStage(IR).
An instruction IR can be executed by a Stage if method `Stage::isAvailable(IR)`
returns true.
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.
Stages are now responsible for moving instructions to the next stage in sequence
if necessary.
Instructions are allowed to transition through multiple stages during a single
cycle (as long as stages are available, and as long as all the calls to
`checkNextStage(IR)` returns true).
Methods `Stage::preExecute()` and `Stage::postExecute()` have now become
redundant, and those are removed by this patch.
Method Pipeline::runCycle() is now simpler, and it correctly visits stages
on every begin/end of cycle.
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.
Overally, 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, and I plan to do it in a follow-up patch.
Differential revision: https://reviews.llvm.org/D50849
llvm-svn: 339923
2018-08-16 21:00:48 +02:00
|
|
|
Stage &FirstStage = *Stages[0];
|
|
|
|
while (!Err && FirstStage.isAvailable(IR))
|
|
|
|
Err = FirstStage.execute(IR);
|
2018-07-13 00:59:53 +02:00
|
|
|
|
2018-08-16 17:43:09 +02:00
|
|
|
// Update stages in preparation for a new cycle.
|
2019-03-27 16:41:53 +01:00
|
|
|
for (const std::unique_ptr<Stage> &S : Stages) {
|
2019-03-27 17:22:36 +01:00
|
|
|
Err = S->cycleEnd();
|
|
|
|
if (Err)
|
2019-03-27 16:41:53 +01:00
|
|
|
break;
|
2018-08-16 17:43:09 +02:00
|
|
|
}
|
[llvm-mca] Refactor how execution is orchestrated by the Pipeline.
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 both the stage API and the Pipeline execution. At
the same time, this patch fixes some design issues which are currently latent,
but that are likely to cause problems in future if people start defining custom
pipelines.
The new design assumes that each pipeline stage knows the "next-in-sequence".
The Stage API has gained three new methods:
- isAvailable(IR)
- checkNextStage(IR)
- moveToTheNextStage(IR).
An instruction IR can be executed by a Stage if method `Stage::isAvailable(IR)`
returns true.
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.
Stages are now responsible for moving instructions to the next stage in sequence
if necessary.
Instructions are allowed to transition through multiple stages during a single
cycle (as long as stages are available, and as long as all the calls to
`checkNextStage(IR)` returns true).
Methods `Stage::preExecute()` and `Stage::postExecute()` have now become
redundant, and those are removed by this patch.
Method Pipeline::runCycle() is now simpler, and it correctly visits stages
on every begin/end of cycle.
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.
Overally, 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, and I plan to do it in a follow-up patch.
Differential revision: https://reviews.llvm.org/D50849
llvm-svn: 339923
2018-08-16 21:00:48 +02:00
|
|
|
|
2018-08-16 17:43:09 +02:00
|
|
|
return Err;
|
2018-03-08 14:05:02 +01:00
|
|
|
}
|
|
|
|
|
[llvm-mca] Refactor how execution is orchestrated by the Pipeline.
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 both the stage API and the Pipeline execution. At
the same time, this patch fixes some design issues which are currently latent,
but that are likely to cause problems in future if people start defining custom
pipelines.
The new design assumes that each pipeline stage knows the "next-in-sequence".
The Stage API has gained three new methods:
- isAvailable(IR)
- checkNextStage(IR)
- moveToTheNextStage(IR).
An instruction IR can be executed by a Stage if method `Stage::isAvailable(IR)`
returns true.
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.
Stages are now responsible for moving instructions to the next stage in sequence
if necessary.
Instructions are allowed to transition through multiple stages during a single
cycle (as long as stages are available, and as long as all the calls to
`checkNextStage(IR)` returns true).
Methods `Stage::preExecute()` and `Stage::postExecute()` have now become
redundant, and those are removed by this patch.
Method Pipeline::runCycle() is now simpler, and it correctly visits stages
on every begin/end of cycle.
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.
Overally, 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, and I plan to do it in a follow-up patch.
Differential revision: https://reviews.llvm.org/D50849
llvm-svn: 339923
2018-08-16 21:00:48 +02:00
|
|
|
void Pipeline::appendStage(std::unique_ptr<Stage> S) {
|
|
|
|
assert(S && "Invalid null stage in input!");
|
|
|
|
if (!Stages.empty()) {
|
|
|
|
Stage *Last = Stages.back().get();
|
|
|
|
Last->setNextInSequence(S.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
Stages.push_back(std::move(S));
|
|
|
|
}
|
|
|
|
|
2018-07-13 11:27:34 +02:00
|
|
|
void Pipeline::notifyCycleBegin() {
|
2019-01-10 14:59:13 +01:00
|
|
|
LLVM_DEBUG(dbgs() << "\n[E] Cycle begin: " << Cycles << '\n');
|
2018-03-08 14:05:02 +01:00
|
|
|
for (HWEventListener *Listener : Listeners)
|
2018-04-12 12:49:40 +02:00
|
|
|
Listener->onCycleBegin();
|
2018-03-08 14:05:02 +01:00
|
|
|
}
|
|
|
|
|
2018-07-13 11:27:34 +02:00
|
|
|
void Pipeline::notifyCycleEnd() {
|
2019-01-10 14:59:13 +01:00
|
|
|
LLVM_DEBUG(dbgs() << "[E] Cycle end: " << Cycles << "\n");
|
2018-03-08 14:05:02 +01:00
|
|
|
for (HWEventListener *Listener : Listeners)
|
2018-04-12 12:49:40 +02:00
|
|
|
Listener->onCycleEnd();
|
2018-03-08 14:05:02 +01:00
|
|
|
}
|
|
|
|
} // namespace mca.
|
2018-10-30 16:56:08 +01:00
|
|
|
} // namespace llvm
|