Rewrote memory manager to use a bunch of mapped views.
This commit is contained in:
parent
d00f4d58e9
commit
ca208fa60a
|
@ -27,7 +27,7 @@
|
||||||
#define MALLOC_ALIGNMENT 32
|
#define MALLOC_ALIGNMENT 32
|
||||||
#define MALLOC_INSPECT_ALL 1
|
#define MALLOC_INSPECT_ALL 1
|
||||||
#if XE_DEBUG
|
#if XE_DEBUG
|
||||||
#define FOOTERS 1
|
#define FOOTERS 0
|
||||||
#endif // XE_DEBUG
|
#endif // XE_DEBUG
|
||||||
#include <third_party/dlmalloc/malloc.c.h>
|
#include <third_party/dlmalloc/malloc.c.h>
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ DEFINE_uint64(
|
||||||
* for previous commits and assert that we really isn't committing twice.
|
* for previous commits and assert that we really isn't committing twice.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define XE_MEMORY_HEAP_LOW 0x20000000
|
#define XE_MEMORY_HEAP_LOW 0x00000000
|
||||||
#define XE_MEMORY_HEAP_HIGH 0x40000000
|
#define XE_MEMORY_HEAP_HIGH 0x40000000
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,62 +76,89 @@ struct xe_memory {
|
||||||
|
|
||||||
size_t system_page_size;
|
size_t system_page_size;
|
||||||
|
|
||||||
size_t length;
|
HANDLE mapping;
|
||||||
void* ptr;
|
uint8_t* mapping_base;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint8_t* v00000000;
|
||||||
|
uint8_t* v40000000;
|
||||||
|
uint8_t* v80000000;
|
||||||
|
uint8_t* vA0000000;
|
||||||
|
uint8_t* vC0000000;
|
||||||
|
uint8_t* vE0000000;
|
||||||
|
};
|
||||||
|
uint8_t* all_views[6];
|
||||||
|
} views;
|
||||||
|
|
||||||
xe_mutex_t* heap_mutex;
|
xe_mutex_t* heap_mutex;
|
||||||
|
size_t heap_size;
|
||||||
|
uint8_t* heap_ptr;
|
||||||
mspace heap;
|
mspace heap;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
int xe_memory_map_views(xe_memory_ref memory, uint8_t* mapping_base);
|
||||||
|
void xe_memory_unmap_views(xe_memory_ref memory);
|
||||||
|
|
||||||
|
|
||||||
xe_memory_ref xe_memory_create(xe_memory_options_t options) {
|
xe_memory_ref xe_memory_create(xe_memory_options_t options) {
|
||||||
|
|
||||||
xe_memory_ref memory = (xe_memory_ref)xe_calloc(sizeof(xe_memory));
|
xe_memory_ref memory = (xe_memory_ref)xe_calloc(sizeof(xe_memory));
|
||||||
xe_ref_init((xe_ref)memory);
|
xe_ref_init((xe_ref)memory);
|
||||||
|
|
||||||
#if XE_PLATFORM(WIN32)
|
|
||||||
SYSTEM_INFO si;
|
SYSTEM_INFO si;
|
||||||
GetSystemInfo(&si);
|
GetSystemInfo(&si);
|
||||||
memory->system_page_size = si.dwPageSize;
|
memory->system_page_size = si.dwPageSize;
|
||||||
#else
|
|
||||||
#error need to implement page size retrieval
|
|
||||||
#endif // WIN32
|
|
||||||
|
|
||||||
memory->length = 0xC0000000;
|
// Create main page file-backed mapping. This is all reserved but
|
||||||
|
// uncommitted (so it shouldn't expand page file).
|
||||||
|
memory->mapping = CreateFileMapping(
|
||||||
|
INVALID_HANDLE_VALUE,
|
||||||
|
NULL,
|
||||||
|
PAGE_READWRITE | SEC_RESERVE,
|
||||||
|
0, 0xFFFFFFFF, // entire 4gb space
|
||||||
|
NULL);
|
||||||
|
if (!memory->mapping) {
|
||||||
|
XELOGE("Unable to reserve the 4gb guest address space.");
|
||||||
|
XEASSERTNOTNULL(memory->mapping);
|
||||||
|
XEFAIL();
|
||||||
|
}
|
||||||
|
|
||||||
#if XE_PLATFORM(WIN32)
|
// Attempt to create our views. This may fail at the first address
|
||||||
// Reserve the entire usable address space.
|
// we pick, so try a few times.
|
||||||
// We're 64-bit, so this should be no problem.
|
memory->mapping_base = 0;
|
||||||
memory->ptr = VirtualAlloc(0, memory->length,
|
for (size_t n = 32; n < 64; n++) {
|
||||||
MEM_RESERVE,
|
uint8_t* mapping_base = (uint8_t*)(1ull << n);
|
||||||
PAGE_READWRITE);
|
if (!xe_memory_map_views(memory, mapping_base)) {
|
||||||
XEEXPECTNOTNULL(memory->ptr);
|
memory->mapping_base = mapping_base;
|
||||||
#else
|
break;
|
||||||
memory->ptr = mmap(0, memory->length, PROT_READ | PROT_WRITE,
|
}
|
||||||
MAP_PRIVATE | MAP_ANON, -1, 0);
|
}
|
||||||
XEEXPECT(memory->ptr != MAP_FAILED);
|
if (!memory->mapping_base) {
|
||||||
XEEXPECTNOTNULL(memory->ptr);
|
XELOGE("Unable to find a continuous block in the 64bit address space.");
|
||||||
#endif // WIN32
|
XEASSERTALWAYS();
|
||||||
|
XEFAIL();
|
||||||
|
}
|
||||||
|
|
||||||
// Lock used around heap allocs/frees.
|
// Lock used around heap allocs/frees.
|
||||||
memory->heap_mutex = xe_mutex_alloc(10000);
|
memory->heap_mutex = xe_mutex_alloc(10000);
|
||||||
XEEXPECTNOTNULL(memory->heap_mutex);
|
XEEXPECTNOTNULL(memory->heap_mutex);
|
||||||
|
|
||||||
// Commit the memory where our heap will live.
|
// Commit the memory where our heap will live.
|
||||||
// We don't allocate at 0 to make bad writes easier to find.
|
// TODO(benvanik): replace dlmalloc with an implementation that can commit
|
||||||
|
// as it goes.
|
||||||
uint32_t heap_offset = XE_MEMORY_HEAP_LOW;
|
uint32_t heap_offset = XE_MEMORY_HEAP_LOW;
|
||||||
uint32_t heap_size = XE_MEMORY_HEAP_HIGH - XE_MEMORY_HEAP_LOW;
|
uint32_t heap_size = XE_MEMORY_HEAP_HIGH - XE_MEMORY_HEAP_LOW;
|
||||||
uint8_t* heap_ptr = (uint8_t*)memory->ptr + heap_offset;
|
memory->heap_size = heap_size;
|
||||||
#if XE_PLATFORM(WIN32)
|
memory->heap_ptr = memory->views.v00000000 + heap_offset;
|
||||||
void* heap_result = VirtualAlloc(heap_ptr, heap_size,
|
void* heap_result = VirtualAlloc(
|
||||||
MEM_COMMIT, PAGE_READWRITE);
|
memory->heap_ptr, heap_size,
|
||||||
|
MEM_COMMIT,
|
||||||
|
PAGE_READWRITE);
|
||||||
XEEXPECTNOTNULL(heap_result);
|
XEEXPECTNOTNULL(heap_result);
|
||||||
#else
|
|
||||||
#error ?
|
|
||||||
#endif // WIN32
|
|
||||||
|
|
||||||
// Allocate the mspace for our heap.
|
// Allocate the mspace for our heap.
|
||||||
memory->heap = create_mspace_with_base(heap_ptr, heap_size, 0);
|
memory->heap = create_mspace_with_base(memory->heap_ptr, heap_size, 0);
|
||||||
|
|
||||||
return memory;
|
return memory;
|
||||||
|
|
||||||
|
@ -152,12 +179,52 @@ void xe_memory_dealloc(xe_memory_ref memory) {
|
||||||
memory->heap_mutex = NULL;
|
memory->heap_mutex = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if XE_PLATFORM(WIN32)
|
|
||||||
// This decommits all pages and releases everything.
|
// This decommits all pages and releases everything.
|
||||||
XEIGNORE(VirtualFree(memory->ptr, 0, MEM_RELEASE));
|
XEIGNORE(VirtualFree(memory->heap_ptr, 0, MEM_RELEASE));
|
||||||
#else
|
|
||||||
munmap(memory->ptr, memory->length);
|
// Unmap all views and close mapping.
|
||||||
#endif // WIN32
|
if (memory->mapping) {
|
||||||
|
xe_memory_unmap_views(memory);
|
||||||
|
CloseHandle(memory->mapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int xe_memory_map_views(xe_memory_ref memory, uint8_t* mapping_base) {
|
||||||
|
static struct {
|
||||||
|
uint32_t virtual_address_start;
|
||||||
|
uint32_t virtual_address_end;
|
||||||
|
uint32_t target_address;
|
||||||
|
} map_info[] = {
|
||||||
|
0x00000000, 0x3FFFFFFF, 0x00000000, // (1024mb) - virtual 4k pages
|
||||||
|
0x40000000, 0x7FFFFFFF, 0x40000000, // (1024mb) - virtual 64k pages
|
||||||
|
0x80000000, 0x9FFFFFFF, 0x80000000, // (512mb) - xex pages
|
||||||
|
0xA0000000, 0xBFFFFFFF, 0xA0000000, // (512mb) - physical 64k pages
|
||||||
|
0xC0000000, 0xDFFFFFFF, 0xA0000000, // - physical 16mb pages
|
||||||
|
0xE0000000, 0xFFFFFFFF, 0xA0000000, // - physical 4k pages
|
||||||
|
};
|
||||||
|
XEASSERT(XECOUNT(map_info) == XECOUNT(memory->views.all_views));
|
||||||
|
for (size_t n = 0; n < XECOUNT(map_info); n++) {
|
||||||
|
memory->views.all_views[n] = (uint8_t*)MapViewOfFileEx(
|
||||||
|
memory->mapping,
|
||||||
|
FILE_MAP_ALL_ACCESS,
|
||||||
|
0x00000000, map_info[n].target_address,
|
||||||
|
map_info[n].virtual_address_end - map_info[n].virtual_address_start + 1,
|
||||||
|
mapping_base + map_info[n].virtual_address_start);
|
||||||
|
XEEXPECTNOTNULL(memory->views.all_views[n]);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
XECLEANUP:
|
||||||
|
xe_memory_unmap_views(memory);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void xe_memory_unmap_views(xe_memory_ref memory) {
|
||||||
|
for (size_t n = 0; n < XECOUNT(memory->views.all_views); n++) {
|
||||||
|
if (memory->views.all_views[n]) {
|
||||||
|
UnmapViewOfFile(memory->views.all_views[n]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
xe_memory_ref xe_memory_retain(xe_memory_ref memory) {
|
xe_memory_ref xe_memory_retain(xe_memory_ref memory) {
|
||||||
|
@ -169,18 +236,14 @@ void xe_memory_release(xe_memory_ref memory) {
|
||||||
xe_ref_release((xe_ref)memory, (xe_ref_dealloc_t)xe_memory_dealloc);
|
xe_ref_release((xe_ref)memory, (xe_ref_dealloc_t)xe_memory_dealloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t xe_memory_get_length(xe_memory_ref memory) {
|
|
||||||
return memory->length;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t *xe_memory_addr(xe_memory_ref memory, size_t guest_addr) {
|
uint8_t *xe_memory_addr(xe_memory_ref memory, size_t guest_addr) {
|
||||||
return (uint8_t*)memory->ptr + guest_addr;
|
return memory->mapping_base + guest_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void xe_memory_copy(xe_memory_ref memory,
|
void xe_memory_copy(xe_memory_ref memory,
|
||||||
uint32_t dest, uint32_t src, uint32_t size) {
|
uint32_t dest, uint32_t src, uint32_t size) {
|
||||||
uint8_t* pdest = (uint8_t*)memory->ptr + dest;
|
uint8_t* pdest = memory->mapping_base + dest;
|
||||||
uint8_t* psrc = (uint8_t*)memory->ptr + src;
|
uint8_t* psrc = memory->mapping_base + src;
|
||||||
XEIGNORE(xe_copy_memory(pdest, size, psrc, size));
|
XEIGNORE(xe_copy_memory(pdest, size, psrc, size));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +264,7 @@ uint32_t xe_memory_search_aligned(xe_memory_ref memory, size_t start,
|
||||||
matched++;
|
matched++;
|
||||||
}
|
}
|
||||||
if (matched == value_count) {
|
if (matched == value_count) {
|
||||||
return (uint32_t)((uint8_t*)p - (uint8_t*)memory->ptr);
|
return (uint32_t)((uint8_t*)p - memory->mapping_base);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p++;
|
p++;
|
||||||
|
@ -215,12 +278,19 @@ void xe_memory_heap_dump_handler(
|
||||||
size_t heap_guard_size = FLAGS_heap_guard_pages * 4096;
|
size_t heap_guard_size = FLAGS_heap_guard_pages * 4096;
|
||||||
uint64_t start_addr = (uint64_t)start + heap_guard_size;
|
uint64_t start_addr = (uint64_t)start + heap_guard_size;
|
||||||
uint64_t end_addr = (uint64_t)end - heap_guard_size;
|
uint64_t end_addr = (uint64_t)end - heap_guard_size;
|
||||||
uint32_t guest_start = (uint32_t)(start_addr - (uintptr_t)memory->ptr);
|
uint32_t guest_start =
|
||||||
uint32_t guest_end = (uint32_t)(end_addr - (uintptr_t)memory->ptr);
|
(uint32_t)(start_addr - (uintptr_t)memory->mapping_base);
|
||||||
XELOGI(" - %.8X-%.8X (%9db) %.16llX-%.16llX - %9db used",
|
uint32_t guest_end =
|
||||||
|
(uint32_t)(end_addr - (uintptr_t)memory->mapping_base);
|
||||||
|
if (used_bytes > 0) {
|
||||||
|
XELOGI(" - %.8X-%.8X (%10db) %.16llX-%.16llX - %9db used",
|
||||||
guest_start, guest_end, (guest_end - guest_start),
|
guest_start, guest_end, (guest_end - guest_start),
|
||||||
start_addr, end_addr,
|
start_addr, end_addr,
|
||||||
used_bytes);
|
used_bytes);
|
||||||
|
} else {
|
||||||
|
XELOGI(" - %.16llX-%.16llX - %9db used",
|
||||||
|
start_addr, end_addr, used_bytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void xe_memory_heap_dump(xe_memory_ref memory) {
|
void xe_memory_heap_dump(xe_memory_ref memory) {
|
||||||
XELOGI("xe_memory_heap_dump:");
|
XELOGI("xe_memory_heap_dump:");
|
||||||
|
@ -272,7 +342,7 @@ uint32_t xe_memory_heap_alloc(
|
||||||
if (!p) {
|
if (!p) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return (uint32_t)((uintptr_t)p - (uintptr_t)memory->ptr);
|
return (uint32_t)((uintptr_t)p - (uintptr_t)memory->mapping_base);
|
||||||
} else {
|
} else {
|
||||||
if (base_address >= XE_MEMORY_HEAP_LOW &&
|
if (base_address >= XE_MEMORY_HEAP_LOW &&
|
||||||
base_address < XE_MEMORY_HEAP_HIGH) {
|
base_address < XE_MEMORY_HEAP_HIGH) {
|
||||||
|
@ -281,8 +351,7 @@ uint32_t xe_memory_heap_alloc(
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t* p = (uint8_t*)memory->ptr + base_address;
|
uint8_t* p = memory->mapping_base + base_address;
|
||||||
#if XE_PLATFORM(WIN32)
|
|
||||||
// TODO(benvanik): check if address range is in use with a query.
|
// TODO(benvanik): check if address range is in use with a query.
|
||||||
|
|
||||||
void* pv = VirtualAlloc(p, size, MEM_COMMIT, PAGE_READWRITE);
|
void* pv = VirtualAlloc(p, size, MEM_COMMIT, PAGE_READWRITE);
|
||||||
|
@ -291,9 +360,6 @@ uint32_t xe_memory_heap_alloc(
|
||||||
XEASSERTALWAYS();
|
XEASSERTALWAYS();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
#error ?
|
|
||||||
#endif // WIN32
|
|
||||||
|
|
||||||
return base_address;
|
return base_address;
|
||||||
}
|
}
|
||||||
|
@ -301,7 +367,7 @@ uint32_t xe_memory_heap_alloc(
|
||||||
|
|
||||||
int xe_memory_heap_free(
|
int xe_memory_heap_free(
|
||||||
xe_memory_ref memory, uint32_t address, uint32_t size) {
|
xe_memory_ref memory, uint32_t address, uint32_t size) {
|
||||||
uint8_t* p = (uint8_t*)memory->ptr + address;
|
uint8_t* p = memory->mapping_base + address;
|
||||||
if (address >= XE_MEMORY_HEAP_LOW && address < XE_MEMORY_HEAP_HIGH) {
|
if (address >= XE_MEMORY_HEAP_LOW && address < XE_MEMORY_HEAP_HIGH) {
|
||||||
// Heap allocated address.
|
// Heap allocated address.
|
||||||
size_t heap_guard_size = FLAGS_heap_guard_pages * 4096;
|
size_t heap_guard_size = FLAGS_heap_guard_pages * 4096;
|
||||||
|
@ -326,16 +392,12 @@ int xe_memory_heap_free(
|
||||||
return (uint32_t)real_size;
|
return (uint32_t)real_size;
|
||||||
} else {
|
} else {
|
||||||
// A placed address. Decommit.
|
// A placed address. Decommit.
|
||||||
#if XE_PLATFORM(WIN32)
|
|
||||||
return VirtualFree(p, size, MEM_DECOMMIT) ? 0 : 1;
|
return VirtualFree(p, size, MEM_DECOMMIT) ? 0 : 1;
|
||||||
#else
|
|
||||||
#error decommit
|
|
||||||
#endif // WIN32
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool xe_memory_is_valid(xe_memory_ref memory, uint32_t address) {
|
bool xe_memory_is_valid(xe_memory_ref memory, uint32_t address) {
|
||||||
uint8_t* p = (uint8_t*)memory->ptr + address;
|
uint8_t* p = memory->mapping_base + address;
|
||||||
if (address >= XE_MEMORY_HEAP_LOW && address < XE_MEMORY_HEAP_HIGH) {
|
if (address >= XE_MEMORY_HEAP_LOW && address < XE_MEMORY_HEAP_HIGH) {
|
||||||
// Within heap range, ask dlmalloc.
|
// Within heap range, ask dlmalloc.
|
||||||
size_t heap_guard_size = FLAGS_heap_guard_pages * 4096;
|
size_t heap_guard_size = FLAGS_heap_guard_pages * 4096;
|
||||||
|
@ -349,12 +411,11 @@ bool xe_memory_is_valid(xe_memory_ref memory, uint32_t address) {
|
||||||
|
|
||||||
int xe_memory_protect(
|
int xe_memory_protect(
|
||||||
xe_memory_ref memory, uint32_t address, uint32_t size, uint32_t access) {
|
xe_memory_ref memory, uint32_t address, uint32_t size, uint32_t access) {
|
||||||
uint8_t* p = (uint8_t*)memory->ptr + address;
|
uint8_t* p = memory->mapping_base + address;
|
||||||
|
|
||||||
size_t heap_guard_size = FLAGS_heap_guard_pages * 4096;
|
size_t heap_guard_size = FLAGS_heap_guard_pages * 4096;
|
||||||
p += heap_guard_size;
|
p += heap_guard_size;
|
||||||
|
|
||||||
#if XE_PLATFORM(WIN32)
|
|
||||||
DWORD new_protect = 0;
|
DWORD new_protect = 0;
|
||||||
if (access & XE_MEMORY_ACCESS_WRITE) {
|
if (access & XE_MEMORY_ACCESS_WRITE) {
|
||||||
new_protect = PAGE_READWRITE;
|
new_protect = PAGE_READWRITE;
|
||||||
|
@ -365,14 +426,4 @@ int xe_memory_protect(
|
||||||
}
|
}
|
||||||
DWORD old_protect;
|
DWORD old_protect;
|
||||||
return VirtualProtect(p, size, new_protect, &old_protect) == TRUE ? 0 : 1;
|
return VirtualProtect(p, size, new_protect, &old_protect) == TRUE ? 0 : 1;
|
||||||
#else
|
|
||||||
int prot = 0;
|
|
||||||
if (access & XE_MEMORY_ACCESS_READ) {
|
|
||||||
prot = PROT_READ;
|
|
||||||
}
|
|
||||||
if (access & XE_MEMORY_ACCESS_WRITE) {
|
|
||||||
prot = PROT_WRITE;
|
|
||||||
}
|
|
||||||
return mprotect(p, size, prot);
|
|
||||||
#endif // WIN32
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,6 @@ xe_memory_ref xe_memory_create(xe_memory_options_t options);
|
||||||
xe_memory_ref xe_memory_retain(xe_memory_ref memory);
|
xe_memory_ref xe_memory_retain(xe_memory_ref memory);
|
||||||
void xe_memory_release(xe_memory_ref memory);
|
void xe_memory_release(xe_memory_ref memory);
|
||||||
|
|
||||||
size_t xe_memory_get_length(xe_memory_ref memory);
|
|
||||||
uint8_t *xe_memory_addr(xe_memory_ref memory, size_t guest_addr = 0);
|
uint8_t *xe_memory_addr(xe_memory_ref memory, size_t guest_addr = 0);
|
||||||
|
|
||||||
void xe_memory_copy(xe_memory_ref memory,
|
void xe_memory_copy(xe_memory_ref memory,
|
||||||
|
|
|
@ -145,9 +145,10 @@ int Processor::LoadRawBinary(const xechar_t* path, uint32_t start_address) {
|
||||||
int result_code = 1;
|
int result_code = 1;
|
||||||
|
|
||||||
// Place the data into memory at the desired address.
|
// Place the data into memory at the desired address.
|
||||||
XEEXPECTZERO(xe_copy_memory(xe_memory_addr(memory_, start_address),
|
XEEXPECTNOTZERO(xe_memory_heap_alloc(
|
||||||
xe_memory_get_length(memory_),
|
memory_, start_address, (uint32_t)length, 0));
|
||||||
addr, length));
|
XEEXPECTZERO(xe_copy_memory(
|
||||||
|
xe_memory_addr(memory_, start_address), length, addr, length));
|
||||||
|
|
||||||
char name_a[XE_MAX_PATH];
|
char name_a[XE_MAX_PATH];
|
||||||
XEEXPECTTRUE(xestrnarrow(name_a, XECOUNT(name_a), name));
|
XEEXPECTTRUE(xestrnarrow(name_a, XECOUNT(name_a), name));
|
||||||
|
@ -172,6 +173,7 @@ int Processor::LoadRawBinary(const xechar_t* path, uint32_t start_address) {
|
||||||
result_code = 0;
|
result_code = 0;
|
||||||
XECLEANUP:
|
XECLEANUP:
|
||||||
if (result_code) {
|
if (result_code) {
|
||||||
|
xe_memory_heap_free(memory_, start_address, (uint32_t)length);
|
||||||
delete exec_module;
|
delete exec_module;
|
||||||
}
|
}
|
||||||
xe_mmap_release(mmap);
|
xe_mmap_release(mmap);
|
||||||
|
|
Loading…
Reference in New Issue