| 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 | |