267 lines · cpp
1#include "MCTargetDesc/X86MCTargetDesc.h"2#include "Views/SummaryView.h"3#include "X86TestBase.h"4#include "llvm/ADT/SmallPtrSet.h"5#include "llvm/MC/MCInstBuilder.h"6#include "llvm/MCA/CustomBehaviour.h"7#include "llvm/MCA/IncrementalSourceMgr.h"8#include "llvm/MCA/InstrBuilder.h"9#include "llvm/MCA/Pipeline.h"10#include "llvm/Support/Format.h"11#include "llvm/Support/JSON.h"12#include "llvm/Support/raw_ostream.h"13#include <memory>14#include <unordered_map>15 16using namespace llvm;17using namespace mca;18 19TEST_F(X86TestBase, TestResumablePipeline) {20 mca::Context MCA(*MRI, *STI);21 22 mca::IncrementalSourceMgr ISM;23 // Empty CustomBehaviour.24 auto CB = std::make_unique<mca::CustomBehaviour>(*STI, ISM, *MCII);25 26 auto PO = getDefaultPipelineOptions();27 auto P = MCA.createDefaultPipeline(PO, ISM, *CB);28 ASSERT_TRUE(P);29 30 SmallVector<MCInst> MCIs;31 getSimpleInsts(MCIs, /*Repeats=*/100);32 33 // Add views.34 auto SV = std::make_unique<SummaryView>(STI->getSchedModel(), MCIs,35 PO.DispatchWidth);36 P->addEventListener(SV.get());37 38 auto IM = std::make_unique<mca::InstrumentManager>(*STI, *MCII);39 mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get(), *IM, /*CallLatency=*/100);40 41 const SmallVector<mca::Instrument *> Instruments;42 // Tile size = 743 for (unsigned i = 0U, E = MCIs.size(); i < E;) {44 for (unsigned TE = i + 7; i < TE && i < E; ++i) {45 Expected<std::unique_ptr<mca::Instruction>> InstOrErr =46 IB.createInstruction(MCIs[i], Instruments);47 ASSERT_TRUE(bool(InstOrErr));48 ISM.addInst(std::move(InstOrErr.get()));49 }50 51 // Run the pipeline.52 Expected<unsigned> Cycles = P->run();53 if (!Cycles) {54 // Should be a stream pause error.55 ASSERT_TRUE(Cycles.errorIsA<mca::InstStreamPause>());56 llvm::consumeError(Cycles.takeError());57 }58 }59 60 ISM.endOfStream();61 // Has to terminate properly.62 Expected<unsigned> Cycles = P->run();63 ASSERT_TRUE(bool(Cycles));64 65 json::Value Result = SV->toJSON();66 auto *ResultObj = Result.getAsObject();67 ASSERT_TRUE(ResultObj);68 69 // Run the baseline.70 json::Object BaselineResult;71 auto E = runBaselineMCA(BaselineResult, MCIs);72 ASSERT_FALSE(bool(E)) << "Failed to run baseline";73 auto *BaselineObj = BaselineResult.getObject(SV->getNameAsString());74 ASSERT_TRUE(BaselineObj) << "Does not contain SummaryView result";75 76 // Compare the results.77 constexpr const char *Fields[] = {"Instructions", "TotalCycles", "TotaluOps",78 "BlockRThroughput"};79 for (const auto *F : Fields) {80 auto V = ResultObj->getInteger(F);81 auto BV = BaselineObj->getInteger(F);82 ASSERT_TRUE(V && BV);83 ASSERT_EQ(*BV, *V) << "Value of '" << F << "' does not match";84 }85}86 87TEST_F(X86TestBase, TestInstructionRecycling) {88 mca::Context MCA(*MRI, *STI);89 90 std::unordered_map<const mca::InstrDesc *, SmallPtrSet<mca::Instruction *, 2>>91 RecycledInsts;92 auto GetRecycledInst = [&](const mca::InstrDesc &Desc) -> mca::Instruction * {93 auto It = RecycledInsts.find(&Desc);94 if (It != RecycledInsts.end()) {95 auto &Insts = It->second;96 if (Insts.size()) {97 mca::Instruction *I = *Insts.begin();98 Insts.erase(I);99 return I;100 }101 }102 return nullptr;103 };104 auto AddRecycledInst = [&](mca::Instruction *I) {105 const mca::InstrDesc &D = I->getDesc();106 RecycledInsts[&D].insert(I);107 };108 109 mca::IncrementalSourceMgr ISM;110 ISM.setOnInstFreedCallback(AddRecycledInst);111 112 // Empty CustomBehaviour.113 auto CB = std::make_unique<mca::CustomBehaviour>(*STI, ISM, *MCII);114 115 auto PO = getDefaultPipelineOptions();116 auto P = MCA.createDefaultPipeline(PO, ISM, *CB);117 ASSERT_TRUE(P);118 119 SmallVector<MCInst> MCIs;120 getSimpleInsts(MCIs, /*Repeats=*/100);121 122 // Add views.123 auto SV = std::make_unique<SummaryView>(STI->getSchedModel(), MCIs,124 PO.DispatchWidth);125 P->addEventListener(SV.get());126 127 // Default InstrumentManager128 auto IM = std::make_unique<mca::InstrumentManager>(*STI, *MCII);129 130 mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get(), *IM, /*CallLatency=*/100);131 IB.setInstRecycleCallback(GetRecycledInst);132 133 // Setup a generic IPP that does not do anything (as it is not target134 // specific) for testing purposes.135 auto IPP = std::make_unique<InstrPostProcess>(*STI, *MCII);136 137 const SmallVector<mca::Instrument *> Instruments;138 // Tile size = 7139 for (unsigned i = 0U, E = MCIs.size(); i < E;) {140 for (unsigned TE = i + 7; i < TE && i < E; ++i) {141 Expected<std::unique_ptr<mca::Instruction>> InstOrErr =142 IB.createInstruction(MCIs[i], Instruments);143 144 if (!InstOrErr) {145 mca::Instruction *RecycledInst = nullptr;146 // Check if the returned instruction is a recycled147 // one.148 auto RemainingE = handleErrors(InstOrErr.takeError(),149 [&](const mca::RecycledInstErr &RC) {150 RecycledInst = RC.getInst();151 });152 ASSERT_FALSE(bool(RemainingE));153 ASSERT_TRUE(RecycledInst);154 IPP->postProcessInstruction(*RecycledInst, MCIs[i]);155 ISM.addRecycledInst(RecycledInst);156 } else {157 IPP->postProcessInstruction(*InstOrErr.get(), MCIs[i]);158 ISM.addInst(std::move(InstOrErr.get()));159 }160 }161 162 // Run the pipeline.163 Expected<unsigned> Cycles = P->run();164 if (!Cycles) {165 // Should be a stream pause error.166 ASSERT_TRUE(Cycles.errorIsA<mca::InstStreamPause>());167 llvm::consumeError(Cycles.takeError());168 }169 }170 171 ISM.endOfStream();172 // Has to terminate properly.173 Expected<unsigned> Cycles = P->run();174 ASSERT_TRUE(bool(Cycles));175 176 json::Value Result = SV->toJSON();177 auto *ResultObj = Result.getAsObject();178 ASSERT_TRUE(ResultObj);179 180 // Run the baseline.181 json::Object BaselineResult;182 auto E = runBaselineMCA(BaselineResult, MCIs);183 ASSERT_FALSE(bool(E)) << "Failed to run baseline";184 auto *BaselineObj = BaselineResult.getObject(SV->getNameAsString());185 ASSERT_TRUE(BaselineObj) << "Does not contain SummaryView result";186 187 // Compare the results.188 constexpr const char *Fields[] = {"Instructions", "TotalCycles", "TotaluOps",189 "BlockRThroughput"};190 for (const auto *F : Fields) {191 auto V = ResultObj->getInteger(F);192 auto BV = BaselineObj->getInteger(F);193 ASSERT_TRUE(V && BV);194 ASSERT_EQ(*BV, *V) << "Value of '" << F << "' does not match";195 }196}197 198// Test that we do not depend upon the MCInst address for variant description199// construction. This test creates two instructions that will use variant200// description as they are both zeroing idioms, but write to different201// registers. If the key used to access the variant instruction description is202// the same between the descriptions (like the MCInst pointer), we will run into203// an assertion failure due to the different writes.204TEST_F(X86TestBase, TestVariantInstructionsSameAddress) {205 mca::Context MCA(*MRI, *STI);206 207 mca::IncrementalSourceMgr ISM;208 // Empty CustomBehaviour.209 auto CB = std::make_unique<mca::CustomBehaviour>(*STI, ISM, *MCII);210 211 auto PO = getDefaultPipelineOptions();212 auto P = MCA.createDefaultPipeline(PO, ISM, *CB);213 ASSERT_TRUE(P);214 215 auto IM = std::make_unique<mca::InstrumentManager>(*STI, *MCII);216 mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get(), *IM, 100);217 218 const SmallVector<mca::Instrument *> Instruments;219 220 MCInst InstructionToAdd;221 InstructionToAdd = MCInstBuilder(X86::XOR64rr)222 .addReg(X86::RAX)223 .addReg(X86::RAX)224 .addReg(X86::RAX);225 Expected<std::unique_ptr<mca::Instruction>> Instruction1OrErr =226 IB.createInstruction(InstructionToAdd, Instruments);227 ASSERT_TRUE(static_cast<bool>(Instruction1OrErr));228 ISM.addInst(std::move(Instruction1OrErr.get()));229 230 InstructionToAdd = MCInstBuilder(X86::XORPSrr)231 .addReg(X86::XMM0)232 .addReg(X86::XMM0)233 .addReg(X86::XMM0);234 Expected<std::unique_ptr<mca::Instruction>> Instruction2OrErr =235 IB.createInstruction(InstructionToAdd, Instruments);236 ASSERT_TRUE(static_cast<bool>(Instruction2OrErr));237 ISM.addInst(std::move(Instruction2OrErr.get()));238 239 ISM.endOfStream();240 Expected<unsigned> Cycles = P->run();241 ASSERT_TRUE(static_cast<bool>(Cycles));242}243 244// Test customization of instruction latency with instruments245TEST_F(X86TestBase, TestInstructionCustomization) {246 const unsigned ExplicitLatency = 100;247 SmallVector<MCInst> MCIs;248 MCInst InstructionToAdd = MCInstBuilder(X86::XOR64rr)249 .addReg(X86::RAX)250 .addReg(X86::RBX)251 .addReg(X86::RCX);252 MCIs.push_back(InstructionToAdd);253 SmallVector<std::pair<StringRef, StringRef>> InstrDescs;254 auto LatStr = std::to_string(ExplicitLatency);255 InstrDescs.push_back(std::make_pair(StringRef("LATENCY"), StringRef(LatStr)));256 257 // Run the baseline.258 json::Object BaselineResult;259 auto E = runBaselineMCA(BaselineResult, MCIs, {}, nullptr, InstrDescs);260 ASSERT_FALSE(bool(E)) << "Failed to run baseline";261 auto *BaselineObj = BaselineResult.getObject("SummaryView");262 auto V = BaselineObj->getInteger("TotalCycles");263 ASSERT_TRUE(V);264 // Additional 3 cycles for Dispatch, Executed and Retired states265 ASSERT_EQ(unsigned(*V), ExplicitLatency + 3) << "Total cycles do not match";266}267