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 "Target.h" 13 #include "llvm/MC/MCContext.h" 14 #include "llvm/MC/MCInstPrinter.h" 15 #include "llvm/MC/MCObjectFileInfo.h" 16 #include "llvm/MC/MCParser/MCAsmLexer.h" 17 #include "llvm/MC/MCParser/MCAsmParser.h" 18 #include "llvm/MC/MCParser/MCTargetAsmParser.h" 19 #include "llvm/MC/MCRegisterInfo.h" 20 #include "llvm/MC/MCStreamer.h" 21 #include "llvm/MC/TargetRegistry.h" 22 #include "llvm/Support/Format.h" 23 #include "llvm/Support/Path.h" 24 #include "llvm/Support/SourceMgr.h" 25 #include <string> 26 27 #ifdef __linux__ 28 #include <unistd.h> 29 #endif // __linux__ 30 31 namespace llvm { 32 namespace exegesis { 33 namespace { 34 35 // An MCStreamer that reads a BenchmarkCode definition from a file. 36 class BenchmarkCodeStreamer : public MCStreamer, public AsmCommentConsumer { 37 public: 38 explicit BenchmarkCodeStreamer( 39 MCContext *Context, const DenseMap<StringRef, MCRegister> &RegNameToRegNo, 40 BenchmarkCode *Result) 41 : MCStreamer(*Context), RegNameToRegNo(RegNameToRegNo), Result(Result) {} 42 43 // Implementation of the MCStreamer interface. We only care about 44 // instructions. 45 void emitInstruction(const MCInst &Instruction, 46 const MCSubtargetInfo &STI) override { 47 Result->Key.Instructions.push_back(Instruction); 48 } 49 50 // Implementation of the AsmCommentConsumer. 51 void HandleComment(SMLoc Loc, StringRef CommentText) override { 52 CommentText = CommentText.trim(); 53 if (!CommentText.consume_front("LLVM-EXEGESIS-")) 54 return; 55 if (CommentText.consume_front("DEFREG")) { 56 // LLVM-EXEGESIS-DEFREF <reg> <hex_value> 57 RegisterValue RegVal; 58 SmallVector<StringRef, 2> Parts; 59 CommentText.split(Parts, ' ', /*unlimited splits*/ -1, 60 /*do not keep empty strings*/ false); 61 if (Parts.size() != 2) { 62 errs() << "invalid comment 'LLVM-EXEGESIS-DEFREG " << CommentText 63 << "', expected two parameters <REG> <HEX_VALUE>\n"; 64 ++InvalidComments; 65 return; 66 } 67 if (!(RegVal.Register = findRegisterByName(Parts[0].trim()))) { 68 errs() << "unknown register '" << Parts[0] 69 << "' in 'LLVM-EXEGESIS-DEFREG " << CommentText << "'\n"; 70 ++InvalidComments; 71 return; 72 } 73 const StringRef HexValue = Parts[1].trim(); 74 RegVal.Value = APInt( 75 /* each hex digit is 4 bits */ HexValue.size() * 4, HexValue, 16); 76 Result->Key.RegisterInitialValues.push_back(std::move(RegVal)); 77 return; 78 } 79 if (CommentText.consume_front("LIVEIN")) { 80 // LLVM-EXEGESIS-LIVEIN <reg> 81 const auto RegName = CommentText.ltrim(); 82 if (unsigned Reg = findRegisterByName(RegName)) 83 Result->LiveIns.push_back(Reg); 84 else { 85 errs() << "unknown register '" << RegName 86 << "' in 'LLVM-EXEGESIS-LIVEIN " << CommentText << "'\n"; 87 ++InvalidComments; 88 } 89 return; 90 } 91 if (CommentText.consume_front("MEM-DEF")) { 92 // LLVM-EXEGESIS-MEM-DEF <name> <size> <value> 93 SmallVector<StringRef, 3> Parts; 94 CommentText.split(Parts, ' ', -1, false); 95 if (Parts.size() != 3) { 96 errs() << "invalid comment 'LLVM-EXEGESIS-MEM-DEF " << CommentText 97 << "', expected three parameters <NAME> <SIZE> <VALUE>"; 98 ++InvalidComments; 99 return; 100 } 101 const StringRef HexValue = Parts[2].trim(); 102 MemoryValue MemVal; 103 MemVal.SizeBytes = std::stol(Parts[1].trim().str()); 104 if (HexValue.size() % 2 != 0) { 105 errs() << "invalid comment 'LLVM-EXEGESIS-MEM-DEF " << CommentText 106 << "', expected <VALUE> to contain a whole number of bytes"; 107 } 108 MemVal.Value = APInt(HexValue.size() * 4, HexValue, 16); 109 MemVal.Index = Result->Key.MemoryValues.size(); 110 Result->Key.MemoryValues[Parts[0].trim().str()] = MemVal; 111 return; 112 } 113 if (CommentText.consume_front("MEM-MAP")) { 114 // LLVM-EXEGESIS-MEM-MAP <value name> <address> 115 SmallVector<StringRef, 2> Parts; 116 CommentText.split(Parts, ' ', -1, false); 117 if (Parts.size() != 2) { 118 errs() << "invalid comment 'LLVM-EXEGESIS-MEM-MAP " << CommentText 119 << "', expected two parameters <VALUE NAME> <ADDRESS>"; 120 ++InvalidComments; 121 return; 122 } 123 MemoryMapping MemMap; 124 MemMap.MemoryValueName = Parts[0].trim().str(); 125 MemMap.Address = std::stol(Parts[1].trim().str()); 126 127 #ifdef __linux__ 128 // Validate that the annotation is a multiple of the platform's page 129 // size. 130 if (MemMap.Address % getpagesize() != 0) { 131 errs() << "invalid comment 'LLVM-EXEGESIS-MEM-MAP " << CommentText 132 << "', expected <ADDRESS> to be a multiple of the platform page " 133 "size."; 134 ++InvalidComments; 135 return; 136 } 137 #endif // __linux__ 138 139 // validate that the annotation refers to an already existing memory 140 // definition 141 auto MemValIT = Result->Key.MemoryValues.find(Parts[0].trim().str()); 142 if (MemValIT == Result->Key.MemoryValues.end()) { 143 errs() << "invalid comment 'LLVM-EXEGESIS-MEM-MAP " << CommentText 144 << "', expected <VALUE NAME> to contain the name of an already " 145 "specified memory definition"; 146 ++InvalidComments; 147 return; 148 } 149 Result->Key.MemoryMappings.push_back(std::move(MemMap)); 150 return; 151 } 152 if (CommentText.consume_front("SNIPPET-ADDRESS")) { 153 // LLVM-EXEGESIS-SNIPPET-ADDRESS <address> 154 if (!to_integer<uintptr_t>(CommentText.trim(), Result->Key.SnippetAddress, 155 16)) { 156 errs() << "invalid comment 'LLVM-EXEGESIS-SNIPPET-ADDRESS " 157 << CommentText 158 << "', expected <ADDRESS> to contain a valid integer in " 159 "hexadecimal format"; 160 ++InvalidComments; 161 return; 162 } 163 164 #ifdef __linux__ 165 // Validate that the address in the annotation is a multiple of the 166 // platform's page size. 167 if (Result->Key.SnippetAddress % getpagesize() != 0) { 168 errs() << "invalid comment 'LLVM-EXEGESIS-SNIPPET-ADDRESS " 169 << CommentText 170 << ", expected <ADDRESS> to be a multiple of the platform page " 171 "size."; 172 ++InvalidComments; 173 return; 174 } 175 #endif // __linux__ 176 177 return; 178 } 179 if (CommentText.consume_front("LOOP-REGISTER")) { 180 // LLVM-EXEGESIS-LOOP-REGISTER <loop register> 181 unsigned LoopRegister; 182 183 if (!(LoopRegister = findRegisterByName(CommentText.trim()))) { 184 errs() << "unknown register '" << CommentText 185 << "' in 'LLVM-EXEGESIS-LOOP-REGISTER " << CommentText << "'\n"; 186 ++InvalidComments; 187 return; 188 } 189 190 Result->Key.LoopRegister = LoopRegister; 191 return; 192 } 193 } 194 195 unsigned numInvalidComments() const { return InvalidComments; } 196 197 private: 198 // We only care about instructions, we don't implement this part of the API. 199 void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, 200 Align ByteAlignment) override {} 201 bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override { 202 return false; 203 } 204 void emitValueToAlignment(Align Alignment, int64_t Value, unsigned ValueSize, 205 unsigned MaxBytesToEmit) override {} 206 void emitZerofill(MCSection *Section, MCSymbol *Symbol, uint64_t Size, 207 Align ByteAlignment, SMLoc Loc) override {} 208 209 unsigned findRegisterByName(const StringRef RegName) const { 210 auto Iter = RegNameToRegNo.find(RegName); 211 if (Iter != RegNameToRegNo.end()) 212 return Iter->second; 213 errs() << "'" << RegName 214 << "' is not a valid register name for the target\n"; 215 return 0; 216 } 217 218 const DenseMap<StringRef, MCRegister> &RegNameToRegNo; 219 BenchmarkCode *const Result; 220 unsigned InvalidComments = 0; 221 }; 222 223 } // namespace 224 225 // Reads code snippets from file `Filename`. 226 Expected<std::vector<BenchmarkCode>> readSnippets(const LLVMState &State, 227 StringRef Filename) { 228 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr = 229 MemoryBuffer::getFileOrSTDIN(Filename); 230 if (std::error_code EC = BufferPtr.getError()) { 231 return make_error<Failure>("cannot read snippet: " + Filename + ": " + 232 EC.message()); 233 } 234 SourceMgr SM; 235 SM.AddNewSourceBuffer(std::move(BufferPtr.get()), SMLoc()); 236 237 BenchmarkCode Result; 238 239 // Ensure that there is a default loop register value specified. 240 Result.Key.LoopRegister = 241 State.getExegesisTarget().getDefaultLoopCounterRegister( 242 State.getTargetMachine().getTargetTriple()); 243 244 const TargetMachine &TM = State.getTargetMachine(); 245 MCContext Context(TM.getTargetTriple(), TM.getMCAsmInfo(), 246 TM.getMCRegisterInfo(), TM.getMCSubtargetInfo()); 247 std::unique_ptr<MCObjectFileInfo> ObjectFileInfo( 248 TM.getTarget().createMCObjectFileInfo(Context, /*PIC=*/false)); 249 Context.setObjectFileInfo(ObjectFileInfo.get()); 250 Context.initInlineSourceManager(); 251 BenchmarkCodeStreamer Streamer(&Context, State.getRegNameToRegNoMapping(), 252 &Result); 253 254 std::string Error; 255 raw_string_ostream ErrorStream(Error); 256 formatted_raw_ostream InstPrinterOStream(ErrorStream); 257 const std::unique_ptr<MCInstPrinter> InstPrinter( 258 TM.getTarget().createMCInstPrinter( 259 TM.getTargetTriple(), TM.getMCAsmInfo()->getAssemblerDialect(), 260 *TM.getMCAsmInfo(), *TM.getMCInstrInfo(), *TM.getMCRegisterInfo())); 261 // The following call will take care of calling Streamer.setTargetStreamer. 262 TM.getTarget().createAsmTargetStreamer(Streamer, InstPrinterOStream, 263 InstPrinter.get()); 264 if (!Streamer.getTargetStreamer()) 265 return make_error<Failure>("cannot create target asm streamer"); 266 267 const std::unique_ptr<MCAsmParser> AsmParser( 268 createMCAsmParser(SM, Context, Streamer, *TM.getMCAsmInfo())); 269 if (!AsmParser) 270 return make_error<Failure>("cannot create asm parser"); 271 AsmParser->getLexer().setCommentConsumer(&Streamer); 272 273 const std::unique_ptr<MCTargetAsmParser> TargetAsmParser( 274 TM.getTarget().createMCAsmParser(*TM.getMCSubtargetInfo(), *AsmParser, 275 *TM.getMCInstrInfo(), 276 MCTargetOptions())); 277 278 if (!TargetAsmParser) 279 return make_error<Failure>("cannot create target asm parser"); 280 AsmParser->setTargetParser(*TargetAsmParser); 281 282 if (AsmParser->Run(false)) 283 return make_error<Failure>("cannot parse asm file"); 284 if (Streamer.numInvalidComments()) 285 return make_error<Failure>(Twine("found ") 286 .concat(Twine(Streamer.numInvalidComments())) 287 .concat(" invalid LLVM-EXEGESIS comments")); 288 return std::vector<BenchmarkCode>{std::move(Result)}; 289 } 290 291 } // namespace exegesis 292 } // namespace llvm 293