Index: llvm/trunk/tools/llvm-mca/DispatchStage.h =================================================================== --- llvm/trunk/tools/llvm-mca/DispatchStage.h +++ llvm/trunk/tools/llvm-mca/DispatchStage.h @@ -94,7 +94,7 @@ // 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 Index: llvm/trunk/tools/llvm-mca/DispatchStage.cpp =================================================================== --- llvm/trunk/tools/llvm-mca/DispatchStage.cpp +++ llvm/trunk/tools/llvm-mca/DispatchStage.cpp @@ -136,12 +136,12 @@ 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 Index: llvm/trunk/tools/llvm-mca/ExecuteStage.h =================================================================== --- llvm/trunk/tools/llvm-mca/ExecuteStage.h +++ llvm/trunk/tools/llvm-mca/ExecuteStage.h @@ -46,7 +46,7 @@ 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, Index: llvm/trunk/tools/llvm-mca/ExecuteStage.cpp =================================================================== --- llvm/trunk/tools/llvm-mca/ExecuteStage.cpp +++ llvm/trunk/tools/llvm-mca/ExecuteStage.cpp @@ -96,7 +96,7 @@ } // 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 @@ // 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 @@ // 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 @@ if (IR.getInstruction()->isExecuted()) notifyInstructionExecuted(IR); - return true; + return Stage::Continue; } void ExecuteStage::notifyInstructionExecuted(const InstRef &IR) { Index: llvm/trunk/tools/llvm-mca/FetchStage.h =================================================================== --- llvm/trunk/tools/llvm-mca/FetchStage.h +++ llvm/trunk/tools/llvm-mca/FetchStage.h @@ -35,7 +35,7 @@ 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; }; Index: llvm/trunk/tools/llvm-mca/FetchStage.cpp =================================================================== --- llvm/trunk/tools/llvm-mca/FetchStage.cpp +++ llvm/trunk/tools/llvm-mca/FetchStage.cpp @@ -19,14 +19,18 @@ 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 I = IB.createInstruction(*SR.second); + llvm::Expected> InstOrErr = + IB.createInstruction(*SR.second); + if (!InstOrErr) + return InstOrErr.takeError(); + std::unique_ptr 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(); } Index: llvm/trunk/tools/llvm-mca/InstrBuilder.h =================================================================== --- llvm/trunk/tools/llvm-mca/InstrBuilder.h +++ llvm/trunk/tools/llvm-mca/InstrBuilder.h @@ -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 @@ llvm::DenseMap> VariantDescriptors; - const InstrDesc &createInstrDescImpl(const llvm::MCInst &MCI); - const InstrDesc &getOrCreateInstrDesc(const llvm::MCInst &MCI); + llvm::Expected + createInstrDescImpl(const llvm::MCInst &MCI); + llvm::Expected + 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 @@ void clear() { VariantDescriptors.shrink_and_clear(); } - std::unique_ptr createInstruction(const llvm::MCInst &MCI); + llvm::Expected> + createInstruction(const llvm::MCInst &MCI); }; } // namespace mca Index: llvm/trunk/tools/llvm-mca/InstrBuilder.cpp =================================================================== --- llvm/trunk/tools/llvm-mca/InstrBuilder.cpp +++ llvm/trunk/tools/llvm-mca/InstrBuilder.cpp @@ -155,8 +155,8 @@ ID.MaxLatency = Latency < 0 ? 100U : static_cast(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 @@ CurrentDef++; } - if (CurrentDef != NumExplicitDefs) - llvm::report_fatal_error( - "error: Expected more register operand definitions. "); + if (CurrentDef != NumExplicitDefs) { + return make_error( + "error: Expected more register operand definitions.", + inconvertibleErrorCode()); + } CurrentDef = 0; for (CurrentDef = 0; CurrentDef < NumImplicitDefs; ++CurrentDef) { @@ -253,10 +255,10 @@ // MCInst sequence. const MCOperand &Op = MCI.getOperand(MCI.getNumOperands() - 1); if (i == MCI.getNumOperands() || !Op.isReg()) - llvm::report_fatal_error( + return make_error( "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 @@ 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 @@ NumExplicitDefs--; } - if (NumExplicitDefs) - llvm::report_fatal_error( - "error: Expected more register operand definitions. ", false); + if (NumExplicitDefs) { + return make_error( + "error: Expected more register operand definitions. ", + inconvertibleErrorCode()); + } unsigned NumExplicitUses = MCI.getNumOperands() - i; unsigned NumImplicitUses = MCDesc.getNumImplicitUses(); @@ -292,7 +298,7 @@ } unsigned TotalUses = NumExplicitUses + NumImplicitUses; if (!TotalUses) - return; + return ErrorSuccess(); ID.Reads.resize(TotalUses); for (unsigned CurrentUse = 0; CurrentUse < NumExplicitUses; ++CurrentUse) { @@ -313,9 +319,11 @@ 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 +InstrBuilder::createInstrDescImpl(const MCInst &MCI) { assert(STI.getSchedModel().hasInstrSchedModel() && "Itineraries are not yet supported!"); @@ -333,11 +341,13 @@ 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("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 @@ << " 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( + "Don't know how to analyze unsupported instructions", + inconvertibleErrorCode()); } // Create a new empty descriptor. @@ -375,8 +385,10 @@ 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 @@ return *VariantDescriptors[&MCI]; } -const InstrDesc &InstrBuilder::getOrCreateInstrDesc(const MCInst &MCI) { +Expected +InstrBuilder::getOrCreateInstrDesc(const MCInst &MCI) { if (Descriptors.find_as(MCI.getOpcode()) != Descriptors.end()) return *Descriptors[MCI.getOpcode()]; @@ -402,9 +415,12 @@ return createInstrDescImpl(MCI); } -std::unique_ptr +Expected> InstrBuilder::createInstruction(const MCInst &MCI) { - const InstrDesc &D = getOrCreateInstrDesc(MCI); + Expected DescOrErr = getOrCreateInstrDesc(MCI); + if (!DescOrErr) + return DescOrErr.takeError(); + const InstrDesc &D = *DescOrErr; std::unique_ptr NewIS = llvm::make_unique(D); // Initialize Reads first. @@ -433,7 +449,7 @@ // 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 @@ ++WriteIndex; } - return NewIS; + return std::move(NewIS); } } // namespace mca Index: llvm/trunk/tools/llvm-mca/InstructionTables.h =================================================================== --- llvm/trunk/tools/llvm-mca/InstructionTables.h +++ llvm/trunk/tools/llvm-mca/InstructionTables.h @@ -36,7 +36,7 @@ : 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 Index: llvm/trunk/tools/llvm-mca/InstructionTables.cpp =================================================================== --- llvm/trunk/tools/llvm-mca/InstructionTables.cpp +++ llvm/trunk/tools/llvm-mca/InstructionTables.cpp @@ -21,7 +21,7 @@ using namespace llvm; -bool InstructionTables::execute(InstRef &IR) { +Stage::Status InstructionTables::execute(InstRef &IR) { ArrayRef Masks = IB.getProcResourceMasks(); const InstrDesc &Desc = IR.getInstruction()->getDesc(); UsedResources.clear(); @@ -64,7 +64,7 @@ // Send a fake instruction issued event to all the views. HWInstructionIssuedEvent Event(IR, UsedResources); notifyEvent(Event); - return true; + return Stage::Continue; } } // namespace mca Index: llvm/trunk/tools/llvm-mca/Pipeline.h =================================================================== --- llvm/trunk/tools/llvm-mca/Pipeline.h +++ llvm/trunk/tools/llvm-mca/Pipeline.h @@ -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 @@ 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 @@ public: Pipeline() : Cycles(0) {} void appendStage(std::unique_ptr S) { Stages.push_back(std::move(S)); } - void run(); + llvm::Error run(); void addEventListener(HWEventListener *Listener); }; } // namespace mca Index: llvm/trunk/tools/llvm-mca/Pipeline.cpp =================================================================== --- llvm/trunk/tools/llvm-mca/Pipeline.cpp +++ llvm/trunk/tools/llvm-mca/Pipeline.cpp @@ -40,11 +40,15 @@ // 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 &S : Stages) - if (!S->execute(IR)) - return false; - return true; +Stage::Status Pipeline::executeStages(InstRef &IR) { + for (const std::unique_ptr &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 @@ 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 @@ // 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() { Index: llvm/trunk/tools/llvm-mca/RegisterFile.h =================================================================== --- llvm/trunk/tools/llvm-mca/RegisterFile.h +++ llvm/trunk/tools/llvm-mca/RegisterFile.h @@ -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 { Index: llvm/trunk/tools/llvm-mca/RegisterFile.cpp =================================================================== --- llvm/trunk/tools/llvm-mca/RegisterFile.cpp +++ llvm/trunk/tools/llvm-mca/RegisterFile.cpp @@ -306,8 +306,14 @@ // 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)) Index: llvm/trunk/tools/llvm-mca/RetireStage.h =================================================================== --- llvm/trunk/tools/llvm-mca/RetireStage.h +++ llvm/trunk/tools/llvm-mca/RetireStage.h @@ -38,7 +38,7 @@ 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); }; Index: llvm/trunk/tools/llvm-mca/Stage.h =================================================================== --- llvm/trunk/tools/llvm-mca/Stage.h +++ llvm/trunk/tools/llvm-mca/Stage.h @@ -17,6 +17,7 @@ #define LLVM_TOOLS_LLVM_MCA_STAGE_H #include "HWEventListener.h" +#include "llvm/Support/Error.h" #include namespace mca { @@ -28,6 +29,19 @@ Stage &operator=(const Stage &Other) = delete; std::set 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; + protected: const std::set &getListeners() const { return Listeners; } @@ -60,7 +74,7 @@ /// 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); Index: llvm/trunk/tools/llvm-mca/llvm-mca.cpp =================================================================== --- llvm/trunk/tools/llvm-mca/llvm-mca.cpp +++ llvm/trunk/tools/llvm-mca/llvm-mca.cpp @@ -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 @@ } Printer.addView( llvm::make_unique(*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 @@ *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.