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.
~CodeRegionGenerator()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:
MCStreamerWrapper(MCContext & Context,mca::CodeRegions & R)37 MCStreamerWrapper(MCContext &Context, mca::CodeRegions &R)
38 : MCStreamer(Context), Regions(R) {}
39
40 // We only want to intercept the emission of new instructions.
emitInstruction(const MCInst & Inst,const MCSubtargetInfo &)41 void emitInstruction(const MCInst &Inst,
42 const MCSubtargetInfo & /* unused */) override {
43 Regions.addInstruction(Inst);
44 }
45
emitSymbolAttribute(MCSymbol * Symbol,MCSymbolAttr Attribute)46 bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override {
47 return true;
48 }
49
emitCommonSymbol(MCSymbol * Symbol,uint64_t Size,Align ByteAlignment)50 void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
51 Align ByteAlignment) override {}
emitZerofill(MCSection * Section,MCSymbol * Symbol=nullptr,uint64_t Size=0,Align ByteAlignment=Align (1),SMLoc Loc=SMLoc ())52 void emitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr,
53 uint64_t Size = 0, Align ByteAlignment = Align(1),
54 SMLoc Loc = SMLoc()) override {}
emitGPRel32Value(const MCExpr * Value)55 void emitGPRel32Value(const MCExpr *Value) override {}
beginCOFFSymbolDef(const MCSymbol * Symbol)56 void beginCOFFSymbolDef(const MCSymbol *Symbol) override {}
emitCOFFSymbolStorageClass(int StorageClass)57 void emitCOFFSymbolStorageClass(int StorageClass) override {}
emitCOFFSymbolType(int Type)58 void emitCOFFSymbolType(int Type) override {}
endCOFFSymbolDef()59 void endCOFFSymbolDef() override {}
60
GetInstructionSequence(unsigned Index) const61 ArrayRef<MCInst> GetInstructionSequence(unsigned Index) const {
62 return Regions.getInstructionSequence(Index);
63 }
64 };
65
parseCodeRegions(const std::unique_ptr<MCInstPrinter> & IP)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
HandleComment(SMLoc Loc,StringRef CommentText)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
HandleComment(SMLoc Loc,StringRef CommentText)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