293 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "SnippetFile.h"10#include "BenchmarkRunner.h"11#include "Error.h"12#include "LlvmState.h"13#include "Target.h"14#include "llvm/MC/MCContext.h"15#include "llvm/MC/MCInstPrinter.h"16#include "llvm/MC/MCObjectFileInfo.h"17#include "llvm/MC/MCParser/AsmLexer.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"27#include <string>28 29#ifdef __linux__30#include <unistd.h>31#endif // __linux__32 33namespace llvm {34namespace exegesis {35namespace {36 37// An MCStreamer that reads a BenchmarkCode definition from a file.38class BenchmarkCodeStreamer : public MCStreamer, public AsmCommentConsumer {39public:40 explicit BenchmarkCodeStreamer(MCContext *Context, const LLVMState &State,41 BenchmarkCode *Result)42 : MCStreamer(*Context), State(State), Result(Result) {}43 44 // Implementation of the MCStreamer interface. We only care about45 // instructions.46 void emitInstruction(const MCInst &Instruction,47 const MCSubtargetInfo &STI) override {48 Result->Key.Instructions.push_back(Instruction);49 }50 51 // Implementation of the AsmCommentConsumer.52 void HandleComment(SMLoc Loc, StringRef CommentText) override {53 CommentText = CommentText.trim();54 if (!CommentText.consume_front("LLVM-EXEGESIS-"))55 return;56 if (CommentText.consume_front("DEFREG")) {57 // LLVM-EXEGESIS-DEFREF <reg> <hex_value>58 RegisterValue RegVal;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 " << CommentText64 << "', expected two parameters <REG> <HEX_VALUE>\n";65 ++InvalidComments;66 return;67 }68 if (!(RegVal.Register = findRegisterByName(Parts[0].trim()))) {69 errs() << "unknown register '" << Parts[0]70 << "' in 'LLVM-EXEGESIS-DEFREG " << CommentText << "'\n";71 ++InvalidComments;72 return;73 }74 const StringRef HexValue = Parts[1].trim();75 RegVal.Value = APInt(76 /* each hex digit is 4 bits */ HexValue.size() * 4, HexValue, 16);77 Result->Key.RegisterInitialValues.push_back(std::move(RegVal));78 return;79 }80 if (CommentText.consume_front("LIVEIN")) {81 // LLVM-EXEGESIS-LIVEIN <reg>82 const auto RegName = CommentText.ltrim();83 if (MCRegister Reg = findRegisterByName(RegName))84 Result->LiveIns.push_back(Reg);85 else {86 errs() << "unknown register '" << RegName87 << "' in 'LLVM-EXEGESIS-LIVEIN " << CommentText << "'\n";88 ++InvalidComments;89 }90 return;91 }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 " << CommentText98 << "', expected three parameters <NAME> <SIZE> <VALUE>";99 ++InvalidComments;100 return;101 }102 const StringRef HexValue = Parts[2].trim();103 MemoryValue MemVal;104 MemVal.SizeBytes = std::stol(Parts[1].trim().str());105 if (HexValue.size() % 2 != 0) {106 errs() << "invalid comment 'LLVM-EXEGESIS-MEM-DEF " << CommentText107 << "', expected <VALUE> to contain a whole number of bytes";108 }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;112 return;113 }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 " << CommentText120 << "', expected two parameters <VALUE NAME> <ADDRESS>";121 ++InvalidComments;122 return;123 }124 MemoryMapping MemMap;125 MemMap.MemoryValueName = Parts[0].trim().str();126 MemMap.Address = std::stol(Parts[1].trim().str());127 128#ifdef __linux__129 // Validate that the annotation is a multiple of the platform's page130 // size.131 if (MemMap.Address % getpagesize() != 0) {132 errs() << "invalid comment 'LLVM-EXEGESIS-MEM-MAP " << CommentText133 << "', expected <ADDRESS> to be a multiple of the platform page "134 "size.";135 ++InvalidComments;136 return;137 }138#endif // __linux__139 140 // validate that the annotation refers to an already existing memory141 // definition142 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 " << CommentText145 << "', expected <VALUE NAME> to contain the name of an already "146 "specified memory definition";147 ++InvalidComments;148 return;149 }150 Result->Key.MemoryMappings.push_back(std::move(MemMap));151 return;152 }153 if (CommentText.consume_front("SNIPPET-ADDRESS")) {154 // LLVM-EXEGESIS-SNIPPET-ADDRESS <address>155 if (!to_integer<uintptr_t>(CommentText.trim(), Result->Key.SnippetAddress,156 16)) {157 errs() << "invalid comment 'LLVM-EXEGESIS-SNIPPET-ADDRESS "158 << CommentText159 << "', expected <ADDRESS> to contain a valid integer in "160 "hexadecimal format";161 ++InvalidComments;162 return;163 }164 165#ifdef __linux__166 // Validate that the address in the annotation is a multiple of the167 // platform's page size.168 if (Result->Key.SnippetAddress % getpagesize() != 0) {169 errs() << "invalid comment 'LLVM-EXEGESIS-SNIPPET-ADDRESS "170 << CommentText171 << ", expected <ADDRESS> to be a multiple of the platform page "172 "size.";173 ++InvalidComments;174 return;175 }176#endif // __linux__177 178 return;179 }180 if (CommentText.consume_front("LOOP-REGISTER")) {181 // LLVM-EXEGESIS-LOOP-REGISTER <loop register>182 MCRegister LoopRegister;183 184 if (!(LoopRegister = findRegisterByName(CommentText.trim()))) {185 errs() << "unknown register '" << CommentText186 << "' in 'LLVM-EXEGESIS-LOOP-REGISTER " << CommentText << "'\n";187 ++InvalidComments;188 return;189 }190 191 Result->Key.LoopRegister = LoopRegister;192 return;193 }194 }195 196 unsigned numInvalidComments() const { return InvalidComments; }197 198private: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 {203 return false;204 }205 void emitZerofill(MCSection *Section, MCSymbol *Symbol, uint64_t Size,206 Align ByteAlignment, SMLoc Loc) override {}207 208 MCRegister findRegisterByName(const StringRef RegName) const {209 std::optional<MCRegister> RegisterNumber =210 State.getRegisterNumberFromName(RegName);211 if (!RegisterNumber.has_value()) {212 errs() << "'" << RegName213 << "' is not a valid register name for the target\n";214 return MCRegister();215 }216 return *RegisterNumber;217 }218 219 const LLVMState &State;220 BenchmarkCode *const Result;221 unsigned InvalidComments = 0;222};223 224} // namespace225 226// Reads code snippets from file `Filename`.227Expected<std::vector<BenchmarkCode>> readSnippets(const LLVMState &State,228 StringRef Filename) {229 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr =230 MemoryBuffer::getFileOrSTDIN(Filename);231 if (std::error_code EC = BufferPtr.getError()) {232 return make_error<Failure>("cannot read snippet: " + Filename + ": " +233 EC.message());234 }235 SourceMgr SM;236 SM.AddNewSourceBuffer(std::move(BufferPtr.get()), SMLoc());237 238 BenchmarkCode Result;239 240 // Ensure that there is a default loop register value specified.241 Result.Key.LoopRegister =242 State.getExegesisTarget().getDefaultLoopCounterRegister(243 State.getTargetMachine().getTargetTriple());244 245 const TargetMachine &TM = State.getTargetMachine();246 MCContext Context(TM.getTargetTriple(), TM.getMCAsmInfo(),247 TM.getMCRegisterInfo(), TM.getMCSubtargetInfo());248 std::unique_ptr<MCObjectFileInfo> ObjectFileInfo(249 TM.getTarget().createMCObjectFileInfo(Context, /*PIC=*/false));250 Context.setObjectFileInfo(ObjectFileInfo.get());251 Context.initInlineSourceManager();252 BenchmarkCodeStreamer Streamer(&Context, State, &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 exegesis292} // namespace llvm293