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