1 //===----------------------- CodeRegionGenerator.cpp ------------*- 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 /// \file 9 /// 10 /// This file defines classes responsible for generating llvm-mca 11 /// CodeRegions from various types of input. llvm-mca only analyzes CodeRegions, 12 /// so the classes here provide the input-to-CodeRegions translation. 13 // 14 //===----------------------------------------------------------------------===// 15 16 #include "CodeRegionGenerator.h" 17 #include "llvm/ADT/ArrayRef.h" 18 #include "llvm/ADT/StringRef.h" 19 #include "llvm/MC/MCParser/MCTargetAsmParser.h" 20 #include "llvm/MC/MCTargetOptions.h" 21 #include "llvm/Support/Error.h" 22 #include "llvm/Support/SMLoc.h" 23 #include <memory> 24 25 namespace llvm { 26 namespace mca { 27 28 // This virtual dtor serves as the anchor for the CodeRegionGenerator class. 29 CodeRegionGenerator::~CodeRegionGenerator() {} 30 31 Expected<const CodeRegions &> AsmCodeRegionGenerator::parseCodeRegions( 32 const std::unique_ptr<MCInstPrinter> &IP, bool SkipFailures) { 33 MCTargetOptions Opts; 34 Opts.PreserveAsmComments = false; 35 CodeRegions &Regions = getRegions(); 36 MCStreamerWrapper *Str = getMCStreamer(); 37 38 // Need to initialize an MCTargetStreamer otherwise 39 // certain asm directives will cause a segfault. 40 // Using nulls() so that anything emitted by the MCTargetStreamer 41 // doesn't show up in the llvm-mca output. 42 raw_ostream &OSRef = nulls(); 43 formatted_raw_ostream FOSRef(OSRef); 44 TheTarget.createAsmTargetStreamer(*Str, FOSRef, IP.get(), 45 /*IsVerboseAsm=*/true); 46 47 // Create a MCAsmParser and setup the lexer to recognize llvm-mca ASM 48 // comments. 49 std::unique_ptr<MCAsmParser> Parser( 50 createMCAsmParser(Regions.getSourceMgr(), Ctx, *Str, MAI)); 51 MCAsmLexer &Lexer = Parser->getLexer(); 52 MCACommentConsumer *CCP = getCommentConsumer(); 53 Lexer.setCommentConsumer(CCP); 54 // Enable support for MASM literal numbers (example: 05h, 101b). 55 Lexer.setLexMasmIntegers(true); 56 57 std::unique_ptr<MCTargetAsmParser> TAP( 58 TheTarget.createMCAsmParser(STI, *Parser, MCII, Opts)); 59 if (!TAP) 60 return make_error<StringError>( 61 "This target does not support assembly parsing.", 62 inconvertibleErrorCode()); 63 Parser->setTargetParser(*TAP); 64 // Parser->Run() confusingly returns true on errors, in which case the errors 65 // were already shown to the user. SkipFailures implies continuing in the 66 // presence of any kind of failure within the parser, in which case failing 67 // input lines are not represented, but the rest of the input remains. 68 if (Parser->Run(false) && !SkipFailures) { 69 const char *Message = "Assembly input parsing had errors, use " 70 "-skip-unsupported-instructions=parse-failure " 71 "to drop failing lines from the input."; 72 return make_error<StringError>(Message, inconvertibleErrorCode()); 73 } 74 75 if (CCP->hadErr()) 76 return make_error<StringError>("There was an error parsing comments.", 77 inconvertibleErrorCode()); 78 79 // Set the assembler dialect from the input. llvm-mca will use this as the 80 // default dialect when printing reports. 81 AssemblerDialect = Parser->getAssemblerDialect(); 82 return Regions; 83 } 84 85 void AnalysisRegionCommentConsumer::HandleComment(SMLoc Loc, 86 StringRef CommentText) { 87 // Skip empty comments. 88 StringRef Comment(CommentText); 89 if (Comment.empty()) 90 return; 91 92 // Skip spaces and tabs. 93 unsigned Position = Comment.find_first_not_of(" \t"); 94 if (Position >= Comment.size()) 95 // We reached the end of the comment. Bail out. 96 return; 97 98 Comment = Comment.drop_front(Position); 99 if (Comment.consume_front("LLVM-MCA-END")) { 100 // Skip spaces and tabs. 101 Position = Comment.find_first_not_of(" \t"); 102 if (Position < Comment.size()) 103 Comment = Comment.drop_front(Position); 104 Regions.endRegion(Comment, Loc); 105 return; 106 } 107 108 // Try to parse the LLVM-MCA-BEGIN comment. 109 if (!Comment.consume_front("LLVM-MCA-BEGIN")) 110 return; 111 112 // Skip spaces and tabs. 113 Position = Comment.find_first_not_of(" \t"); 114 if (Position < Comment.size()) 115 Comment = Comment.drop_front(Position); 116 // Use the rest of the string as a descriptor for this code snippet. 117 Regions.beginRegion(Comment, Loc); 118 } 119 120 void InstrumentRegionCommentConsumer::HandleComment(SMLoc Loc, 121 StringRef CommentText) { 122 // Skip empty comments. 123 StringRef Comment(CommentText); 124 if (Comment.empty()) 125 return; 126 127 // Skip spaces and tabs. 128 unsigned Position = Comment.find_first_not_of(" \t"); 129 if (Position >= Comment.size()) 130 // We reached the end of the comment. Bail out. 131 return; 132 Comment = Comment.drop_front(Position); 133 134 // Bail out if not an MCA style comment 135 if (!Comment.consume_front("LLVM-MCA-")) 136 return; 137 138 // Skip AnalysisRegion comments 139 if (Comment.consume_front("BEGIN") || Comment.consume_front("END")) 140 return; 141 142 if (IM.shouldIgnoreInstruments()) 143 return; 144 145 auto [InstrumentKind, Data] = Comment.split(" "); 146 147 // An error if not of the form LLVM-MCA-TARGET-KIND 148 if (!IM.supportsInstrumentType(InstrumentKind)) { 149 if (InstrumentKind.empty()) 150 SM.PrintMessage( 151 Loc, llvm::SourceMgr::DK_Error, 152 "No instrumentation kind was provided in LLVM-MCA comment"); 153 else 154 SM.PrintMessage(Loc, llvm::SourceMgr::DK_Error, 155 "Unknown instrumentation type in LLVM-MCA comment: " + 156 InstrumentKind); 157 FoundError = true; 158 return; 159 } 160 161 UniqueInstrument I = IM.createInstrument(InstrumentKind, Data); 162 if (!I) { 163 if (Data.empty()) 164 SM.PrintMessage(Loc, llvm::SourceMgr::DK_Error, 165 "Failed to create " + InstrumentKind + 166 " instrument with no data"); 167 else 168 SM.PrintMessage(Loc, llvm::SourceMgr::DK_Error, 169 "Failed to create " + InstrumentKind + 170 " instrument with data: " + Data); 171 FoundError = true; 172 return; 173 } 174 175 // End InstrumentType region if one is open 176 if (Regions.isRegionActive(InstrumentKind)) 177 Regions.endRegion(InstrumentKind, Loc); 178 // Start new instrumentation region 179 Regions.beginRegion(InstrumentKind, Loc, std::move(I)); 180 } 181 182 } // namespace mca 183 } // namespace llvm 184