LLVM  15.0.0git
MemoryMapper.cpp
Go to the documentation of this file.
1 //===- MemoryMapper.cpp - Cross-process memory mapper ------------*- 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 
10 
13 
14 #if defined(LLVM_ON_UNIX)
15 #include <fcntl.h>
16 #include <sys/mman.h>
17 #include <unistd.h>
18 #elif defined(_WIN32)
19 #include <windows.h>
20 #endif
21 
22 namespace llvm {
23 namespace orc {
24 
26 
27 void InProcessMemoryMapper::reserve(size_t NumBytes,
28  OnReservedFunction OnReserved) {
29  std::error_code EC;
31  NumBytes, nullptr, sys::Memory::MF_READ | sys::Memory::MF_WRITE, EC);
32 
33  if (EC)
34  return OnReserved(errorCodeToError(EC));
35 
36  {
37  std::lock_guard<std::mutex> Lock(Mutex);
38  Reservations[MB.base()].Size = MB.allocatedSize();
39  }
40 
41  OnReserved(
42  ExecutorAddrRange(ExecutorAddr::fromPtr(MB.base()), MB.allocatedSize()));
43 }
44 
45 char *InProcessMemoryMapper::prepare(ExecutorAddr Addr, size_t ContentSize) {
46  return Addr.toPtr<char *>();
47 }
48 
50  OnInitializedFunction OnInitialized) {
51  ExecutorAddr MinAddr(~0ULL);
52 
53  for (auto &Segment : AI.Segments) {
54  auto Base = AI.MappingBase + Segment.Offset;
55  auto Size = Segment.ContentSize + Segment.ZeroFillSize;
56 
57  if (Base < MinAddr)
58  MinAddr = Base;
59 
60  std::memset((Base + Segment.ContentSize).toPtr<void *>(), 0,
61  Segment.ZeroFillSize);
62 
63  if (auto EC = sys::Memory::protectMappedMemory({Base.toPtr<void *>(), Size},
64  Segment.Prot)) {
65  return OnInitialized(errorCodeToError(EC));
66  }
67  if (Segment.Prot & sys::Memory::MF_EXEC)
68  sys::Memory::InvalidateInstructionCache(Base.toPtr<void *>(), Size);
69  }
70 
71  auto DeinitializeActions = shared::runFinalizeActions(AI.Actions);
72  if (!DeinitializeActions)
73  return OnInitialized(DeinitializeActions.takeError());
74 
75  {
76  std::lock_guard<std::mutex> Lock(Mutex);
77  Allocations[MinAddr].DeinitializationActions =
78  std::move(*DeinitializeActions);
79  Reservations[AI.MappingBase.toPtr<void *>()].Allocations.push_back(MinAddr);
80  }
81 
82  OnInitialized(MinAddr);
83 }
84 
87  MemoryMapper::OnDeinitializedFunction OnDeinitialized) {
88  Error AllErr = Error::success();
89 
90  {
91  std::lock_guard<std::mutex> Lock(Mutex);
92 
93  for (auto Base : Bases) {
94 
96  Allocations[Base].DeinitializationActions)) {
97  AllErr = joinErrors(std::move(AllErr), std::move(Err));
98  }
99 
100  Allocations.erase(Base);
101  }
102  }
103 
104  OnDeinitialized(std::move(AllErr));
105 }
106 
108  OnReleasedFunction OnReleased) {
109  Error Err = Error::success();
110 
111  for (auto Base : Bases) {
112  std::vector<ExecutorAddr> AllocAddrs;
113  size_t Size;
114  {
115  std::lock_guard<std::mutex> Lock(Mutex);
116  auto &R = Reservations[Base.toPtr<void *>()];
117  Size = R.Size;
118  AllocAddrs.swap(R.Allocations);
119  }
120 
121  // deinitialize sub allocations
122  std::promise<MSVCPError> P;
123  auto F = P.get_future();
124  deinitialize(AllocAddrs, [&](Error Err) { P.set_value(std::move(Err)); });
125  if (Error E = F.get()) {
126  Err = joinErrors(std::move(Err), std::move(E));
127  }
128 
129  // free the memory
130  auto MB = sys::MemoryBlock(Base.toPtr<void *>(), Size);
131 
132  auto EC = sys::Memory::releaseMappedMemory(MB);
133  if (EC) {
134  Err = joinErrors(std::move(Err), errorCodeToError(EC));
135  }
136 
137  std::lock_guard<std::mutex> Lock(Mutex);
138  Reservations.erase(Base.toPtr<void *>());
139  }
140 
141  OnReleased(std::move(Err));
142 }
143 
145  std::vector<ExecutorAddr> ReservationAddrs;
146  {
147  std::lock_guard<std::mutex> Lock(Mutex);
148 
149  ReservationAddrs.reserve(Reservations.size());
150  for (const auto &R : Reservations) {
151  ReservationAddrs.push_back(ExecutorAddr::fromPtr(R.getFirst()));
152  }
153  }
154 
155  std::promise<MSVCPError> P;
156  auto F = P.get_future();
157  release(ReservationAddrs, [&](Error Err) { P.set_value(std::move(Err)); });
158  cantFail(F.get());
159 }
160 
161 // SharedMemoryMapper
162 
163 void SharedMemoryMapper::reserve(size_t NumBytes,
164  OnReservedFunction OnReserved) {
165 #if defined(LLVM_ON_UNIX) || defined(_WIN32)
166 
169  SAs.Reserve,
170  [this, NumBytes, OnReserved = std::move(OnReserved)](
171  Error SerializationErr,
173  if (SerializationErr) {
174  cantFail(Result.takeError());
175  return OnReserved(std::move(SerializationErr));
176  }
177 
178  if (!Result)
179  return OnReserved(Result.takeError());
180 
181  ExecutorAddr RemoteAddr;
182  std::string SharedMemoryName;
183  std::tie(RemoteAddr, SharedMemoryName) = std::move(*Result);
184 
185  void *LocalAddr = nullptr;
186 
187 #if defined(LLVM_ON_UNIX)
188 
189  int SharedMemoryFile = shm_open(SharedMemoryName.c_str(), O_RDWR, 0700);
190  if (SharedMemoryFile < 0) {
191  return OnReserved(errorCodeToError(
192  std::error_code(errno, std::generic_category())));
193  }
194 
195  // this prevents other processes from accessing it by name
196  shm_unlink(SharedMemoryName.c_str());
197 
198  LocalAddr = mmap(nullptr, NumBytes, PROT_READ | PROT_WRITE, MAP_SHARED,
199  SharedMemoryFile, 0);
200  if (LocalAddr == MAP_FAILED) {
201  return OnReserved(errorCodeToError(
202  std::error_code(errno, std::generic_category())));
203  }
204 
205  close(SharedMemoryFile);
206 
207 #elif defined(_WIN32)
208 
209  std::wstring WideSharedMemoryName(SharedMemoryName.begin(),
210  SharedMemoryName.end());
211  HANDLE SharedMemoryFile = OpenFileMappingW(
212  FILE_MAP_ALL_ACCESS, FALSE, WideSharedMemoryName.c_str());
213  if (!SharedMemoryFile)
214  return OnReserved(errorCodeToError(mapWindowsError(GetLastError())));
215 
216  LocalAddr =
217  MapViewOfFile(SharedMemoryFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
218  if (!LocalAddr) {
219  CloseHandle(SharedMemoryFile);
220  return OnReserved(errorCodeToError(mapWindowsError(GetLastError())));
221  }
222 
223  CloseHandle(SharedMemoryFile);
224 
225 #endif
226  {
227  std::lock_guard<std::mutex> Lock(Mutex);
228  Reservations.insert({RemoteAddr, {LocalAddr, NumBytes}});
229  }
230 
231  OnReserved(ExecutorAddrRange(RemoteAddr, NumBytes));
232  },
233  SAs.Instance, static_cast<uint64_t>(NumBytes));
234 
235 #else
236  OnReserved(make_error<StringError>(
237  "SharedMemoryMapper is not supported on this platform yet",
239 #endif
240 }
241 
242 char *SharedMemoryMapper::prepare(ExecutorAddr Addr, size_t ContentSize) {
243  auto R = Reservations.upper_bound(Addr);
244  assert(R != Reservations.begin() && "Attempt to prepare unknown range");
245  R--;
246 
247  ExecutorAddrDiff Offset = Addr - R->first;
248 
249  return static_cast<char *>(R->second.LocalAddr) + Offset;
250 }
251 
253  OnInitializedFunction OnInitialized) {
254  auto Reservation = Reservations.find(AI.MappingBase);
255  assert(Reservation != Reservations.end() &&
256  "Attempt to initialize unreserved range");
257 
259 
260  AI.Actions.swap(FR.Actions);
261 
262  FR.Segments.reserve(AI.Segments.size());
263 
264  for (auto Segment : AI.Segments) {
265  char *Base =
266  static_cast<char *>(Reservation->second.LocalAddr) + Segment.Offset;
267  std::memset(Base + Segment.ContentSize, 0, Segment.ZeroFillSize);
268 
271  static_cast<sys::Memory::ProtectionFlags>(Segment.Prot));
272  SegReq.Addr = AI.MappingBase + Segment.Offset;
273  SegReq.Size = Segment.ContentSize + Segment.ZeroFillSize;
274 
275  FR.Segments.push_back(SegReq);
276  }
277 
280  SAs.Initialize,
281  [OnInitialized = std::move(OnInitialized)](
282  Error SerializationErr, Expected<ExecutorAddr> Result) mutable {
283  if (SerializationErr) {
284  cantFail(Result.takeError());
285  return OnInitialized(std::move(SerializationErr));
286  }
287 
288  OnInitialized(std::move(Result));
289  },
290  SAs.Instance, AI.MappingBase, std::move(FR));
291 }
292 
294  ArrayRef<ExecutorAddr> Allocations,
295  MemoryMapper::OnDeinitializedFunction OnDeinitialized) {
298  SAs.Deinitialize,
299  [OnDeinitialized = std::move(OnDeinitialized)](Error SerializationErr,
300  Error Result) mutable {
301  if (SerializationErr) {
302  cantFail(std::move(Result));
303  return OnDeinitialized(std::move(SerializationErr));
304  }
305 
306  OnDeinitialized(std::move(Result));
307  },
308  SAs.Instance, Allocations);
309 }
310 
312  OnReleasedFunction OnReleased) {
313 #if defined(LLVM_ON_UNIX) || defined(_WIN32)
314  Error Err = Error::success();
315 
316  {
317  std::lock_guard<std::mutex> Lock(Mutex);
318 
319  for (auto Base : Bases) {
320 
321 #if defined(LLVM_ON_UNIX)
322 
323  if (munmap(Reservations[Base].LocalAddr, Reservations[Base].Size) != 0)
324  Err = joinErrors(std::move(Err), errorCodeToError(std::error_code(
325  errno, std::generic_category())));
326 
327 #elif defined(_WIN32)
328 
329  if (!UnmapViewOfFile(Reservations[Base].LocalAddr))
330  joinErrors(std::move(Err),
331  errorCodeToError(mapWindowsError(GetLastError())));
332 
333 #endif
334 
335  Reservations.erase(Base);
336  }
337  }
338 
341  SAs.Release,
342  [OnReleased = std::move(OnReleased),
343  Err = std::move(Err)](Error SerializationErr, Error Result) mutable {
344  if (SerializationErr) {
345  cantFail(std::move(Result));
346  return OnReleased(
347  joinErrors(std::move(Err), std::move(SerializationErr)));
348  }
349 
350  return OnReleased(joinErrors(std::move(Err), std::move(Result)));
351  },
352  SAs.Instance, Bases);
353 #else
354  OnReleased(make_error<StringError>(
355  "SharedMemoryMapper is not supported on this platform yet",
357 #endif
358 }
359 
361  std::vector<ExecutorAddr> ReservationAddrs;
362  if (!Reservations.empty()) {
363  std::lock_guard<std::mutex> Lock(Mutex);
364  {
365  ReservationAddrs.reserve(Reservations.size());
366  for (const auto &R : Reservations) {
367  ReservationAddrs.push_back(R.first);
368  }
369  }
370  }
371 
372  std::promise<MSVCPError> P;
373  auto F = P.get_future();
374  release(ReservationAddrs, [&](Error Err) { P.set_value(std::move(Err)); });
375  // FIXME: Release can actually fail. The error should be propagated.
376  // Meanwhile, a better option is to explicitly call release().
377  cantFail(F.get());
378 }
379 
380 } // namespace orc
381 
382 } // namespace llvm
llvm::orc::ExecutorAddr
Represents an address in the executor process.
Definition: ExecutorAddress.h:30
llvm::orc::tpctypes::toWireProtectionFlags
WireProtectionFlags toWireProtectionFlags(sys::Memory::ProtectionFlags PF)
Convert from sys::Memory::ProtectionFlags.
Definition: TargetProcessControlTypes.h:42
llvm::orc::InProcessMemoryMapper::release
void release(ArrayRef< ExecutorAddr > Reservations, OnReleasedFunction OnRelease) override
Release address space acquired through reserve()
Definition: MemoryMapper.cpp:107
llvm::sys::Memory::MF_READ
@ MF_READ
Definition: Memory.h:55
llvm
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:17
llvm::sys::Memory::MF_WRITE
@ MF_WRITE
Definition: Memory.h:56
llvm::orc::SharedMemoryMapper::SymbolAddrs::Deinitialize
ExecutorAddr Deinitialize
Definition: MemoryMapper.h:118
llvm::unique_function
unique_function is a type-erasing functor similar to std::function.
Definition: FunctionExtras.h:56
llvm::orc::SharedMemoryMapper::SymbolAddrs::Instance
ExecutorAddr Instance
Definition: MemoryMapper.h:115
MemoryMapper.h
P
This currently compiles esp xmm0 movsd esp eax eax esp ret We should use not the dag combiner This is because dagcombine2 needs to be able to see through the X86ISD::Wrapper which DAGCombine can t really do The code for turning x load into a single vector load is target independent and should be moved to the dag combiner The code for turning x load into a vector load can only handle a direct load from a global or a direct load from the stack It should be generalized to handle any load from P
Definition: README-SSE.txt:411
llvm::sys::Memory::InvalidateInstructionCache
static void InvalidateInstructionCache(const void *Addr, size_t Len)
InvalidateInstructionCache - Before the JIT can run a block of code that has been emitted it must inv...
llvm::orc::tpctypes::SharedMemoryFinalizeRequest
Definition: TargetProcessControlTypes.h:91
llvm::Error::success
static ErrorSuccess success()
Create a success value.
Definition: Error.h:329
llvm::orc::InProcessMemoryMapper::reserve
void reserve(size_t NumBytes, OnReservedFunction OnReserved) override
Reserves address space in executor process.
Definition: MemoryMapper.cpp:27
llvm::sys::Memory::allocateMappedMemory
static MemoryBlock allocateMappedMemory(size_t NumBytes, const MemoryBlock *const NearBlock, unsigned Flags, std::error_code &EC)
This method allocates a block of memory that is suitable for loading dynamically generated code (e....
llvm::sys::Memory::releaseMappedMemory
static std::error_code releaseMappedMemory(MemoryBlock &Block)
This method releases a block of memory that was allocated with the allocateMappedMemory method.
llvm::DenseMapBase::erase
bool erase(const KeyT &Val)
Definition: DenseMap.h:304
llvm::orc::MemoryMapper::~MemoryMapper
virtual ~MemoryMapper()
Definition: MemoryMapper.cpp:25
llvm::sys::MemoryBlock
This class encapsulates the notion of a memory block which has an address and a size.
Definition: Memory.h:31
llvm::orc::ExecutorAddr::toPtr
std::enable_if_t< std::is_pointer< T >::value, T > toPtr() const
Cast this ExecutorAddr to a pointer of the given type.
Definition: ExecutorAddress.h:47
llvm::orc::MemoryMapper::AllocInfo::MappingBase
ExecutorAddr MappingBase
Definition: MemoryMapper.h:37
llvm::orc::tpctypes::SharedMemorySegFinalizeRequest::Prot
WireProtectionFlags Prot
Definition: TargetProcessControlTypes.h:86
llvm::Expected
Tagged union holding either a T or a Error.
Definition: APFloat.h:41
F
#define F(x, y, z)
Definition: MD5.cpp:55
llvm::orc::tpctypes::SharedMemorySegFinalizeRequest
Definition: TargetProcessControlTypes.h:85
llvm::orc::SharedMemoryMapper::SymbolAddrs::Reserve
ExecutorAddr Reserve
Definition: MemoryMapper.h:116
llvm::orc::shared::runFinalizeActions
Expected< std::vector< WrapperFunctionCall > > runFinalizeActions(AllocActions &AAs)
Run finalize actions.
Definition: AllocationActions.cpp:16
llvm::orc::tpctypes::SharedMemorySegFinalizeRequest::Size
uint64_t Size
Definition: TargetProcessControlTypes.h:88
E
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
llvm::orc::rt::SPSExecutorSharedMemoryMapperServiceInitializeSignature
shared::SPSExpected< shared::SPSExecutorAddr >(shared::SPSExecutorAddr, shared::SPSExecutorAddr, shared::SPSSharedMemoryFinalizeRequest) SPSExecutorSharedMemoryMapperServiceInitializeSignature
Definition: OrcRTBridge.h:75
llvm::errorCodeToError
Error errorCodeToError(std::error_code EC)
Helper for converting an std::error_code to a Error.
Definition: Error.cpp:92
llvm::sys::Memory::ProtectionFlags
ProtectionFlags
Definition: Memory.h:54
llvm::mapWindowsError
std::error_code mapWindowsError(unsigned EV)
llvm::orc::ExecutorAddrRange
Represents an address range in the exceutor process.
Definition: ExecutorAddress.h:148
llvm::orc::ExecutorAddr::fromPtr
static ExecutorAddr fromPtr(T *Value)
Create an ExecutorAddr from the given pointer.
Definition: ExecutorAddress.h:39
llvm::sys::SmartMutex< false >
llvm::orc::SharedMemoryMapper::reserve
void reserve(size_t NumBytes, OnReservedFunction OnReserved) override
Reserves address space in executor process.
Definition: MemoryMapper.cpp:163
llvm::orc::SharedMemoryMapper::SymbolAddrs::Initialize
ExecutorAddr Initialize
Definition: MemoryMapper.h:117
llvm::sys::Memory::MF_EXEC
@ MF_EXEC
Definition: Memory.h:57
llvm::orc::rt::SPSExecutorSharedMemoryMapperServiceDeinitializeSignature
shared::SPSError(shared::SPSExecutorAddr, shared::SPSSequence< shared::SPSExecutorAddr >) SPSExecutorSharedMemoryMapperServiceDeinitializeSignature
Definition: OrcRTBridge.h:78
llvm::orc::rt::SPSExecutorSharedMemoryMapperServiceReserveSignature
shared::SPSExpected< shared::SPSTuple< shared::SPSExecutorAddr, shared::SPSString > >(shared::SPSExecutorAddr, uint64_t) SPSExecutorSharedMemoryMapperServiceReserveSignature
Definition: OrcRTBridge.h:71
llvm::orc::SharedMemoryMapper::SymbolAddrs::Release
ExecutorAddr Release
Definition: MemoryMapper.h:119
uint64_t
Addr
uint64_t Addr
Definition: ELFObjHandler.cpp:78
llvm::orc::tpctypes::SharedMemoryFinalizeRequest::Segments
std::vector< SharedMemorySegFinalizeRequest > Segments
Definition: TargetProcessControlTypes.h:92
move
compiles ldr LCPI1_0 ldr ldr mov lsr tst moveq r1 ldr LCPI1_1 and r0 bx lr It would be better to do something like to fold the shift into the conditional move
Definition: README.txt:546
llvm::orc::InProcessMemoryMapper::initialize
void initialize(AllocInfo &AI, OnInitializedFunction OnInitialized) override
Ensures executor memory is synchronized with working copy memory, sends functions to be called after ...
Definition: MemoryMapper.cpp:49
llvm::joinErrors
Error joinErrors(Error E1, Error E2)
Concatenate errors.
Definition: Error.h:426
llvm::orc::shared::runDeallocActions
Error runDeallocActions(ArrayRef< WrapperFunctionCall > DAs)
Run deallocation actions.
Definition: AllocationActions.cpp:33
assert
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
llvm::orc::SharedMemoryMapper::release
void release(ArrayRef< ExecutorAddr > Reservations, OnReleasedFunction OnRelease) override
Release address space acquired through reserve()
Definition: MemoryMapper.cpp:311
llvm::orc::SharedMemoryMapper::deinitialize
void deinitialize(ArrayRef< ExecutorAddr > Allocations, OnDeinitializedFunction OnDeInitialized) override
Runs previously specified deinitialization actions Executor addresses returned by initialize should b...
Definition: MemoryMapper.cpp:293
llvm::ArrayRef
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition: APInt.h:32
llvm::cantFail
void cantFail(Error Err, const char *Msg=nullptr)
Report a fatal error if Err is a failure value.
Definition: Error.h:745
llvm::orc::SharedMemoryMapper::initialize
void initialize(AllocInfo &AI, OnInitializedFunction OnInitialized) override
Ensures executor memory is synchronized with working copy memory, sends functions to be called after ...
Definition: MemoryMapper.cpp:252
llvm::orc::InProcessMemoryMapper::~InProcessMemoryMapper
~InProcessMemoryMapper() override
Definition: MemoryMapper.cpp:144
llvm::inconvertibleErrorCode
std::error_code inconvertibleErrorCode()
The value returned by this function can be returned from convertToErrorCode for Error values where no...
Definition: Error.cpp:79
llvm::Error
Lightweight error class with error context and mandatory checking.
Definition: Error.h:155
llvm::orc::tpctypes::SharedMemorySegFinalizeRequest::Addr
ExecutorAddr Addr
Definition: TargetProcessControlTypes.h:87
llvm::orc::InProcessMemoryMapper::prepare
char * prepare(ExecutorAddr Addr, size_t ContentSize) override
Provides working memory.
Definition: MemoryMapper.cpp:45
llvm::DenseMapBase::size
unsigned size() const
Definition: DenseMap.h:101
llvm::orc::MemoryMapper::AllocInfo::Actions
shared::AllocActions Actions
Definition: MemoryMapper.h:39
llvm::orc::SharedMemoryMapper::prepare
char * prepare(ExecutorAddr Addr, size_t ContentSize) override
Provides working memory.
Definition: MemoryMapper.cpp:242
llvm::sys::Memory::protectMappedMemory
static std::error_code protectMappedMemory(const MemoryBlock &Block, unsigned Flags)
This method sets the protection flags for a block of memory to the state specified by /p Flags.
llvm::orc::MemoryMapper::AllocInfo::Segments
std::vector< SegInfo > Segments
Definition: MemoryMapper.h:38
llvm::orc::ExecutorProcessControl::callSPSWrapperAsync
void callSPSWrapperAsync(RunPolicyT &&Runner, ExecutorAddr WrapperFnAddr, SendResultT &&SendResult, const ArgTs &...Args)
Run a wrapper function using SPS to serialize the arguments and deserialize the results.
Definition: ExecutorProcessControl.h:314
llvm::orc::tpctypes::SharedMemoryFinalizeRequest::Actions
shared::AllocActions Actions
Definition: TargetProcessControlTypes.h:93
OrcRTBridge.h
llvm::orc::MemoryMapper::AllocInfo
Represents a single allocation containing multiple segments and initialization and deinitialization a...
Definition: MemoryMapper.h:28
llvm::orc::InProcessMemoryMapper::deinitialize
void deinitialize(ArrayRef< ExecutorAddr > Allocations, OnDeinitializedFunction OnDeInitialized) override
Runs previously specified deinitialization actions Executor addresses returned by initialize should b...
Definition: MemoryMapper.cpp:85
llvm::orc::rt::SPSExecutorSharedMemoryMapperServiceReleaseSignature
shared::SPSError(shared::SPSExecutorAddr, shared::SPSSequence< shared::SPSExecutorAddr >) SPSExecutorSharedMemoryMapperServiceReleaseSignature
Definition: OrcRTBridge.h:80
llvm::orc::SharedMemoryMapper::~SharedMemoryMapper
~SharedMemoryMapper() override
Definition: MemoryMapper.cpp:360
llvm::sampleprof::Base
@ Base
Definition: Discriminator.h:58
WindowsError.h