[llvm] 5a63b2b - [llvm-exegesis] Introduce SubprocessMemory Utility Class
Aiden Grossman via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 26 11:22:13 PDT 2023
Author: Aiden Grossman
Date: 2023-06-26T18:22:04Z
New Revision: 5a63b2b3049e06d83c43c037aab36e9bc3e797cb
URL: https://github.com/llvm/llvm-project/commit/5a63b2b3049e06d83c43c037aab36e9bc3e797cb
DIFF: https://github.com/llvm/llvm-project/commit/5a63b2b3049e06d83c43c037aab36e9bc3e797cb.diff
LOG: [llvm-exegesis] Introduce SubprocessMemory Utility Class
This patch introduces the SubprocessMemory class to llvm-exegesis. This
class contains several utilities that are needed for managing memory to
set up an execution environment for memory annotations.
Reviewed By: courbet
Differential Revision: https://reviews.llvm.org/D151022
Added:
llvm/tools/llvm-exegesis/lib/SubprocessMemory.cpp
llvm/tools/llvm-exegesis/lib/SubprocessMemory.h
llvm/unittests/tools/llvm-exegesis/X86/SubprocessMemoryTest.cpp
Modified:
llvm/tools/llvm-exegesis/lib/BenchmarkResult.h
llvm/tools/llvm-exegesis/lib/CMakeLists.txt
llvm/unittests/tools/llvm-exegesis/X86/CMakeLists.txt
Removed:
################################################################################
diff --git a/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h b/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h
index 0ad18cee0e725..eb3b839ea09fb 100644
--- a/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h
+++ b/llvm/tools/llvm-exegesis/lib/BenchmarkResult.h
@@ -43,6 +43,22 @@ enum class BenchmarkPhaseSelectorE {
enum class BenchmarkFilter { All, RegOnly, WithMem };
+struct MemoryValue {
+ // The arbitrary bit width constant that defines the value.
+ APInt Value;
+ // The size of the value in bytes.
+ size_t SizeBytes;
+ // The index of the memory value.
+ size_t Index;
+};
+
+struct MemoryMapping {
+ // The address to place the mapping at.
+ intptr_t Address;
+ // The name of the value that should be mapped.
+ std::string MemoryValueName;
+};
+
struct BenchmarkKey {
// The LLVM opcode name.
std::vector<MCInst> Instructions;
diff --git a/llvm/tools/llvm-exegesis/lib/CMakeLists.txt b/llvm/tools/llvm-exegesis/lib/CMakeLists.txt
index 92894601d39f6..92135e6aaf673 100644
--- a/llvm/tools/llvm-exegesis/lib/CMakeLists.txt
+++ b/llvm/tools/llvm-exegesis/lib/CMakeLists.txt
@@ -42,6 +42,9 @@ set(libs)
if(LLVM_ENABLE_LIBPFM AND HAVE_LIBPFM)
list(APPEND libs pfm)
endif()
+if(HAVE_LIBRT)
+ list(APPEND libs rt)
+endif()
add_llvm_library(LLVMExegesis
DISABLE_LLVM_LINK_LLVM_DYLIB
@@ -66,6 +69,7 @@ add_llvm_library(LLVMExegesis
SnippetFile.cpp
SnippetGenerator.cpp
SnippetRepetitor.cpp
+ SubprocessMemory.cpp
Target.cpp
UopsBenchmarkRunner.cpp
diff --git a/llvm/tools/llvm-exegesis/lib/SubprocessMemory.cpp b/llvm/tools/llvm-exegesis/lib/SubprocessMemory.cpp
new file mode 100644
index 0000000000000..28dfa06283e89
--- /dev/null
+++ b/llvm/tools/llvm-exegesis/lib/SubprocessMemory.cpp
@@ -0,0 +1,144 @@
+//===-- SubprocessMemory.cpp ------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SubprocessMemory.h"
+#include "Error.h"
+#include "llvm/Support/Error.h"
+
+#ifdef __linux__
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#endif
+
+namespace llvm {
+namespace exegesis {
+
+#ifdef __linux__
+
+Error SubprocessMemory::initializeSubprocessMemory(pid_t ProcessID) {
+ // Add the PID to the shared memory name so that if we're running multiple
+ // processes at the same time, they won't interfere with each other.
+ // This comes up particularly often when running the exegesis tests with
+ // llvm-lit
+ std::string AuxiliaryMemoryName = "/auxmem" + std::to_string(ProcessID);
+ int AuxiliaryMemoryFD = shm_open(AuxiliaryMemoryName.c_str(),
+ O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+ if (AuxiliaryMemoryFD == -1)
+ return make_error<Failure>(
+ "Failed to create shared memory object for auxiliary memory: " +
+ Twine(strerror(errno)));
+ if (ftruncate(AuxiliaryMemoryFD, AuxiliaryMemorySize) != 0) {
+ return make_error<Failure>("Truncating the auxiliary memory failed: " +
+ Twine(strerror(errno)));
+ }
+ return Error::success();
+}
+
+Error SubprocessMemory::addMemoryDefinition(
+ std::unordered_map<std::string, MemoryValue> MemoryDefinitions,
+ pid_t ProcessPID) {
+ SharedMemoryNames.reserve(MemoryDefinitions.size());
+ for (auto &[Name, MemVal] : MemoryDefinitions) {
+ std::string SharedMemoryName = "/" + std::to_string(ProcessPID) + "memdef" +
+ std::to_string(MemVal.Index);
+ SharedMemoryNames.push_back(SharedMemoryName);
+ int SharedMemoryFD =
+ shm_open(SharedMemoryName.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+ if (ftruncate(SharedMemoryFD, MemVal.SizeBytes) != 0) {
+ return make_error<Failure>("Truncating a memory definiton failed: " +
+ Twine(strerror(errno)));
+ }
+
+ char *SharedMemoryMapping =
+ (char *)mmap(NULL, MemVal.SizeBytes, PROT_READ | PROT_WRITE, MAP_SHARED,
+ SharedMemoryFD, 0);
+ // fill the buffer with the specified value
+ size_t CurrentByte = 0;
+ const size_t ValueWidthBytes = MemVal.Value.getBitWidth() / 8;
+ while (CurrentByte < MemVal.SizeBytes - ValueWidthBytes) {
+ memcpy(SharedMemoryMapping + CurrentByte, MemVal.Value.getRawData(),
+ ValueWidthBytes);
+ CurrentByte += ValueWidthBytes;
+ }
+ // fill the last section
+ memcpy(SharedMemoryMapping + CurrentByte, MemVal.Value.getRawData(),
+ MemVal.SizeBytes - CurrentByte);
+ if (munmap(SharedMemoryMapping, MemVal.SizeBytes) != 0) {
+ return make_error<Failure>(
+ "Unmapping a memory definition in the parent failed: " +
+ Twine(strerror(errno)));
+ }
+ }
+ return Error::success();
+}
+
+Expected<int> SubprocessMemory::setupAuxiliaryMemoryInSubprocess(
+ std::unordered_map<std::string, MemoryValue> MemoryDefinitions,
+ pid_t ParentPID, int CounterFileDescriptor) {
+ std::string AuxiliaryMemoryName = "/auxmem" + std::to_string(ParentPID);
+ int AuxiliaryMemoryFileDescriptor =
+ shm_open(AuxiliaryMemoryName.c_str(), O_RDWR, S_IRUSR | S_IWUSR);
+ if (AuxiliaryMemoryFileDescriptor == -1)
+ return make_error<Failure>(
+ "Getting file descriptor for auxiliary memory failed");
+ // set up memory value file descriptors
+ int *AuxiliaryMemoryMapping =
+ (int *)mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED,
+ AuxiliaryMemoryFileDescriptor, 0);
+ if ((intptr_t)AuxiliaryMemoryMapping == -1)
+ return make_error<Failure>("Mapping auxiliary memory failed");
+ AuxiliaryMemoryMapping[0] = CounterFileDescriptor;
+ for (auto &[Name, MemVal] : MemoryDefinitions) {
+ std::string MemoryValueName = "/" + std::to_string(ParentPID) + "memdef" +
+ std::to_string(MemVal.Index);
+ AuxiliaryMemoryMapping[AuxiliaryMemoryOffset + MemVal.Index] =
+ shm_open(MemoryValueName.c_str(), O_RDWR, S_IRUSR | S_IWUSR);
+ if (AuxiliaryMemoryMapping[AuxiliaryMemoryOffset + MemVal.Index] == -1)
+ return make_error<Failure>("Mapping shared memory failed");
+ }
+ if (munmap(AuxiliaryMemoryMapping, 4096) == -1)
+ return make_error<Failure>("Unmapping auxiliary memory failed");
+ return AuxiliaryMemoryFileDescriptor;
+}
+
+SubprocessMemory::~SubprocessMemory() {
+ for (std::string SharedMemoryName : SharedMemoryNames) {
+ if (shm_unlink(SharedMemoryName.c_str()) != 0) {
+ errs() << "Failed to unlink shared memory section: " << strerror(errno)
+ << "\n";
+ }
+ }
+}
+
+#else
+
+Error SubprocessMemory::initializeSubprocessMemory(pid_t ProcessPID) {
+ return make_error<Failure>(
+ "initializeSubprocessMemory is only supported on Linux");
+}
+
+Error SubprocessMemory::addMemoryDefinition(
+ std::unordered_map<std::string, MemoryValue> MemoryDefinitions,
+ pid_t ProcessPID) {
+ return make_error<Failure>("addMemoryDefinitions is only supported on Linux");
+}
+
+Expected<int> SubprocessMemory::setupAuxiliaryMemoryInSubprocess(
+ std::unordered_map<std::string, MemoryValue> MemoryDefinitions,
+ pid_t ParentPID, int CounterFileDescriptor) {
+ return make_error<Failure>(
+ "setupAuxiliaryMemoryInSubprocess is only supported on Linux");
+}
+
+SubprocessMemory::~SubprocessMemory() {}
+
+#endif // __linux__
+
+} // namespace exegesis
+} // namespace llvm
diff --git a/llvm/tools/llvm-exegesis/lib/SubprocessMemory.h b/llvm/tools/llvm-exegesis/lib/SubprocessMemory.h
new file mode 100644
index 0000000000000..9b55bc40272b3
--- /dev/null
+++ b/llvm/tools/llvm-exegesis/lib/SubprocessMemory.h
@@ -0,0 +1,65 @@
+//===-- SubprocessMemory.h --------------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Defines a class that automatically handles auxiliary memory and the
+/// underlying shared memory backings for memory definitions
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_EXEGESIS_SUBPROCESSMEMORY_H
+#define LLVM_TOOLS_LLVM_EXEGESIS_SUBPROCESSMEMORY_H
+
+#include "BenchmarkResult.h"
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#ifndef __linux__
+typedef int pid_t;
+#endif // __linux__
+
+namespace llvm {
+namespace exegesis {
+
+class SubprocessMemory {
+public:
+ static constexpr const size_t AuxiliaryMemoryOffset = 1;
+ static constexpr const size_t AuxiliaryMemorySize = 4096;
+
+ Error initializeSubprocessMemory(pid_t ProcessID);
+
+ // The following function sets up memory definitions. It creates shared
+ // memory objects for the definitions and fills them with the specified
+ // values. Arguments: MemoryDefinitions - A map from memory value names to
+ // MemoryValues, ProcessID - The ID of the current process.
+ Error addMemoryDefinition(
+ std::unordered_map<std::string, MemoryValue> MemoryDefinitions,
+ pid_t ProcessID);
+
+ // The following function sets up the auxiliary memory by opening shared
+ // memory objects backing memory definitions and putting file descriptors
+ // into appropriate places. Arguments: MemoryDefinitions - A map from memory
+ // values names to Memoryvalues, ParentPID - The ID of the process that
+ // setup the memory definitions, CounterFileDescriptor - The file descriptor
+ // for the performance counter that will be placed in the auxiliary memory
+ // section.
+ static Expected<int> setupAuxiliaryMemoryInSubprocess(
+ std::unordered_map<std::string, MemoryValue> MemoryDefinitions,
+ pid_t ParentPID, int CounterFileDescriptor);
+
+ ~SubprocessMemory();
+
+private:
+ std::vector<std::string> SharedMemoryNames;
+};
+
+} // namespace exegesis
+} // namespace llvm
+
+#endif
diff --git a/llvm/unittests/tools/llvm-exegesis/X86/CMakeLists.txt b/llvm/unittests/tools/llvm-exegesis/X86/CMakeLists.txt
index 2ac3a1ae4dea4..dda63e37f29be 100644
--- a/llvm/unittests/tools/llvm-exegesis/X86/CMakeLists.txt
+++ b/llvm/unittests/tools/llvm-exegesis/X86/CMakeLists.txt
@@ -4,7 +4,7 @@ add_llvm_exegesis_unittest_includes(
${LLVM_MAIN_SRC_DIR}/tools/llvm-exegesis/lib
)
-add_llvm_exegesis_unittest_sources(
+set(LLVM_EXEGESIS_X86_UNITTEST_SOURCES
BenchmarkResultTest.cpp
RegisterAliasingTest.cpp
SchedClassResolutionTest.cpp
@@ -12,6 +12,19 @@ add_llvm_exegesis_unittest_sources(
SnippetGeneratorTest.cpp
SnippetRepetitorTest.cpp
TargetTest.cpp
+)
+
+# Only compile the subprocess memory unittests on x86_64 linux. They only
+# sense to run on POSIX systems due to the implementations use of the POSIX
+# shared memory APIs and there are currently test failures on certain platforms
+# (s390x/PPC) that need further investigation.
+# TODO(boomanaiden154): Investigate and fix test failures on PPC
+if(LLVM_HOST_TRIPLE MATCHES "^x86_64-.*-linux")
+ list(APPEND LLVM_EXEGESIS_X86_UNITTEST_SOURCES SubprocessMemoryTest.cpp)
+endif()
+
+add_llvm_exegesis_unittest_sources(
+ ${LLVM_EXEGESIS_X86_UNITTEST_SOURCES}
)
add_llvm_exegesis_unittest_link_components(
diff --git a/llvm/unittests/tools/llvm-exegesis/X86/SubprocessMemoryTest.cpp b/llvm/unittests/tools/llvm-exegesis/X86/SubprocessMemoryTest.cpp
new file mode 100644
index 0000000000000..a7ddd7596825c
--- /dev/null
+++ b/llvm/unittests/tools/llvm-exegesis/X86/SubprocessMemoryTest.cpp
@@ -0,0 +1,114 @@
+//===-- SubprocessMemoryTest.cpp --------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SubprocessMemory.h"
+
+#include "X86/TestBase.h"
+#include "gtest/gtest.h"
+#include <unordered_map>
+
+#ifdef __linux__
+#include <endian.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#endif // __linux__
+
+namespace llvm {
+namespace exegesis {
+
+#ifdef __linux__
+
+class SubprocessMemoryTest : public X86TestBase {
+protected:
+ void
+ testCommon(std::unordered_map<std::string, MemoryValue> MemoryDefinitions,
+ const int MainProcessPID) {
+ EXPECT_FALSE(SM.initializeSubprocessMemory(MainProcessPID));
+ EXPECT_FALSE(SM.addMemoryDefinition(MemoryDefinitions, MainProcessPID));
+ }
+
+ void checkSharedMemoryDefinition(const std::string &DefinitionName,
+ size_t DefinitionSize,
+ std::vector<uint8_t> ExpectedValue) {
+ int SharedMemoryFD =
+ shm_open(DefinitionName.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+ uint8_t *SharedMemoryMapping = (uint8_t *)mmap(
+ NULL, DefinitionSize, PROT_READ, MAP_SHARED, SharedMemoryFD, 0);
+ EXPECT_NE((intptr_t)SharedMemoryMapping, -1);
+ for (size_t I = 0; I < ExpectedValue.size(); ++I) {
+ EXPECT_EQ(SharedMemoryMapping[I], ExpectedValue[I]);
+ }
+ munmap(SharedMemoryMapping, DefinitionSize);
+ }
+
+ SubprocessMemory SM;
+};
+
+TEST_F(SubprocessMemoryTest, OneDefinition) {
+ testCommon({{"test1", {APInt(8, 0xff), 4096, 0}}}, 0);
+ checkSharedMemoryDefinition("/0memdef0", 4096, {0xff});
+}
+
+TEST_F(SubprocessMemoryTest, MultipleDefinitions) {
+ testCommon({{"test1", {APInt(8, 0xaa), 4096, 0}},
+ {"test2", {APInt(8, 0xbb), 4096, 1}},
+ {"test3", {APInt(8, 0xcc), 4096, 2}}},
+ 1);
+ checkSharedMemoryDefinition("/1memdef0", 4096, {0xaa});
+ checkSharedMemoryDefinition("/1memdef1", 4096, {0xbb});
+ checkSharedMemoryDefinition("/1memdef2", 4096, {0xcc});
+}
+
+TEST_F(SubprocessMemoryTest, DefinitionFillsCompletely) {
+ testCommon({{"test1", {APInt(8, 0xaa), 4096, 0}},
+ {"test2", {APInt(16, 0xbbbb), 4096, 1}},
+ {"test3", {APInt(24, 0xcccccc), 4096, 2}}},
+ 2);
+ std::vector<uint8_t> Test1Expected(512, 0xaa);
+ std::vector<uint8_t> Test2Expected(512, 0xbb);
+ std::vector<uint8_t> Test3Expected(512, 0xcc);
+ checkSharedMemoryDefinition("/2memdef0", 4096, Test1Expected);
+ checkSharedMemoryDefinition("/2memdef1", 4096, Test2Expected);
+ checkSharedMemoryDefinition("/2memdef2", 4096, Test3Expected);
+}
+
+// The test below only works on little endian systems.
+#ifdef __ORDER_LITTLE_ENDIAN__
+TEST_F(SubprocessMemoryTest, DefinitionEndTruncation) {
+ testCommon({{"test1", {APInt(48, 0xaabbccddeeff), 4096, 0}}}, 3);
+ std::vector<uint8_t> Test1Expected(512, 0);
+ // order is reversed since we're assuming a little endian system.
+ for (size_t I = 0; I < Test1Expected.size(); ++I) {
+ switch (I % 6) {
+ case 0:
+ Test1Expected[I] = 0xff;
+ break;
+ case 1:
+ Test1Expected[I] = 0xee;
+ break;
+ case 2:
+ Test1Expected[I] = 0xdd;
+ break;
+ case 3:
+ Test1Expected[I] = 0xcc;
+ break;
+ case 4:
+ Test1Expected[I] = 0xbb;
+ break;
+ case 5:
+ Test1Expected[I] = 0xaa;
+ }
+ }
+ checkSharedMemoryDefinition("/3memdef0", 4096, Test1Expected);
+}
+#endif // __ORDER_LITTLE_ENDIAN__
+
+#endif // __linux__
+
+} // namespace exegesis
+} // namespace llvm
More information about the llvm-commits
mailing list