xref: /llvm-project/llvm/tools/llvm-exegesis/lib/SnippetFile.cpp (revision ff1b01bb7897bf2401540096af775d35b12eb247)
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/MCAsmLexer.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 
33 namespace llvm {
34 namespace exegesis {
35 namespace {
36 
37 // An MCStreamer that reads a BenchmarkCode definition from a file.
38 class BenchmarkCodeStreamer : public MCStreamer, public AsmCommentConsumer {
39 public:
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(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("LLVM-EXEGESIS-"))
55       return;
56     if (CommentText.consume_front("DEFREG")) {
57       // LLVM-EXEGESIS-DEFREF <reg> <hex_value>
58       RegisterValue RegVal;
59       SmallVector<StringRef, 2> Parts;
60       CommentText.split(Parts, ' ', /*unlimited splits*/ -1,
61                         /*do not keep empty strings*/ 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(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(std::move(RegVal));
78       return;
79     }
80     if (CommentText.consume_front("LIVEIN")) {
81       // LLVM-EXEGESIS-LIVEIN <reg>
82       const auto RegName = CommentText.ltrim();
83       if (MCRegister Reg = findRegisterByName(RegName))
84         Result->LiveIns.push_back(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("MEM-DEF")) {
93       // LLVM-EXEGESIS-MEM-DEF <name> <size> <value>
94       SmallVector<StringRef, 3> Parts;
95       CommentText.split(Parts, ' ', -1, 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(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("MEM-MAP")) {
115       // LLVM-EXEGESIS-MEM-MAP <value name> <address>
116       SmallVector<StringRef, 2> Parts;
117       CommentText.split(Parts, ' ', -1, 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(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(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(std::move(MemMap));
151       return;
152     }
153     if (CommentText.consume_front("SNIPPET-ADDRESS")) {
154       // LLVM-EXEGESIS-SNIPPET-ADDRESS <address>
155       if (!to_integer<uintptr_t>(CommentText.trim(), Result->Key.SnippetAddress,
156                                  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("LOOP-REGISTER")) {
181       // LLVM-EXEGESIS-LOOP-REGISTER <loop register>
182       MCRegister LoopRegister;
183 
184       if (!(LoopRegister = findRegisterByName(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 
198 private:
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 emitValueToAlignment(Align Alignment, int64_t Value, unsigned ValueSize,
206                             unsigned MaxBytesToEmit) override {}
207   void emitZerofill(MCSection *Section, MCSymbol *Symbol, uint64_t Size,
208                     Align ByteAlignment, SMLoc Loc) override {}
209 
210   MCRegister findRegisterByName(const StringRef RegName) const {
211     std::optional<MCRegister> RegisterNumber =
212         State.getRegisterNumberFromName(RegName);
213     if (!RegisterNumber.has_value()) {
214       errs() << "'" << RegName
215              << "' is not a valid register name for the target\n";
216       return MCRegister();
217     }
218     return *RegisterNumber;
219   }
220 
221   const LLVMState &State;
222   BenchmarkCode *const Result;
223   unsigned InvalidComments = 0;
224 };
225 
226 } // namespace
227 
228 // Reads code snippets from file `Filename`.
229 Expected<std::vector<BenchmarkCode>> readSnippets(const LLVMState &State,
230                                                   StringRef Filename) {
231   ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr =
232       MemoryBuffer::getFileOrSTDIN(Filename);
233   if (std::error_code EC = BufferPtr.getError()) {
234     return make_error<Failure>("cannot read snippet: " + Filename + ": " +
235                                EC.message());
236   }
237   SourceMgr SM;
238   SM.AddNewSourceBuffer(std::move(BufferPtr.get()), SMLoc());
239 
240   BenchmarkCode Result;
241 
242   // Ensure that there is a default loop register value specified.
243   Result.Key.LoopRegister =
244       State.getExegesisTarget().getDefaultLoopCounterRegister(
245           State.getTargetMachine().getTargetTriple());
246 
247   const TargetMachine &TM = State.getTargetMachine();
248   MCContext Context(TM.getTargetTriple(), TM.getMCAsmInfo(),
249                     TM.getMCRegisterInfo(), TM.getMCSubtargetInfo());
250   std::unique_ptr<MCObjectFileInfo> ObjectFileInfo(
251       TM.getTarget().createMCObjectFileInfo(Context, /*PIC=*/false));
252   Context.setObjectFileInfo(ObjectFileInfo.get());
253   Context.initInlineSourceManager();
254   BenchmarkCodeStreamer Streamer(&Context, State, &Result);
255 
256   std::string Error;
257   raw_string_ostream ErrorStream(Error);
258   formatted_raw_ostream InstPrinterOStream(ErrorStream);
259   const std::unique_ptr<MCInstPrinter> InstPrinter(
260       TM.getTarget().createMCInstPrinter(
261           TM.getTargetTriple(), TM.getMCAsmInfo()->getAssemblerDialect(),
262           *TM.getMCAsmInfo(), *TM.getMCInstrInfo(), *TM.getMCRegisterInfo()));
263   // The following call will take care of calling Streamer.setTargetStreamer.
264   TM.getTarget().createAsmTargetStreamer(Streamer, InstPrinterOStream,
265                                          InstPrinter.get());
266   if (!Streamer.getTargetStreamer())
267     return make_error<Failure>("cannot create target asm streamer");
268 
269   const std::unique_ptr<MCAsmParser> AsmParser(
270       createMCAsmParser(SM, Context, Streamer, *TM.getMCAsmInfo()));
271   if (!AsmParser)
272     return make_error<Failure>("cannot create asm parser");
273   AsmParser->getLexer().setCommentConsumer(&Streamer);
274 
275   const std::unique_ptr<MCTargetAsmParser> TargetAsmParser(
276       TM.getTarget().createMCAsmParser(*TM.getMCSubtargetInfo(), *AsmParser,
277                                        *TM.getMCInstrInfo(),
278                                        MCTargetOptions()));
279 
280   if (!TargetAsmParser)
281     return make_error<Failure>("cannot create target asm parser");
282   AsmParser->setTargetParser(*TargetAsmParser);
283 
284   if (AsmParser->Run(false))
285     return make_error<Failure>("cannot parse asm file");
286   if (Streamer.numInvalidComments())
287     return make_error<Failure>(Twine("found ")
288                                    .concat(Twine(Streamer.numInvalidComments()))
289                                    .concat(" invalid LLVM-EXEGESIS comments"));
290   return std::vector<BenchmarkCode>{std::move(Result)};
291 }
292 
293 } // namespace exegesis
294 } // namespace llvm
295