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