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