[llvm-mca] Propagate fatal llvm-mca errors from library classes to driver.

Summary:
This patch introduces error handling to propagate the errors from llvm-mca library classes (or what will become library classes) up to the driver.  This patch also introduces an enum to make clearer the intention of the return value for Stage::execute.

This supports PR38101.

Reviewers: andreadb, courbet, RKSimon

Reviewed By: andreadb

Subscribers: llvm-commits, tschuett, gbedwell

Differential Revision: https://reviews.llvm.org/D50561

llvm-svn: 339594
This commit is contained in:
Matt Davis 2018-08-13 18:11:48 +00:00
parent 1a3caef148
commit 4bcf369d9b
17 changed files with 131 additions and 70 deletions

View File

@ -136,12 +136,12 @@ void DispatchStage::cycleStart() {
CarryOver = CarryOver >= DispatchWidth ? CarryOver - DispatchWidth : 0U;
}
bool DispatchStage::execute(InstRef &IR) {
Stage::Status DispatchStage::execute(InstRef &IR) {
const InstrDesc &Desc = IR.getInstruction()->getDesc();
if (!isAvailable(Desc.NumMicroOps) || !canDispatch(IR))
return false;
return Stage::Stop;
dispatch(IR);
return true;
return Stage::Continue;
}
#ifndef NDEBUG

View File

@ -94,7 +94,7 @@ public:
// RetireStage::hasWorkToComplete will check for that case.
virtual bool hasWorkToComplete() const override final { return false; }
virtual void cycleStart() override final;
virtual bool execute(InstRef &IR) override final;
virtual Status execute(InstRef &IR) override final;
void notifyDispatchStall(const InstRef &IR, unsigned EventType);
#ifndef NDEBUG

View File

@ -96,7 +96,7 @@ void ExecuteStage::cycleStart() {
}
// Schedule the instruction for execution on the hardware.
bool ExecuteStage::execute(InstRef &IR) {
Stage::Status ExecuteStage::execute(InstRef &IR) {
#ifndef NDEBUG
// Ensure that the HWS has not stored this instruction in its queues.
HWS.sanityCheck(IR);
@ -112,7 +112,7 @@ bool ExecuteStage::execute(InstRef &IR) {
// Obtain a slot in the LSU. If we cannot reserve resources, return true, so
// that succeeding stages can make progress.
if (!HWS.reserveResources(IR))
return true;
return Stage::Continue;
// If we did not return early, then the scheduler is ready for execution.
notifyInstructionReady(IR);
@ -133,7 +133,7 @@ bool ExecuteStage::execute(InstRef &IR) {
// If we cannot issue immediately, the HWS will add IR to its ready queue for
// execution later, so we must return early here.
if (!HWS.issueImmediately(IR))
return true;
return Stage::Continue;
LLVM_DEBUG(dbgs() << "[SCHEDULER] Instruction #" << IR
<< " issued immediately\n");
@ -148,7 +148,7 @@ bool ExecuteStage::execute(InstRef &IR) {
if (IR.getInstruction()->isExecuted())
notifyInstructionExecuted(IR);
return true;
return Stage::Continue;
}
void ExecuteStage::notifyInstructionExecuted(const InstRef &IR) {

View File

@ -46,7 +46,7 @@ public:
virtual bool hasWorkToComplete() const override final { return false; }
virtual void cycleStart() override final;
virtual bool execute(InstRef &IR) override final;
virtual Status execute(InstRef &IR) override final;
void
notifyInstructionIssued(const InstRef &IR,

View File

@ -19,14 +19,18 @@ namespace mca {
bool FetchStage::hasWorkToComplete() const { return SM.hasNext(); }
bool FetchStage::execute(InstRef &IR) {
Stage::Status FetchStage::execute(InstRef &IR) {
if (!SM.hasNext())
return false;
return Stage::Stop;
const SourceRef SR = SM.peekNext();
std::unique_ptr<Instruction> I = IB.createInstruction(*SR.second);
llvm::Expected<std::unique_ptr<Instruction>> InstOrErr =
IB.createInstruction(*SR.second);
if (!InstOrErr)
return InstOrErr.takeError();
std::unique_ptr<Instruction> I = std::move(*InstOrErr);
IR = InstRef(SR.first, I.get());
Instructions[IR.getSourceIndex()] = std::move(I);
return true;
return Stage::Continue;
}
void FetchStage::postExecute() { SM.updateNext(); }

View File

@ -35,7 +35,7 @@ public:
FetchStage &operator=(const FetchStage &Other) = delete;
bool hasWorkToComplete() const override final;
bool execute(InstRef &IR) override final;
Status execute(InstRef &IR) override final;
void postExecute() override final;
void cycleEnd() override final;
};

View File

@ -155,8 +155,8 @@ static void computeMaxLatency(InstrDesc &ID, const MCInstrDesc &MCDesc,
ID.MaxLatency = Latency < 0 ? 100U : static_cast<unsigned>(Latency);
}
void InstrBuilder::populateWrites(InstrDesc &ID, const MCInst &MCI,
unsigned SchedClassID) {
Error InstrBuilder::populateWrites(InstrDesc &ID, const MCInst &MCI,
unsigned SchedClassID) {
const MCInstrDesc &MCDesc = MCII.get(MCI.getOpcode());
const MCSchedModel &SM = STI.getSchedModel();
const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID);
@ -215,9 +215,11 @@ void InstrBuilder::populateWrites(InstrDesc &ID, const MCInst &MCI,
CurrentDef++;
}
if (CurrentDef != NumExplicitDefs)
llvm::report_fatal_error(
"error: Expected more register operand definitions. ");
if (CurrentDef != NumExplicitDefs) {
return make_error<StringError>(
"error: Expected more register operand definitions.",
inconvertibleErrorCode());
}
CurrentDef = 0;
for (CurrentDef = 0; CurrentDef < NumImplicitDefs; ++CurrentDef) {
@ -253,10 +255,10 @@ void InstrBuilder::populateWrites(InstrDesc &ID, const MCInst &MCI,
// MCInst sequence.
const MCOperand &Op = MCI.getOperand(MCI.getNumOperands() - 1);
if (i == MCI.getNumOperands() || !Op.isReg())
llvm::report_fatal_error(
return make_error<StringError>(
"error: expected a register operand for an optional "
"definition. Instruction has not be correctly analyzed.\n",
false);
"definition. Instruction has not be correctly analyzed.",
inconvertibleErrorCode());
WriteDescriptor &Write = ID.Writes[TotalDefs - 1];
Write.OpIndex = MCI.getNumOperands() - 1;
@ -265,10 +267,12 @@ void InstrBuilder::populateWrites(InstrDesc &ID, const MCInst &MCI,
Write.SClassOrWriteResourceID = 0;
Write.IsOptionalDef = true;
}
return ErrorSuccess();
}
void InstrBuilder::populateReads(InstrDesc &ID, const MCInst &MCI,
unsigned SchedClassID) {
Error InstrBuilder::populateReads(InstrDesc &ID, const MCInst &MCI,
unsigned SchedClassID) {
const MCInstrDesc &MCDesc = MCII.get(MCI.getOpcode());
unsigned NumExplicitDefs = MCDesc.getNumDefs();
@ -280,9 +284,11 @@ void InstrBuilder::populateReads(InstrDesc &ID, const MCInst &MCI,
NumExplicitDefs--;
}
if (NumExplicitDefs)
llvm::report_fatal_error(
"error: Expected more register operand definitions. ", false);
if (NumExplicitDefs) {
return make_error<StringError>(
"error: Expected more register operand definitions. ",
inconvertibleErrorCode());
}
unsigned NumExplicitUses = MCI.getNumOperands() - i;
unsigned NumImplicitUses = MCDesc.getNumImplicitUses();
@ -292,7 +298,7 @@ void InstrBuilder::populateReads(InstrDesc &ID, const MCInst &MCI,
}
unsigned TotalUses = NumExplicitUses + NumImplicitUses;
if (!TotalUses)
return;
return ErrorSuccess();
ID.Reads.resize(TotalUses);
for (unsigned CurrentUse = 0; CurrentUse < NumExplicitUses; ++CurrentUse) {
@ -313,9 +319,11 @@ void InstrBuilder::populateReads(InstrDesc &ID, const MCInst &MCI,
LLVM_DEBUG(dbgs() << "\t\t[Use] OpIdx=" << Read.OpIndex << ", RegisterID="
<< MRI.getName(Read.RegisterID) << '\n');
}
return ErrorSuccess();
}
const InstrDesc &InstrBuilder::createInstrDescImpl(const MCInst &MCI) {
Expected<const InstrDesc &>
InstrBuilder::createInstrDescImpl(const MCInst &MCI) {
assert(STI.getSchedModel().hasInstrSchedModel() &&
"Itineraries are not yet supported!");
@ -333,11 +341,13 @@ const InstrDesc &InstrBuilder::createInstrDescImpl(const MCInst &MCI) {
while (SchedClassID && SM.getSchedClassDesc(SchedClassID)->isVariant())
SchedClassID = STI.resolveVariantSchedClass(SchedClassID, &MCI, CPUID);
if (!SchedClassID)
llvm::report_fatal_error("unable to resolve this variant class.");
if (!SchedClassID) {
return make_error<StringError>("unable to resolve this variant class.",
inconvertibleErrorCode());
}
}
// Check if this instruction is supported. Otherwise, report a fatal error.
// Check if this instruction is supported. Otherwise, report an error.
const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID);
if (SCDesc.NumMicroOps == MCSchedClassDesc::InvalidNumMicroOps) {
std::string ToString;
@ -346,10 +356,10 @@ const InstrDesc &InstrBuilder::createInstrDescImpl(const MCInst &MCI) {
<< " assembly sequence.\n";
MCIP.printInst(&MCI, OS, "", STI);
OS.flush();
WithColor::note() << "instruction: " << ToString << '\n';
llvm::report_fatal_error(
"Don't know how to analyze unsupported instructions.");
return make_error<StringError>(
"Don't know how to analyze unsupported instructions",
inconvertibleErrorCode());
}
// Create a new empty descriptor.
@ -375,8 +385,10 @@ const InstrDesc &InstrBuilder::createInstrDescImpl(const MCInst &MCI) {
initializeUsedResources(*ID, SCDesc, STI, ProcResourceMasks);
computeMaxLatency(*ID, MCDesc, SCDesc, STI);
populateWrites(*ID, MCI, SchedClassID);
populateReads(*ID, MCI, SchedClassID);
if (auto Err = populateWrites(*ID, MCI, SchedClassID))
return std::move(Err);
if (auto Err = populateReads(*ID, MCI, SchedClassID))
return std::move(Err);
LLVM_DEBUG(dbgs() << "\t\tMaxLatency=" << ID->MaxLatency << '\n');
LLVM_DEBUG(dbgs() << "\t\tNumMicroOps=" << ID->NumMicroOps << '\n');
@ -392,7 +404,8 @@ const InstrDesc &InstrBuilder::createInstrDescImpl(const MCInst &MCI) {
return *VariantDescriptors[&MCI];
}
const InstrDesc &InstrBuilder::getOrCreateInstrDesc(const MCInst &MCI) {
Expected<const InstrDesc &>
InstrBuilder::getOrCreateInstrDesc(const MCInst &MCI) {
if (Descriptors.find_as(MCI.getOpcode()) != Descriptors.end())
return *Descriptors[MCI.getOpcode()];
@ -402,9 +415,12 @@ const InstrDesc &InstrBuilder::getOrCreateInstrDesc(const MCInst &MCI) {
return createInstrDescImpl(MCI);
}
std::unique_ptr<Instruction>
Expected<std::unique_ptr<Instruction>>
InstrBuilder::createInstruction(const MCInst &MCI) {
const InstrDesc &D = getOrCreateInstrDesc(MCI);
Expected<const InstrDesc &> DescOrErr = getOrCreateInstrDesc(MCI);
if (!DescOrErr)
return DescOrErr.takeError();
const InstrDesc &D = *DescOrErr;
std::unique_ptr<Instruction> NewIS = llvm::make_unique<Instruction>(D);
// Initialize Reads first.
@ -433,7 +449,7 @@ InstrBuilder::createInstruction(const MCInst &MCI) {
// Early exit if there are no writes.
if (D.Writes.empty())
return NewIS;
return std::move(NewIS);
// Track register writes that implicitly clear the upper portion of the
// underlying super-registers using an APInt.
@ -464,6 +480,6 @@ InstrBuilder::createInstruction(const MCInst &MCI) {
++WriteIndex;
}
return NewIS;
return std::move(NewIS);
}
} // namespace mca

View File

@ -22,6 +22,7 @@
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/Support/Error.h"
namespace mca {
@ -49,16 +50,18 @@ class InstrBuilder {
llvm::DenseMap<const llvm::MCInst *, std::unique_ptr<const InstrDesc>>
VariantDescriptors;
const InstrDesc &createInstrDescImpl(const llvm::MCInst &MCI);
const InstrDesc &getOrCreateInstrDesc(const llvm::MCInst &MCI);
llvm::Expected<const InstrDesc &>
createInstrDescImpl(const llvm::MCInst &MCI);
llvm::Expected<const InstrDesc &>
getOrCreateInstrDesc(const llvm::MCInst &MCI);
InstrBuilder(const InstrBuilder &) = delete;
InstrBuilder &operator=(const InstrBuilder &) = delete;
void populateWrites(InstrDesc &ID, const llvm::MCInst &MCI,
unsigned SchedClassID);
void populateReads(InstrDesc &ID, const llvm::MCInst &MCI,
unsigned SchedClassID);
llvm::Error populateWrites(InstrDesc &ID, const llvm::MCInst &MCI,
unsigned SchedClassID);
llvm::Error populateReads(InstrDesc &ID, const llvm::MCInst &MCI,
unsigned SchedClassID);
public:
InstrBuilder(const llvm::MCSubtargetInfo &sti, const llvm::MCInstrInfo &mcii,
@ -79,7 +82,8 @@ public:
void clear() { VariantDescriptors.shrink_and_clear(); }
std::unique_ptr<Instruction> createInstruction(const llvm::MCInst &MCI);
llvm::Expected<std::unique_ptr<Instruction>>
createInstruction(const llvm::MCInst &MCI);
};
} // namespace mca

View File

@ -21,7 +21,7 @@ namespace mca {
using namespace llvm;
bool InstructionTables::execute(InstRef &IR) {
Stage::Status InstructionTables::execute(InstRef &IR) {
ArrayRef<uint64_t> Masks = IB.getProcResourceMasks();
const InstrDesc &Desc = IR.getInstruction()->getDesc();
UsedResources.clear();
@ -64,7 +64,7 @@ bool InstructionTables::execute(InstRef &IR) {
// Send a fake instruction issued event to all the views.
HWInstructionIssuedEvent Event(IR, UsedResources);
notifyEvent<HWInstructionIssuedEvent>(Event);
return true;
return Stage::Continue;
}
} // namespace mca

View File

@ -36,7 +36,7 @@ public:
: Stage(), SM(Model), IB(Builder) {}
bool hasWorkToComplete() const override final { return false; }
bool execute(InstRef &IR) override final;
Status execute(InstRef &IR) override final;
};
} // namespace mca

View File

@ -40,11 +40,15 @@ bool Pipeline::hasWorkToProcess() {
// This routine returns early if any stage returns 'false' after execute() is
// called on it.
bool Pipeline::executeStages(InstRef &IR) {
for (const std::unique_ptr<Stage> &S : Stages)
if (!S->execute(IR))
return false;
return true;
Stage::Status Pipeline::executeStages(InstRef &IR) {
for (const std::unique_ptr<Stage> &S : Stages) {
Stage::Status StatusOrErr = S->execute(IR);
if (!StatusOrErr)
return StatusOrErr.takeError();
else if (StatusOrErr.get() == Stage::Stop)
return Stage::Stop;
}
return Stage::Continue;
}
void Pipeline::preExecuteStages() {
@ -57,16 +61,18 @@ void Pipeline::postExecuteStages() {
S->postExecute();
}
void Pipeline::run() {
llvm::Error Pipeline::run() {
while (hasWorkToProcess()) {
notifyCycleBegin();
runCycle();
if (llvm::Error Err = runCycle())
return Err;
notifyCycleEnd();
++Cycles;
}
return llvm::ErrorSuccess();
}
void Pipeline::runCycle() {
llvm::Error Pipeline::runCycle() {
// Update the stages before we do any processing for this cycle.
InstRef IR;
for (auto &S : Stages)
@ -76,13 +82,17 @@ void Pipeline::runCycle() {
// progress.
while (true) {
preExecuteStages();
if (!executeStages(IR))
Stage::Status Val = executeStages(IR);
if (!Val)
return Val.takeError();
if (Val.get() == Stage::Stop)
break;
postExecuteStages();
}
for (auto &S : Stages)
S->cycleEnd();
return llvm::ErrorSuccess();
}
void Pipeline::notifyCycleBegin() {

View File

@ -19,6 +19,7 @@
#include "Scheduler.h"
#include "Stage.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Error.h"
namespace mca {
@ -60,9 +61,9 @@ class Pipeline {
unsigned Cycles;
void preExecuteStages();
bool executeStages(InstRef &IR);
Stage::Status executeStages(InstRef &IR);
void postExecuteStages();
void runCycle();
llvm::Error runCycle();
bool hasWorkToProcess();
void notifyCycleBegin();
@ -71,7 +72,7 @@ class Pipeline {
public:
Pipeline() : Cycles(0) {}
void appendStage(std::unique_ptr<Stage> S) { Stages.push_back(std::move(S)); }
void run();
llvm::Error run();
void addEventListener(HWEventListener *Listener);
};
} // namespace mca

View File

@ -306,8 +306,14 @@ unsigned RegisterFile::isAvailable(ArrayRef<unsigned> Regs) const {
// microarchitectural registers in register file #0 was changed by the
// users via flag -reg-file-size. Alternatively, the scheduling model
// specified a too small number of registers for this register file.
report_fatal_error(
"Not enough microarchitectural registers in the register file");
LLVM_DEBUG(dbgs() << "Not enough registers in the register file.\n");
// FIXME: Normalize the instruction register count to match the
// NumPhysRegs value. This is a highly unusual case, and is not expected
// to occur. This normalization is hiding an inconsistency in either the
// scheduling model or in the value that the user might have specified
// for NumPhysRegs.
NumRegs = RMT.NumPhysRegs;
}
if (RMT.NumPhysRegs < (RMT.NumUsedPhysRegs + NumRegs))

View File

@ -21,6 +21,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSchedule.h"
#include "llvm/Support/Error.h"
namespace mca {

View File

@ -38,7 +38,7 @@ public:
return !RCU.isEmpty();
}
virtual void cycleStart() override final;
virtual bool execute(InstRef &IR) override final { return true; }
virtual Status execute(InstRef &IR) override final { return Stage::Continue; }
void notifyInstructionRetired(const InstRef &IR);
void onInstructionExecuted(unsigned TokenID);
};

View File

@ -17,6 +17,7 @@
#define LLVM_TOOLS_LLVM_MCA_STAGE_H
#include "HWEventListener.h"
#include "llvm/Support/Error.h"
#include <set>
namespace mca {
@ -28,6 +29,19 @@ class Stage {
Stage &operator=(const Stage &Other) = delete;
std::set<HWEventListener *> Listeners;
public:
/// A Stage's execute() returns Continue, Stop, or an error. Returning
/// Continue means that the stage successfully completed its 'execute'
/// action, and that the instruction being processed can be moved to the next
/// pipeline stage during this cycle. Continue allows the pipeline to
/// continue calling 'execute' on subsequent stages. Returning Stop
/// signifies that the stage ran into an error, and tells the pipeline to stop
/// passing the instruction to subsequent stages during this cycle. Any
/// failures that occur during 'execute' are represented by the error variant
/// that is provided by the Expected template.
enum State { Stop, Continue };
using Status = llvm::Expected<State>;
protected:
const std::set<HWEventListener *> &getListeners() const { return Listeners; }
@ -60,7 +74,7 @@ public:
/// The primary action that this stage performs.
/// Returning false prevents successor stages from having their 'execute'
/// routine called. This can be called multiple times during a single cycle.
virtual bool execute(InstRef &IR) = 0;
virtual Status execute(InstRef &IR) = 0;
/// Add a listener to receive callbacks during the execution of this stage.
void addListener(HWEventListener *Listener);

View File

@ -42,6 +42,7 @@
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
@ -502,7 +503,9 @@ int main(int argc, char **argv) {
}
Printer.addView(
llvm::make_unique<mca::ResourcePressureView>(*STI, *IP, S));
P->run();
auto Err = P->run();
if (Err)
report_fatal_error(toString(std::move(Err)));
Printer.printReport(TOF->os());
continue;
}
@ -539,7 +542,9 @@ int main(int argc, char **argv) {
*STI, *IP, S, TimelineMaxIterations, TimelineMaxCycles));
}
P->run();
auto Err = P->run();
if (Err)
report_fatal_error(toString(std::move(Err)));
Printer.printReport(TOF->os());
// Clear the InstrBuilder internal state in preparation for another round.