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