1 //===-- SnippetFile.cpp -----------------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "SnippetFile.h"
10 #include "BenchmarkRunner.h"
12 #include "LlvmState.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"
37 // An MCStreamer that reads a BenchmarkCode definition from a file.
38 class BenchmarkCodeStreamer
: public MCStreamer
, public AsmCommentConsumer
{
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
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-"))
56 if (CommentText
.consume_front("DEFREG")) {
57 // LLVM-EXEGESIS-DEFREF <reg> <hex_value>
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";
68 if (!(RegVal
.Register
= findRegisterByName(Parts
[0].trim()))) {
69 errs() << "unknown register '" << Parts
[0]
70 << "' in 'LLVM-EXEGESIS-DEFREG " << CommentText
<< "'\n";
74 const StringRef HexValue
= Parts
[1].trim();
76 /* each hex digit is 4 bits */ HexValue
.size() * 4, HexValue
, 16);
77 Result
->Key
.RegisterInitialValues
.push_back(std::move(RegVal
));
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
);
86 errs() << "unknown register '" << RegName
87 << "' in 'LLVM-EXEGESIS-LIVEIN " << CommentText
<< "'\n";
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>";
102 const StringRef HexValue
= Parts
[2].trim();
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
;
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>";
124 MemoryMapping MemMap
;
125 MemMap
.MemoryValueName
= Parts
[0].trim().str();
126 MemMap
.Address
= std::stol(Parts
[1].trim().str());
129 // Validate that the annotation is a multiple of the platform's page
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 "
140 // validate that the annotation refers to an already existing memory
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";
150 Result
->Key
.MemoryMappings
.push_back(std::move(MemMap
));
153 if (CommentText
.consume_front("SNIPPET-ADDRESS")) {
154 // LLVM-EXEGESIS-SNIPPET-ADDRESS <address>
155 if (!to_integer
<uintptr_t>(CommentText
.trim(), Result
->Key
.SnippetAddress
,
157 errs() << "invalid comment 'LLVM-EXEGESIS-SNIPPET-ADDRESS "
159 << "', expected <ADDRESS> to contain a valid integer in "
160 "hexadecimal format";
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 "
171 << ", expected <ADDRESS> to be a multiple of the platform page "
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";
191 Result
->Key
.LoopRegister
= LoopRegister
;
196 unsigned numInvalidComments() const { return InvalidComments
; }
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
{
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;
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
+ ": " +
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
);
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
,
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()));
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(),
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