diff --git a/llvm/include/llvm/MCA/IncrementalSourceMgr.h b/llvm/include/llvm/MCA/IncrementalSourceMgr.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/MCA/IncrementalSourceMgr.h @@ -0,0 +1,72 @@ +//===---------------- IncrementalSourceMgr.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 contains IncrementalSourceMgr, an implementation of SourceMgr +/// that allows users to add new instructions incrementally / dynamically. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_MCA_INCREMENTALSOURCEMGR_H +#define LLVM_MCA_INCREMENTALSOURCEMGR_H + +#include "llvm/MCA/SourceMgr.h" +#include + +namespace llvm { +namespace mca { + +/// An implementation of \a SourceMgr that allows users to add new instructions +/// incrementally / dynamically. +/// Note that this SourceMgr takes ownership of all \a mca::Instruction. +class IncrementalSourceMgr : public SourceMgr { + /// Owner of all mca::Instruction instances. Note that we use std::deque here + /// to have a better throughput, in comparison to std::vector or + /// llvm::SmallVector, as they usually pay a higher re-allocation cost when + /// there is a large number of instructions. + std::deque InstStorage; + + /// Current instruction index. + unsigned TotalCounter; + + /// End-of-stream flag. + bool EOS; + +public: + IncrementalSourceMgr() : TotalCounter(0U), EOS(false) {} + + void clear() { + InstStorage.clear(); + TotalCounter = 0U; + EOS = false; + } + + ArrayRef getInstructions() const override { + llvm_unreachable("Not applicable"); + } + + bool hasNext() const override { return TotalCounter < InstStorage.size(); } + bool isEnd() const override { return EOS; } + + SourceRef peekNext() const override { + assert(hasNext()); + return SourceRef(TotalCounter, *InstStorage[TotalCounter]); + } + + /// Add a new instruction. + void addInst(UniqueInst &&Inst) { InstStorage.emplace_back(std::move(Inst)); } + + void updateNext() override { ++TotalCounter; } + + /// Mark the end of instruction stream. + void endOfStream() { EOS = true; } +}; + +} // end namespace mca +} // end namespace llvm + +#endif // LLVM_MCA_INCREMENTALSOURCEMGR_H diff --git a/llvm/include/llvm/MCA/Pipeline.h b/llvm/include/llvm/MCA/Pipeline.h --- a/llvm/include/llvm/MCA/Pipeline.h +++ b/llvm/include/llvm/MCA/Pipeline.h @@ -51,6 +51,13 @@ Pipeline(const Pipeline &P) = delete; Pipeline &operator=(const Pipeline &P) = delete; + enum class State { + Created, // Pipeline was just created. The default state. + Started, // Pipeline has started running. + Paused // Pipeline is paused. + }; + State CurrentState; + /// An ordered list of stages that define this instruction pipeline. SmallVector, 8> Stages; std::set Listeners; @@ -62,13 +69,16 @@ void notifyCycleEnd(); public: - Pipeline() : Cycles(0) {} + Pipeline() : CurrentState(State::Created), Cycles(0) {} void appendStage(std::unique_ptr S); /// Returns the total number of simulated cycles. Expected run(); void addEventListener(HWEventListener *Listener); + + /// Returns whether the pipeline is currently paused. + bool isPaused() const { return CurrentState == State::Paused; } }; } // namespace mca } // namespace llvm diff --git a/llvm/include/llvm/MCA/SourceMgr.h b/llvm/include/llvm/MCA/SourceMgr.h --- a/llvm/include/llvm/MCA/SourceMgr.h +++ b/llvm/include/llvm/MCA/SourceMgr.h @@ -6,9 +6,8 @@ // //===----------------------------------------------------------------------===// /// \file -/// This file implements class SourceMgr. Class SourceMgr abstracts the input -/// code sequence (a sequence of MCInst), and assings unique identifiers to -/// every instruction in the sequence. +/// This file contains abstract class SourceMgr and the default implementation, +/// CircularSourceMgr. /// //===----------------------------------------------------------------------===// @@ -25,30 +24,62 @@ // prevent compiler error C2139 about intrinsic type trait '__is_assignable'. typedef std::pair SourceRef; -class SourceMgr { +/// Abstracting the input code sequence (a sequence of MCInst) and assigning +/// unique identifiers to every instruction in the sequence. +struct SourceMgr { using UniqueInst = std::unique_ptr; + + /// Provides a fixed range of \a UniqueInst to iterate. + virtual ArrayRef getInstructions() const = 0; + + /// (Fixed) Number of \a UniqueInst. Returns the size of + /// \a getInstructions by default. + virtual size_t size() const { return getInstructions().size(); } + + /// Whether there is any \a SourceRef to inspect / peek next. + /// Note that returning false from this doesn't mean the instruction + /// stream has ended. + virtual bool hasNext() const = 0; + + /// Whether the instruction stream has eneded. + virtual bool isEnd() const = 0; + + /// The next \a SourceRef. + virtual SourceRef peekNext() const = 0; + + /// Advance to the next \a SourceRef. + virtual void updateNext() = 0; + + virtual ~SourceMgr() {} +}; + +/// The default implementation of \a SourceMgr. It always takes a fixed number +/// of instructions and provides an option to loop the given sequence for a +/// certain iterations. +class CircularSourceMgr : public SourceMgr { ArrayRef Sequence; unsigned Current; const unsigned Iterations; static const unsigned DefaultIterations = 100; public: - SourceMgr(ArrayRef S, unsigned Iter) - : Sequence(S), Current(0), Iterations(Iter ? Iter : DefaultIterations) {} + CircularSourceMgr(ArrayRef S, unsigned Iter) + : Sequence(S), Current(0U), Iterations(Iter ? Iter : DefaultIterations) {} + + ArrayRef getInstructions() const override { return Sequence; } unsigned getNumIterations() const { return Iterations; } - unsigned size() const { return Sequence.size(); } - bool hasNext() const { return Current < (Iterations * Sequence.size()); } - void updateNext() { ++Current; } + bool hasNext() const override { + return Current < (Iterations * Sequence.size()); + } + bool isEnd() const override { return !hasNext(); } - SourceRef peekNext() const { + SourceRef peekNext() const override { assert(hasNext() && "Already at end of sequence!"); return SourceRef(Current, *Sequence[Current % Sequence.size()]); } - using const_iterator = ArrayRef::const_iterator; - const_iterator begin() const { return Sequence.begin(); } - const_iterator end() const { return Sequence.end(); } + void updateNext() override { ++Current; } }; } // namespace mca diff --git a/llvm/include/llvm/MCA/Stages/EntryStage.h b/llvm/include/llvm/MCA/Stages/EntryStage.h --- a/llvm/include/llvm/MCA/Stages/EntryStage.h +++ b/llvm/include/llvm/MCA/Stages/EntryStage.h @@ -30,7 +30,7 @@ unsigned NumRetired; // Updates the program counter, and sets 'CurrentInstruction'. - void getNextInstruction(); + Error getNextInstruction(); EntryStage(const EntryStage &Other) = delete; EntryStage &operator=(const EntryStage &Other) = delete; @@ -42,6 +42,7 @@ bool hasWorkToComplete() const override; Error execute(InstRef &IR) override; Error cycleStart() override; + Error cycleResume() override; Error cycleEnd() override; }; diff --git a/llvm/include/llvm/MCA/Stages/Stage.h b/llvm/include/llvm/MCA/Stages/Stage.h --- a/llvm/include/llvm/MCA/Stages/Stage.h +++ b/llvm/include/llvm/MCA/Stages/Stage.h @@ -48,6 +48,9 @@ /// phase to prepare for the executions during the cycle. virtual Error cycleStart() { return ErrorSuccess(); } + /// Called after the pipeline is resumed from pausing state. + virtual Error cycleResume() { return ErrorSuccess(); } + /// Called once at the end of each cycle. virtual Error cycleEnd() { return ErrorSuccess(); } @@ -82,6 +85,16 @@ } }; +/// This is actually not an error but a marker to indicate that +/// the instruction stream is paused. +struct InstStreamPause : public ErrorInfo { + static char ID; + + std::error_code convertToErrorCode() const override { + return llvm::inconvertibleErrorCode(); + } + void log(raw_ostream &OS) const override { OS << "Stream is paused"; } +}; } // namespace mca } // namespace llvm #endif // LLVM_MCA_STAGES_STAGE_H diff --git a/llvm/lib/MCA/Pipeline.cpp b/llvm/lib/MCA/Pipeline.cpp --- a/llvm/lib/MCA/Pipeline.cpp +++ b/llvm/lib/MCA/Pipeline.cpp @@ -38,7 +38,8 @@ assert(!Stages.empty() && "Unexpected empty pipeline found!"); do { - notifyCycleBegin(); + if (!isPaused()) + notifyCycleBegin(); if (Error Err = runCycle()) return std::move(Err); notifyCycleEnd(); @@ -53,15 +54,25 @@ // Update stages before we start processing new instructions. for (auto I = Stages.rbegin(), E = Stages.rend(); I != E && !Err; ++I) { const std::unique_ptr &S = *I; - Err = S->cycleStart(); + if (isPaused()) + Err = S->cycleResume(); + else + Err = S->cycleStart(); } + CurrentState = State::Started; + // Now fetch and execute new instructions. InstRef IR; Stage &FirstStage = *Stages[0]; while (!Err && FirstStage.isAvailable(IR)) Err = FirstStage.execute(IR); + if (Err.isA()) { + CurrentState = State::Paused; + return Err; + } + // Update stages in preparation for a new cycle. for (const std::unique_ptr &S : Stages) { Err = S->cycleEnd(); diff --git a/llvm/lib/MCA/Stages/EntryStage.cpp b/llvm/lib/MCA/Stages/EntryStage.cpp --- a/llvm/lib/MCA/Stages/EntryStage.cpp +++ b/llvm/lib/MCA/Stages/EntryStage.cpp @@ -19,7 +19,7 @@ namespace mca { bool EntryStage::hasWorkToComplete() const { - return static_cast(CurrentInstruction); + return static_cast(CurrentInstruction) || !SM.isEnd(); } bool EntryStage::isAvailable(const InstRef & /* unused */) const { @@ -28,15 +28,20 @@ return false; } -void EntryStage::getNextInstruction() { +Error EntryStage::getNextInstruction() { assert(!CurrentInstruction && "There is already an instruction to process!"); - if (!SM.hasNext()) - return; + if (!SM.hasNext()) { + if (!SM.isEnd()) + return llvm::make_error(); + else + return llvm::ErrorSuccess(); + } SourceRef SR = SM.peekNext(); std::unique_ptr Inst = std::make_unique(SR.second); CurrentInstruction = InstRef(SR.first, Inst.get()); Instructions.emplace_back(std::move(Inst)); SM.updateNext(); + return llvm::ErrorSuccess(); } llvm::Error EntryStage::execute(InstRef & /*unused */) { @@ -46,16 +51,20 @@ // Move the program counter. CurrentInstruction.invalidate(); - getNextInstruction(); - return llvm::ErrorSuccess(); + return getNextInstruction(); } llvm::Error EntryStage::cycleStart() { if (!CurrentInstruction) - getNextInstruction(); + return getNextInstruction(); return llvm::ErrorSuccess(); } +llvm::Error EntryStage::cycleResume() { + assert(!CurrentInstruction); + return getNextInstruction(); +} + llvm::Error EntryStage::cycleEnd() { // Find the first instruction which hasn't been retired. auto Range = make_range(&Instructions[NumRetired], Instructions.end()); diff --git a/llvm/lib/MCA/Stages/Stage.cpp b/llvm/lib/MCA/Stages/Stage.cpp --- a/llvm/lib/MCA/Stages/Stage.cpp +++ b/llvm/lib/MCA/Stages/Stage.cpp @@ -24,5 +24,6 @@ Listeners.insert(Listener); } +char InstStreamPause::ID = 0; } // namespace mca } // namespace llvm diff --git a/llvm/lib/Target/AMDGPU/MCA/AMDGPUCustomBehaviour.cpp b/llvm/lib/Target/AMDGPU/MCA/AMDGPUCustomBehaviour.cpp --- a/llvm/lib/Target/AMDGPU/MCA/AMDGPUCustomBehaviour.cpp +++ b/llvm/lib/Target/AMDGPU/MCA/AMDGPUCustomBehaviour.cpp @@ -239,9 +239,9 @@ AMDGPU::IsaVersion IV = AMDGPU::getIsaVersion(STI.getCPU()); InstrWaitCntInfo.resize(SrcMgr.size()); - int Index = 0; - for (auto I = SrcMgr.begin(), E = SrcMgr.end(); I != E; ++I, ++Index) { - const std::unique_ptr &Inst = *I; + for (const auto &EN : llvm::enumerate(SrcMgr.getInstructions())) { + const std::unique_ptr &Inst = EN.value(); + unsigned Index = EN.index(); unsigned Opcode = Inst->getOpcode(); const MCInstrDesc &MCID = MCII.get(Opcode); if ((MCID.TSFlags & SIInstrFlags::DS) && diff --git a/llvm/tools/llvm-mca/llvm-mca.cpp b/llvm/tools/llvm-mca/llvm-mca.cpp --- a/llvm/tools/llvm-mca/llvm-mca.cpp +++ b/llvm/tools/llvm-mca/llvm-mca.cpp @@ -542,7 +542,8 @@ LoweredSequence.emplace_back(std::move(Inst.get())); } - mca::SourceMgr S(LoweredSequence, PrintInstructionTables ? 1 : Iterations); + mca::CircularSourceMgr S(LoweredSequence, + PrintInstructionTables ? 1 : Iterations); if (PrintInstructionTables) { // Create a pipeline, stages, and a printer. diff --git a/llvm/unittests/tools/CMakeLists.txt b/llvm/unittests/tools/CMakeLists.txt --- a/llvm/unittests/tools/CMakeLists.txt +++ b/llvm/unittests/tools/CMakeLists.txt @@ -8,3 +8,4 @@ llvm-exegesis ) add_subdirectory(llvm-profgen) +add_subdirectory(llvm-mca) diff --git a/llvm/unittests/tools/llvm-mca/CMakeLists.txt b/llvm/unittests/tools/llvm-mca/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/unittests/tools/llvm-mca/CMakeLists.txt @@ -0,0 +1,52 @@ +set(LLVM_LINK_COMPONENTS + MC + MCA + Object + Support + ) + +set(mca_root ${LLVM_MAIN_SRC_DIR}/tools/llvm-mca) + +set(mca_includes + ${CMAKE_CURRENT_SOURCE_DIR} + ${mca_root} + ) + +# Right now we only need SummaryView. +set(mca_views_sources + SummaryView.cpp + ) +list(TRANSFORM mca_views_sources PREPEND "${mca_root}/Views/") + +set(mca_sources + MCATestBase.cpp + ${mca_views_sources} + ) + +function(add_llvm_mca_unittest_includes) + set(mca_includes ${mca_includes} ${ARGV} PARENT_SCOPE) +endfunction() + +function(add_llvm_mca_unittest_sources) + set(sources ${ARGV}) + list(TRANSFORM sources PREPEND "${CMAKE_CURRENT_LIST_DIR}/") + set(mca_sources ${mca_sources} ${sources} PARENT_SCOPE) +endfunction() + +function(add_llvm_mca_unittest_link_components comps) + set(LLVM_LINK_COMPONENTS ${LLVM_LINK_COMPONENTS} ${ARGV} PARENT_SCOPE) +endfunction() + +if(LLVM_TARGETS_TO_BUILD MATCHES "X86") + include(X86/CMakeLists.txt) +endif() + +list(REMOVE_DUPLICATES LLVM_LINK_COMPONENTS) + +include_directories(${mca_includes}) + +add_llvm_target_unittest(LLVMMCATests + ${mca_sources} + ) + +set_property(TARGET LLVMMCATests PROPERTY FOLDER "Tests/UnitTests/ToolTests") diff --git a/llvm/unittests/tools/llvm-mca/MCATestBase.h b/llvm/unittests/tools/llvm-mca/MCATestBase.h new file mode 100644 --- /dev/null +++ b/llvm/unittests/tools/llvm-mca/MCATestBase.h @@ -0,0 +1,83 @@ +//===---- MCATestBase.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 +// +//===----------------------------------------------------------------------===// +// Test fixture common to all MCA tests. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_UNITTESTS_TOOLS_LLVMMCA_MCATESTBASE_H +#define LLVM_UNITTESTS_TOOLS_LLVMMCA_MCATESTBASE_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Triple.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCInstrAnalysis.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCTargetOptions.h" +#include "llvm/MC/SubtargetFeature.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/MCA/Context.h" + +#include "gtest/gtest.h" + +namespace llvm { +namespace json { +class Object; +} // end namespace json + +namespace mca { +class View; + +class MCATestBase : public ::testing::Test { +protected: + // Note: Subclass ctors are expected to perform target-specific + // initializations. + MCATestBase(StringRef TripleStr, StringRef CPUName, StringRef MAttr = "") + : TheTriple(TripleStr), CPUName(CPUName), MAttr(MAttr) {} + + /// Factory function to create a Target. + virtual const Target *getLLVMTarget() const; + + /// Factory function to create a MCTargetOptions instance. Returns an + /// empty one by default. + virtual MCTargetOptions getMCTargetOptions() { return MCTargetOptions(); } + + const Target *TheTarget; + const Triple TheTriple; + StringRef CPUName; + StringRef MAttr; + + // MC components. + std::unique_ptr STI; + std::unique_ptr MRI; + std::unique_ptr MAI; + std::unique_ptr MOFI; + std::unique_ptr Ctx; + std::unique_ptr MCII; + std::unique_ptr MCIA; + std::unique_ptr IP; + + static mca::PipelineOptions getDefaultPipelineOptions(); + + void SetUp() override; + + /// Utility function to run MCA with (nearly) the same configuration as the + /// `llvm-mca` tool to verify result correctness. + /// This function only displays on SummaryView by default. + virtual Error runBaselineMCA(json::Object &Result, ArrayRef Insts, + ArrayRef Views = None, + const mca::PipelineOptions *PO = nullptr); +}; + +} // end namespace mca +} // end namespace llvm +#endif diff --git a/llvm/unittests/tools/llvm-mca/MCATestBase.cpp b/llvm/unittests/tools/llvm-mca/MCATestBase.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/tools/llvm-mca/MCATestBase.cpp @@ -0,0 +1,123 @@ +#include "MCATestBase.h" +#include "Views/SummaryView.h" +#include "llvm/MCA/CustomBehaviour.h" +#include "llvm/MCA/InstrBuilder.h" +#include "llvm/MCA/Pipeline.h" +#include "llvm/MCA/SourceMgr.h" +#include "llvm/MCA/View.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/WithColor.h" +#include + +using namespace llvm; +using namespace mca; + +const Target *MCATestBase::getLLVMTarget() const { + std::string Error; + return TargetRegistry::lookupTarget(TheTriple.getTriple(), Error); +} + +mca::PipelineOptions MCATestBase::getDefaultPipelineOptions() { + mca::PipelineOptions PO(/*MicroOpQueue=*/0, /*DecoderThroughput=*/0, + /*DispatchWidth=*/0, + /*RegisterFileSize=*/0, + /*LoadQueueSize=*/0, /*StoreQueueSize=*/0, + /*AssumeNoAlias=*/true, + /*EnableBottleneckAnalysis=*/false); + return PO; +} + +void MCATestBase::SetUp() { + TheTarget = getLLVMTarget(); + ASSERT_NE(TheTarget, nullptr); + + StringRef TripleName = TheTriple.getTriple(); + + STI.reset(TheTarget->createMCSubtargetInfo(TripleName, CPUName, MAttr)); + ASSERT_TRUE(STI); + ASSERT_TRUE(STI->isCPUStringValid(CPUName)); + + MRI.reset(TheTarget->createMCRegInfo(TripleName)); + ASSERT_TRUE(MRI); + + auto MCOptions = getMCTargetOptions(); + MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName, MCOptions)); + ASSERT_TRUE(MAI); + + Ctx = std::make_unique(TheTriple, MAI.get(), MRI.get(), STI.get()); + MOFI.reset(TheTarget->createMCObjectFileInfo(*Ctx, /*PIC=*/false)); + Ctx->setObjectFileInfo(MOFI.get()); + + MCII.reset(TheTarget->createMCInstrInfo()); + ASSERT_TRUE(MCII); + + MCIA.reset(TheTarget->createMCInstrAnalysis(MCII.get())); + ASSERT_TRUE(MCIA); + + IP.reset(TheTarget->createMCInstPrinter(TheTriple, /*AssemblerDialect=*/0, + *MAI, *MCII, *MRI)); + ASSERT_TRUE(IP); +} + +Error MCATestBase::runBaselineMCA(json::Object &Result, ArrayRef Insts, + ArrayRef Views, + const mca::PipelineOptions *PO) { + mca::Context MCA(*MRI, *STI); + + mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get()); + + SmallVector> LoweredInsts; + for (const auto &MCI : Insts) { + Expected> Inst = + IB.createInstruction(MCI); + if (!Inst) { + if (auto NewE = + handleErrors(Inst.takeError(), + [this](const mca::InstructionError &IE) { + std::string InstructionStr; + raw_string_ostream SS(InstructionStr); + WithColor::error() << IE.Message << '\n'; + IP->printInst(&IE.Inst, 0, "", *STI, SS); + WithColor::note() + << "instruction: " << InstructionStr << '\n'; + })) { + // Default case. + return std::move(NewE); + } + } else { + LoweredInsts.emplace_back(std::move(Inst.get())); + } + } + + mca::CircularSourceMgr SM(LoweredInsts, /*Iterations=*/1); + + // Empty CustomBehaviour. + auto CB = std::make_unique(*STI, SM, *MCII); + + mca::PipelineOptions ThePO = PO ? *PO : getDefaultPipelineOptions(); + auto P = MCA.createDefaultPipeline(ThePO, SM, *CB); + + SmallVector, 1> DefaultViews; + if (Views.empty()) { + // By default, we only add SummaryView. + auto SV = std::make_unique(STI->getSchedModel(), Insts, + ThePO.DispatchWidth); + P->addEventListener(SV.get()); + DefaultViews.emplace_back(std::move(SV)); + } else { + for (auto *V : Views) + P->addEventListener(V); + } + + // Run the pipeline. + Expected Cycles = P->run(); + if (!Cycles) + return Cycles.takeError(); + + for (const auto *V : Views) + Result[V->getNameAsString()] = V->toJSON(); + for (const auto &V : DefaultViews) + Result[V->getNameAsString()] = V->toJSON(); + + return Error::success(); +} diff --git a/llvm/unittests/tools/llvm-mca/X86/CMakeLists.txt b/llvm/unittests/tools/llvm-mca/X86/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/unittests/tools/llvm-mca/X86/CMakeLists.txt @@ -0,0 +1,13 @@ +add_llvm_mca_unittest_includes( + ${LLVM_MAIN_SRC_DIR}/lib/Target/X86 + ${LLVM_BINARY_DIR}/lib/Target/X86 + ) + +add_llvm_mca_unittest_sources( + TestIncrementalMCA.cpp + X86TestBase.cpp + ) + +add_llvm_mca_unittest_link_components( + X86 + ) diff --git a/llvm/unittests/tools/llvm-mca/X86/TestIncrementalMCA.cpp b/llvm/unittests/tools/llvm-mca/X86/TestIncrementalMCA.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/tools/llvm-mca/X86/TestIncrementalMCA.cpp @@ -0,0 +1,80 @@ +#include "Views/SummaryView.h" +#include "X86TestBase.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/MCA/CustomBehaviour.h" +#include "llvm/MCA/IncrementalSourceMgr.h" +#include "llvm/MCA/InstrBuilder.h" +#include "llvm/MCA/Pipeline.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/raw_ostream.h" +#include + +using namespace llvm; +using namespace mca; + +TEST_F(X86TestBase, TestResumablePipeline) { + mca::Context MCA(*MRI, *STI); + + mca::IncrementalSourceMgr ISM; + // Empty CustomBehaviour. + auto CB = std::make_unique(*STI, ISM, *MCII); + + auto PO = getDefaultPipelineOptions(); + auto P = MCA.createDefaultPipeline(PO, ISM, *CB); + ASSERT_TRUE(P); + + SmallVector MCIs; + getSimpleInsts(MCIs, /*Repeats=*/100); + + // Add views. + auto SV = std::make_unique(STI->getSchedModel(), MCIs, + PO.DispatchWidth); + P->addEventListener(SV.get()); + + mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get()); + + // Tile size = 7 + for (unsigned i = 0U, E = MCIs.size(); i < E;) { + for (unsigned TE = i + 7; i < TE && i < E; ++i) { + Expected> InstOrErr = + IB.createInstruction(MCIs[i]); + ASSERT_TRUE(bool(InstOrErr)); + ISM.addInst(std::move(InstOrErr.get())); + } + + // Run the pipeline. + Expected Cycles = P->run(); + if (!Cycles) { + // Should be a stream pause error. + ASSERT_TRUE(Cycles.errorIsA()); + llvm::consumeError(Cycles.takeError()); + } + } + + ISM.endOfStream(); + // Has to terminate properly. + Expected Cycles = P->run(); + ASSERT_TRUE(bool(Cycles)); + + json::Value Result = SV->toJSON(); + auto *ResultObj = Result.getAsObject(); + ASSERT_TRUE(ResultObj); + + // Run the baseline. + json::Object BaselineResult; + auto E = runBaselineMCA(BaselineResult, MCIs); + ASSERT_FALSE(bool(E)) << "Failed to run baseline"; + auto *BaselineObj = BaselineResult.getObject(SV->getNameAsString()); + ASSERT_TRUE(BaselineObj) << "Does not contain SummaryView result"; + + // Compare the results. + constexpr const char *Fields[] = {"Instructions", "TotalCycles", "TotaluOps", + "BlockRThroughput"}; + for (const auto *F : Fields) { + auto V = ResultObj->getInteger(F); + auto BV = BaselineObj->getInteger(F); + ASSERT_TRUE(V && BV); + ASSERT_EQ(*BV, *V) << "Value of '" << F << "' does not match"; + } +} diff --git a/llvm/unittests/tools/llvm-mca/X86/X86TestBase.h b/llvm/unittests/tools/llvm-mca/X86/X86TestBase.h new file mode 100644 --- /dev/null +++ b/llvm/unittests/tools/llvm-mca/X86/X86TestBase.h @@ -0,0 +1,30 @@ +//===---- X86TestBase.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 +// +//===----------------------------------------------------------------------===// +// Test fixture common to all X86 MCA tests. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_UNITTESTS_TOOLS_LLVMMCA_X86_X86TESTBASE_H +#define LLVM_UNITTESTS_TOOLS_LLVMMCA_X86_X86TESTBASE_H + +#include "MCATestBase.h" +#include "llvm/ADT/SmallVector.h" + +namespace llvm { +namespace mca { + +class X86TestBase : public MCATestBase { +protected: + X86TestBase(); + + void getSimpleInsts(SmallVectorImpl &Insts, unsigned Repeats = 1); +}; + +} // end namespace mca +} // end namespace llvm + +#endif diff --git a/llvm/unittests/tools/llvm-mca/X86/X86TestBase.cpp b/llvm/unittests/tools/llvm-mca/X86/X86TestBase.cpp new file mode 100644 --- /dev/null +++ b/llvm/unittests/tools/llvm-mca/X86/X86TestBase.cpp @@ -0,0 +1,35 @@ +#include "X86TestBase.h" +#include "MCTargetDesc/X86MCTargetDesc.h" +#include "llvm/MC/MCInstBuilder.h" +#include "llvm/Support/TargetSelect.h" + +using namespace llvm; +using namespace mca; + +X86TestBase::X86TestBase() : MCATestBase("x86_64-unknown-linux", "skylake") { + LLVMInitializeX86TargetInfo(); + LLVMInitializeX86TargetMC(); + LLVMInitializeX86Target(); + LLVMInitializeX86AsmPrinter(); +} + +void X86TestBase::getSimpleInsts(SmallVectorImpl &Insts, + unsigned Repeats) { + for (unsigned i = 0U; i < Repeats; ++i) { + // vmulps %xmm0, %xmm1, %xmm2 + Insts.push_back(MCInstBuilder(X86::VMULPSrr) + .addReg(X86::XMM2) + .addReg(X86::XMM1) + .addReg(X86::XMM0)); + // vhaddps %xmm2, %xmm2, %xmm3 + Insts.push_back(MCInstBuilder(X86::VHADDPSrr) + .addReg(X86::XMM3) + .addReg(X86::XMM2) + .addReg(X86::XMM2)); + // vhaddps %xmm3, %xmm3, %xmm4 + Insts.push_back(MCInstBuilder(X86::VHADDPSrr) + .addReg(X86::XMM4) + .addReg(X86::XMM3) + .addReg(X86::XMM3)); + } +}