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