Skip to content

Commit 5a63b2b

Browse files
[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
1 parent 9aaf3cf commit 5a63b2b

File tree

6 files changed

+357
-1
lines changed

6 files changed

+357
-1
lines changed

llvm/tools/llvm-exegesis/lib/BenchmarkResult.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,22 @@ enum class BenchmarkPhaseSelectorE {
4343

4444
enum class BenchmarkFilter { All, RegOnly, WithMem };
4545

46+
struct MemoryValue {
47+
// The arbitrary bit width constant that defines the value.
48+
APInt Value;
49+
// The size of the value in bytes.
50+
size_t SizeBytes;
51+
// The index of the memory value.
52+
size_t Index;
53+
};
54+
55+
struct MemoryMapping {
56+
// The address to place the mapping at.
57+
intptr_t Address;
58+
// The name of the value that should be mapped.
59+
std::string MemoryValueName;
60+
};
61+
4662
struct BenchmarkKey {
4763
// The LLVM opcode name.
4864
std::vector<MCInst> Instructions;

llvm/tools/llvm-exegesis/lib/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ set(libs)
4242
if(LLVM_ENABLE_LIBPFM AND HAVE_LIBPFM)
4343
list(APPEND libs pfm)
4444
endif()
45+
if(HAVE_LIBRT)
46+
list(APPEND libs rt)
47+
endif()
4548

4649
add_llvm_library(LLVMExegesis
4750
DISABLE_LLVM_LINK_LLVM_DYLIB
@@ -66,6 +69,7 @@ add_llvm_library(LLVMExegesis
6669
SnippetFile.cpp
6770
SnippetGenerator.cpp
6871
SnippetRepetitor.cpp
72+
SubprocessMemory.cpp
6973
Target.cpp
7074
UopsBenchmarkRunner.cpp
7175

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
//===-- SubprocessMemory.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-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "SubprocessMemory.h"
10+
#include "Error.h"
11+
#include "llvm/Support/Error.h"
12+
13+
#ifdef __linux__
14+
#include <fcntl.h>
15+
#include <sys/mman.h>
16+
#include <unistd.h>
17+
#endif
18+
19+
namespace llvm {
20+
namespace exegesis {
21+
22+
#ifdef __linux__
23+
24+
Error SubprocessMemory::initializeSubprocessMemory(pid_t ProcessID) {
25+
// Add the PID to the shared memory name so that if we're running multiple
26+
// processes at the same time, they won't interfere with each other.
27+
// This comes up particularly often when running the exegesis tests with
28+
// llvm-lit
29+
std::string AuxiliaryMemoryName = "/auxmem" + std::to_string(ProcessID);
30+
int AuxiliaryMemoryFD = shm_open(AuxiliaryMemoryName.c_str(),
31+
O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
32+
if (AuxiliaryMemoryFD == -1)
33+
return make_error<Failure>(
34+
"Failed to create shared memory object for auxiliary memory: " +
35+
Twine(strerror(errno)));
36+
if (ftruncate(AuxiliaryMemoryFD, AuxiliaryMemorySize) != 0) {
37+
return make_error<Failure>("Truncating the auxiliary memory failed: " +
38+
Twine(strerror(errno)));
39+
}
40+
return Error::success();
41+
}
42+
43+
Error SubprocessMemory::addMemoryDefinition(
44+
std::unordered_map<std::string, MemoryValue> MemoryDefinitions,
45+
pid_t ProcessPID) {
46+
SharedMemoryNames.reserve(MemoryDefinitions.size());
47+
for (auto &[Name, MemVal] : MemoryDefinitions) {
48+
std::string SharedMemoryName = "/" + std::to_string(ProcessPID) + "memdef" +
49+
std::to_string(MemVal.Index);
50+
SharedMemoryNames.push_back(SharedMemoryName);
51+
int SharedMemoryFD =
52+
shm_open(SharedMemoryName.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
53+
if (ftruncate(SharedMemoryFD, MemVal.SizeBytes) != 0) {
54+
return make_error<Failure>("Truncating a memory definiton failed: " +
55+
Twine(strerror(errno)));
56+
}
57+
58+
char *SharedMemoryMapping =
59+
(char *)mmap(NULL, MemVal.SizeBytes, PROT_READ | PROT_WRITE, MAP_SHARED,
60+
SharedMemoryFD, 0);
61+
// fill the buffer with the specified value
62+
size_t CurrentByte = 0;
63+
const size_t ValueWidthBytes = MemVal.Value.getBitWidth() / 8;
64+
while (CurrentByte < MemVal.SizeBytes - ValueWidthBytes) {
65+
memcpy(SharedMemoryMapping + CurrentByte, MemVal.Value.getRawData(),
66+
ValueWidthBytes);
67+
CurrentByte += ValueWidthBytes;
68+
}
69+
// fill the last section
70+
memcpy(SharedMemoryMapping + CurrentByte, MemVal.Value.getRawData(),
71+
MemVal.SizeBytes - CurrentByte);
72+
if (munmap(SharedMemoryMapping, MemVal.SizeBytes) != 0) {
73+
return make_error<Failure>(
74+
"Unmapping a memory definition in the parent failed: " +
75+
Twine(strerror(errno)));
76+
}
77+
}
78+
return Error::success();
79+
}
80+
81+
Expected<int> SubprocessMemory::setupAuxiliaryMemoryInSubprocess(
82+
std::unordered_map<std::string, MemoryValue> MemoryDefinitions,
83+
pid_t ParentPID, int CounterFileDescriptor) {
84+
std::string AuxiliaryMemoryName = "/auxmem" + std::to_string(ParentPID);
85+
int AuxiliaryMemoryFileDescriptor =
86+
shm_open(AuxiliaryMemoryName.c_str(), O_RDWR, S_IRUSR | S_IWUSR);
87+
if (AuxiliaryMemoryFileDescriptor == -1)
88+
return make_error<Failure>(
89+
"Getting file descriptor for auxiliary memory failed");
90+
// set up memory value file descriptors
91+
int *AuxiliaryMemoryMapping =
92+
(int *)mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED,
93+
AuxiliaryMemoryFileDescriptor, 0);
94+
if ((intptr_t)AuxiliaryMemoryMapping == -1)
95+
return make_error<Failure>("Mapping auxiliary memory failed");
96+
AuxiliaryMemoryMapping[0] = CounterFileDescriptor;
97+
for (auto &[Name, MemVal] : MemoryDefinitions) {
98+
std::string MemoryValueName = "/" + std::to_string(ParentPID) + "memdef" +
99+
std::to_string(MemVal.Index);
100+
AuxiliaryMemoryMapping[AuxiliaryMemoryOffset + MemVal.Index] =
101+
shm_open(MemoryValueName.c_str(), O_RDWR, S_IRUSR | S_IWUSR);
102+
if (AuxiliaryMemoryMapping[AuxiliaryMemoryOffset + MemVal.Index] == -1)
103+
return make_error<Failure>("Mapping shared memory failed");
104+
}
105+
if (munmap(AuxiliaryMemoryMapping, 4096) == -1)
106+
return make_error<Failure>("Unmapping auxiliary memory failed");
107+
return AuxiliaryMemoryFileDescriptor;
108+
}
109+
110+
SubprocessMemory::~SubprocessMemory() {
111+
for (std::string SharedMemoryName : SharedMemoryNames) {
112+
if (shm_unlink(SharedMemoryName.c_str()) != 0) {
113+
errs() << "Failed to unlink shared memory section: " << strerror(errno)
114+
<< "\n";
115+
}
116+
}
117+
}
118+
119+
#else
120+
121+
Error SubprocessMemory::initializeSubprocessMemory(pid_t ProcessPID) {
122+
return make_error<Failure>(
123+
"initializeSubprocessMemory is only supported on Linux");
124+
}
125+
126+
Error SubprocessMemory::addMemoryDefinition(
127+
std::unordered_map<std::string, MemoryValue> MemoryDefinitions,
128+
pid_t ProcessPID) {
129+
return make_error<Failure>("addMemoryDefinitions is only supported on Linux");
130+
}
131+
132+
Expected<int> SubprocessMemory::setupAuxiliaryMemoryInSubprocess(
133+
std::unordered_map<std::string, MemoryValue> MemoryDefinitions,
134+
pid_t ParentPID, int CounterFileDescriptor) {
135+
return make_error<Failure>(
136+
"setupAuxiliaryMemoryInSubprocess is only supported on Linux");
137+
}
138+
139+
SubprocessMemory::~SubprocessMemory() {}
140+
141+
#endif // __linux__
142+
143+
} // namespace exegesis
144+
} // namespace llvm
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//===-- SubprocessMemory.h --------------------------------------*- 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-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
///
9+
/// \file
10+
/// Defines a class that automatically handles auxiliary memory and the
11+
/// underlying shared memory backings for memory definitions
12+
///
13+
//===----------------------------------------------------------------------===//
14+
15+
#ifndef LLVM_TOOLS_LLVM_EXEGESIS_SUBPROCESSMEMORY_H
16+
#define LLVM_TOOLS_LLVM_EXEGESIS_SUBPROCESSMEMORY_H
17+
18+
#include "BenchmarkResult.h"
19+
#include <string>
20+
#include <unordered_map>
21+
#include <vector>
22+
23+
#ifndef __linux__
24+
typedef int pid_t;
25+
#endif // __linux__
26+
27+
namespace llvm {
28+
namespace exegesis {
29+
30+
class SubprocessMemory {
31+
public:
32+
static constexpr const size_t AuxiliaryMemoryOffset = 1;
33+
static constexpr const size_t AuxiliaryMemorySize = 4096;
34+
35+
Error initializeSubprocessMemory(pid_t ProcessID);
36+
37+
// The following function sets up memory definitions. It creates shared
38+
// memory objects for the definitions and fills them with the specified
39+
// values. Arguments: MemoryDefinitions - A map from memory value names to
40+
// MemoryValues, ProcessID - The ID of the current process.
41+
Error addMemoryDefinition(
42+
std::unordered_map<std::string, MemoryValue> MemoryDefinitions,
43+
pid_t ProcessID);
44+
45+
// The following function sets up the auxiliary memory by opening shared
46+
// memory objects backing memory definitions and putting file descriptors
47+
// into appropriate places. Arguments: MemoryDefinitions - A map from memory
48+
// values names to Memoryvalues, ParentPID - The ID of the process that
49+
// setup the memory definitions, CounterFileDescriptor - The file descriptor
50+
// for the performance counter that will be placed in the auxiliary memory
51+
// section.
52+
static Expected<int> setupAuxiliaryMemoryInSubprocess(
53+
std::unordered_map<std::string, MemoryValue> MemoryDefinitions,
54+
pid_t ParentPID, int CounterFileDescriptor);
55+
56+
~SubprocessMemory();
57+
58+
private:
59+
std::vector<std::string> SharedMemoryNames;
60+
};
61+
62+
} // namespace exegesis
63+
} // namespace llvm
64+
65+
#endif

llvm/unittests/tools/llvm-exegesis/X86/CMakeLists.txt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,27 @@ add_llvm_exegesis_unittest_includes(
44
${LLVM_MAIN_SRC_DIR}/tools/llvm-exegesis/lib
55
)
66

7-
add_llvm_exegesis_unittest_sources(
7+
set(LLVM_EXEGESIS_X86_UNITTEST_SOURCES
88
BenchmarkResultTest.cpp
99
RegisterAliasingTest.cpp
1010
SchedClassResolutionTest.cpp
1111
SnippetFileTest.cpp
1212
SnippetGeneratorTest.cpp
1313
SnippetRepetitorTest.cpp
1414
TargetTest.cpp
15+
)
16+
17+
# Only compile the subprocess memory unittests on x86_64 linux. They only
18+
# sense to run on POSIX systems due to the implementations use of the POSIX
19+
# shared memory APIs and there are currently test failures on certain platforms
20+
# (s390x/PPC) that need further investigation.
21+
# TODO(boomanaiden154): Investigate and fix test failures on PPC
22+
if(LLVM_HOST_TRIPLE MATCHES "^x86_64-.*-linux")
23+
list(APPEND LLVM_EXEGESIS_X86_UNITTEST_SOURCES SubprocessMemoryTest.cpp)
24+
endif()
25+
26+
add_llvm_exegesis_unittest_sources(
27+
${LLVM_EXEGESIS_X86_UNITTEST_SOURCES}
1528
)
1629

1730
add_llvm_exegesis_unittest_link_components(
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
//===-- SubprocessMemoryTest.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-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "SubprocessMemory.h"
10+
11+
#include "X86/TestBase.h"
12+
#include "gtest/gtest.h"
13+
#include <unordered_map>
14+
15+
#ifdef __linux__
16+
#include <endian.h>
17+
#include <fcntl.h>
18+
#include <sys/mman.h>
19+
#endif // __linux__
20+
21+
namespace llvm {
22+
namespace exegesis {
23+
24+
#ifdef __linux__
25+
26+
class SubprocessMemoryTest : public X86TestBase {
27+
protected:
28+
void
29+
testCommon(std::unordered_map<std::string, MemoryValue> MemoryDefinitions,
30+
const int MainProcessPID) {
31+
EXPECT_FALSE(SM.initializeSubprocessMemory(MainProcessPID));
32+
EXPECT_FALSE(SM.addMemoryDefinition(MemoryDefinitions, MainProcessPID));
33+
}
34+
35+
void checkSharedMemoryDefinition(const std::string &DefinitionName,
36+
size_t DefinitionSize,
37+
std::vector<uint8_t> ExpectedValue) {
38+
int SharedMemoryFD =
39+
shm_open(DefinitionName.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
40+
uint8_t *SharedMemoryMapping = (uint8_t *)mmap(
41+
NULL, DefinitionSize, PROT_READ, MAP_SHARED, SharedMemoryFD, 0);
42+
EXPECT_NE((intptr_t)SharedMemoryMapping, -1);
43+
for (size_t I = 0; I < ExpectedValue.size(); ++I) {
44+
EXPECT_EQ(SharedMemoryMapping[I], ExpectedValue[I]);
45+
}
46+
munmap(SharedMemoryMapping, DefinitionSize);
47+
}
48+
49+
SubprocessMemory SM;
50+
};
51+
52+
TEST_F(SubprocessMemoryTest, OneDefinition) {
53+
testCommon({{"test1", {APInt(8, 0xff), 4096, 0}}}, 0);
54+
checkSharedMemoryDefinition("/0memdef0", 4096, {0xff});
55+
}
56+
57+
TEST_F(SubprocessMemoryTest, MultipleDefinitions) {
58+
testCommon({{"test1", {APInt(8, 0xaa), 4096, 0}},
59+
{"test2", {APInt(8, 0xbb), 4096, 1}},
60+
{"test3", {APInt(8, 0xcc), 4096, 2}}},
61+
1);
62+
checkSharedMemoryDefinition("/1memdef0", 4096, {0xaa});
63+
checkSharedMemoryDefinition("/1memdef1", 4096, {0xbb});
64+
checkSharedMemoryDefinition("/1memdef2", 4096, {0xcc});
65+
}
66+
67+
TEST_F(SubprocessMemoryTest, DefinitionFillsCompletely) {
68+
testCommon({{"test1", {APInt(8, 0xaa), 4096, 0}},
69+
{"test2", {APInt(16, 0xbbbb), 4096, 1}},
70+
{"test3", {APInt(24, 0xcccccc), 4096, 2}}},
71+
2);
72+
std::vector<uint8_t> Test1Expected(512, 0xaa);
73+
std::vector<uint8_t> Test2Expected(512, 0xbb);
74+
std::vector<uint8_t> Test3Expected(512, 0xcc);
75+
checkSharedMemoryDefinition("/2memdef0", 4096, Test1Expected);
76+
checkSharedMemoryDefinition("/2memdef1", 4096, Test2Expected);
77+
checkSharedMemoryDefinition("/2memdef2", 4096, Test3Expected);
78+
}
79+
80+
// The test below only works on little endian systems.
81+
#ifdef __ORDER_LITTLE_ENDIAN__
82+
TEST_F(SubprocessMemoryTest, DefinitionEndTruncation) {
83+
testCommon({{"test1", {APInt(48, 0xaabbccddeeff), 4096, 0}}}, 3);
84+
std::vector<uint8_t> Test1Expected(512, 0);
85+
// order is reversed since we're assuming a little endian system.
86+
for (size_t I = 0; I < Test1Expected.size(); ++I) {
87+
switch (I % 6) {
88+
case 0:
89+
Test1Expected[I] = 0xff;
90+
break;
91+
case 1:
92+
Test1Expected[I] = 0xee;
93+
break;
94+
case 2:
95+
Test1Expected[I] = 0xdd;
96+
break;
97+
case 3:
98+
Test1Expected[I] = 0xcc;
99+
break;
100+
case 4:
101+
Test1Expected[I] = 0xbb;
102+
break;
103+
case 5:
104+
Test1Expected[I] = 0xaa;
105+
}
106+
}
107+
checkSharedMemoryDefinition("/3memdef0", 4096, Test1Expected);
108+
}
109+
#endif // __ORDER_LITTLE_ENDIAN__
110+
111+
#endif // __linux__
112+
113+
} // namespace exegesis
114+
} // namespace llvm

0 commit comments

Comments
 (0)