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.
~CodeRegionGenerator()29 CodeRegionGenerator::~CodeRegionGenerator() {}
30
parseCodeRegions(const std::unique_ptr<MCInstPrinter> & IP,bool SkipFailures)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
46 // Create a MCAsmParser and setup the lexer to recognize llvm-mca ASM
47 // comments.
48 std::unique_ptr<MCAsmParser> Parser(
49 createMCAsmParser(Regions.getSourceMgr(), Ctx, *Str, MAI));
50 MCAsmLexer &Lexer = Parser->getLexer();
51 MCACommentConsumer *CCP = getCommentConsumer();
52 Lexer.setCommentConsumer(CCP);
53 // Enable support for MASM literal numbers (example: 05h, 101b).
54 Lexer.setLexMasmIntegers(true);
55
56 std::unique_ptr<MCTargetAsmParser> TAP(
57 TheTarget.createMCAsmParser(STI, *Parser, MCII, Opts));
58 if (!TAP)
59 return make_error<StringError>(
60 "This target does not support assembly parsing.",
61 inconvertibleErrorCode());
62 Parser->setTargetParser(*TAP);
63 // Parser->Run() confusingly returns true on errors, in which case the errors
64 // were already shown to the user. SkipFailures implies continuing in the
65 // presence of any kind of failure within the parser, in which case failing
66 // input lines are not represented, but the rest of the input remains.
67 if (Parser->Run(false) && !SkipFailures) {
68 const char *Message = "Assembly input parsing had errors, use "
69 "-skip-unsupported-instructions=parse-failure "
70 "to drop failing lines from the input.";
71 return make_error<StringError>(Message, inconvertibleErrorCode());
72 }
73
74 if (CCP->hadErr())
75 return make_error<StringError>("There was an error parsing comments.",
76 inconvertibleErrorCode());
77
78 // Set the assembler dialect from the input. llvm-mca will use this as the
79 // default dialect when printing reports.
80 AssemblerDialect = Parser->getAssemblerDialect();
81 return Regions;
82 }
83
HandleComment(SMLoc Loc,StringRef CommentText)84 void AnalysisRegionCommentConsumer::HandleComment(SMLoc Loc,
85 StringRef CommentText) {
86 // Skip empty comments.
87 StringRef Comment(CommentText);
88 if (Comment.empty())
89 return;
90
91 // Skip spaces and tabs.
92 unsigned Position = Comment.find_first_not_of(" \t");
93 if (Position >= Comment.size())
94 // We reached the end of the comment. Bail out.
95 return;
96
97 Comment = Comment.drop_front(Position);
98 if (Comment.consume_front("LLVM-MCA-END")) {
99 // Skip spaces and tabs.
100 Position = Comment.find_first_not_of(" \t");
101 if (Position < Comment.size())
102 Comment = Comment.drop_front(Position);
103 Regions.endRegion(Comment, Loc);
104 return;
105 }
106
107 // Try to parse the LLVM-MCA-BEGIN comment.
108 if (!Comment.consume_front("LLVM-MCA-BEGIN"))
109 return;
110
111 // Skip spaces and tabs.
112 Position = Comment.find_first_not_of(" \t");
113 if (Position < Comment.size())
114 Comment = Comment.drop_front(Position);
115 // Use the rest of the string as a descriptor for this code snippet.
116 Regions.beginRegion(Comment, Loc);
117 }
118
HandleComment(SMLoc Loc,StringRef CommentText)119 void InstrumentRegionCommentConsumer::HandleComment(SMLoc Loc,
120 StringRef CommentText) {
121 // Skip empty comments.
122 StringRef Comment(CommentText);
123 if (Comment.empty())
124 return;
125
126 // Skip spaces and tabs.
127 unsigned Position = Comment.find_first_not_of(" \t");
128 if (Position >= Comment.size())
129 // We reached the end of the comment. Bail out.
130 return;
131 Comment = Comment.drop_front(Position);
132
133 // Bail out if not an MCA style comment
134 if (!Comment.consume_front("LLVM-MCA-"))
135 return;
136
137 // Skip AnalysisRegion comments
138 if (Comment.consume_front("BEGIN") || Comment.consume_front("END"))
139 return;
140
141 if (IM.shouldIgnoreInstruments())
142 return;
143
144 auto [InstrumentKind, Data] = Comment.split(" ");
145
146 // An error if not of the form LLVM-MCA-TARGET-KIND
147 if (!IM.supportsInstrumentType(InstrumentKind)) {
148 if (InstrumentKind.empty())
149 SM.PrintMessage(
150 Loc, llvm::SourceMgr::DK_Error,
151 "No instrumentation kind was provided in LLVM-MCA comment");
152 else
153 SM.PrintMessage(Loc, llvm::SourceMgr::DK_Error,
154 "Unknown instrumentation type in LLVM-MCA comment: " +
155 InstrumentKind);
156 FoundError = true;
157 return;
158 }
159
160 UniqueInstrument I = IM.createInstrument(InstrumentKind, Data);
161 if (!I) {
162 if (Data.empty())
163 SM.PrintMessage(Loc, llvm::SourceMgr::DK_Error,
164 "Failed to create " + InstrumentKind +
165 " instrument with no data");
166 else
167 SM.PrintMessage(Loc, llvm::SourceMgr::DK_Error,
168 "Failed to create " + InstrumentKind +
169 " instrument with data: " + Data);
170 FoundError = true;
171 return;
172 }
173
174 // End InstrumentType region if one is open
175 if (Regions.isRegionActive(InstrumentKind))
176 Regions.endRegion(InstrumentKind, Loc);
177 // Start new instrumentation region
178 Regions.beginRegion(InstrumentKind, Loc, std::move(I));
179 }
180
181 } // namespace mca
182 } // namespace llvm
183