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/MCStreamer.h" 21 #include "llvm/MC/MCTargetOptions.h" 22 #include "llvm/Support/Error.h" 23 #include "llvm/Support/SMLoc.h" 24 #include <memory> 25 26 namespace llvm { 27 namespace mca { 28 29 // This virtual dtor serves as the anchor for the CodeRegionGenerator class. 30 CodeRegionGenerator::~CodeRegionGenerator() {} 31 32 // This class provides the callbacks that occur when parsing input assembly. 33 class MCStreamerWrapper final : public MCStreamer { 34 CodeRegions &Regions; 35 36 public: 37 MCStreamerWrapper(MCContext &Context, mca::CodeRegions &R) 38 : MCStreamer(Context), Regions(R) {} 39 40 // We only want to intercept the emission of new instructions. 41 void emitInstruction(const MCInst &Inst, 42 const MCSubtargetInfo & /* unused */) override { 43 Regions.addInstruction(Inst); 44 } 45 46 bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override { 47 return true; 48 } 49 50 void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, 51 Align ByteAlignment) override {} 52 void emitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr, 53 uint64_t Size = 0, Align ByteAlignment = Align(1), 54 SMLoc Loc = SMLoc()) override {} 55 void emitGPRel32Value(const MCExpr *Value) override {} 56 void beginCOFFSymbolDef(const MCSymbol *Symbol) override {} 57 void emitCOFFSymbolStorageClass(int StorageClass) override {} 58 void emitCOFFSymbolType(int Type) override {} 59 void endCOFFSymbolDef() override {} 60 61 ArrayRef<MCInst> GetInstructionSequence(unsigned Index) const { 62 return Regions.getInstructionSequence(Index); 63 } 64 }; 65 66 Expected<const CodeRegions &> AsmCodeRegionGenerator::parseCodeRegions( 67 const std::unique_ptr<MCInstPrinter> &IP) { 68 MCTargetOptions Opts; 69 Opts.PreserveAsmComments = false; 70 CodeRegions &Regions = getRegions(); 71 MCStreamerWrapper Str(Ctx, Regions); 72 73 // Need to initialize an MCTargetStreamer otherwise 74 // certain asm directives will cause a segfault. 75 // Using nulls() so that anything emitted by the MCTargetStreamer 76 // doesn't show up in the llvm-mca output. 77 raw_ostream &OSRef = nulls(); 78 formatted_raw_ostream FOSRef(OSRef); 79 TheTarget.createAsmTargetStreamer(Str, FOSRef, IP.get(), 80 /*IsVerboseAsm=*/true); 81 82 // Create a MCAsmParser and setup the lexer to recognize llvm-mca ASM 83 // comments. 84 std::unique_ptr<MCAsmParser> Parser( 85 createMCAsmParser(Regions.getSourceMgr(), Ctx, Str, MAI)); 86 MCAsmLexer &Lexer = Parser->getLexer(); 87 MCACommentConsumer *CCP = getCommentConsumer(); 88 Lexer.setCommentConsumer(CCP); 89 // Enable support for MASM literal numbers (example: 05h, 101b). 90 Lexer.setLexMasmIntegers(true); 91 92 std::unique_ptr<MCTargetAsmParser> TAP( 93 TheTarget.createMCAsmParser(STI, *Parser, MCII, Opts)); 94 if (!TAP) 95 return make_error<StringError>( 96 "This target does not support assembly parsing.", 97 inconvertibleErrorCode()); 98 Parser->setTargetParser(*TAP); 99 Parser->Run(false); 100 101 if (CCP->hadErr()) 102 return make_error<StringError>("There was an error parsing comments.", 103 inconvertibleErrorCode()); 104 105 // Set the assembler dialect from the input. llvm-mca will use this as the 106 // default dialect when printing reports. 107 AssemblerDialect = Parser->getAssemblerDialect(); 108 return Regions; 109 } 110 111 void AnalysisRegionCommentConsumer::HandleComment(SMLoc Loc, 112 StringRef CommentText) { 113 // Skip empty comments. 114 StringRef Comment(CommentText); 115 if (Comment.empty()) 116 return; 117 118 // Skip spaces and tabs. 119 unsigned Position = Comment.find_first_not_of(" \t"); 120 if (Position >= Comment.size()) 121 // We reached the end of the comment. Bail out. 122 return; 123 124 Comment = Comment.drop_front(Position); 125 if (Comment.consume_front("LLVM-MCA-END")) { 126 // Skip spaces and tabs. 127 Position = Comment.find_first_not_of(" \t"); 128 if (Position < Comment.size()) 129 Comment = Comment.drop_front(Position); 130 Regions.endRegion(Comment, Loc); 131 return; 132 } 133 134 // Try to parse the LLVM-MCA-BEGIN comment. 135 if (!Comment.consume_front("LLVM-MCA-BEGIN")) 136 return; 137 138 // Skip spaces and tabs. 139 Position = Comment.find_first_not_of(" \t"); 140 if (Position < Comment.size()) 141 Comment = Comment.drop_front(Position); 142 // Use the rest of the string as a descriptor for this code snippet. 143 Regions.beginRegion(Comment, Loc); 144 } 145 146 void InstrumentRegionCommentConsumer::HandleComment(SMLoc Loc, 147 StringRef CommentText) { 148 // Skip empty comments. 149 StringRef Comment(CommentText); 150 if (Comment.empty()) 151 return; 152 153 // Skip spaces and tabs. 154 unsigned Position = Comment.find_first_not_of(" \t"); 155 if (Position >= Comment.size()) 156 // We reached the end of the comment. Bail out. 157 return; 158 Comment = Comment.drop_front(Position); 159 160 // Bail out if not an MCA style comment 161 if (!Comment.consume_front("LLVM-MCA-")) 162 return; 163 164 // Skip AnalysisRegion comments 165 if (Comment.consume_front("BEGIN") || Comment.consume_front("END")) 166 return; 167 168 if (IM.shouldIgnoreInstruments()) 169 return; 170 171 auto [InstrumentKind, Data] = Comment.split(" "); 172 173 // An error if not of the form LLVM-MCA-TARGET-KIND 174 if (!IM.supportsInstrumentType(InstrumentKind)) { 175 if (InstrumentKind.empty()) 176 SM.PrintMessage( 177 Loc, llvm::SourceMgr::DK_Error, 178 "No instrumentation kind was provided in LLVM-MCA comment"); 179 else 180 SM.PrintMessage(Loc, llvm::SourceMgr::DK_Error, 181 "Unknown instrumentation type in LLVM-MCA comment: " + 182 InstrumentKind); 183 FoundError = true; 184 return; 185 } 186 187 SharedInstrument I = IM.createInstrument(InstrumentKind, Data); 188 if (!I) { 189 if (Data.empty()) 190 SM.PrintMessage(Loc, llvm::SourceMgr::DK_Error, 191 "Failed to create " + InstrumentKind + 192 " instrument with no data"); 193 else 194 SM.PrintMessage(Loc, llvm::SourceMgr::DK_Error, 195 "Failed to create " + InstrumentKind + 196 " instrument with data: " + Data); 197 FoundError = true; 198 return; 199 } 200 201 // End InstrumentType region if one is open 202 if (Regions.isRegionActive(InstrumentKind)) 203 Regions.endRegion(InstrumentKind, Loc); 204 // Start new instrumentation region 205 Regions.beginRegion(InstrumentKind, Loc, I); 206 } 207 208 } // namespace mca 209 } // namespace llvm 210