xref: /llvm-project/llvm/tools/llvm-exegesis/lib/SnippetFile.cpp (revision 1d1186de34c55149be336068bf312e8f755dca37)
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 
31 namespace llvm {
32 namespace exegesis {
33 namespace {
34 
35 // An MCStreamer that reads a BenchmarkCode definition from a file.
36 class BenchmarkCodeStreamer : public MCStreamer, public AsmCommentConsumer {
37 public:
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(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("LLVM-EXEGESIS-"))
54       return;
55     if (CommentText.consume_front("DEFREG")) {
56       // LLVM-EXEGESIS-DEFREF <reg> <hex_value>
57       RegisterValue RegVal;
58       SmallVector<StringRef, 2> Parts;
59       CommentText.split(Parts, ' ', /*unlimited splits*/ -1,
60                         /*do not keep empty strings*/ 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(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(std::move(RegVal));
77       return;
78     }
79     if (CommentText.consume_front("LIVEIN")) {
80       // LLVM-EXEGESIS-LIVEIN <reg>
81       const auto RegName = CommentText.ltrim();
82       if (unsigned Reg = findRegisterByName(RegName))
83         Result->LiveIns.push_back(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("MEM-DEF")) {
92       // LLVM-EXEGESIS-MEM-DEF <name> <size> <value>
93       SmallVector<StringRef, 3> Parts;
94       CommentText.split(Parts, ' ', -1, 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(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("MEM-MAP")) {
114       // LLVM-EXEGESIS-MEM-MAP <value name> <address>
115       SmallVector<StringRef, 2> Parts;
116       CommentText.split(Parts, ' ', -1, 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(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(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(std::move(MemMap));
150       return;
151     }
152     if (CommentText.consume_front("SNIPPET-ADDRESS")) {
153       // LLVM-EXEGESIS-SNIPPET-ADDRESS <address>
154       if (!to_integer<intptr_t>(CommentText.trim(), Result->Key.SnippetAddress,
155                                 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("LOOP-REGISTER")) {
180       // LLVM-EXEGESIS-LOOP-REGISTER <loop register>
181       unsigned LoopRegister;
182 
183       if (!(LoopRegister = findRegisterByName(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 
197 private:
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(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`.
226 Expected<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>("cannot read snippet: " + Filename + ": " +
232                                EC.message());
233   }
234   SourceMgr SM;
235   SM.AddNewSourceBuffer(std::move(BufferPtr.get()), 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(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           TM.getTargetTriple(), TM.getMCAsmInfo()->getAssemblerDialect(),
260           *TM.getMCAsmInfo(), *TM.getMCInstrInfo(), *TM.getMCRegisterInfo()));
261   // The following call will take care of calling Streamer.setTargetStreamer.
262   TM.getTarget().createAsmTargetStreamer(Streamer, InstPrinterOStream,
263                                          InstPrinter.get(),
264                                          TM.Options.MCOptions.AsmVerbose);
265   if (!Streamer.getTargetStreamer())
266     return make_error<Failure>("cannot create target asm streamer");
267 
268   const std::unique_ptr<MCAsmParser> AsmParser(
269       createMCAsmParser(SM, Context, Streamer, *TM.getMCAsmInfo()));
270   if (!AsmParser)
271     return make_error<Failure>("cannot create asm parser");
272   AsmParser->getLexer().setCommentConsumer(&Streamer);
273 
274   const std::unique_ptr<MCTargetAsmParser> TargetAsmParser(
275       TM.getTarget().createMCAsmParser(*TM.getMCSubtargetInfo(), *AsmParser,
276                                        *TM.getMCInstrInfo(),
277                                        MCTargetOptions()));
278 
279   if (!TargetAsmParser)
280     return make_error<Failure>("cannot create target asm parser");
281   AsmParser->setTargetParser(*TargetAsmParser);
282 
283   if (AsmParser->Run(false))
284     return make_error<Failure>("cannot parse asm file");
285   if (Streamer.numInvalidComments())
286     return make_error<Failure>(Twine("found ")
287                                    .concat(Twine(Streamer.numInvalidComments()))
288                                    .concat(" invalid LLVM-EXEGESIS comments"));
289   return std::vector<BenchmarkCode>{std::move(Result)};
290 }
291 
292 } // namespace exegesis
293 } // namespace llvm
294