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 "llvm/MC/MCContext.h"
13 #include "llvm/MC/MCInstPrinter.h"
14 #include "llvm/MC/MCObjectFileInfo.h"
15 #include "llvm/MC/MCParser/MCAsmLexer.h"
16 #include "llvm/MC/MCParser/MCAsmParser.h"
17 #include "llvm/MC/MCParser/MCTargetAsmParser.h"
18 #include "llvm/MC/MCRegisterInfo.h"
19 #include "llvm/MC/MCStreamer.h"
20 #include "llvm/MC/TargetRegistry.h"
21 #include "llvm/Support/Format.h"
22 #include "llvm/Support/Path.h"
23 #include "llvm/Support/SourceMgr.h"
30 // An MCStreamer that reads a BenchmarkCode definition from a file.
31 class BenchmarkCodeStreamer
: public MCStreamer
, public AsmCommentConsumer
{
33 explicit BenchmarkCodeStreamer(
34 MCContext
*Context
, const DenseMap
<StringRef
, unsigned> &RegNameToRegNo
,
35 BenchmarkCode
*Result
)
36 : MCStreamer(*Context
), RegNameToRegNo(RegNameToRegNo
), Result(Result
) {}
38 // Implementation of the MCStreamer interface. We only care about
40 void emitInstruction(const MCInst
&Instruction
,
41 const MCSubtargetInfo
&STI
) override
{
42 Result
->Key
.Instructions
.push_back(Instruction
);
45 // Implementation of the AsmCommentConsumer.
46 void HandleComment(SMLoc Loc
, StringRef CommentText
) override
{
47 CommentText
= CommentText
.trim();
48 if (!CommentText
.consume_front("LLVM-EXEGESIS-"))
50 if (CommentText
.consume_front("DEFREG")) {
51 // LLVM-EXEGESIS-DEFREF <reg> <hex_value>
53 SmallVector
<StringRef
, 2> Parts
;
54 CommentText
.split(Parts
, ' ', /*unlimited splits*/ -1,
55 /*do not keep empty strings*/ false);
56 if (Parts
.size() != 2) {
57 errs() << "invalid comment 'LLVM-EXEGESIS-DEFREG " << CommentText
58 << "', expected two parameters <REG> <HEX_VALUE>\n";
62 if (!(RegVal
.Register
= findRegisterByName(Parts
[0].trim()))) {
63 errs() << "unknown register '" << Parts
[0]
64 << "' in 'LLVM-EXEGESIS-DEFREG " << CommentText
<< "'\n";
68 const StringRef HexValue
= Parts
[1].trim();
70 /* each hex digit is 4 bits */ HexValue
.size() * 4, HexValue
, 16);
71 Result
->Key
.RegisterInitialValues
.push_back(std::move(RegVal
));
74 if (CommentText
.consume_front("LIVEIN")) {
75 // LLVM-EXEGESIS-LIVEIN <reg>
76 const auto RegName
= CommentText
.ltrim();
77 if (unsigned Reg
= findRegisterByName(RegName
))
78 Result
->LiveIns
.push_back(Reg
);
80 errs() << "unknown register '" << RegName
81 << "' in 'LLVM-EXEGESIS-LIVEIN " << CommentText
<< "'\n";
86 if (CommentText
.consume_front("MEM-DEF")) {
87 // LLVM-EXEGESIS-MEM-DEF <name> <size> <value>
88 SmallVector
<StringRef
, 3> Parts
;
89 CommentText
.split(Parts
, ' ', -1, false);
90 if (Parts
.size() != 3) {
91 errs() << "invalid comment 'LLVM-EXEGESIS-MEM-DEF " << CommentText
92 << "', expected three parameters <NAME> <SIZE> <VALUE>";
96 const StringRef HexValue
= Parts
[2].trim();
98 MemVal
.SizeBytes
= std::stol(Parts
[1].trim().str());
99 if (HexValue
.size() % 2 != 0) {
100 errs() << "invalid comment 'LLVM-EXEGESIS-MEM-DEF " << CommentText
101 << "', expected <VALUE> to contain a whole number of bytes";
103 MemVal
.Value
= APInt(HexValue
.size() * 4, HexValue
, 16);
104 MemVal
.Index
= Result
->Key
.MemoryValues
.size();
105 Result
->Key
.MemoryValues
[Parts
[0].trim().str()] = MemVal
;
108 if (CommentText
.consume_front("MEM-MAP")) {
109 // LLVM-EXEGESIS-MEM-MAP <value name> <address>
110 SmallVector
<StringRef
, 2> Parts
;
111 CommentText
.split(Parts
, ' ', -1, false);
112 if (Parts
.size() != 2) {
113 errs() << "invalid comment 'LLVM-EXEGESIS-MEM-MAP " << CommentText
114 << "', expected two parameters <VALUE NAME> <ADDRESS>";
118 MemoryMapping MemMap
;
119 MemMap
.MemoryValueName
= Parts
[0].trim().str();
120 MemMap
.Address
= std::stol(Parts
[1].trim().str());
121 // validate that the annotation refers to an already existing memory
123 auto MemValIT
= Result
->Key
.MemoryValues
.find(Parts
[0].trim().str());
124 if (MemValIT
== Result
->Key
.MemoryValues
.end()) {
125 errs() << "invalid comment 'LLVM-EXEGESIS-MEM-MAP " << CommentText
126 << "', expected <VALUE NAME> to contain the name of an already "
127 "specified memory definition";
131 Result
->Key
.MemoryMappings
.push_back(std::move(MemMap
));
136 unsigned numInvalidComments() const { return InvalidComments
; }
139 // We only care about instructions, we don't implement this part of the API.
140 void emitCommonSymbol(MCSymbol
*Symbol
, uint64_t Size
,
141 Align ByteAlignment
) override
{}
142 bool emitSymbolAttribute(MCSymbol
*Symbol
, MCSymbolAttr Attribute
) override
{
145 void emitValueToAlignment(Align Alignment
, int64_t Value
, unsigned ValueSize
,
146 unsigned MaxBytesToEmit
) override
{}
147 void emitZerofill(MCSection
*Section
, MCSymbol
*Symbol
, uint64_t Size
,
148 Align ByteAlignment
, SMLoc Loc
) override
{}
150 unsigned findRegisterByName(const StringRef RegName
) const {
151 auto Iter
= RegNameToRegNo
.find(RegName
);
152 if (Iter
!= RegNameToRegNo
.end())
154 errs() << "'" << RegName
155 << "' is not a valid register name for the target\n";
159 const DenseMap
<StringRef
, unsigned> &RegNameToRegNo
;
160 BenchmarkCode
*const Result
;
161 unsigned InvalidComments
= 0;
166 // Reads code snippets from file `Filename`.
167 Expected
<std::vector
<BenchmarkCode
>> readSnippets(const LLVMState
&State
,
168 StringRef Filename
) {
169 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> BufferPtr
=
170 MemoryBuffer::getFileOrSTDIN(Filename
);
171 if (std::error_code EC
= BufferPtr
.getError()) {
172 return make_error
<Failure
>("cannot read snippet: " + Filename
+ ": " +
176 SM
.AddNewSourceBuffer(std::move(BufferPtr
.get()), SMLoc());
178 BenchmarkCode Result
;
180 const TargetMachine
&TM
= State
.getTargetMachine();
181 MCContext
Context(TM
.getTargetTriple(), TM
.getMCAsmInfo(),
182 TM
.getMCRegisterInfo(), TM
.getMCSubtargetInfo());
183 std::unique_ptr
<MCObjectFileInfo
> ObjectFileInfo(
184 TM
.getTarget().createMCObjectFileInfo(Context
, /*PIC=*/false));
185 Context
.setObjectFileInfo(ObjectFileInfo
.get());
186 Context
.initInlineSourceManager();
187 BenchmarkCodeStreamer
Streamer(&Context
, State
.getRegNameToRegNoMapping(),
191 raw_string_ostream
ErrorStream(Error
);
192 formatted_raw_ostream
InstPrinterOStream(ErrorStream
);
193 const std::unique_ptr
<MCInstPrinter
> InstPrinter(
194 TM
.getTarget().createMCInstPrinter(
195 TM
.getTargetTriple(), TM
.getMCAsmInfo()->getAssemblerDialect(),
196 *TM
.getMCAsmInfo(), *TM
.getMCInstrInfo(), *TM
.getMCRegisterInfo()));
197 // The following call will take care of calling Streamer.setTargetStreamer.
198 TM
.getTarget().createAsmTargetStreamer(Streamer
, InstPrinterOStream
,
200 TM
.Options
.MCOptions
.AsmVerbose
);
201 if (!Streamer
.getTargetStreamer())
202 return make_error
<Failure
>("cannot create target asm streamer");
204 const std::unique_ptr
<MCAsmParser
> AsmParser(
205 createMCAsmParser(SM
, Context
, Streamer
, *TM
.getMCAsmInfo()));
207 return make_error
<Failure
>("cannot create asm parser");
208 AsmParser
->getLexer().setCommentConsumer(&Streamer
);
210 const std::unique_ptr
<MCTargetAsmParser
> TargetAsmParser(
211 TM
.getTarget().createMCAsmParser(*TM
.getMCSubtargetInfo(), *AsmParser
,
212 *TM
.getMCInstrInfo(),
215 if (!TargetAsmParser
)
216 return make_error
<Failure
>("cannot create target asm parser");
217 AsmParser
->setTargetParser(*TargetAsmParser
);
219 if (AsmParser
->Run(false))
220 return make_error
<Failure
>("cannot parse asm file");
221 if (Streamer
.numInvalidComments())
222 return make_error
<Failure
>(Twine("found ")
223 .concat(Twine(Streamer
.numInvalidComments()))
224 .concat(" invalid LLVM-EXEGESIS comments"));
225 return std::vector
<BenchmarkCode
>{std::move(Result
)};
228 } // namespace exegesis