1 | //===-- llvm-mca.cpp - Machine Code Analyzer -------------------*- C++ -* -===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This utility is a simple driver that allows static performance analysis on |
10 | // machine code similarly to how IACA (Intel Architecture Code Analyzer) works. |
11 | // |
12 | // llvm-mca [options] <file-name> |
13 | // -march <type> |
14 | // -mcpu <cpu> |
15 | // -o <file> |
16 | // |
17 | // The target defaults to the host target. |
18 | // The cpu defaults to the 'native' host cpu. |
19 | // The output defaults to standard output. |
20 | // |
21 | //===----------------------------------------------------------------------===// |
22 | |
23 | #include "CodeRegion.h" |
24 | #include "CodeRegionGenerator.h" |
25 | #include "PipelinePrinter.h" |
26 | #include "Views/BottleneckAnalysis.h" |
27 | #include "Views/DispatchStatistics.h" |
28 | #include "Views/InstructionInfoView.h" |
29 | #include "Views/RegisterFileStatistics.h" |
30 | #include "Views/ResourcePressureView.h" |
31 | #include "Views/RetireControlUnitStatistics.h" |
32 | #include "Views/SchedulerStatistics.h" |
33 | #include "Views/SummaryView.h" |
34 | #include "Views/TimelineView.h" |
35 | #include "llvm/MC/MCAsmBackend.h" |
36 | #include "llvm/MC/MCAsmInfo.h" |
37 | #include "llvm/MC/MCCodeEmitter.h" |
38 | #include "llvm/MC/MCContext.h" |
39 | #include "llvm/MC/MCObjectFileInfo.h" |
40 | #include "llvm/MC/MCRegisterInfo.h" |
41 | #include "llvm/MC/MCSubtargetInfo.h" |
42 | #include "llvm/MC/MCTargetOptionsCommandFlags.h" |
43 | #include "llvm/MC/TargetRegistry.h" |
44 | #include "llvm/MCA/CodeEmitter.h" |
45 | #include "llvm/MCA/Context.h" |
46 | #include "llvm/MCA/CustomBehaviour.h" |
47 | #include "llvm/MCA/InstrBuilder.h" |
48 | #include "llvm/MCA/Pipeline.h" |
49 | #include "llvm/MCA/Stages/EntryStage.h" |
50 | #include "llvm/MCA/Stages/InstructionTables.h" |
51 | #include "llvm/MCA/Support.h" |
52 | #include "llvm/Support/CommandLine.h" |
53 | #include "llvm/Support/ErrorHandling.h" |
54 | #include "llvm/Support/ErrorOr.h" |
55 | #include "llvm/Support/FileSystem.h" |
56 | #include "llvm/Support/InitLLVM.h" |
57 | #include "llvm/Support/MemoryBuffer.h" |
58 | #include "llvm/Support/SourceMgr.h" |
59 | #include "llvm/Support/TargetSelect.h" |
60 | #include "llvm/Support/ToolOutputFile.h" |
61 | #include "llvm/Support/WithColor.h" |
62 | #include "llvm/TargetParser/Host.h" |
63 | |
64 | using namespace llvm; |
65 | |
66 | static mc::RegisterMCTargetOptionsFlags MOF; |
67 | |
68 | static cl::OptionCategory ToolOptions("Tool Options" ); |
69 | static cl::OptionCategory ViewOptions("View Options" ); |
70 | |
71 | static cl::opt<std::string> InputFilename(cl::Positional, |
72 | cl::desc("<input file>" ), |
73 | cl::cat(ToolOptions), cl::init(Val: "-" )); |
74 | |
75 | static cl::opt<std::string> OutputFilename("o" , cl::desc("Output filename" ), |
76 | cl::init(Val: "-" ), cl::cat(ToolOptions), |
77 | cl::value_desc("filename" )); |
78 | |
79 | static cl::opt<std::string> |
80 | ArchName("march" , |
81 | cl::desc("Target architecture. " |
82 | "See -version for available targets" ), |
83 | cl::cat(ToolOptions)); |
84 | |
85 | static cl::opt<std::string> |
86 | TripleName("mtriple" , |
87 | cl::desc("Target triple. See -version for available targets" ), |
88 | cl::cat(ToolOptions)); |
89 | |
90 | static cl::opt<std::string> |
91 | MCPU("mcpu" , |
92 | cl::desc("Target a specific cpu type (-mcpu=help for details)" ), |
93 | cl::value_desc("cpu-name" ), cl::cat(ToolOptions), cl::init(Val: "native" )); |
94 | |
95 | static cl::list<std::string> |
96 | MATTRS("mattr" , cl::CommaSeparated, |
97 | cl::desc("Target specific attributes (-mattr=help for details)" ), |
98 | cl::value_desc("a1,+a2,-a3,..." ), cl::cat(ToolOptions)); |
99 | |
100 | static cl::opt<bool> PrintJson("json" , |
101 | cl::desc("Print the output in json format" ), |
102 | cl::cat(ToolOptions), cl::init(Val: false)); |
103 | |
104 | static cl::opt<int> |
105 | OutputAsmVariant("output-asm-variant" , |
106 | cl::desc("Syntax variant to use for output printing" ), |
107 | cl::cat(ToolOptions), cl::init(Val: -1)); |
108 | |
109 | static cl::opt<bool> |
110 | PrintImmHex("print-imm-hex" , cl::cat(ToolOptions), cl::init(Val: false), |
111 | cl::desc("Prefer hex format when printing immediate values" )); |
112 | |
113 | static cl::opt<unsigned> Iterations("iterations" , |
114 | cl::desc("Number of iterations to run" ), |
115 | cl::cat(ToolOptions), cl::init(Val: 0)); |
116 | |
117 | static cl::opt<unsigned> |
118 | DispatchWidth("dispatch" , cl::desc("Override the processor dispatch width" ), |
119 | cl::cat(ToolOptions), cl::init(Val: 0)); |
120 | |
121 | static cl::opt<unsigned> |
122 | RegisterFileSize("register-file-size" , |
123 | cl::desc("Maximum number of physical registers which can " |
124 | "be used for register mappings" ), |
125 | cl::cat(ToolOptions), cl::init(Val: 0)); |
126 | |
127 | static cl::opt<unsigned> |
128 | MicroOpQueue("micro-op-queue-size" , cl::Hidden, |
129 | cl::desc("Number of entries in the micro-op queue" ), |
130 | cl::cat(ToolOptions), cl::init(Val: 0)); |
131 | |
132 | static cl::opt<unsigned> |
133 | DecoderThroughput("decoder-throughput" , cl::Hidden, |
134 | cl::desc("Maximum throughput from the decoders " |
135 | "(instructions per cycle)" ), |
136 | cl::cat(ToolOptions), cl::init(Val: 0)); |
137 | |
138 | static cl::opt<bool> |
139 | PrintRegisterFileStats("register-file-stats" , |
140 | cl::desc("Print register file statistics" ), |
141 | cl::cat(ViewOptions), cl::init(Val: false)); |
142 | |
143 | static cl::opt<bool> PrintDispatchStats("dispatch-stats" , |
144 | cl::desc("Print dispatch statistics" ), |
145 | cl::cat(ViewOptions), cl::init(Val: false)); |
146 | |
147 | static cl::opt<bool> |
148 | PrintSummaryView("summary-view" , cl::Hidden, |
149 | cl::desc("Print summary view (enabled by default)" ), |
150 | cl::cat(ViewOptions), cl::init(Val: true)); |
151 | |
152 | static cl::opt<bool> PrintSchedulerStats("scheduler-stats" , |
153 | cl::desc("Print scheduler statistics" ), |
154 | cl::cat(ViewOptions), cl::init(Val: false)); |
155 | |
156 | static cl::opt<bool> |
157 | PrintRetireStats("retire-stats" , |
158 | cl::desc("Print retire control unit statistics" ), |
159 | cl::cat(ViewOptions), cl::init(Val: false)); |
160 | |
161 | static cl::opt<bool> PrintResourcePressureView( |
162 | "resource-pressure" , |
163 | cl::desc("Print the resource pressure view (enabled by default)" ), |
164 | cl::cat(ViewOptions), cl::init(Val: true)); |
165 | |
166 | static cl::opt<bool> PrintTimelineView("timeline" , |
167 | cl::desc("Print the timeline view" ), |
168 | cl::cat(ViewOptions), cl::init(Val: false)); |
169 | |
170 | static cl::opt<unsigned> TimelineMaxIterations( |
171 | "timeline-max-iterations" , |
172 | cl::desc("Maximum number of iterations to print in timeline view" ), |
173 | cl::cat(ViewOptions), cl::init(Val: 0)); |
174 | |
175 | static cl::opt<unsigned> |
176 | TimelineMaxCycles("timeline-max-cycles" , |
177 | cl::desc("Maximum number of cycles in the timeline view, " |
178 | "or 0 for unlimited. Defaults to 80 cycles" ), |
179 | cl::cat(ViewOptions), cl::init(Val: 80)); |
180 | |
181 | static cl::opt<bool> |
182 | AssumeNoAlias("noalias" , |
183 | cl::desc("If set, assume that loads and stores do not alias" ), |
184 | cl::cat(ToolOptions), cl::init(Val: true)); |
185 | |
186 | static cl::opt<unsigned> LoadQueueSize("lqueue" , |
187 | cl::desc("Size of the load queue" ), |
188 | cl::cat(ToolOptions), cl::init(Val: 0)); |
189 | |
190 | static cl::opt<unsigned> StoreQueueSize("squeue" , |
191 | cl::desc("Size of the store queue" ), |
192 | cl::cat(ToolOptions), cl::init(Val: 0)); |
193 | |
194 | static cl::opt<bool> |
195 | PrintInstructionTables("instruction-tables" , |
196 | cl::desc("Print instruction tables" ), |
197 | cl::cat(ToolOptions), cl::init(Val: false)); |
198 | |
199 | static cl::opt<bool> PrintInstructionInfoView( |
200 | "instruction-info" , |
201 | cl::desc("Print the instruction info view (enabled by default)" ), |
202 | cl::cat(ViewOptions), cl::init(Val: true)); |
203 | |
204 | static cl::opt<bool> EnableAllStats("all-stats" , |
205 | cl::desc("Print all hardware statistics" ), |
206 | cl::cat(ViewOptions), cl::init(Val: false)); |
207 | |
208 | static cl::opt<bool> |
209 | EnableAllViews("all-views" , |
210 | cl::desc("Print all views including hardware statistics" ), |
211 | cl::cat(ViewOptions), cl::init(Val: false)); |
212 | |
213 | static cl::opt<bool> EnableBottleneckAnalysis( |
214 | "bottleneck-analysis" , |
215 | cl::desc("Enable bottleneck analysis (disabled by default)" ), |
216 | cl::cat(ViewOptions), cl::init(Val: false)); |
217 | |
218 | static cl::opt<bool> ShowEncoding( |
219 | "show-encoding" , |
220 | cl::desc("Print encoding information in the instruction info view" ), |
221 | cl::cat(ViewOptions), cl::init(Val: false)); |
222 | |
223 | static cl::opt<bool> ShowBarriers( |
224 | "show-barriers" , |
225 | cl::desc("Print memory barrier information in the instruction info view" ), |
226 | cl::cat(ViewOptions), cl::init(Val: false)); |
227 | |
228 | static cl::opt<bool> DisableCustomBehaviour( |
229 | "disable-cb" , |
230 | cl::desc( |
231 | "Disable custom behaviour (use the default class which does nothing)." ), |
232 | cl::cat(ViewOptions), cl::init(Val: false)); |
233 | |
234 | static cl::opt<bool> DisableInstrumentManager( |
235 | "disable-im" , |
236 | cl::desc("Disable instrumentation manager (use the default class which " |
237 | "ignores instruments.)." ), |
238 | cl::cat(ViewOptions), cl::init(Val: false)); |
239 | |
240 | namespace { |
241 | |
242 | const Target *getTarget(const char *ProgName) { |
243 | if (TripleName.empty()) |
244 | TripleName = Triple::normalize(Str: sys::getDefaultTargetTriple()); |
245 | Triple TheTriple(TripleName); |
246 | |
247 | // Get the target specific parser. |
248 | std::string Error; |
249 | const Target *TheTarget = |
250 | TargetRegistry::lookupTarget(ArchName, TheTriple, Error); |
251 | if (!TheTarget) { |
252 | errs() << ProgName << ": " << Error; |
253 | return nullptr; |
254 | } |
255 | |
256 | // Update TripleName with the updated triple from the target lookup. |
257 | TripleName = TheTriple.str(); |
258 | |
259 | // Return the found target. |
260 | return TheTarget; |
261 | } |
262 | |
263 | ErrorOr<std::unique_ptr<ToolOutputFile>> getOutputStream() { |
264 | if (OutputFilename == "" ) |
265 | OutputFilename = "-" ; |
266 | std::error_code EC; |
267 | auto Out = std::make_unique<ToolOutputFile>(args&: OutputFilename, args&: EC, |
268 | args: sys::fs::OF_TextWithCRLF); |
269 | if (!EC) |
270 | return std::move(Out); |
271 | return EC; |
272 | } |
273 | } // end of anonymous namespace |
274 | |
275 | static void processOptionImpl(cl::opt<bool> &O, const cl::opt<bool> &Default) { |
276 | if (!O.getNumOccurrences() || O.getPosition() < Default.getPosition()) |
277 | O = Default.getValue(); |
278 | } |
279 | |
280 | static void processViewOptions(bool IsOutOfOrder) { |
281 | if (!EnableAllViews.getNumOccurrences() && |
282 | !EnableAllStats.getNumOccurrences()) |
283 | return; |
284 | |
285 | if (EnableAllViews.getNumOccurrences()) { |
286 | processOptionImpl(O&: PrintSummaryView, Default: EnableAllViews); |
287 | if (IsOutOfOrder) |
288 | processOptionImpl(O&: EnableBottleneckAnalysis, Default: EnableAllViews); |
289 | processOptionImpl(O&: PrintResourcePressureView, Default: EnableAllViews); |
290 | processOptionImpl(O&: PrintTimelineView, Default: EnableAllViews); |
291 | processOptionImpl(O&: PrintInstructionInfoView, Default: EnableAllViews); |
292 | } |
293 | |
294 | const cl::opt<bool> &Default = |
295 | EnableAllViews.getPosition() < EnableAllStats.getPosition() |
296 | ? EnableAllStats |
297 | : EnableAllViews; |
298 | processOptionImpl(O&: PrintRegisterFileStats, Default); |
299 | processOptionImpl(O&: PrintDispatchStats, Default); |
300 | processOptionImpl(O&: PrintSchedulerStats, Default); |
301 | if (IsOutOfOrder) |
302 | processOptionImpl(O&: PrintRetireStats, Default); |
303 | } |
304 | |
305 | // Returns true on success. |
306 | static bool runPipeline(mca::Pipeline &P) { |
307 | // Handle pipeline errors here. |
308 | Expected<unsigned> Cycles = P.run(); |
309 | if (!Cycles) { |
310 | WithColor::error() << toString(E: Cycles.takeError()); |
311 | return false; |
312 | } |
313 | return true; |
314 | } |
315 | |
316 | int main(int argc, char **argv) { |
317 | InitLLVM X(argc, argv); |
318 | |
319 | // Initialize targets and assembly parsers. |
320 | InitializeAllTargetInfos(); |
321 | InitializeAllTargetMCs(); |
322 | InitializeAllAsmParsers(); |
323 | InitializeAllTargetMCAs(); |
324 | |
325 | // Register the Target and CPU printer for --version. |
326 | cl::AddExtraVersionPrinter(func: sys::printDefaultTargetAndDetectedCPU); |
327 | |
328 | // Enable printing of available targets when flag --version is specified. |
329 | cl::AddExtraVersionPrinter(func: TargetRegistry::printRegisteredTargetsForVersion); |
330 | |
331 | cl::HideUnrelatedOptions(Categories: {&ToolOptions, &ViewOptions}); |
332 | |
333 | // Parse flags and initialize target options. |
334 | cl::ParseCommandLineOptions(argc, argv, |
335 | Overview: "llvm machine code performance analyzer.\n" ); |
336 | |
337 | // Get the target from the triple. If a triple is not specified, then select |
338 | // the default triple for the host. If the triple doesn't correspond to any |
339 | // registered target, then exit with an error message. |
340 | const char *ProgName = argv[0]; |
341 | const Target *TheTarget = getTarget(ProgName); |
342 | if (!TheTarget) |
343 | return 1; |
344 | |
345 | // GetTarget() may replaced TripleName with a default triple. |
346 | // For safety, reconstruct the Triple object. |
347 | Triple TheTriple(TripleName); |
348 | |
349 | ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr = |
350 | MemoryBuffer::getFileOrSTDIN(Filename: InputFilename); |
351 | if (std::error_code EC = BufferPtr.getError()) { |
352 | WithColor::error() << InputFilename << ": " << EC.message() << '\n'; |
353 | return 1; |
354 | } |
355 | |
356 | if (MCPU == "native" ) |
357 | MCPU = std::string(llvm::sys::getHostCPUName()); |
358 | |
359 | // Package up features to be passed to target/subtarget |
360 | std::string FeaturesStr; |
361 | if (MATTRS.size()) { |
362 | SubtargetFeatures Features; |
363 | for (std::string &MAttr : MATTRS) |
364 | Features.AddFeature(String: MAttr); |
365 | FeaturesStr = Features.getString(); |
366 | } |
367 | |
368 | std::unique_ptr<MCSubtargetInfo> STI( |
369 | TheTarget->createMCSubtargetInfo(TheTriple: TripleName, CPU: MCPU, Features: FeaturesStr)); |
370 | assert(STI && "Unable to create subtarget info!" ); |
371 | if (!STI->isCPUStringValid(CPU: MCPU)) |
372 | return 1; |
373 | |
374 | if (!STI->getSchedModel().hasInstrSchedModel()) { |
375 | WithColor::error() |
376 | << "unable to find instruction-level scheduling information for" |
377 | << " target triple '" << TheTriple.normalize() << "' and cpu '" << MCPU |
378 | << "'.\n" ; |
379 | |
380 | if (STI->getSchedModel().InstrItineraries) |
381 | WithColor::note() |
382 | << "cpu '" << MCPU << "' provides itineraries. However, " |
383 | << "instruction itineraries are currently unsupported.\n" ; |
384 | return 1; |
385 | } |
386 | |
387 | // Apply overrides to llvm-mca specific options. |
388 | bool IsOutOfOrder = STI->getSchedModel().isOutOfOrder(); |
389 | processViewOptions(IsOutOfOrder); |
390 | |
391 | std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TT: TripleName)); |
392 | assert(MRI && "Unable to create target register info!" ); |
393 | |
394 | MCTargetOptions MCOptions = mc::InitMCTargetOptionsFromFlags(); |
395 | std::unique_ptr<MCAsmInfo> MAI( |
396 | TheTarget->createMCAsmInfo(MRI: *MRI, TheTriple: TripleName, Options: MCOptions)); |
397 | assert(MAI && "Unable to create target asm info!" ); |
398 | |
399 | SourceMgr SrcMgr; |
400 | |
401 | // Tell SrcMgr about this buffer, which is what the parser will pick up. |
402 | SrcMgr.AddNewSourceBuffer(F: std::move(*BufferPtr), IncludeLoc: SMLoc()); |
403 | |
404 | std::unique_ptr<buffer_ostream> BOS; |
405 | |
406 | std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo()); |
407 | assert(MCII && "Unable to create instruction info!" ); |
408 | |
409 | std::unique_ptr<MCInstrAnalysis> MCIA( |
410 | TheTarget->createMCInstrAnalysis(Info: MCII.get())); |
411 | |
412 | // Need to initialize an MCInstPrinter as it is |
413 | // required for initializing the MCTargetStreamer |
414 | // which needs to happen within the CRG.parseAnalysisRegions() call below. |
415 | // Without an MCTargetStreamer, certain assembly directives can trigger a |
416 | // segfault. (For example, the .cv_fpo_proc directive on x86 will segfault if |
417 | // we don't initialize the MCTargetStreamer.) |
418 | unsigned IPtempOutputAsmVariant = |
419 | OutputAsmVariant == -1 ? 0 : OutputAsmVariant; |
420 | std::unique_ptr<MCInstPrinter> IPtemp(TheTarget->createMCInstPrinter( |
421 | T: Triple(TripleName), SyntaxVariant: IPtempOutputAsmVariant, MAI: *MAI, MII: *MCII, MRI: *MRI)); |
422 | if (!IPtemp) { |
423 | WithColor::error() |
424 | << "unable to create instruction printer for target triple '" |
425 | << TheTriple.normalize() << "' with assembly variant " |
426 | << IPtempOutputAsmVariant << ".\n" ; |
427 | return 1; |
428 | } |
429 | |
430 | // Parse the input and create CodeRegions that llvm-mca can analyze. |
431 | MCContext ACtx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr); |
432 | std::unique_ptr<MCObjectFileInfo> AMOFI( |
433 | TheTarget->createMCObjectFileInfo(Ctx&: ACtx, /*PIC=*/false)); |
434 | ACtx.setObjectFileInfo(AMOFI.get()); |
435 | mca::AsmAnalysisRegionGenerator CRG(*TheTarget, SrcMgr, ACtx, *MAI, *STI, |
436 | *MCII); |
437 | Expected<const mca::AnalysisRegions &> RegionsOrErr = |
438 | CRG.parseAnalysisRegions(IP: std::move(IPtemp)); |
439 | if (!RegionsOrErr) { |
440 | if (auto Err = |
441 | handleErrors(E: RegionsOrErr.takeError(), Hs: [](const StringError &E) { |
442 | WithColor::error() << E.getMessage() << '\n'; |
443 | })) { |
444 | // Default case. |
445 | WithColor::error() << toString(E: std::move(Err)) << '\n'; |
446 | } |
447 | return 1; |
448 | } |
449 | const mca::AnalysisRegions &Regions = *RegionsOrErr; |
450 | |
451 | // Early exit if errors were found by the code region parsing logic. |
452 | if (!Regions.isValid()) |
453 | return 1; |
454 | |
455 | if (Regions.empty()) { |
456 | WithColor::error() << "no assembly instructions found.\n" ; |
457 | return 1; |
458 | } |
459 | |
460 | std::unique_ptr<mca::InstrumentManager> IM; |
461 | if (!DisableInstrumentManager) { |
462 | IM = std::unique_ptr<mca::InstrumentManager>( |
463 | TheTarget->createInstrumentManager(STI: *STI, MCII: *MCII)); |
464 | } |
465 | if (!IM) { |
466 | // If the target doesn't have its own IM implemented (or the -disable-cb |
467 | // flag is set) then we use the base class (which does nothing). |
468 | IM = std::make_unique<mca::InstrumentManager>(args&: *STI, args&: *MCII); |
469 | } |
470 | |
471 | // Parse the input and create InstrumentRegion that llvm-mca |
472 | // can use to improve analysis. |
473 | MCContext ICtx(TheTriple, MAI.get(), MRI.get(), STI.get(), &SrcMgr); |
474 | std::unique_ptr<MCObjectFileInfo> IMOFI( |
475 | TheTarget->createMCObjectFileInfo(Ctx&: ICtx, /*PIC=*/false)); |
476 | ICtx.setObjectFileInfo(IMOFI.get()); |
477 | mca::AsmInstrumentRegionGenerator IRG(*TheTarget, SrcMgr, ICtx, *MAI, *STI, |
478 | *MCII, *IM); |
479 | Expected<const mca::InstrumentRegions &> InstrumentRegionsOrErr = |
480 | IRG.parseInstrumentRegions(IP: std::move(IPtemp)); |
481 | if (!InstrumentRegionsOrErr) { |
482 | if (auto Err = handleErrors(E: InstrumentRegionsOrErr.takeError(), |
483 | Hs: [](const StringError &E) { |
484 | WithColor::error() << E.getMessage() << '\n'; |
485 | })) { |
486 | // Default case. |
487 | WithColor::error() << toString(E: std::move(Err)) << '\n'; |
488 | } |
489 | return 1; |
490 | } |
491 | const mca::InstrumentRegions &InstrumentRegions = *InstrumentRegionsOrErr; |
492 | |
493 | // Early exit if errors were found by the instrumentation parsing logic. |
494 | if (!InstrumentRegions.isValid()) |
495 | return 1; |
496 | |
497 | // Now initialize the output file. |
498 | auto OF = getOutputStream(); |
499 | if (std::error_code EC = OF.getError()) { |
500 | WithColor::error() << EC.message() << '\n'; |
501 | return 1; |
502 | } |
503 | |
504 | unsigned AssemblerDialect = CRG.getAssemblerDialect(); |
505 | if (OutputAsmVariant >= 0) |
506 | AssemblerDialect = static_cast<unsigned>(OutputAsmVariant); |
507 | std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter( |
508 | T: Triple(TripleName), SyntaxVariant: AssemblerDialect, MAI: *MAI, MII: *MCII, MRI: *MRI)); |
509 | if (!IP) { |
510 | WithColor::error() |
511 | << "unable to create instruction printer for target triple '" |
512 | << TheTriple.normalize() << "' with assembly variant " |
513 | << AssemblerDialect << ".\n" ; |
514 | return 1; |
515 | } |
516 | |
517 | // Set the display preference for hex vs. decimal immediates. |
518 | IP->setPrintImmHex(PrintImmHex); |
519 | |
520 | std::unique_ptr<ToolOutputFile> TOF = std::move(*OF); |
521 | |
522 | const MCSchedModel &SM = STI->getSchedModel(); |
523 | |
524 | std::unique_ptr<mca::InstrPostProcess> IPP; |
525 | if (!DisableCustomBehaviour) { |
526 | // TODO: It may be a good idea to separate CB and IPP so that they can |
527 | // be used independently of each other. What I mean by this is to add |
528 | // an extra command-line arg --disable-ipp so that CB and IPP can be |
529 | // toggled without needing to toggle both of them together. |
530 | IPP = std::unique_ptr<mca::InstrPostProcess>( |
531 | TheTarget->createInstrPostProcess(STI: *STI, MCII: *MCII)); |
532 | } |
533 | if (!IPP) { |
534 | // If the target doesn't have its own IPP implemented (or the -disable-cb |
535 | // flag is set) then we use the base class (which does nothing). |
536 | IPP = std::make_unique<mca::InstrPostProcess>(args&: *STI, args&: *MCII); |
537 | } |
538 | |
539 | // Create an instruction builder. |
540 | mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get(), *IM); |
541 | |
542 | // Create a context to control ownership of the pipeline hardware. |
543 | mca::Context MCA(*MRI, *STI); |
544 | |
545 | mca::PipelineOptions PO(MicroOpQueue, DecoderThroughput, DispatchWidth, |
546 | RegisterFileSize, LoadQueueSize, StoreQueueSize, |
547 | AssumeNoAlias, EnableBottleneckAnalysis); |
548 | |
549 | // Number each region in the sequence. |
550 | unsigned RegionIdx = 0; |
551 | |
552 | std::unique_ptr<MCCodeEmitter> MCE( |
553 | TheTarget->createMCCodeEmitter(II: *MCII, Ctx&: ACtx)); |
554 | assert(MCE && "Unable to create code emitter!" ); |
555 | |
556 | std::unique_ptr<MCAsmBackend> MAB(TheTarget->createMCAsmBackend( |
557 | STI: *STI, MRI: *MRI, Options: mc::InitMCTargetOptionsFromFlags())); |
558 | assert(MAB && "Unable to create asm backend!" ); |
559 | |
560 | json::Object JSONOutput; |
561 | for (const std::unique_ptr<mca::AnalysisRegion> &Region : Regions) { |
562 | // Skip empty code regions. |
563 | if (Region->empty()) |
564 | continue; |
565 | |
566 | IB.clear(); |
567 | |
568 | // Lower the MCInst sequence into an mca::Instruction sequence. |
569 | ArrayRef<MCInst> Insts = Region->getInstructions(); |
570 | mca::CodeEmitter CE(*STI, *MAB, *MCE, Insts); |
571 | |
572 | IPP->resetState(); |
573 | |
574 | DenseMap<const MCInst *, SmallVector<mca::Instrument *>> |
575 | InstToInstruments; |
576 | SmallVector<std::unique_ptr<mca::Instruction>> LoweredSequence; |
577 | for (const MCInst &MCI : Insts) { |
578 | SMLoc Loc = MCI.getLoc(); |
579 | const SmallVector<mca::Instrument *> Instruments = |
580 | InstrumentRegions.getActiveInstruments(Loc); |
581 | InstToInstruments.insert(KV: {&MCI, Instruments}); |
582 | |
583 | Expected<std::unique_ptr<mca::Instruction>> Inst = |
584 | IB.createInstruction(MCI, IVec: Instruments); |
585 | if (!Inst) { |
586 | if (auto NewE = handleErrors( |
587 | E: Inst.takeError(), |
588 | Hs: [&IP, &STI](const mca::InstructionError<MCInst> &IE) { |
589 | std::string InstructionStr; |
590 | raw_string_ostream SS(InstructionStr); |
591 | WithColor::error() << IE.Message << '\n'; |
592 | IP->printInst(MI: &IE.Inst, Address: 0, Annot: "" , STI: *STI, OS&: SS); |
593 | SS.flush(); |
594 | WithColor::note() |
595 | << "instruction: " << InstructionStr << '\n'; |
596 | })) { |
597 | // Default case. |
598 | WithColor::error() << toString(E: std::move(NewE)); |
599 | } |
600 | return 1; |
601 | } |
602 | |
603 | IPP->postProcessInstruction(Inst&: Inst.get(), MCI); |
604 | |
605 | LoweredSequence.emplace_back(Args: std::move(Inst.get())); |
606 | } |
607 | |
608 | mca::CircularSourceMgr S(LoweredSequence, |
609 | PrintInstructionTables ? 1 : Iterations); |
610 | |
611 | if (PrintInstructionTables) { |
612 | // Create a pipeline, stages, and a printer. |
613 | auto P = std::make_unique<mca::Pipeline>(); |
614 | P->appendStage(S: std::make_unique<mca::EntryStage>(args&: S)); |
615 | P->appendStage(S: std::make_unique<mca::InstructionTables>(args: SM)); |
616 | |
617 | mca::PipelinePrinter Printer(*P, *Region, RegionIdx, *STI, PO); |
618 | if (PrintJson) { |
619 | Printer.addView( |
620 | V: std::make_unique<mca::InstructionView>(args&: *STI, args&: *IP, args&: Insts)); |
621 | } |
622 | |
623 | // Create the views for this pipeline, execute, and emit a report. |
624 | if (PrintInstructionInfoView) { |
625 | Printer.addView(V: std::make_unique<mca::InstructionInfoView>( |
626 | args&: *STI, args&: *MCII, args&: CE, args&: ShowEncoding, args&: Insts, args&: *IP, args&: LoweredSequence, |
627 | args&: ShowBarriers, args&: *IM, args&: InstToInstruments)); |
628 | } |
629 | Printer.addView( |
630 | V: std::make_unique<mca::ResourcePressureView>(args&: *STI, args&: *IP, args&: Insts)); |
631 | |
632 | if (!runPipeline(P&: *P)) |
633 | return 1; |
634 | |
635 | if (PrintJson) { |
636 | Printer.printReport(JO&: JSONOutput); |
637 | } else { |
638 | Printer.printReport(OS&: TOF->os()); |
639 | } |
640 | |
641 | ++RegionIdx; |
642 | continue; |
643 | } |
644 | |
645 | // Create the CustomBehaviour object for enforcing Target Specific |
646 | // behaviours and dependencies that aren't expressed well enough |
647 | // in the tablegen. CB cannot depend on the list of MCInst or |
648 | // the source code (but it can depend on the list of |
649 | // mca::Instruction or any objects that can be reconstructed |
650 | // from the target information). |
651 | std::unique_ptr<mca::CustomBehaviour> CB; |
652 | if (!DisableCustomBehaviour) |
653 | CB = std::unique_ptr<mca::CustomBehaviour>( |
654 | TheTarget->createCustomBehaviour(STI: *STI, SrcMgr: S, MCII: *MCII)); |
655 | if (!CB) |
656 | // If the target doesn't have its own CB implemented (or the -disable-cb |
657 | // flag is set) then we use the base class (which does nothing). |
658 | CB = std::make_unique<mca::CustomBehaviour>(args&: *STI, args&: S, args&: *MCII); |
659 | |
660 | // Create a basic pipeline simulating an out-of-order backend. |
661 | auto P = MCA.createDefaultPipeline(Opts: PO, SrcMgr&: S, CB&: *CB); |
662 | |
663 | mca::PipelinePrinter Printer(*P, *Region, RegionIdx, *STI, PO); |
664 | |
665 | // Targets can define their own custom Views that exist within their |
666 | // /lib/Target/ directory so that the View can utilize their CustomBehaviour |
667 | // or other backend symbols / functionality that are not already exposed |
668 | // through one of the MC-layer classes. These Views will be initialized |
669 | // using the CustomBehaviour::getViews() variants. |
670 | // If a target makes a custom View that does not depend on their target |
671 | // CB or their backend, they should put the View within |
672 | // /tools/llvm-mca/Views/ instead. |
673 | if (!DisableCustomBehaviour) { |
674 | std::vector<std::unique_ptr<mca::View>> CBViews = |
675 | CB->getStartViews(IP&: *IP, Insts); |
676 | for (auto &CBView : CBViews) |
677 | Printer.addView(V: std::move(CBView)); |
678 | } |
679 | |
680 | // When we output JSON, we add a view that contains the instructions |
681 | // and CPU resource information. |
682 | if (PrintJson) { |
683 | auto IV = std::make_unique<mca::InstructionView>(args&: *STI, args&: *IP, args&: Insts); |
684 | Printer.addView(V: std::move(IV)); |
685 | } |
686 | |
687 | if (PrintSummaryView) |
688 | Printer.addView( |
689 | V: std::make_unique<mca::SummaryView>(args: SM, args&: Insts, args&: DispatchWidth)); |
690 | |
691 | if (EnableBottleneckAnalysis) { |
692 | if (!IsOutOfOrder) { |
693 | WithColor::warning() |
694 | << "bottleneck analysis is not supported for in-order CPU '" << MCPU |
695 | << "'.\n" ; |
696 | } |
697 | Printer.addView(V: std::make_unique<mca::BottleneckAnalysis>( |
698 | args&: *STI, args&: *IP, args&: Insts, args: S.getNumIterations())); |
699 | } |
700 | |
701 | if (PrintInstructionInfoView) |
702 | Printer.addView(V: std::make_unique<mca::InstructionInfoView>( |
703 | args&: *STI, args&: *MCII, args&: CE, args&: ShowEncoding, args&: Insts, args&: *IP, args&: LoweredSequence, |
704 | args&: ShowBarriers, args&: *IM, args&: InstToInstruments)); |
705 | |
706 | // Fetch custom Views that are to be placed after the InstructionInfoView. |
707 | // Refer to the comment paired with the CB->getStartViews(*IP, Insts); line |
708 | // for more info. |
709 | if (!DisableCustomBehaviour) { |
710 | std::vector<std::unique_ptr<mca::View>> CBViews = |
711 | CB->getPostInstrInfoViews(IP&: *IP, Insts); |
712 | for (auto &CBView : CBViews) |
713 | Printer.addView(V: std::move(CBView)); |
714 | } |
715 | |
716 | if (PrintDispatchStats) |
717 | Printer.addView(V: std::make_unique<mca::DispatchStatistics>()); |
718 | |
719 | if (PrintSchedulerStats) |
720 | Printer.addView(V: std::make_unique<mca::SchedulerStatistics>(args&: *STI)); |
721 | |
722 | if (PrintRetireStats) |
723 | Printer.addView(V: std::make_unique<mca::RetireControlUnitStatistics>(args: SM)); |
724 | |
725 | if (PrintRegisterFileStats) |
726 | Printer.addView(V: std::make_unique<mca::RegisterFileStatistics>(args&: *STI)); |
727 | |
728 | if (PrintResourcePressureView) |
729 | Printer.addView( |
730 | V: std::make_unique<mca::ResourcePressureView>(args&: *STI, args&: *IP, args&: Insts)); |
731 | |
732 | if (PrintTimelineView) { |
733 | unsigned TimelineIterations = |
734 | TimelineMaxIterations ? TimelineMaxIterations : 10; |
735 | Printer.addView(V: std::make_unique<mca::TimelineView>( |
736 | args&: *STI, args&: *IP, args&: Insts, args: std::min(a: TimelineIterations, b: S.getNumIterations()), |
737 | args&: TimelineMaxCycles)); |
738 | } |
739 | |
740 | // Fetch custom Views that are to be placed after all other Views. |
741 | // Refer to the comment paired with the CB->getStartViews(*IP, Insts); line |
742 | // for more info. |
743 | if (!DisableCustomBehaviour) { |
744 | std::vector<std::unique_ptr<mca::View>> CBViews = |
745 | CB->getEndViews(IP&: *IP, Insts); |
746 | for (auto &CBView : CBViews) |
747 | Printer.addView(V: std::move(CBView)); |
748 | } |
749 | |
750 | if (!runPipeline(P&: *P)) |
751 | return 1; |
752 | |
753 | if (PrintJson) { |
754 | Printer.printReport(JO&: JSONOutput); |
755 | } else { |
756 | Printer.printReport(OS&: TOF->os()); |
757 | } |
758 | |
759 | ++RegionIdx; |
760 | } |
761 | |
762 | if (PrintJson) |
763 | TOF->os() << formatv(Fmt: "{0:2}" , Vals: json::Value(std::move(JSONOutput))) << "\n" ; |
764 | |
765 | TOF->keep(); |
766 | return 0; |
767 | } |
768 | |