xref: /llvm-project/llvm/tools/llvm-exegesis/lib/SnippetFile.cpp (revision 3ab41f912a6c219a93b87c257139822ea07c8863)
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 "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"
24 #include <string>
25 
26 namespace llvm {
27 namespace exegesis {
28 namespace {
29 
30 // An MCStreamer that reads a BenchmarkCode definition from a file.
31 class BenchmarkCodeStreamer : public MCStreamer, public AsmCommentConsumer {
32 public:
33   explicit BenchmarkCodeStreamer(
34       MCContext *Context, const DenseMap<StringRef, unsigned> &RegNameToRegNo,
35       BenchmarkCode *Result)
36       : MCStreamer(*Context), RegNameToRegNo(RegNameToRegNo), Result(Result) {}
37 
38   // Implementation of the MCStreamer interface. We only care about
39   // instructions.
40   void emitInstruction(const MCInst &Instruction,
41                        const MCSubtargetInfo &STI) override {
42     Result->Key.Instructions.push_back(Instruction);
43   }
44 
45   // Implementation of the AsmCommentConsumer.
46   void HandleComment(SMLoc Loc, StringRef CommentText) override {
47     CommentText = CommentText.trim();
48     if (!CommentText.consume_front("LLVM-EXEGESIS-"))
49       return;
50     if (CommentText.consume_front("DEFREG")) {
51       // LLVM-EXEGESIS-DEFREF <reg> <hex_value>
52       RegisterValue RegVal;
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";
59         ++InvalidComments;
60         return;
61       }
62       if (!(RegVal.Register = findRegisterByName(Parts[0].trim()))) {
63         errs() << "unknown register '" << Parts[0]
64                << "' in 'LLVM-EXEGESIS-DEFREG " << CommentText << "'\n";
65         ++InvalidComments;
66         return;
67       }
68       const StringRef HexValue = Parts[1].trim();
69       RegVal.Value = APInt(
70           /* each hex digit is 4 bits */ HexValue.size() * 4, HexValue, 16);
71       Result->Key.RegisterInitialValues.push_back(std::move(RegVal));
72       return;
73     }
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);
79       else {
80         errs() << "unknown register '" << RegName
81                << "' in 'LLVM-EXEGESIS-LIVEIN " << CommentText << "'\n";
82         ++InvalidComments;
83       }
84       return;
85     }
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>";
93         ++InvalidComments;
94         return;
95       }
96       const StringRef HexValue = Parts[2].trim();
97       MemoryValue MemVal;
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";
102       }
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;
106       return;
107     }
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>";
115         ++InvalidComments;
116         return;
117       }
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
122       // definition
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";
128         ++InvalidComments;
129         return;
130       }
131       Result->Key.MemoryMappings.push_back(std::move(MemMap));
132       return;
133     }
134     if (CommentText.consume_front("SNIPPET-ADDRESS")) {
135       // LLVM-EXEGESIS-SNIPPET-ADDRESS <address>
136       if (!to_integer<intptr_t>(CommentText.trim(), Result->Key.SnippetAddress,
137                                 16)) {
138         errs() << "invalid comment 'LLVM-EXEGESIS-SNIPPET-ADDRESS "
139                << CommentText
140                << "', expected <ADDRESS> to contain a valid integer in "
141                   "hexadecimal format";
142         ++InvalidComments;
143         return;
144       }
145       return;
146     }
147   }
148 
149   unsigned numInvalidComments() const { return InvalidComments; }
150 
151 private:
152   // We only care about instructions, we don't implement this part of the API.
153   void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
154                         Align ByteAlignment) override {}
155   bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override {
156     return false;
157   }
158   void emitValueToAlignment(Align Alignment, int64_t Value, unsigned ValueSize,
159                             unsigned MaxBytesToEmit) override {}
160   void emitZerofill(MCSection *Section, MCSymbol *Symbol, uint64_t Size,
161                     Align ByteAlignment, SMLoc Loc) override {}
162 
163   unsigned findRegisterByName(const StringRef RegName) const {
164     auto Iter = RegNameToRegNo.find(RegName);
165     if (Iter != RegNameToRegNo.end())
166       return Iter->second;
167     errs() << "'" << RegName
168            << "' is not a valid register name for the target\n";
169     return 0;
170   }
171 
172   const DenseMap<StringRef, unsigned> &RegNameToRegNo;
173   BenchmarkCode *const Result;
174   unsigned InvalidComments = 0;
175 };
176 
177 } // namespace
178 
179 // Reads code snippets from file `Filename`.
180 Expected<std::vector<BenchmarkCode>> readSnippets(const LLVMState &State,
181                                                   StringRef Filename) {
182   ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr =
183       MemoryBuffer::getFileOrSTDIN(Filename);
184   if (std::error_code EC = BufferPtr.getError()) {
185     return make_error<Failure>("cannot read snippet: " + Filename + ": " +
186                                EC.message());
187   }
188   SourceMgr SM;
189   SM.AddNewSourceBuffer(std::move(BufferPtr.get()), SMLoc());
190 
191   BenchmarkCode Result;
192 
193   const TargetMachine &TM = State.getTargetMachine();
194   MCContext Context(TM.getTargetTriple(), TM.getMCAsmInfo(),
195                     TM.getMCRegisterInfo(), TM.getMCSubtargetInfo());
196   std::unique_ptr<MCObjectFileInfo> ObjectFileInfo(
197       TM.getTarget().createMCObjectFileInfo(Context, /*PIC=*/false));
198   Context.setObjectFileInfo(ObjectFileInfo.get());
199   Context.initInlineSourceManager();
200   BenchmarkCodeStreamer Streamer(&Context, State.getRegNameToRegNoMapping(),
201                                  &Result);
202 
203   std::string Error;
204   raw_string_ostream ErrorStream(Error);
205   formatted_raw_ostream InstPrinterOStream(ErrorStream);
206   const std::unique_ptr<MCInstPrinter> InstPrinter(
207       TM.getTarget().createMCInstPrinter(
208           TM.getTargetTriple(), TM.getMCAsmInfo()->getAssemblerDialect(),
209           *TM.getMCAsmInfo(), *TM.getMCInstrInfo(), *TM.getMCRegisterInfo()));
210   // The following call will take care of calling Streamer.setTargetStreamer.
211   TM.getTarget().createAsmTargetStreamer(Streamer, InstPrinterOStream,
212                                          InstPrinter.get(),
213                                          TM.Options.MCOptions.AsmVerbose);
214   if (!Streamer.getTargetStreamer())
215     return make_error<Failure>("cannot create target asm streamer");
216 
217   const std::unique_ptr<MCAsmParser> AsmParser(
218       createMCAsmParser(SM, Context, Streamer, *TM.getMCAsmInfo()));
219   if (!AsmParser)
220     return make_error<Failure>("cannot create asm parser");
221   AsmParser->getLexer().setCommentConsumer(&Streamer);
222 
223   const std::unique_ptr<MCTargetAsmParser> TargetAsmParser(
224       TM.getTarget().createMCAsmParser(*TM.getMCSubtargetInfo(), *AsmParser,
225                                        *TM.getMCInstrInfo(),
226                                        MCTargetOptions()));
227 
228   if (!TargetAsmParser)
229     return make_error<Failure>("cannot create target asm parser");
230   AsmParser->setTargetParser(*TargetAsmParser);
231 
232   if (AsmParser->Run(false))
233     return make_error<Failure>("cannot parse asm file");
234   if (Streamer.numInvalidComments())
235     return make_error<Failure>(Twine("found ")
236                                    .concat(Twine(Streamer.numInvalidComments()))
237                                    .concat(" invalid LLVM-EXEGESIS comments"));
238   return std::vector<BenchmarkCode>{std::move(Result)};
239 }
240 
241 } // namespace exegesis
242 } // namespace llvm
243