1//===-- SnippetFile.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
9#include "SnippetFile.h"
10#include "BenchmarkRunner.h"
11#include "Error.h"
12#include "LlvmState.h"
13#include "Target.h"
14#include "llvm/MC/MCContext.h"
15#include "llvm/MC/MCInstPrinter.h"
16#include "llvm/MC/MCObjectFileInfo.h"
17#include "llvm/MC/MCParser/AsmLexer.h"
18#include "llvm/MC/MCParser/MCAsmParser.h"
19#include "llvm/MC/MCParser/MCTargetAsmParser.h"
20#include "llvm/MC/MCRegister.h"
21#include "llvm/MC/MCRegisterInfo.h"
22#include "llvm/MC/MCStreamer.h"
23#include "llvm/MC/TargetRegistry.h"
24#include "llvm/Support/Format.h"
25#include "llvm/Support/Path.h"
26#include "llvm/Support/SourceMgr.h"
27#include <string>
28
29#ifdef __linux__
30#include <unistd.h>
31#endif // __linux__
32
33namespace llvm {
34namespace exegesis {
35namespace {
36
37// An MCStreamer that reads a BenchmarkCode definition from a file.
38class BenchmarkCodeStreamer : public MCStreamer, public AsmCommentConsumer {
39public:
40 explicit BenchmarkCodeStreamer(MCContext *Context, const LLVMState &State,
41 BenchmarkCode *Result)
42 : MCStreamer(*Context), State(State), Result(Result) {}
43
44 // Implementation of the MCStreamer interface. We only care about
45 // instructions.
46 void emitInstruction(const MCInst &Instruction,
47 const MCSubtargetInfo &STI) override {
48 Result->Key.Instructions.push_back(x: Instruction);
49 }
50
51 // Implementation of the AsmCommentConsumer.
52 void HandleComment(SMLoc Loc, StringRef CommentText) override {
53 CommentText = CommentText.trim();
54 if (!CommentText.consume_front(Prefix: "LLVM-EXEGESIS-"))
55 return;
56 if (CommentText.consume_front(Prefix: "DEFREG")) {
57 // LLVM-EXEGESIS-DEFREF <reg> <hex_value>
58 RegisterValue RegVal;
59 SmallVector<StringRef, 2> Parts;
60 CommentText.split(A&: Parts, Separator: ' ', /*unlimited splits*/ MaxSplit: -1,
61 /*do not keep empty strings*/ KeepEmpty: false);
62 if (Parts.size() != 2) {
63 errs() << "invalid comment 'LLVM-EXEGESIS-DEFREG " << CommentText
64 << "', expected two parameters <REG> <HEX_VALUE>\n";
65 ++InvalidComments;
66 return;
67 }
68 if (!(RegVal.Register = findRegisterByName(RegName: Parts[0].trim()))) {
69 errs() << "unknown register '" << Parts[0]
70 << "' in 'LLVM-EXEGESIS-DEFREG " << CommentText << "'\n";
71 ++InvalidComments;
72 return;
73 }
74 const StringRef HexValue = Parts[1].trim();
75 RegVal.Value = APInt(
76 /* each hex digit is 4 bits */ HexValue.size() * 4, HexValue, 16);
77 Result->Key.RegisterInitialValues.push_back(x: std::move(RegVal));
78 return;
79 }
80 if (CommentText.consume_front(Prefix: "LIVEIN")) {
81 // LLVM-EXEGESIS-LIVEIN <reg>
82 const auto RegName = CommentText.ltrim();
83 if (MCRegister Reg = findRegisterByName(RegName))
84 Result->LiveIns.push_back(x: Reg);
85 else {
86 errs() << "unknown register '" << RegName
87 << "' in 'LLVM-EXEGESIS-LIVEIN " << CommentText << "'\n";
88 ++InvalidComments;
89 }
90 return;
91 }
92 if (CommentText.consume_front(Prefix: "MEM-DEF")) {
93 // LLVM-EXEGESIS-MEM-DEF <name> <size> <value>
94 SmallVector<StringRef, 3> Parts;
95 CommentText.split(A&: Parts, Separator: ' ', MaxSplit: -1, KeepEmpty: false);
96 if (Parts.size() != 3) {
97 errs() << "invalid comment 'LLVM-EXEGESIS-MEM-DEF " << CommentText
98 << "', expected three parameters <NAME> <SIZE> <VALUE>";
99 ++InvalidComments;
100 return;
101 }
102 const StringRef HexValue = Parts[2].trim();
103 MemoryValue MemVal;
104 MemVal.SizeBytes = std::stol(str: Parts[1].trim().str());
105 if (HexValue.size() % 2 != 0) {
106 errs() << "invalid comment 'LLVM-EXEGESIS-MEM-DEF " << CommentText
107 << "', expected <VALUE> to contain a whole number of bytes";
108 }
109 MemVal.Value = APInt(HexValue.size() * 4, HexValue, 16);
110 MemVal.Index = Result->Key.MemoryValues.size();
111 Result->Key.MemoryValues[Parts[0].trim().str()] = MemVal;
112 return;
113 }
114 if (CommentText.consume_front(Prefix: "MEM-MAP")) {
115 // LLVM-EXEGESIS-MEM-MAP <value name> <address>
116 SmallVector<StringRef, 2> Parts;
117 CommentText.split(A&: Parts, Separator: ' ', MaxSplit: -1, KeepEmpty: false);
118 if (Parts.size() != 2) {
119 errs() << "invalid comment 'LLVM-EXEGESIS-MEM-MAP " << CommentText
120 << "', expected two parameters <VALUE NAME> <ADDRESS>";
121 ++InvalidComments;
122 return;
123 }
124 MemoryMapping MemMap;
125 MemMap.MemoryValueName = Parts[0].trim().str();
126 MemMap.Address = std::stol(str: Parts[1].trim().str());
127
128#ifdef __linux__
129 // Validate that the annotation is a multiple of the platform's page
130 // size.
131 if (MemMap.Address % getpagesize() != 0) {
132 errs() << "invalid comment 'LLVM-EXEGESIS-MEM-MAP " << CommentText
133 << "', expected <ADDRESS> to be a multiple of the platform page "
134 "size.";
135 ++InvalidComments;
136 return;
137 }
138#endif // __linux__
139
140 // validate that the annotation refers to an already existing memory
141 // definition
142 auto MemValIT = Result->Key.MemoryValues.find(x: Parts[0].trim().str());
143 if (MemValIT == Result->Key.MemoryValues.end()) {
144 errs() << "invalid comment 'LLVM-EXEGESIS-MEM-MAP " << CommentText
145 << "', expected <VALUE NAME> to contain the name of an already "
146 "specified memory definition";
147 ++InvalidComments;
148 return;
149 }
150 Result->Key.MemoryMappings.push_back(x: std::move(MemMap));
151 return;
152 }
153 if (CommentText.consume_front(Prefix: "SNIPPET-ADDRESS")) {
154 // LLVM-EXEGESIS-SNIPPET-ADDRESS <address>
155 if (!to_integer<uintptr_t>(S: CommentText.trim(), Num&: Result->Key.SnippetAddress,
156 Base: 16)) {
157 errs() << "invalid comment 'LLVM-EXEGESIS-SNIPPET-ADDRESS "
158 << CommentText
159 << "', expected <ADDRESS> to contain a valid integer in "
160 "hexadecimal format";
161 ++InvalidComments;
162 return;
163 }
164
165#ifdef __linux__
166 // Validate that the address in the annotation is a multiple of the
167 // platform's page size.
168 if (Result->Key.SnippetAddress % getpagesize() != 0) {
169 errs() << "invalid comment 'LLVM-EXEGESIS-SNIPPET-ADDRESS "
170 << CommentText
171 << ", expected <ADDRESS> to be a multiple of the platform page "
172 "size.";
173 ++InvalidComments;
174 return;
175 }
176#endif // __linux__
177
178 return;
179 }
180 if (CommentText.consume_front(Prefix: "LOOP-REGISTER")) {
181 // LLVM-EXEGESIS-LOOP-REGISTER <loop register>
182 MCRegister LoopRegister;
183
184 if (!(LoopRegister = findRegisterByName(RegName: CommentText.trim()))) {
185 errs() << "unknown register '" << CommentText
186 << "' in 'LLVM-EXEGESIS-LOOP-REGISTER " << CommentText << "'\n";
187 ++InvalidComments;
188 return;
189 }
190
191 Result->Key.LoopRegister = LoopRegister;
192 return;
193 }
194 }
195
196 unsigned numInvalidComments() const { return InvalidComments; }
197
198private:
199 // We only care about instructions, we don't implement this part of the API.
200 void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
201 Align ByteAlignment) override {}
202 bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override {
203 return false;
204 }
205 void emitZerofill(MCSection *Section, MCSymbol *Symbol, uint64_t Size,
206 Align ByteAlignment, SMLoc Loc) override {}
207
208 MCRegister findRegisterByName(const StringRef RegName) const {
209 std::optional<MCRegister> RegisterNumber =
210 State.getRegisterNumberFromName(RegisterName: RegName);
211 if (!RegisterNumber.has_value()) {
212 errs() << "'" << RegName
213 << "' is not a valid register name for the target\n";
214 return MCRegister();
215 }
216 return *RegisterNumber;
217 }
218
219 const LLVMState &State;
220 BenchmarkCode *const Result;
221 unsigned InvalidComments = 0;
222};
223
224} // namespace
225
226// Reads code snippets from file `Filename`.
227Expected<std::vector<BenchmarkCode>> readSnippets(const LLVMState &State,
228 StringRef Filename) {
229 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr =
230 MemoryBuffer::getFileOrSTDIN(Filename);
231 if (std::error_code EC = BufferPtr.getError()) {
232 return make_error<Failure>(Args: "cannot read snippet: " + Filename + ": " +
233 EC.message());
234 }
235 SourceMgr SM;
236 SM.AddNewSourceBuffer(F: std::move(BufferPtr.get()), IncludeLoc: SMLoc());
237
238 BenchmarkCode Result;
239
240 // Ensure that there is a default loop register value specified.
241 Result.Key.LoopRegister =
242 State.getExegesisTarget().getDefaultLoopCounterRegister(
243 State.getTargetMachine().getTargetTriple());
244
245 const TargetMachine &TM = State.getTargetMachine();
246 MCContext Context(TM.getTargetTriple(), TM.getMCAsmInfo(),
247 TM.getMCRegisterInfo(), TM.getMCSubtargetInfo());
248 std::unique_ptr<MCObjectFileInfo> ObjectFileInfo(
249 TM.getTarget().createMCObjectFileInfo(Ctx&: Context, /*PIC=*/false));
250 Context.setObjectFileInfo(ObjectFileInfo.get());
251 Context.initInlineSourceManager();
252 BenchmarkCodeStreamer Streamer(&Context, State, &Result);
253
254 std::string Error;
255 raw_string_ostream ErrorStream(Error);
256 formatted_raw_ostream InstPrinterOStream(ErrorStream);
257 const std::unique_ptr<MCInstPrinter> InstPrinter(
258 TM.getTarget().createMCInstPrinter(
259 T: TM.getTargetTriple(), SyntaxVariant: TM.getMCAsmInfo()->getAssemblerDialect(),
260 MAI: *TM.getMCAsmInfo(), MII: *TM.getMCInstrInfo(), MRI: *TM.getMCRegisterInfo()));
261 // The following call will take care of calling Streamer.setTargetStreamer.
262 TM.getTarget().createAsmTargetStreamer(S&: Streamer, OS&: InstPrinterOStream,
263 InstPrint: InstPrinter.get());
264 if (!Streamer.getTargetStreamer())
265 return make_error<Failure>(Args: "cannot create target asm streamer");
266
267 const std::unique_ptr<MCAsmParser> AsmParser(
268 createMCAsmParser(SM, Context, Streamer, *TM.getMCAsmInfo()));
269 if (!AsmParser)
270 return make_error<Failure>(Args: "cannot create asm parser");
271 AsmParser->getLexer().setCommentConsumer(&Streamer);
272
273 const std::unique_ptr<MCTargetAsmParser> TargetAsmParser(
274 TM.getTarget().createMCAsmParser(STI: *TM.getMCSubtargetInfo(), Parser&: *AsmParser,
275 MII: *TM.getMCInstrInfo(),
276 Options: MCTargetOptions()));
277
278 if (!TargetAsmParser)
279 return make_error<Failure>(Args: "cannot create target asm parser");
280 AsmParser->setTargetParser(*TargetAsmParser);
281
282 if (AsmParser->Run(NoInitialTextSection: false))
283 return make_error<Failure>(Args: "cannot parse asm file");
284 if (Streamer.numInvalidComments())
285 return make_error<Failure>(Args: Twine("found ")
286 .concat(Suffix: Twine(Streamer.numInvalidComments()))
287 .concat(Suffix: " invalid LLVM-EXEGESIS comments"));
288 return std::vector<BenchmarkCode>{std::move(Result)};
289}
290
291} // namespace exegesis
292} // namespace llvm
293