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 @@ -43,6 +43,17 @@ enum class BenchmarkFilter { All, RegOnly, WithMem }; +struct MemoryValue { + APInt Value; + size_t Size; + size_t Number; +}; + +struct MemoryMapping { + intptr_t Address; + std::string MemoryValueName; +}; + struct BenchmarkKey { // The LLVM opcode name. std::vector Instructions; diff --git a/llvm/tools/llvm-exegesis/lib/CMakeLists.txt b/llvm/tools/llvm-exegesis/lib/CMakeLists.txt --- a/llvm/tools/llvm-exegesis/lib/CMakeLists.txt +++ b/llvm/tools/llvm-exegesis/lib/CMakeLists.txt @@ -66,6 +66,7 @@ SnippetFile.cpp SnippetGenerator.cpp SnippetRepetitor.cpp + SubprocessMemory.cpp Target.cpp UopsBenchmarkRunner.cpp diff --git a/llvm/tools/llvm-exegesis/lib/SubprocessMemory.h b/llvm/tools/llvm-exegesis/lib/SubprocessMemory.h new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-exegesis/lib/SubprocessMemory.h @@ -0,0 +1,47 @@ +//===-- 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 +#include +#include + +namespace llvm { +namespace exegesis { + +static constexpr const size_t AuxiliaryMemoryOffset = 1; +static constexpr const size_t AuxiliaryMemorySize = 4096; + +class SubprocessMemory { +public: + Error initializeSubprocessMemory(pid_t ProcessPID); + Error addMemoryDefinition( + std::unordered_map MemoryDefinitions, + pid_t ProcessPID); + static Expected setupAuxiliaryMemoryInSubprocess( + std::unordered_map MemoryDefinitions, + pid_t ParentPID, int CounterFileDescriptor); + ~SubprocessMemory(); + +private: + std::vector SharedMemoryNames; +}; + +} // namespace exegesis +} //namespace llvm + +#endif diff --git a/llvm/tools/llvm-exegesis/lib/SubprocessMemory.cpp b/llvm/tools/llvm-exegesis/lib/SubprocessMemory.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-exegesis/lib/SubprocessMemory.cpp @@ -0,0 +1,139 @@ +//===-- 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" + +#ifdef __linux__ +#include +#include +#include +#endif + +// TODO(boomanaiden154): Add unit tests for this file + +namespace llvm { +namespace exegesis { + +#ifdef __linux__ +Error SubprocessMemory::initializeSubprocessMemory(pid_t ProcessPID) { + // 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(ProcessPID); + int AuxiliaryMemoryFD = shm_open(AuxiliaryMemoryName.c_str(), + O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (AuxiliaryMemoryFD == -1) + return make_error( + "Failed to create shared memory object for auxiliary memory: " + + Twine(strerror(errno))); + if (ftruncate(AuxiliaryMemoryFD, AuxiliaryMemorySize) != 0) { + return make_error("Truncating the auxiliary memory failed: " + + Twine(strerror(errno))); + } + return Error::success(); +} + +Error SubprocessMemory::addMemoryDefinition( + std::unordered_map 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.Number); + SharedMemoryNames.push_back(SharedMemoryName); + int SharedMemoryFD = + shm_open(SharedMemoryName.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (ftruncate(SharedMemoryFD, MemVal.Size) != 0) { + return make_error("Truncating a memory definiton failed: " + + Twine(strerror(errno))); + } + + char *SharedMemoryMapping = + (char *)mmap(NULL, MemVal.Size, 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.Size - ValueWidthBytes) { + memcpy(SharedMemoryMapping + CurrentByte, MemVal.Value.getRawData(), + ValueWidthBytes); + CurrentByte += ValueWidthBytes; + } + // fill the last section + memcpy(SharedMemoryMapping + CurrentByte, MemVal.Value.getRawData(), + MemVal.Size - CurrentByte); + if (munmap(SharedMemoryMapping, MemVal.Size) != 0) { + return make_error( + "Unmappng a memory definition in the parent failed: " + + Twine(strerror(errno))); + } + } + return Error::success(); +} + +Expected SubprocessMemory::setupAuxiliaryMemoryInSubprocess( + std::unordered_map 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( + "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("Mapping auxiliary memory failed"); + AuxiliaryMemoryMapping[0] = CounterFileDescriptor; + for (auto &[Name, MemVal] : MemoryDefinitions) { + std::string MemoryValueName = "/" + std::to_string(ParentPID) + "memdef" + + std::to_string(MemVal.Number); + AuxiliaryMemoryMapping[AuxiliaryMemoryOffset + MemVal.Number] = + shm_open(MemoryValueName.c_str(), O_RDWR, S_IRUSR | S_IWUSR); + if (AuxiliaryMemoryMapping[AuxiliaryMemoryOffset + MemVal.Number] == -1) + return make_error("Mapping shared memory failed"); + } + if (munmap(AuxiliaryMemoryMapping, 4096) == -1) + return make_error("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) { + report_fatal_error("initializeSubprocessMemory is only supported on Linux"); +} + +Error SubprocessMemory::addMemoryDefinition( + std::unordered_map MemoryDefinitions, + pid_t ProcessPID) { + report_fatal_error("addMemoryDefinitions is only supported on Linux"); +} + +Expected SubprocessMemory::setupAuxiliaryMemoryInSubprocess( + std::unordered_map MemoryDefinitions, + pid_t ParentPID, int CounterFileDescriptor) { + report_fatal_error( + "setupAuxiliaryMemoryInSubprocess is only supported on Linux"); +} +#endif + +} // namespace exegesis +} // namespace llvm