1*370555c0SAiden Grossman #include "MCTargetDesc/X86MCTargetDesc.h"
297579dccSMin-Yih Hsu #include "Views/SummaryView.h"
397579dccSMin-Yih Hsu #include "X86TestBase.h"
497579dccSMin-Yih Hsu #include "llvm/ADT/SmallPtrSet.h"
5*370555c0SAiden Grossman #include "llvm/MC/MCInstBuilder.h"
697579dccSMin-Yih Hsu #include "llvm/MCA/CustomBehaviour.h"
797579dccSMin-Yih Hsu #include "llvm/MCA/IncrementalSourceMgr.h"
897579dccSMin-Yih Hsu #include "llvm/MCA/InstrBuilder.h"
997579dccSMin-Yih Hsu #include "llvm/MCA/Pipeline.h"
1097579dccSMin-Yih Hsu #include "llvm/Support/Format.h"
1197579dccSMin-Yih Hsu #include "llvm/Support/JSON.h"
1297579dccSMin-Yih Hsu #include "llvm/Support/raw_ostream.h"
13*370555c0SAiden Grossman #include <memory>
1497579dccSMin-Yih Hsu #include <unordered_map>
1597579dccSMin-Yih Hsu
1697579dccSMin-Yih Hsu using namespace llvm;
1797579dccSMin-Yih Hsu using namespace mca;
1897579dccSMin-Yih Hsu
TEST_F(X86TestBase,TestResumablePipeline)1997579dccSMin-Yih Hsu TEST_F(X86TestBase, TestResumablePipeline) {
2097579dccSMin-Yih Hsu mca::Context MCA(*MRI, *STI);
2197579dccSMin-Yih Hsu
2297579dccSMin-Yih Hsu mca::IncrementalSourceMgr ISM;
2397579dccSMin-Yih Hsu // Empty CustomBehaviour.
2497579dccSMin-Yih Hsu auto CB = std::make_unique<mca::CustomBehaviour>(*STI, ISM, *MCII);
2597579dccSMin-Yih Hsu
2697579dccSMin-Yih Hsu auto PO = getDefaultPipelineOptions();
2797579dccSMin-Yih Hsu auto P = MCA.createDefaultPipeline(PO, ISM, *CB);
2897579dccSMin-Yih Hsu ASSERT_TRUE(P);
2997579dccSMin-Yih Hsu
3097579dccSMin-Yih Hsu SmallVector<MCInst> MCIs;
3197579dccSMin-Yih Hsu getSimpleInsts(MCIs, /*Repeats=*/100);
3297579dccSMin-Yih Hsu
3397579dccSMin-Yih Hsu // Add views.
3497579dccSMin-Yih Hsu auto SV = std::make_unique<SummaryView>(STI->getSchedModel(), MCIs,
3597579dccSMin-Yih Hsu PO.DispatchWidth);
3697579dccSMin-Yih Hsu P->addEventListener(SV.get());
3797579dccSMin-Yih Hsu
3898e342dcSMichael Maitland auto IM = std::make_unique<mca::InstrumentManager>(*STI, *MCII);
39848bef5dSChinmay Deshpande mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get(), *IM, /*CallLatency=*/100);
4097579dccSMin-Yih Hsu
4156674e8eSMichael Maitland const SmallVector<mca::Instrument *> Instruments;
4297579dccSMin-Yih Hsu // Tile size = 7
4397579dccSMin-Yih Hsu for (unsigned i = 0U, E = MCIs.size(); i < E;) {
4497579dccSMin-Yih Hsu for (unsigned TE = i + 7; i < TE && i < E; ++i) {
4597579dccSMin-Yih Hsu Expected<std::unique_ptr<mca::Instruction>> InstOrErr =
4698e342dcSMichael Maitland IB.createInstruction(MCIs[i], Instruments);
4797579dccSMin-Yih Hsu ASSERT_TRUE(bool(InstOrErr));
4897579dccSMin-Yih Hsu ISM.addInst(std::move(InstOrErr.get()));
4997579dccSMin-Yih Hsu }
5097579dccSMin-Yih Hsu
5197579dccSMin-Yih Hsu // Run the pipeline.
5297579dccSMin-Yih Hsu Expected<unsigned> Cycles = P->run();
5397579dccSMin-Yih Hsu if (!Cycles) {
5497579dccSMin-Yih Hsu // Should be a stream pause error.
5597579dccSMin-Yih Hsu ASSERT_TRUE(Cycles.errorIsA<mca::InstStreamPause>());
5697579dccSMin-Yih Hsu llvm::consumeError(Cycles.takeError());
5797579dccSMin-Yih Hsu }
5897579dccSMin-Yih Hsu }
5997579dccSMin-Yih Hsu
6097579dccSMin-Yih Hsu ISM.endOfStream();
6197579dccSMin-Yih Hsu // Has to terminate properly.
6297579dccSMin-Yih Hsu Expected<unsigned> Cycles = P->run();
6397579dccSMin-Yih Hsu ASSERT_TRUE(bool(Cycles));
6497579dccSMin-Yih Hsu
6597579dccSMin-Yih Hsu json::Value Result = SV->toJSON();
6697579dccSMin-Yih Hsu auto *ResultObj = Result.getAsObject();
6797579dccSMin-Yih Hsu ASSERT_TRUE(ResultObj);
6897579dccSMin-Yih Hsu
6997579dccSMin-Yih Hsu // Run the baseline.
7097579dccSMin-Yih Hsu json::Object BaselineResult;
7197579dccSMin-Yih Hsu auto E = runBaselineMCA(BaselineResult, MCIs);
7297579dccSMin-Yih Hsu ASSERT_FALSE(bool(E)) << "Failed to run baseline";
7397579dccSMin-Yih Hsu auto *BaselineObj = BaselineResult.getObject(SV->getNameAsString());
7497579dccSMin-Yih Hsu ASSERT_TRUE(BaselineObj) << "Does not contain SummaryView result";
7597579dccSMin-Yih Hsu
7697579dccSMin-Yih Hsu // Compare the results.
7797579dccSMin-Yih Hsu constexpr const char *Fields[] = {"Instructions", "TotalCycles", "TotaluOps",
7897579dccSMin-Yih Hsu "BlockRThroughput"};
7997579dccSMin-Yih Hsu for (const auto *F : Fields) {
8097579dccSMin-Yih Hsu auto V = ResultObj->getInteger(F);
8197579dccSMin-Yih Hsu auto BV = BaselineObj->getInteger(F);
8297579dccSMin-Yih Hsu ASSERT_TRUE(V && BV);
8397579dccSMin-Yih Hsu ASSERT_EQ(*BV, *V) << "Value of '" << F << "' does not match";
8497579dccSMin-Yih Hsu }
8597579dccSMin-Yih Hsu }
86b847692eSMin-Yih Hsu
TEST_F(X86TestBase,TestInstructionRecycling)87b847692eSMin-Yih Hsu TEST_F(X86TestBase, TestInstructionRecycling) {
88b847692eSMin-Yih Hsu mca::Context MCA(*MRI, *STI);
89b847692eSMin-Yih Hsu
90b847692eSMin-Yih Hsu std::unordered_map<const mca::InstrDesc *, SmallPtrSet<mca::Instruction *, 2>>
91b847692eSMin-Yih Hsu RecycledInsts;
92b847692eSMin-Yih Hsu auto GetRecycledInst = [&](const mca::InstrDesc &Desc) -> mca::Instruction * {
93b847692eSMin-Yih Hsu auto It = RecycledInsts.find(&Desc);
94b847692eSMin-Yih Hsu if (It != RecycledInsts.end()) {
95b847692eSMin-Yih Hsu auto &Insts = It->second;
96b847692eSMin-Yih Hsu if (Insts.size()) {
97b847692eSMin-Yih Hsu mca::Instruction *I = *Insts.begin();
98b847692eSMin-Yih Hsu Insts.erase(I);
99b847692eSMin-Yih Hsu return I;
100b847692eSMin-Yih Hsu }
101b847692eSMin-Yih Hsu }
102b847692eSMin-Yih Hsu return nullptr;
103b847692eSMin-Yih Hsu };
104b847692eSMin-Yih Hsu auto AddRecycledInst = [&](mca::Instruction *I) {
105b847692eSMin-Yih Hsu const mca::InstrDesc &D = I->getDesc();
106b847692eSMin-Yih Hsu RecycledInsts[&D].insert(I);
107b847692eSMin-Yih Hsu };
108b847692eSMin-Yih Hsu
109b847692eSMin-Yih Hsu mca::IncrementalSourceMgr ISM;
110b847692eSMin-Yih Hsu ISM.setOnInstFreedCallback(AddRecycledInst);
111b847692eSMin-Yih Hsu
112b847692eSMin-Yih Hsu // Empty CustomBehaviour.
113b847692eSMin-Yih Hsu auto CB = std::make_unique<mca::CustomBehaviour>(*STI, ISM, *MCII);
114b847692eSMin-Yih Hsu
115b847692eSMin-Yih Hsu auto PO = getDefaultPipelineOptions();
116b847692eSMin-Yih Hsu auto P = MCA.createDefaultPipeline(PO, ISM, *CB);
117b847692eSMin-Yih Hsu ASSERT_TRUE(P);
118b847692eSMin-Yih Hsu
119b847692eSMin-Yih Hsu SmallVector<MCInst> MCIs;
120b847692eSMin-Yih Hsu getSimpleInsts(MCIs, /*Repeats=*/100);
121b847692eSMin-Yih Hsu
122b847692eSMin-Yih Hsu // Add views.
123b847692eSMin-Yih Hsu auto SV = std::make_unique<SummaryView>(STI->getSchedModel(), MCIs,
124b847692eSMin-Yih Hsu PO.DispatchWidth);
125b847692eSMin-Yih Hsu P->addEventListener(SV.get());
126b847692eSMin-Yih Hsu
12798e342dcSMichael Maitland // Default InstrumentManager
12898e342dcSMichael Maitland auto IM = std::make_unique<mca::InstrumentManager>(*STI, *MCII);
12998e342dcSMichael Maitland
130848bef5dSChinmay Deshpande mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get(), *IM, /*CallLatency=*/100);
131b847692eSMin-Yih Hsu IB.setInstRecycleCallback(GetRecycledInst);
132b847692eSMin-Yih Hsu
13356674e8eSMichael Maitland const SmallVector<mca::Instrument *> Instruments;
134b847692eSMin-Yih Hsu // Tile size = 7
135b847692eSMin-Yih Hsu for (unsigned i = 0U, E = MCIs.size(); i < E;) {
136b847692eSMin-Yih Hsu for (unsigned TE = i + 7; i < TE && i < E; ++i) {
137b847692eSMin-Yih Hsu Expected<std::unique_ptr<mca::Instruction>> InstOrErr =
13898e342dcSMichael Maitland IB.createInstruction(MCIs[i], Instruments);
139b847692eSMin-Yih Hsu
140b847692eSMin-Yih Hsu if (!InstOrErr) {
141b847692eSMin-Yih Hsu mca::Instruction *RecycledInst = nullptr;
142b847692eSMin-Yih Hsu // Check if the returned instruction is a recycled
143b847692eSMin-Yih Hsu // one.
144b847692eSMin-Yih Hsu auto RemainingE = handleErrors(InstOrErr.takeError(),
145b847692eSMin-Yih Hsu [&](const mca::RecycledInstErr &RC) {
146b847692eSMin-Yih Hsu RecycledInst = RC.getInst();
147b847692eSMin-Yih Hsu });
148b847692eSMin-Yih Hsu ASSERT_FALSE(bool(RemainingE));
149b847692eSMin-Yih Hsu ASSERT_TRUE(RecycledInst);
150b847692eSMin-Yih Hsu ISM.addRecycledInst(RecycledInst);
151b847692eSMin-Yih Hsu } else {
152b847692eSMin-Yih Hsu ISM.addInst(std::move(InstOrErr.get()));
153b847692eSMin-Yih Hsu }
154b847692eSMin-Yih Hsu }
155b847692eSMin-Yih Hsu
156b847692eSMin-Yih Hsu // Run the pipeline.
157b847692eSMin-Yih Hsu Expected<unsigned> Cycles = P->run();
158b847692eSMin-Yih Hsu if (!Cycles) {
159b847692eSMin-Yih Hsu // Should be a stream pause error.
160b847692eSMin-Yih Hsu ASSERT_TRUE(Cycles.errorIsA<mca::InstStreamPause>());
161b847692eSMin-Yih Hsu llvm::consumeError(Cycles.takeError());
162b847692eSMin-Yih Hsu }
163b847692eSMin-Yih Hsu }
164b847692eSMin-Yih Hsu
165b847692eSMin-Yih Hsu ISM.endOfStream();
166b847692eSMin-Yih Hsu // Has to terminate properly.
167b847692eSMin-Yih Hsu Expected<unsigned> Cycles = P->run();
168b847692eSMin-Yih Hsu ASSERT_TRUE(bool(Cycles));
169b847692eSMin-Yih Hsu
170b847692eSMin-Yih Hsu json::Value Result = SV->toJSON();
171b847692eSMin-Yih Hsu auto *ResultObj = Result.getAsObject();
172b847692eSMin-Yih Hsu ASSERT_TRUE(ResultObj);
173b847692eSMin-Yih Hsu
174b847692eSMin-Yih Hsu // Run the baseline.
175b847692eSMin-Yih Hsu json::Object BaselineResult;
176b847692eSMin-Yih Hsu auto E = runBaselineMCA(BaselineResult, MCIs);
177b847692eSMin-Yih Hsu ASSERT_FALSE(bool(E)) << "Failed to run baseline";
178b847692eSMin-Yih Hsu auto *BaselineObj = BaselineResult.getObject(SV->getNameAsString());
179b847692eSMin-Yih Hsu ASSERT_TRUE(BaselineObj) << "Does not contain SummaryView result";
180b847692eSMin-Yih Hsu
181b847692eSMin-Yih Hsu // Compare the results.
182b847692eSMin-Yih Hsu constexpr const char *Fields[] = {"Instructions", "TotalCycles", "TotaluOps",
183b847692eSMin-Yih Hsu "BlockRThroughput"};
184b847692eSMin-Yih Hsu for (const auto *F : Fields) {
185b847692eSMin-Yih Hsu auto V = ResultObj->getInteger(F);
186b847692eSMin-Yih Hsu auto BV = BaselineObj->getInteger(F);
187b847692eSMin-Yih Hsu ASSERT_TRUE(V && BV);
188b847692eSMin-Yih Hsu ASSERT_EQ(*BV, *V) << "Value of '" << F << "' does not match";
189b847692eSMin-Yih Hsu }
190b847692eSMin-Yih Hsu }
191*370555c0SAiden Grossman
192*370555c0SAiden Grossman // Test that we do not depend upon the MCInst address for variant description
193*370555c0SAiden Grossman // construction. This test creates two instructions that will use variant
194*370555c0SAiden Grossman // description as they are both zeroing idioms, but write to different
195*370555c0SAiden Grossman // registers. If the key used to access the variant instruction description is
196*370555c0SAiden Grossman // the same between the descriptions (like the MCInst pointer), we will run into
197*370555c0SAiden Grossman // an assertion failure due to the different writes.
TEST_F(X86TestBase,TestVariantInstructionsSameAddress)198*370555c0SAiden Grossman TEST_F(X86TestBase, TestVariantInstructionsSameAddress) {
199*370555c0SAiden Grossman mca::Context MCA(*MRI, *STI);
200*370555c0SAiden Grossman
201*370555c0SAiden Grossman mca::IncrementalSourceMgr ISM;
202*370555c0SAiden Grossman // Empty CustomBehaviour.
203*370555c0SAiden Grossman auto CB = std::make_unique<mca::CustomBehaviour>(*STI, ISM, *MCII);
204*370555c0SAiden Grossman
205*370555c0SAiden Grossman auto PO = getDefaultPipelineOptions();
206*370555c0SAiden Grossman auto P = MCA.createDefaultPipeline(PO, ISM, *CB);
207*370555c0SAiden Grossman ASSERT_TRUE(P);
208*370555c0SAiden Grossman
209*370555c0SAiden Grossman auto IM = std::make_unique<mca::InstrumentManager>(*STI, *MCII);
210*370555c0SAiden Grossman mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get(), *IM, 100);
211*370555c0SAiden Grossman
212*370555c0SAiden Grossman const SmallVector<mca::Instrument *> Instruments;
213*370555c0SAiden Grossman
214*370555c0SAiden Grossman MCInst InstructionToAdd;
215*370555c0SAiden Grossman InstructionToAdd = MCInstBuilder(X86::XOR64rr)
216*370555c0SAiden Grossman .addReg(X86::RAX)
217*370555c0SAiden Grossman .addReg(X86::RAX)
218*370555c0SAiden Grossman .addReg(X86::RAX);
219*370555c0SAiden Grossman Expected<std::unique_ptr<mca::Instruction>> Instruction1OrErr =
220*370555c0SAiden Grossman IB.createInstruction(InstructionToAdd, Instruments);
221*370555c0SAiden Grossman ASSERT_TRUE(static_cast<bool>(Instruction1OrErr));
222*370555c0SAiden Grossman ISM.addInst(std::move(Instruction1OrErr.get()));
223*370555c0SAiden Grossman
224*370555c0SAiden Grossman InstructionToAdd = MCInstBuilder(X86::XORPSrr)
225*370555c0SAiden Grossman .addReg(X86::XMM0)
226*370555c0SAiden Grossman .addReg(X86::XMM0)
227*370555c0SAiden Grossman .addReg(X86::XMM0);
228*370555c0SAiden Grossman Expected<std::unique_ptr<mca::Instruction>> Instruction2OrErr =
229*370555c0SAiden Grossman IB.createInstruction(InstructionToAdd, Instruments);
230*370555c0SAiden Grossman ASSERT_TRUE(static_cast<bool>(Instruction2OrErr));
231*370555c0SAiden Grossman ISM.addInst(std::move(Instruction2OrErr.get()));
232*370555c0SAiden Grossman
233*370555c0SAiden Grossman ISM.endOfStream();
234*370555c0SAiden Grossman Expected<unsigned> Cycles = P->run();
235*370555c0SAiden Grossman ASSERT_TRUE(static_cast<bool>(Cycles));
236*370555c0SAiden Grossman }
237