mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-02 11:19:31 -04:00
177 lines
5.8 KiB
Odin
177 lines
5.8 KiB
Odin
#+private
|
|
package runtime
|
|
|
|
import "base:intrinsics"
|
|
|
|
VIRTUAL_MEMORY_SUPPORTED :: true
|
|
|
|
SYS_munmap :: uintptr(73)
|
|
SYS_mmap :: uintptr(477)
|
|
|
|
PROT_READ :: 0x01
|
|
PROT_WRITE :: 0x02
|
|
|
|
MAP_PRIVATE :: 0x0002
|
|
MAP_ANONYMOUS :: 0x1000
|
|
|
|
// The following features are specific to FreeBSD only.
|
|
/*
|
|
* Request specific alignment (n == log2 of the desired alignment).
|
|
*
|
|
* MAP_ALIGNED_SUPER requests optimal superpage alignment, but does
|
|
* not enforce a specific alignment.
|
|
*/
|
|
// #define MAP_ALIGNED(n) ((n) << MAP_ALIGNMENT_SHIFT)
|
|
MAP_ALIGNMENT_SHIFT :: 24
|
|
MAP_ALIGNED_SUPER :: 1 << MAP_ALIGNMENT_SHIFT
|
|
|
|
_init_virtual_memory :: proc "contextless" () {
|
|
page_size = _get_page_size()
|
|
superpage_size = _get_superpage_size()
|
|
}
|
|
|
|
_get_page_size :: proc "contextless" () -> int {
|
|
// This is a fallback value if the auxiliary vector does not supply it.
|
|
DEFAULT_PAGE_SIZE :: 4096
|
|
|
|
if value, found := _get_auxiliary(.AT_PAGESZ); found {
|
|
return int(value.a_val)
|
|
} else {
|
|
return DEFAULT_PAGE_SIZE
|
|
}
|
|
}
|
|
|
|
_get_superpage_size :: proc "contextless" () -> int {
|
|
// This is specific to FreeBSD and not defined in the SysV ABI.
|
|
AT_PAGESIZES :: Auxiliary_Vector_Type(20)
|
|
|
|
if value, found := _get_auxiliary(AT_PAGESIZES); found {
|
|
greatest_size := 0
|
|
supports_2mib := false
|
|
|
|
for sizes := cast([^]uint)value.a_ptr; sizes[0] != 0; sizes = sizes[1:] {
|
|
if sizes[0] == 2 * Megabyte {
|
|
// The standard 2MiB superpage is supported.
|
|
supports_2mib = true
|
|
}
|
|
greatest_size = max(greatest_size, int(sizes[0]))
|
|
}
|
|
|
|
if supports_2mib {
|
|
return 2 * Megabyte
|
|
} else if greatest_size > _get_page_size() {
|
|
return greatest_size
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
_allocate_virtual_memory :: proc "contextless" (size: int) -> rawptr {
|
|
result, ok := intrinsics.syscall_bsd(SYS_mmap, 0, uintptr(size), PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, ~uintptr(0), 0)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return rawptr(result)
|
|
}
|
|
|
|
_allocate_virtual_memory_superpage :: proc "contextless" () -> rawptr {
|
|
superpage_flags := uintptr(intrinsics.count_trailing_zeros(superpage_size) << MAP_ALIGNMENT_SHIFT) | MAP_ALIGNED_SUPER
|
|
|
|
result, ok := intrinsics.syscall_bsd(SYS_mmap, 0, uintptr(superpage_size), PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE|superpage_flags, ~uintptr(0), 0)
|
|
if !ok {
|
|
// It may be the case that FreeBSD couldn't fulfill our alignment
|
|
// request, but it could still give us some memory.
|
|
return _allocate_virtual_memory_manually_aligned(superpage_size, superpage_size)
|
|
}
|
|
return rawptr(result)
|
|
}
|
|
|
|
_allocate_virtual_memory_aligned :: proc "contextless" (size: int, alignment: int) -> rawptr {
|
|
// This procedure uses the `MAP_ALIGNED` API provided by FreeBSD and falls
|
|
// back to manually aligned addresses, if that fails.
|
|
map_aligned_n: uintptr
|
|
if alignment >= page_size {
|
|
map_aligned_n = intrinsics.count_trailing_zeros(uintptr(alignment)) << MAP_ALIGNMENT_SHIFT
|
|
}
|
|
result, ok := intrinsics.syscall_bsd(SYS_mmap, 0, uintptr(size), PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE|map_aligned_n, ~uintptr(0), 0)
|
|
if !ok {
|
|
_allocate_virtual_memory_manually_aligned(size, alignment)
|
|
}
|
|
return rawptr(result)
|
|
}
|
|
|
|
_allocate_virtual_memory_manually_aligned :: proc "contextless" (size: int, alignment: int) -> rawptr {
|
|
if alignment <= page_size {
|
|
// This is the simplest case.
|
|
//
|
|
// By virtue of binary arithmetic, any address aligned to a power of
|
|
// two is necessarily aligned to all lesser powers of two, and because
|
|
// mmap returns page-aligned addresses, we don't have to do anything
|
|
// extra here.
|
|
result, ok := intrinsics.syscall_bsd(SYS_mmap, 0, uintptr(size), PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, ~uintptr(0), 0)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
return cast(rawptr)result
|
|
}
|
|
// We must over-allocate then adjust the address.
|
|
mmap_result, ok := intrinsics.syscall_bsd(SYS_mmap, 0, uintptr(size + alignment), PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, ~uintptr(0), 0)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
assert_contextless(mmap_result % uintptr(page_size) == 0)
|
|
modulo := mmap_result & uintptr(alignment-1)
|
|
if modulo != 0 {
|
|
// The address is misaligned, so we must return an adjusted address
|
|
// and free the pages we don't need.
|
|
delta := uintptr(alignment) - modulo
|
|
adjusted_result := mmap_result + delta
|
|
|
|
// Sanity-checking:
|
|
// - The adjusted address is still page-aligned, so it is a valid argument for munmap.
|
|
// - The adjusted address is aligned to the user's needs.
|
|
assert_contextless(adjusted_result % uintptr(page_size) == 0)
|
|
assert_contextless(adjusted_result % uintptr(alignment) == 0)
|
|
|
|
// Round the delta to a multiple of the page size.
|
|
delta = delta / uintptr(page_size) * uintptr(page_size)
|
|
if delta > 0 {
|
|
// Unmap the pages we don't need.
|
|
intrinsics.syscall_bsd(SYS_munmap, mmap_result, delta)
|
|
}
|
|
|
|
return rawptr(adjusted_result)
|
|
} else if size + alignment > page_size {
|
|
// The address is coincidentally aligned as desired, but we have space
|
|
// that will never be seen by the user, so we must free the backing
|
|
// pages for it.
|
|
start := size / page_size * page_size
|
|
if size % page_size != 0 {
|
|
start += page_size
|
|
}
|
|
length := size + alignment - start
|
|
if length > 0 {
|
|
intrinsics.syscall_bsd(SYS_munmap, mmap_result + uintptr(start), uintptr(length))
|
|
}
|
|
}
|
|
return rawptr(mmap_result)
|
|
}
|
|
|
|
_free_virtual_memory :: proc "contextless" (ptr: rawptr, size: int) {
|
|
intrinsics.syscall_bsd(SYS_munmap, uintptr(ptr), uintptr(size))
|
|
}
|
|
|
|
_resize_virtual_memory :: proc "contextless" (ptr: rawptr, old_size: int, new_size: int, alignment: int) -> rawptr {
|
|
// FreeBSD does not have a mremap syscall.
|
|
// All we can do is mmap a new address, copy the data, and munmap the old.
|
|
result: rawptr = ---
|
|
if alignment == 0 {
|
|
result = _allocate_virtual_memory(new_size)
|
|
} else {
|
|
result = _allocate_virtual_memory_aligned(new_size, alignment)
|
|
}
|
|
intrinsics.mem_copy_non_overlapping(result, ptr, min(new_size, old_size))
|
|
intrinsics.syscall_bsd(SYS_munmap, uintptr(ptr), uintptr(old_size))
|
|
return result
|
|
}
|