Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Implement an allocator for executable (JIT) memory in PAL #2205

Merged
merged 2 commits into from
Dec 3, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/inc/switches.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,17 @@

#elif defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
#define PAGE_SIZE 0x1000
#define USE_UPPER_ADDRESS 1
#define UPPER_ADDRESS_MAPPING_FACTOR 2
#define CLR_UPPER_ADDRESS_MIN 0x64400000000
#define CODEHEAP_START_ADDRESS 0x64480000000
#define CLR_UPPER_ADDRESS_MAX 0x644FC000000

#if !defined(FEATURE_PAL)
#define USE_UPPER_ADDRESS 1
#else
#define USE_UPPER_ADDRESS 0
#endif // !FEATURE_PAL

#else
#error Please add a new #elif clause and define all portability macros for the new platform
#endif
Expand Down
5 changes: 5 additions & 0 deletions src/pal/inc/pal.h
Original file line number Diff line number Diff line change
Expand Up @@ -480,13 +480,17 @@ typedef long time_t;

#define PAL_INITIALIZE_NONE 0x00
#define PAL_INITIALIZE_SYNC_THREAD 0x01
#define PAL_INITIALIZE_EXEC_ALLOCATOR 0x02

// PAL_Initialize() flags
#define PAL_INITIALIZE PAL_INITIALIZE_SYNC_THREAD

// PAL_InitializeDLL() flags - don't start any of the helper threads
#define PAL_INITIALIZE_DLL PAL_INITIALIZE_NONE

// PAL_InitializeCoreCLR() flags
#define PAL_INITIALIZE_CORECLR (PAL_INITIALIZE | PAL_INITIALIZE_EXEC_ALLOCATOR)

typedef DWORD (PALAPI *PTHREAD_START_ROUTINE)(LPVOID lpThreadParameter);
typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE;

Expand Down Expand Up @@ -3538,6 +3542,7 @@ SetErrorMode(
#define MEM_MAPPED 0x40000
#define MEM_TOP_DOWN 0x100000
#define MEM_WRITE_WATCH 0x200000
#define MEM_RESERVE_EXECUTABLE 0x40000000 // reserve memory using executable memory allocator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two extra "0"? Shouldn't this be
0x400000 or
0x10000000 or
0x1000000

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was intentional because I wanted to have some buffer between the standard Windows flags and PAL specific value.


PALIMPORT
HANDLE
Expand Down
97 changes: 96 additions & 1 deletion src/pal/src/include/pal/virtual.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ Return value:
TRUE if initialization succeeded
FALSE otherwise.
--*/
BOOL VIRTUALInitialize( void );
BOOL VIRTUALInitialize(bool initializeExecutableMemoryAllocator);

/*++
Function :
Expand All @@ -105,6 +105,101 @@ BOOL VIRTUALOwnedRegion( IN UINT_PTR address );

#ifdef __cplusplus
}

/*++
Class:
ExecutableMemoryAllocator

This class implements a virtual memory allocator for JIT'ed code.
The purpose of this allocator is to opportunistically reserve a chunk of virtual memory
that is located near the coreclr library (within 2GB range) that can be later used by
JIT. Having executable memory close to the coreclr library allows JIT to generate more
efficient code (by avoiding usage of jump stubs) and thus it can significantly improve
performance of the application.

This allocator is integrated with the VirtualAlloc/Reserve code. If VirtualAlloc has been
called with the MEM_RESERVE_EXECUTABLE flag then it will first try to obtain the requested size
of virtual memory from ExecutableMemoryAllocator. If ExecutableMemoryAllocator runs out of
the reserved memory (or fails to allocate it during initialization) then VirtualAlloc/Reserve code
will simply fall back to reserving memory using OS APIs.

Notes:
- the memory allocated by this class is NOT committed by default. It is responsibility
of the caller to commit the virtual memory before accessing it.
- in addition, this class does not provide ability to free the reserved memory. The caller
has full control of the memory it got from this allocator (i.e. the caller becomes
the owner of the allocated memory), so it is caller's responsibility to free the memory
if it is no longer needed.
--*/
class ExecutableMemoryAllocator
{
public:
/*++
Function:
Initialize

This function initializes the allocator. It should be called early during process startup
(when process address space is pretty much empty) in order to have a chance to reserve
sufficient amount of memory that is close to the coreclr library.
--*/
void Initialize();

/*++
Function:
AllocateMemory

This function attempts to allocate the requested amount of memory from its reserved virtual
address space. The function will return NULL if the allocation request cannot
be satisfied by the memory that is currently available in the allocator.
--*/
void* AllocateMemory(SIZE_T allocationSize);

private:
/*++
Function:
TryReserveInitialMemory

This function is called during initialization. It opportunistically tries to reserve
a large chunk of virtual memory that can be later used to store JIT'ed code.
--*/
void TryReserveInitialMemory();

/*++
Function:
GenerateRandomStartOffset

This function returns a random offset (in multiples of the virtual page size)
at which the allocator should start allocating memory from its reserved memory range.
--*/
int32_t GenerateRandomStartOffset();

private:
// There does not seem to be an easy way find the size of a library on Unix.
// So this constant represents an approximation of the libcoreclr size (on debug build)
// that can be used to calculate an approximate location of the memory that
// is in 2GB range from the coreclr library. In addition, having precise size of libcoreclr
// is not necessary for the calculations.
const int32_t CoreClrLibrarySize = 100 * 1024 * 1024;

// This constant represent the max size of the virtual memory that this allocator
// will try to reserve during initialization. We want all JIT-ed code and the
// entire libcoreclr to be located in a 2GB range.
const int32_t MaxExecutableMemorySize = 0x7FFF0000 - CoreClrLibrarySize;

// Start address of the reserved virtual address space
void* m_startAddress;

// Next available address in the reserved address space
void* m_nextFreeAddress;

// Total size of the virtual memory that the allocator has been able to
// reserve during its initialization.
int32_t m_totalSizeOfReservedMemory;

// Remaining size of the reserved virtual memory that can be used to satisfy allocation requests.
int32_t m_remainingReservedMemory;
};

#endif // __cplusplus

#endif /* _PAL_VIRTUAL_H_ */
Expand Down
7 changes: 4 additions & 3 deletions src/pal/src/init/pal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,8 @@ Initialize(
}

/* Initialize the Virtual* functions. */
if (FALSE == VIRTUALInitialize())
bool initializeExecutableMemoryAllocator = (flags & PAL_INITIALIZE_EXEC_ALLOCATOR) != 0;
if (FALSE == VIRTUALInitialize(initializeExecutableMemoryAllocator))
{
ERROR("Unable to initialize virtual memory support\n");
goto CLEANUP10;
Expand Down Expand Up @@ -623,8 +624,8 @@ PAL_ERROR
PALAPI
PAL_InitializeCoreCLR(const char *szExePath)
{
// Fake up a command line to call PAL_Initialize with.
int result = PAL_Initialize(1, &szExePath);
// Fake up a command line to call PAL initialization with.
int result = Initialize(1, &szExePath, PAL_INITIALIZE_CORECLR);
if (result != 0)
{
return GetLastError();
Expand Down
Loading