diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h b/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h --- a/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h +++ b/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; diff --git a/llvm/tools/llvm-exegesis/lib/SnippetFile.h b/llvm/tools/llvm-exegesis/lib/SnippetFile.h --- a/llvm/tools/llvm-exegesis/lib/SnippetFile.h +++ b/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 diff --git a/llvm/tools/llvm-exegesis/lib/SnippetFile.cpp b/llvm/tools/llvm-exegesis/lib/SnippetFile.cpp --- a/llvm/tools/llvm-exegesis/lib/SnippetFile.cpp +++ b/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. @@ -81,6 +83,65 @@ ++InvalidComments; } 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; } } @@ -110,13 +171,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 +199,7 @@ Context.setObjectFileInfo(ObjectFileInfo.get()); Context.initInlineSourceManager(); BenchmarkCodeStreamer Streamer(&Context, State.getRegNameToRegNoMapping(), - &Result); + &Result, ExecutionMode); std::string Error; raw_string_ostream ErrorStream(Error); diff --git a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp --- a/llvm/tools/llvm-exegesis/llvm-exegesis.cpp +++ b/llvm/tools/llvm-exegesis/llvm-exegesis.cpp @@ -520,7 +520,7 @@ std::back_inserter(Configurations)); } } else { - Configurations = ExitOnErr(readSnippets(State, SnippetsFile)); + Configurations = ExitOnErr(readSnippets(State, SnippetsFile, ExecutionMode)); } if (NumRepetitions == 0) { diff --git a/llvm/unittests/tools/llvm-exegesis/X86/SnippetFileTest.cpp b/llvm/unittests/tools/llvm-exegesis/X86/SnippetFileTest.cpp --- a/llvm/unittests/tools/llvm-exegesis/X86/SnippetFileTest.cpp +++ b/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,80 @@ 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