xref: /llvm-project/llvm/unittests/tools/llvm-mca/X86/TestIncrementalMCA.cpp (revision 370555c02c81fb3ab2f41f608d26aa85b8ca1ec5)
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