Revert "Reland - [Offload] Introduce offload-tblgen and initial new API implementatio...
[llvm-project.git] / llvm / tools / llvm-exegesis / lib / SnippetFile.cpp
blobb37999ab017f59e5419f0a4811111fa1a8ed419b
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 //===----------------------------------------------------------------------===//
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>
29 #ifdef __linux__
30 #include <unistd.h>
31 #endif // __linux__
33 namespace llvm {
34 namespace exegesis {
35 namespace {
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) {}
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);
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;
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;
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;
80 if (CommentText.consume_front("LIVEIN")) {
81 // LLVM-EXEGESIS-LIVEIN <reg>
82 const auto RegName = CommentText.ltrim();
83 if (unsigned 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;
90 return;
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;
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";
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;
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;
124 MemoryMapping MemMap;
125 MemMap.MemoryValueName = Parts[0].trim().str();
126 MemMap.Address = std::stol(Parts[1].trim().str());
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;
138 #endif // __linux__
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;
150 Result->Key.MemoryMappings.push_back(std::move(MemMap));
151 return;
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;
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;
176 #endif // __linux__
178 return;
180 if (CommentText.consume_front("LOOP-REGISTER")) {
181 // LLVM-EXEGESIS-LOOP-REGISTER <loop register>
182 unsigned LoopRegister;
184 if (!(LoopRegister = findRegisterByName(CommentText.trim()))) {
185 errs() << "unknown register '" << CommentText
186 << "' in 'LLVM-EXEGESIS-LOOP-REGISTER " << CommentText << "'\n";
187 ++InvalidComments;
188 return;
191 Result->Key.LoopRegister = LoopRegister;
192 return;
196 unsigned numInvalidComments() const { return InvalidComments; }
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;
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 {}
210 unsigned 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::NoRegister;
218 return *RegisterNumber;
221 const LLVMState &State;
222 BenchmarkCode *const Result;
223 unsigned InvalidComments = 0;
226 } // namespace
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());
237 SourceMgr SM;
238 SM.AddNewSourceBuffer(std::move(BufferPtr.get()), SMLoc());
240 BenchmarkCode Result;
242 // Ensure that there is a default loop register value specified.
243 Result.Key.LoopRegister =
244 State.getExegesisTarget().getDefaultLoopCounterRegister(
245 State.getTargetMachine().getTargetTriple());
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);
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");
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);
275 const std::unique_ptr<MCTargetAsmParser> TargetAsmParser(
276 TM.getTarget().createMCAsmParser(*TM.getMCSubtargetInfo(), *AsmParser,
277 *TM.getMCInstrInfo(),
278 MCTargetOptions()));
280 if (!TargetAsmParser)
281 return make_error<Failure>("cannot create target asm parser");
282 AsmParser->setTargetParser(*TargetAsmParser);
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)};
293 } // namespace exegesis
294 } // namespace llvm