diff --git a/llvm/test/tools/llvm-exegesis/X86/memory-annotations-unsupported.test b/llvm/test/tools/llvm-exegesis/X86/memory-annotations-unsupported.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-exegesis/X86/memory-annotations-unsupported.test @@ -0,0 +1,7 @@ +# RUN: not llvm-exegesis -snippets-file=%s -mode=latency 2>&1 | FileCheck %s + +# CHECK: llvm-exegesis error: Memory annotations are only supported in subprocess execution mode + +# LLVM-EXEGESIS-MEM-DEF test1 4096 ff + +movq $0, %rax 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 @@ -64,6 +64,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" 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" @@ -82,6 +83,54 @@ } return; } + if (CommentText.consume_front("MEM-DEF")) { + // LLVM-EXEGESIS-MEM-DEF + 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.SizeBytes = 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.Index = Result->Key.MemoryValues.size(); + Result->Key.MemoryValues[Parts[0].trim().str()] = MemVal; + return; + } + if (CommentText.consume_front("MEM-MAP")) { + // LLVM-EXEGESIS-MEM-MAP
+ 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; } 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 @@ -15,6 +15,7 @@ #include "lib/BenchmarkResult.h" #include "lib/BenchmarkRunner.h" #include "lib/Clustering.h" +#include "lib/CodeTemplate.h" #include "lib/Error.h" #include "lib/LlvmState.h" #include "lib/PerfHelper.h" @@ -521,6 +522,13 @@ } } else { Configurations = ExitOnErr(readSnippets(State, SnippetsFile)); + for (const auto &Configuration : Configurations) { + if (ExecutionMode != BenchmarkRunner::ExecutionModeE::SubProcess && + (Configuration.Key.MemoryMappings.size() != 0 || + Configuration.Key.MemoryValues.size() != 0)) + ExitWithError("Memory annotations are only supported in subprocess " + "execution mode"); + } } 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,6 +34,7 @@ using testing::Field; using testing::Property; using testing::SizeIs; +using testing::UnorderedElementsAre; using llvm::unittest::TempDir; @@ -69,6 +70,27 @@ return false; } +MATCHER_P3(MemoryDefinitionIs, Name, Value, Size, "") { + if (arg.second.Value.getLimitedValue() == static_cast(Value) && + arg.second.SizeBytes == 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.SizeBytes << "}"; + 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 +146,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 + )"); + 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 + )"); + 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 + )") + .takeError(); + EXPECT_TRUE((bool)Error); + consumeError(std::move(Error)); +} + +TEST_F(X86SnippetFileTest, MemoryMappingMissingParameters) { + auto Error = TestCommon(R"( + # LLVM-EXEGESIS-MEM-MAP test1 + )") + .takeError(); + EXPECT_TRUE((bool)Error); + consumeError(std::move(Error)); +} + +TEST_F(X86SnippetFileTest, MemoryMappingNoDefinition) { + auto Error = TestCommon(R"( + # LLVM-EXEGESIS-MEM-MAP test1 4096 + )") + .takeError(); + EXPECT_TRUE((bool)Error); + consumeError(std::move(Error)); +} + +TEST_F(X86SnippetFileTest, IncompatibleExecutorMode) { + auto Error = TestCommon(R"( + # LLVM-EXEGESIS-MEM-MAP test1 4096 + )") + .takeError(); + EXPECT_TRUE((bool)Error); + consumeError(std::move(Error)); +} + } // namespace } // namespace exegesis } // namespace llvm