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) { 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(false); 65 66 if (CCP->hadErr()) 67 return make_error<StringError>("There was an error parsing comments.", 68 inconvertibleErrorCode()); 69 70 // Set the assembler dialect from the input. llvm-mca will use this as the 71 // default dialect when printing reports. 72 AssemblerDialect = Parser->getAssemblerDialect(); 73 return Regions; 74 } 75 76 void AnalysisRegionCommentConsumer::HandleComment(SMLoc Loc, 77 StringRef CommentText) { 78 // Skip empty comments. 79 StringRef Comment(CommentText); 80 if (Comment.empty()) 81 return; 82 83 // Skip spaces and tabs. 84 unsigned Position = Comment.find_first_not_of(" \t"); 85 if (Position >= Comment.size()) 86 // We reached the end of the comment. Bail out. 87 return; 88 89 Comment = Comment.drop_front(Position); 90 if (Comment.consume_front("LLVM-MCA-END")) { 91 // Skip spaces and tabs. 92 Position = Comment.find_first_not_of(" \t"); 93 if (Position < Comment.size()) 94 Comment = Comment.drop_front(Position); 95 Regions.endRegion(Comment, Loc); 96 return; 97 } 98 99 // Try to parse the LLVM-MCA-BEGIN comment. 100 if (!Comment.consume_front("LLVM-MCA-BEGIN")) 101 return; 102 103 // Skip spaces and tabs. 104 Position = Comment.find_first_not_of(" \t"); 105 if (Position < Comment.size()) 106 Comment = Comment.drop_front(Position); 107 // Use the rest of the string as a descriptor for this code snippet. 108 Regions.beginRegion(Comment, Loc); 109 } 110 111 void InstrumentRegionCommentConsumer::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 Comment = Comment.drop_front(Position); 124 125 // Bail out if not an MCA style comment 126 if (!Comment.consume_front("LLVM-MCA-")) 127 return; 128 129 // Skip AnalysisRegion comments 130 if (Comment.consume_front("BEGIN") || Comment.consume_front("END")) 131 return; 132 133 if (IM.shouldIgnoreInstruments()) 134 return; 135 136 auto [InstrumentKind, Data] = Comment.split(" "); 137 138 // An error if not of the form LLVM-MCA-TARGET-KIND 139 if (!IM.supportsInstrumentType(InstrumentKind)) { 140 if (InstrumentKind.empty()) 141 SM.PrintMessage( 142 Loc, llvm::SourceMgr::DK_Error, 143 "No instrumentation kind was provided in LLVM-MCA comment"); 144 else 145 SM.PrintMessage(Loc, llvm::SourceMgr::DK_Error, 146 "Unknown instrumentation type in LLVM-MCA comment: " + 147 InstrumentKind); 148 FoundError = true; 149 return; 150 } 151 152 UniqueInstrument I = IM.createInstrument(InstrumentKind, Data); 153 if (!I) { 154 if (Data.empty()) 155 SM.PrintMessage(Loc, llvm::SourceMgr::DK_Error, 156 "Failed to create " + InstrumentKind + 157 " instrument with no data"); 158 else 159 SM.PrintMessage(Loc, llvm::SourceMgr::DK_Error, 160 "Failed to create " + InstrumentKind + 161 " instrument with data: " + Data); 162 FoundError = true; 163 return; 164 } 165 166 // End InstrumentType region if one is open 167 if (Regions.isRegionActive(InstrumentKind)) 168 Regions.endRegion(InstrumentKind, Loc); 169 // Start new instrumentation region 170 Regions.beginRegion(InstrumentKind, Loc, std::move(I)); 171 } 172 173 } // namespace mca 174 } // namespace llvm 175