Index: llvm/test/tools/llvm-exegesis/X86/latency/memory-annotations.s =================================================================== --- /dev/null +++ llvm/test/tools/llvm-exegesis/X86/latency/memory-annotations.s @@ -0,0 +1,13 @@ +# REQUIRES: exegesis-can-execute-x86_64, exegesis-can-measure-latency, x86_64-linux + +# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mode=latency -snippets-file=%s -execution-mode=subprocess | FileCheck %s +# RUN: llvm-exegesis -mtriple=x86_64-unknown-unknown -mode=latency -snippets-file=%s -execution-mode=subprocess -repetition-mode=loop | FileCheck %s + +# CHECK: measurements: +# CHECK-NEXT: value: {{.*}}, per_snippet_value: {{.*}} + +# LLVM-EXEGESIS-MEM-DEF test1 4096 2147483647 +# LLVM-EXEGESIS-MEM-MAP test1 8192 + +movq $8192, %rax +movq (%rax), %rdi Index: llvm/tools/llvm-exegesis/lib/Assembler.h =================================================================== --- llvm/tools/llvm-exegesis/lib/Assembler.h +++ llvm/tools/llvm-exegesis/lib/Assembler.h @@ -50,7 +50,8 @@ void addInstruction(const MCInst &Inst, const DebugLoc &DL = DebugLoc()); void addInstructions(ArrayRef Insts, const DebugLoc &DL = DebugLoc()); - void addReturn(const DebugLoc &DL = DebugLoc()); + void addReturn(const ExegesisTarget &ET, bool SubprocessCleanup, + const DebugLoc &DL = DebugLoc()); MachineFunction &MF; MachineBasicBlock *const MBB; @@ -91,7 +92,9 @@ std::unique_ptr TM, ArrayRef LiveIns, ArrayRef RegisterInitialValues, - const FillFunction &Fill, raw_pwrite_stream &AsmStream); + const FillFunction &Fill, raw_pwrite_stream &AsmStreamm, + const BenchmarkKey &Key, + bool GenerateMemoryInstructions); // Creates an ObjectFile in the format understood by the host. // Note: the resulting object keeps a copy of Buffer so it can be discarded once Index: llvm/tools/llvm-exegesis/lib/Assembler.cpp =================================================================== --- llvm/tools/llvm-exegesis/lib/Assembler.cpp +++ llvm/tools/llvm-exegesis/lib/Assembler.cpp @@ -9,6 +9,7 @@ #include "Assembler.h" #include "SnippetRepetitor.h" +#include "SubprocessMemory.h" #include "Target.h" #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/CodeGen/FunctionLoweringInfo.h" @@ -28,6 +29,10 @@ #include "llvm/Support/Alignment.h" #include "llvm/Support/MemoryBuffer.h" +#ifdef __linux__ +#include "perfmon/perf_event.h" +#endif + namespace llvm { namespace exegesis { @@ -39,8 +44,23 @@ // all registers could be setup correctly. static bool generateSnippetSetupCode( const ExegesisTarget &ET, const MCSubtargetInfo *const MSI, - ArrayRef RegisterInitialValues, BasicBlockFiller &BBF) { + ArrayRef RegisterInitialValues, BasicBlockFiller &BBF, + const BenchmarkKey &Key, bool GenerateMemoryInstructions) { bool IsSnippetSetupComplete = true; + if (GenerateMemoryInstructions) { + BBF.addInstructions(ET.moveArgumentRegisters()); + BBF.addInstructions(ET.generateLowerMunmap()); + BBF.addInstructions(ET.generateUpperMunmap()); + BBF.addInstructions(ET.generateMmapAuxMem()); + for (const MemoryMapping &MM : Key.MemoryMappings) { + BBF.addInstructions(ET.generateMmap( + MM.Address, Key.MemoryValues.at(MM.MemoryValueName).Size, + ET.getAuxiliaryMemoryStartAddress() + + sizeof(int) * (Key.MemoryValues.at(MM.MemoryValueName).Number + + AuxiliaryMemoryOffset))); + } + BBF.addInstructions(ET.setStackRegisterToAuxMem()); + } for (const RegisterValue &RV : RegisterInitialValues) { // Load a constant in the register. const auto SetRegisterCode = ET.setRegTo(*MSI, RV.Register, RV.Value); @@ -48,6 +68,11 @@ IsSnippetSetupComplete = false; BBF.addInstructions(SetRegisterCode); } + if (GenerateMemoryInstructions) { +#ifdef __linux__ + BBF.addInstructions(ET.configurePerfCounter(PERF_EVENT_IOC_RESET)); +#endif // __linux__ + } return IsSnippetSetupComplete; } @@ -122,7 +147,13 @@ addInstruction(Inst, DL); } -void BasicBlockFiller::addReturn(const DebugLoc &DL) { +void BasicBlockFiller::addReturn(const ExegesisTarget &ET, + bool SubprocessCleanup, const DebugLoc &DL) { + // Insert cleanup code + if (SubprocessCleanup) { + addInstructions(ET.configurePerfCounter(PERF_EVENT_IOC_DISABLE)); + addInstructions(ET.generateExitSyscall(0)); + } // Insert the return code. const TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo(); if (TII->getReturnOpcode() < TII->getNumOpcodes()) { @@ -176,7 +207,9 @@ std::unique_ptr TM, ArrayRef LiveIns, ArrayRef RegisterInitialValues, - const FillFunction &Fill, raw_pwrite_stream &AsmStream) { + const FillFunction &Fill, raw_pwrite_stream &AsmStream, + const BenchmarkKey &Key, + bool GenerateMemoryInstructions) { auto Context = std::make_unique(); std::unique_ptr Module = createModule(Context, TM->createDataLayout()); @@ -192,8 +225,14 @@ Properties.reset(MachineFunctionProperties::Property::IsSSA); Properties.set(MachineFunctionProperties::Property::NoPHIs); - for (const unsigned Reg : LiveIns) - MF.getRegInfo().addLiveIn(Reg); + if (GenerateMemoryInstructions) { + for (const unsigned Reg : ET.getArgumentRegisters()) { + MF.getRegInfo().addLiveIn(Reg); + } + } else { + for (const unsigned Reg : LiveIns) + MF.getRegInfo().addLiveIn(Reg); + } std::vector RegistersSetUp; for (const auto &InitValue : RegisterInitialValues) { @@ -201,11 +240,18 @@ } FunctionFiller Sink(MF, std::move(RegistersSetUp)); auto Entry = Sink.getEntry(); - for (const unsigned Reg : LiveIns) - Entry.MBB->addLiveIn(Reg); + if (GenerateMemoryInstructions) { + for (const unsigned Reg : ET.getArgumentRegisters()) + Entry.MBB->addLiveIn(Reg); + } else { + for (const unsigned Reg : LiveIns) { + Entry.MBB->addLiveIn(Reg); + } + } const bool IsSnippetSetupComplete = generateSnippetSetupCode( - ET, TM->getMCSubtargetInfo(), RegisterInitialValues, Entry); + ET, TM->getMCSubtargetInfo(), RegisterInitialValues, Entry, Key, + GenerateMemoryInstructions); // If the snippet setup is not complete, we disable liveliness tracking. This // means that we won't know what values are in the registers. Index: llvm/tools/llvm-exegesis/lib/BenchmarkResult.h =================================================================== --- llvm/tools/llvm-exegesis/lib/BenchmarkResult.h +++ llvm/tools/llvm-exegesis/lib/BenchmarkResult.h @@ -59,6 +59,11 @@ std::vector Instructions; // The initial values of the registers. std::vector RegisterInitialValues; + // The memory values that can be mapped into the execution context of the + // snippet. + std::unordered_map MemoryValues; + // The memory mappings that the snippet can access. + std::vector MemoryMappings; // An opaque configuration, that can be used to separate several benchmarks of // the same instruction under different configurations. std::string Config; Index: llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h =================================================================== --- llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h +++ llvm/tools/llvm-exegesis/lib/BenchmarkRunner.h @@ -113,10 +113,10 @@ virtual Expected> runMeasurements(const FunctionExecutor &Executor) const = 0; - Expected> assembleSnippet(const BenchmarkCode &BC, - const SnippetRepetitor &Repetitor, - unsigned MinInstructions, - unsigned LoopBodySize) const; + Expected> + assembleSnippet(const BenchmarkCode &BC, const SnippetRepetitor &Repetitor, + unsigned MinInstructions, unsigned LoopBodySize, + bool GenerateMemoryInstructions) const; Expected writeObjectFile(StringRef Buffer, StringRef FileName) const; Index: llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp =================================================================== --- llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp +++ llvm/tools/llvm-exegesis/lib/BenchmarkRunner.cpp @@ -15,6 +15,7 @@ #include "Error.h" #include "MCInstrDescView.h" #include "PerfHelper.h" +#include "SubprocessMemory.h" #include "Target.h" #include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/StringExtras.h" @@ -34,6 +35,12 @@ #include #include #include + +#ifdef __GLIBC__ +#if __GLIBC_MINOR__ >= 35 +#include +#endif // __GLIBC__MINOR > 35 +#endif // __GLIBC__ #endif // __linux__ namespace llvm { @@ -159,7 +166,11 @@ private: enum ChildProcessExitCodeE { CounterFDReadFailed = 1, - TranslatingCounterFDFailed + TranslatingCounterFDFailed, + RSeqDisableFailed, + FunctionDataMappingFailed, + AuxiliaryMemorySetupFailed + }; StringRef childProcessExitCodeToString(int ExitCode) const { @@ -169,6 +180,12 @@ case ChildProcessExitCodeE::TranslatingCounterFDFailed: return "Translating counter file descriptor into a file descriptor in " "the child process failed"; + case ChildProcessExitCodeE::RSeqDisableFailed: + return "Disabling restartable sequences failed"; + case ChildProcessExitCodeE::FunctionDataMappingFailed: + return "Failed to map memory for assembled snippet"; + case ChildProcessExitCodeE::AuxiliaryMemorySetupFailed: + return "Failed to setup auxiliary memory"; default: return "Child process returned with unknown exit code"; } @@ -184,6 +201,16 @@ "llvm-exegesis and the benchmarking subprocess"); } + SubprocessMemory SPMemory; + Error MemoryInitError = SPMemory.initializeSubprocessMemory(getpid()); + if (MemoryInitError) + return MemoryInitError; + + Error AddMemDefError = + SPMemory.addMemoryDefinition(Key.MemoryValues, getpid()); + if (AddMemDefError) + return AddMemDefError; + pid_t ParentOrChildPID = fork(); if (ParentOrChildPID == 0) { // We are in the child process, close the write end of the pipe @@ -276,9 +303,39 @@ exit(ChildProcessExitCodeE::TranslatingCounterFDFailed); } - ioctl(CounterFileDescriptor, PERF_EVENT_IOC_RESET); - this->Function(nullptr); - ioctl(CounterFileDescriptor, PERF_EVENT_IOC_DISABLE); +// Glibc versions greater than 2.35 automatically call rseq during +// initialization Unmapping the region that glibc sets up for this causes +// segfaults in the program Unregister the rseq region so that we can safely +// unmap it later +#ifdef __GLIBC__ +#if __GLIBC_MINOR__ >= 35 + long RseqDisableOutput = + syscall(SYS_rseq, (intptr_t)__builtin_thread_pointer() + __rseq_offset, + __rseq_size, RSEQ_FLAG_UNREGISTER, RSEQ_SIG); + if (RseqDisableOutput != 0) + exit(ChildProcessExitCodeE::RSeqDisableFailed); +#endif +#endif + + size_t FunctionDataCopySize = this->Function.FunctionBytes.size(); + char *FunctionDataCopy = + (char *)mmap(NULL, FunctionDataCopySize, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); + if ((intptr_t)FunctionDataCopy == -1) + exit(ChildProcessExitCodeE::FunctionDataMappingFailed); + + memcpy(FunctionDataCopy, this->Function.FunctionBytes.data(), + this->Function.FunctionBytes.size()); + mprotect(FunctionDataCopy, FunctionDataCopySize, PROT_READ | PROT_EXEC); + + Expected AuxMemFDOrError = + SubprocessMemory::setupAuxiliaryMemoryInSubprocess( + Key.MemoryValues, ParentPID, CounterFileDescriptor); + if (!AuxMemFDOrError) + exit(ChildProcessExitCodeE::AuxiliaryMemorySetupFailed); + + ((void (*)(size_t, int))(intptr_t)FunctionDataCopy)(FunctionDataCopySize, + *AuxMemFDOrError); exit(0); } @@ -305,14 +362,17 @@ Expected> BenchmarkRunner::assembleSnippet( const BenchmarkCode &BC, const SnippetRepetitor &Repetitor, - unsigned MinInstructions, unsigned LoopBodySize) const { + unsigned MinInstructions, unsigned LoopBodySize, + bool GenerateMemoryInstructions) const { const std::vector &Instructions = BC.Key.Instructions; SmallString<0> Buffer; raw_svector_ostream OS(Buffer); if (Error E = assembleToStream( State.getExegesisTarget(), State.createTargetMachine(), BC.LiveIns, BC.Key.RegisterInitialValues, - Repetitor.Repeat(Instructions, MinInstructions, LoopBodySize), OS)) { + Repetitor.Repeat(Instructions, MinInstructions, LoopBodySize, + GenerateMemoryInstructions), + OS, BC.Key, GenerateMemoryInstructions)) { return std::move(E); } return Buffer; @@ -334,6 +394,8 @@ const std::vector &Instructions = BC.Key.Instructions; + bool GenerateMemoryInstructions = ExecutionMode == ExecutionModeE::SubProcess; + InstrBenchmark.Key = BC.Key; // Assemble at least kMinInstructionsForSnippet instructions by repeating @@ -342,8 +404,9 @@ if (BenchmarkPhaseSelector > BenchmarkPhaseSelectorE::PrepareSnippet) { const int MinInstructionsForSnippet = 4 * Instructions.size(); const int LoopBodySizeForSnippet = 2 * Instructions.size(); - auto Snippet = assembleSnippet(BC, Repetitor, MinInstructionsForSnippet, - LoopBodySizeForSnippet); + auto Snippet = + assembleSnippet(BC, Repetitor, MinInstructionsForSnippet, + LoopBodySizeForSnippet, GenerateMemoryInstructions); if (Error E = Snippet.takeError()) return std::move(E); const ExecutableFunction EF(State.createTargetMachine(), @@ -354,9 +417,10 @@ // Assemble NumRepetitions instructions repetitions of the snippet for // measurements. - if (BenchmarkPhaseSelector > BenchmarkPhaseSelectorE::PrepareAndAssembleSnippet) { + if (BenchmarkPhaseSelector > + BenchmarkPhaseSelectorE::PrepareAndAssembleSnippet) { auto Snippet = assembleSnippet(BC, Repetitor, InstrBenchmark.NumRepetitions, - LoopBodySize); + LoopBodySize, GenerateMemoryInstructions); if (Error E = Snippet.takeError()) return std::move(E); RC.ObjectFile = getObjectFromBuffer(*Snippet); Index: llvm/tools/llvm-exegesis/lib/SnippetFile.h =================================================================== --- llvm/tools/llvm-exegesis/lib/SnippetFile.h +++ llvm/tools/llvm-exegesis/lib/SnippetFile.h @@ -17,6 +17,7 @@ #define LLVM_TOOLS_LLVM_EXEGESIS_SNIPPETFILE_H #include "BenchmarkCode.h" +#include "BenchmarkRunner.h" #include "LlvmState.h" #include "llvm/Support/Error.h" @@ -26,8 +27,9 @@ namespace exegesis { // Reads code snippets from file `Filename`. -Expected> readSnippets(const LLVMState &State, - StringRef Filename); +Expected> +readSnippets(const LLVMState &State, StringRef Filename, + BenchmarkRunner::ExecutionModeE ExecutionMode); } // namespace exegesis } // namespace llvm Index: llvm/tools/llvm-exegesis/lib/SnippetFile.cpp =================================================================== --- llvm/tools/llvm-exegesis/lib/SnippetFile.cpp +++ llvm/tools/llvm-exegesis/lib/SnippetFile.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "SnippetFile.h" +#include "BenchmarkRunner.h" #include "Error.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCInstPrinter.h" @@ -31,8 +32,9 @@ public: explicit BenchmarkCodeStreamer( MCContext *Context, const DenseMap &RegNameToRegNo, - BenchmarkCode *Result) - : MCStreamer(*Context), RegNameToRegNo(RegNameToRegNo), Result(Result) {} + BenchmarkCode *Result, BenchmarkRunner::ExecutionModeE ExecutionMode) + : MCStreamer(*Context), RegNameToRegNo(RegNameToRegNo), Result(Result), + ExecutionMode(ExecutionMode) {} // Implementation of the MCStreamer interface. We only care about // instructions. @@ -82,6 +84,66 @@ } return; } + if (CommentText.consume_front("MEM-DEF")) { + // LLVM-EXEGESIS-MEM-DEF + if (ExecutionMode != BenchmarkRunner::ExecutionModeE::SubProcess) { + errs() << "LLVM-EXEGESIS-MEM-DEF annotations are only valid when " + "benchmarking with the subprocess executor"; + ++InvalidComments; + return; + } + SmallVector Parts; + CommentText.split(Parts, ' ', -1, false); + if (Parts.size() != 3) { + errs() << "invalid comment 'LLVM-EXEGESIS-MEM-DEF " << CommentText + << "', expected three parameters "; + ++InvalidComments; + return; + } + const StringRef HexValue = Parts[2].trim(); + MemoryValue MemVal; + MemVal.Size = std::stol(Parts[1].trim().str()); + if (HexValue.size() % 2 != 0) { + errs() << "invalid comment 'LLVM-EXEGESIS-MEM-DEF " << CommentText + << "', expected to contain a whole number of bytes"; + } + MemVal.Value = APInt(HexValue.size() * 4, HexValue, 16); + MemVal.Number = Result->Key.MemoryValues.size(); + Result->Key.MemoryValues[Parts[0].trim().str()] = MemVal; + return; + } + if (CommentText.consume_front("MEM-MAP")) { + // LLVM-EXEGESIS-MEM-MAP
+ if (ExecutionMode != BenchmarkRunner::ExecutionModeE::SubProcess) { + errs() << "LLVM-EXEGESIS-MEM-MAP annotations are only valid when " + "benchmarking with the subprocess executor."; + ++InvalidComments; + return; + } + SmallVector Parts; + CommentText.split(Parts, ' ', -1, false); + if (Parts.size() != 2) { + errs() << "invalid comment 'LLVM-EXEGESIS-MEM-MAP " << CommentText + << "', expected two parameters
"; + ++InvalidComments; + return; + } + MemoryMapping MemMap; + MemMap.MemoryValueName = Parts[0].trim().str(); + MemMap.Address = std::stol(Parts[1].trim().str()); + // validate that the annotation refers to an already existing memory + // definition + auto MemValIT = Result->Key.MemoryValues.find(Parts[0].trim().str()); + if (MemValIT == Result->Key.MemoryValues.end()) { + errs() << "invalid comment 'LLVM-EXEGESIS-MEM-MAP " << CommentText + << "', expected to contain the name of an already " + "specified memory definition"; + ++InvalidComments; + return; + } + Result->Key.MemoryMappings.push_back(std::move(MemMap)); + return; + } } unsigned numInvalidComments() const { return InvalidComments; } @@ -110,13 +172,15 @@ const DenseMap &RegNameToRegNo; BenchmarkCode *const Result; unsigned InvalidComments = 0; + BenchmarkRunner::ExecutionModeE ExecutionMode; }; } // namespace // Reads code snippets from file `Filename`. -Expected> readSnippets(const LLVMState &State, - StringRef Filename) { +Expected> +readSnippets(const LLVMState &State, StringRef Filename, + BenchmarkRunner::ExecutionModeE ExecutionMode) { ErrorOr> BufferPtr = MemoryBuffer::getFileOrSTDIN(Filename); if (std::error_code EC = BufferPtr.getError()) { @@ -136,7 +200,7 @@ Context.setObjectFileInfo(ObjectFileInfo.get()); Context.initInlineSourceManager(); BenchmarkCodeStreamer Streamer(&Context, State.getRegNameToRegNoMapping(), - &Result); + &Result, ExecutionMode); std::string Error; raw_string_ostream ErrorStream(Error); Index: llvm/tools/llvm-exegesis/lib/SnippetRepetitor.h =================================================================== --- llvm/tools/llvm-exegesis/lib/SnippetRepetitor.h +++ llvm/tools/llvm-exegesis/lib/SnippetRepetitor.h @@ -39,8 +39,8 @@ // Returns a functor that repeats `Instructions` so that the function executes // at least `MinInstructions` instructions. virtual FillFunction Repeat(ArrayRef Instructions, - unsigned MinInstructions, - unsigned LoopBodySize) const = 0; + unsigned MinInstructions, unsigned LoopBodySize, + bool CleanupMemory) const = 0; explicit SnippetRepetitor(const LLVMState &State) : State(State) {} Index: llvm/tools/llvm-exegesis/lib/SnippetRepetitor.cpp =================================================================== --- llvm/tools/llvm-exegesis/lib/SnippetRepetitor.cpp +++ llvm/tools/llvm-exegesis/lib/SnippetRepetitor.cpp @@ -26,8 +26,10 @@ // Repeats the snippet until there are at least MinInstructions in the // resulting code. FillFunction Repeat(ArrayRef Instructions, unsigned MinInstructions, - unsigned LoopBodySize) const override { - return [Instructions, MinInstructions](FunctionFiller &Filler) { + unsigned LoopBodySize, + bool CleanupMemory) const override { + return [this, Instructions, MinInstructions, + CleanupMemory](FunctionFiller &Filler) { auto Entry = Filler.getEntry(); if (!Instructions.empty()) { // Add the whole snippet at least once. @@ -36,7 +38,7 @@ Entry.addInstruction(Instructions[I % Instructions.size()]); } } - Entry.addReturn(); + Entry.addReturn(State.getExegesisTarget(), CleanupMemory); }; } @@ -55,9 +57,10 @@ // Loop over the snippet ceil(MinInstructions / Instructions.Size()) times. FillFunction Repeat(ArrayRef Instructions, unsigned MinInstructions, - unsigned LoopBodySize) const override { - return [this, Instructions, MinInstructions, - LoopBodySize](FunctionFiller &Filler) { + unsigned LoopBodySize, + bool CleanupMemory) const override { + return [this, Instructions, MinInstructions, LoopBodySize, + CleanupMemory](FunctionFiller &Filler) { const auto &ET = State.getExegesisTarget(); auto Entry = Filler.getEntry(); @@ -67,7 +70,7 @@ const MCInstrDesc &MCID = Filler.MCII->get(Opcode); if (!MCID.isTerminator()) continue; - Entry.addReturn(); + Entry.addReturn(State.getExegesisTarget(), CleanupMemory); return; } @@ -112,7 +115,7 @@ // Set up the exit basic block. Loop.MBB->addSuccessor(Exit.MBB, BranchProbability::getZero()); - Exit.addReturn(); + Exit.addReturn(State.getExegesisTarget(), CleanupMemory); }; } Index: llvm/tools/llvm-exegesis/llvm-exegesis.cpp =================================================================== --- llvm/tools/llvm-exegesis/llvm-exegesis.cpp +++ llvm/tools/llvm-exegesis/llvm-exegesis.cpp @@ -520,7 +520,8 @@ std::back_inserter(Configurations)); } } else { - Configurations = ExitOnErr(readSnippets(State, SnippetsFile)); + Configurations = + ExitOnErr(readSnippets(State, SnippetsFile, ExecutionMode)); } if (NumRepetitions == 0) { Index: llvm/unittests/tools/llvm-exegesis/Common/AssemblerUtils.h =================================================================== --- llvm/unittests/tools/llvm-exegesis/Common/AssemblerUtils.h +++ llvm/unittests/tools/llvm-exegesis/Common/AssemblerUtils.h @@ -77,8 +77,11 @@ FillFunction Fill) { SmallString<256> Buffer; raw_svector_ostream AsmStream(Buffer); + BenchmarkKey Key; + Key.RegisterInitialValues = RegisterInitialValues; EXPECT_FALSE(assembleToStream(*ET, createTargetMachine(), /*LiveIns=*/{}, - RegisterInitialValues, Fill, AsmStream)); + RegisterInitialValues, Fill, AsmStream, Key, + false)); return ExecutableFunction(createTargetMachine(), getObjectFromBuffer(AsmStream.str())); } Index: llvm/unittests/tools/llvm-exegesis/X86/SnippetFileTest.cpp =================================================================== --- llvm/unittests/tools/llvm-exegesis/X86/SnippetFileTest.cpp +++ llvm/unittests/tools/llvm-exegesis/X86/SnippetFileTest.cpp @@ -34,12 +34,16 @@ using testing::Field; using testing::Property; using testing::SizeIs; +using testing::UnorderedElementsAre; using llvm::unittest::TempDir; class X86SnippetFileTest : public X86TestBase { protected: - Expected> TestCommon(StringRef Contents) { + Expected> + TestCommon(StringRef Contents, + BenchmarkRunner::ExecutionModeE ExecutionMode = + BenchmarkRunner::ExecutionModeE::InProcess) { TempDir TestDirectory("SnippetFileTestDir", /*Unique*/ true); SmallString<64> Filename(TestDirectory.path()); sys::path::append(Filename, "snippet.s"); @@ -50,7 +54,7 @@ FOS << Contents; EXPECT_FALSE(EC); } - return readSnippets(State, Filename); + return readSnippets(State, Filename, ExecutionMode); } }; @@ -69,6 +73,27 @@ return false; } +MATCHER_P3(MemoryDefinitionIs, Name, Value, Size, "") { + if (arg.second.Value.getLimitedValue() == static_cast(Value) && + arg.second.Size == static_cast(Size) && arg.first == Name) + return true; + *result_listener << "expected: {" << Name << ", " << Value << ", " << Size + << "} "; + *result_listener << "actual: {" << arg.first << ", " + << arg.second.Value.getLimitedValue() << ", " + << arg.second.Size << "}"; + return false; +} + +MATCHER_P2(MemoryMappingIs, Address, Name, "") { + if (arg.Address == Address && arg.MemoryValueName == Name) + return true; + *result_listener << "expected: {" << Address << ", " << Name << "} "; + *result_listener << "actual: {" << arg.Address << ", " << arg.MemoryValueName + << "}"; + return false; +} + TEST_F(X86SnippetFileTest, Works) { auto Snippets = TestCommon(R"( # LLVM-EXEGESIS-DEFREG RAX 0f @@ -124,6 +149,86 @@ EXPECT_FALSE((bool)Snippets.takeError()); } +TEST_F(X86SnippetFileTest, MemoryDefinitionTestSingleDef) { + auto Snippets = TestCommon(R"( + # LLVM-EXEGESIS-MEM-DEF test1 4096 ff + # LLVM-EXEGESIS-MEM-MAP test1 8192 + # LLVM-EXEGESIS-MEM-MAP test1 16384 + movq $8192, %r10 + movq (%r10), %r11 + )", + BenchmarkRunner::ExecutionModeE::SubProcess); + EXPECT_FALSE((bool)Snippets.takeError()); + ASSERT_THAT(*Snippets, SizeIs(1)); + const auto &Snippet = (*Snippets)[0]; + ASSERT_THAT(Snippet.Key.MemoryValues, + UnorderedElementsAre(MemoryDefinitionIs("test1", 255, 4096))); + ASSERT_THAT(Snippet.Key.MemoryMappings, + ElementsAre(MemoryMappingIs(8192, "test1"), + MemoryMappingIs(16384, "test1"))); +} + +TEST_F(X86SnippetFileTest, MemoryDefinitionsTestTwoDef) { + auto Snippets = TestCommon(R"( + # LLVM-EXEGESIS-MEM-DEF test1 4096 ff + # LLVM-EXEGESIS-MEM-DEF test2 4096 100 + # LLVM-EXEGESIS-MEM-MAP test1 8192 + # LLVM-EXEGESIS-MEM-MAP test2 16384 + movq $8192, %r10 + movq (%r10), %r11 + )", + BenchmarkRunner::ExecutionModeE::SubProcess); + EXPECT_FALSE((bool)Snippets.takeError()); + ASSERT_THAT(*Snippets, SizeIs(1)); + const auto &Snippet = (*Snippets)[0]; + ASSERT_THAT(Snippet.Key.MemoryValues, + UnorderedElementsAre(MemoryDefinitionIs("test1", 255, 4096), + MemoryDefinitionIs("test2", 256, 4096))); + ASSERT_THAT(Snippet.Key.MemoryMappings, + ElementsAre(MemoryMappingIs(8192, "test1"), + MemoryMappingIs(16384, "test2"))); +} + +TEST_F(X86SnippetFileTest, MemoryDefinitionMissingParameter) { + auto Error = TestCommon(R"( + # LLVM-EXEGESIS-MEM-DEF test1 4096 + )", + BenchmarkRunner::ExecutionModeE::SubProcess) + .takeError(); + EXPECT_TRUE((bool)Error); + consumeError(std::move(Error)); +} + +TEST_F(X86SnippetFileTest, MemoryMappingMissingParameters) { + auto Error = TestCommon(R"( + # LLVM-EXEGESIS-MEM-MAP test1 + )", + BenchmarkRunner::ExecutionModeE::SubProcess) + .takeError(); + EXPECT_TRUE((bool)Error); + consumeError(std::move(Error)); +} + +TEST_F(X86SnippetFileTest, MemoryMappingNoDefinition) { + auto Error = TestCommon(R"( + # LLVM-EXEGESIS-MEM-MAP test1 4096 + )", + BenchmarkRunner::ExecutionModeE::SubProcess) + .takeError(); + EXPECT_TRUE((bool)Error); + consumeError(std::move(Error)); +} + +TEST_F(X86SnippetFileTest, IncompatibleExecutorMode) { + auto Error = TestCommon(R"( + # LLVM-EXEGESIS-MEM-MAP test1 4096 + )", + BenchmarkRunner::ExecutionModeE::InProcess) + .takeError(); + EXPECT_TRUE((bool)Error); + consumeError(std::move(Error)); +} + } // namespace } // namespace exegesis } // namespace llvm Index: llvm/unittests/tools/llvm-exegesis/X86/SnippetRepetitorTest.cpp =================================================================== --- llvm/unittests/tools/llvm-exegesis/X86/SnippetRepetitorTest.cpp +++ llvm/unittests/tools/llvm-exegesis/X86/SnippetRepetitorTest.cpp @@ -43,7 +43,7 @@ const std::vector Instructions = {MCInstBuilder(X86::NOOP)}; FunctionFiller Sink(*MF, {X86::EAX}); const auto Fill = - Repetitor->Repeat(Instructions, kMinInstructions, kLoopBodySize); + Repetitor->Repeat(Instructions, kMinInstructions, kLoopBodySize, false); Fill(Sink); }