memory stuff

This commit is contained in:
Judah Caruso 2025-09-08 19:58:48 -06:00
parent 5f27b720ea
commit d409969867
11 changed files with 257 additions and 126 deletions

View file

@ -77,7 +77,7 @@ ArenaToAllocator :: (arena: *Arena) -> Allocator {
return .{ proc = JaiAllocatorProc, data = arena };
}
/// PanicArena is an Arena that panics when used.
PanicArena :: () -> Arena {
return .{ proc = PanicArenaProc };
}
@ -119,12 +119,12 @@ PanicArenaProc :: (
if event == {
case .Acquire;
if new_size > 0
{ Panic("jc: cannot acquire memory using the PanicArena", loc = caller); }
{ Panic("jc: PanicArena, acquiring memory is not allowed", loc = caller); }
case .Save;
Panic("jc: cannot save using the PanicArean", loc = caller);
Panic("jc: PanicArena, saving is not allowed", loc = caller);
case .Restore;
Panic("jc: cannot restore using the PanicArean", loc = caller);
Panic("jc: PanicArena, restoring is not allowed", loc = caller);
}
return null;
@ -143,7 +143,7 @@ BumpArenaProc :: (
if event == {
case .Setup;
if bump.memory.data == null
{ Panic("jc: BumpArena has no memory", loc = caller); }
{ Panic("jc: BumpArena, no backing memory", loc = caller); }
return arena_data;
case .Teardown;
@ -157,7 +157,7 @@ BumpArenaProc :: (
ptr := MemAlignForward(end, align);
size := new_size + (ptr - end);
if bump.offset + size > bump.memory.count
{ Panic("jc: BumpArena out of memory", loc = caller); }
{ Panic("jc: BumpArena, out of memory", loc = caller); }
bump.offset += size;
return ptr;
@ -169,7 +169,7 @@ BumpArenaProc :: (
case .Restore;
wm := old_ptr.(int);
if wm < 0 || wm >= bump.memory.count
{ Panic("jc: BumpArena restored invalid savepoint", loc = caller); }
{ Panic("jc: BumpArena, restored invalid savepoint", loc = caller); }
bump.offset = wm;
}
@ -185,7 +185,128 @@ PagingArenaProc :: (
caller: Source_Code_Location
) -> *void {
Panic("not implemented for PagingArena", loc = caller);
MaxPages :: 64;
PageArenaData :: struct {
pages: [MaxPages]Page;
count: int;
in_use: int;
Page :: struct {
base: *u8;
offset: int;
}
}
// @note(judah): page 0 has a special offset because it stores the header
Page0Offset :: #run AlignForward(size_of(PageArenaData), align_of(PageArenaData));
NewPage :: inline (previous_base: *void) -> PageArenaData.Page {
p: PageArenaData.Page = ---;
p.base = MemAcquirePage(.CommitImmediately);
p.offset = 0;
return p;
}
data := arena_data.(*PageArenaData);
if event == {
case .Setup;
page := NewPage(null);
data = page.base.(*PageArenaData);
page.offset = Page0Offset;
data.pages[0] = page;
data.in_use = 1;
data.count = 1;
return data;
case .Teardown;
// @note(judah): release in reverse oreder so we don't release the memory
// containing the header until we don't need it anymore.
i := data.count - 1;
while i >= 0 {
page := data.pages[i];
if page.base != null
{ MemReleasePage(page.base, PageSize); }
i -= 1;
}
case .Acquire;
Assert(data.count != 0, "jc: PageArena, no pages exist");
last := *data.pages[data.in_use - 1];
end := last.base + last.offset;
if new_size == 0
{ return end; }
ptr := MemAlignForward(end, align);
size := new_size + (ptr - end);
// create or reuse page
if last.offset + size > PageSize {
if data.in_use >= MaxPages
{ Panic("jc: PageArena, max page count"); }
page: *PageArenaData.Page;
if data.in_use < data.count {
page = *data.pages[data.in_use];
}
else {
p := NewPage(data.pages[data.count - 1].base);
data.pages[data.count] = p;
page = *data.pages[data.count];
data.count += 1;
}
data.in_use += 1;
ptr = page.base;
size = new_size;
last = page;
}
last.offset += size;
return ptr;
case .Reset;
for 0..data.count - 1 {
page := *data.pages[it];
if it == 0 {
page.offset = Page0Offset;
}
else {
page.offset = 0;
}
}
data.in_use = 1;
case .Save;
return data.in_use.(*void);
case .Restore;
in_use_at_save := old_ptr.(int);
// @note(judah): We need to restore from the savepoint to count
// because we could've created more pages inbetween save/restore
for in_use_at_save..data.count - 1 {
p := *data.pages[it];
if it == 0 {
p.offset = Page0Offset;
}
else {
p.offset = 0;
}
}
data.in_use = in_use_at_save;
}
return null;
} @jc.nodocs

View file

@ -6,9 +6,8 @@
ArrayAppend :: inline (arr: *[..]$T, values: ..T) -> *T {
TrySetAllocator(arr,, allocator = ArenaToAllocator(*context.arena));
if values.count == 0 {
return basic.array_add(arr);
}
if values.count == 0
{ return basic.array_add(arr); }
count := arr.count;
basic.array_add(arr, ..values);

View file

@ -189,6 +189,7 @@ WriteStderrNumber :: #bake_arguments write_number(to_standard_error = true); //
#scope_file
// @todo(judah): this probably needs to be exposed...
DebugBuild :: #run -> bool {
// @note(judah): there's not really a good way to detect opt level/build type,
// so just check if debug info is being emitted.

View file

@ -1,6 +1,7 @@
Kilobyte :: 1024;
Megabyte :: 1024 * Kilobyte;
Gigabyte :: 1024 * Megabyte;
Terabyte :: 1024 * Gigabyte;
DefaultAlign :: size_of(*void);
@ -106,6 +107,63 @@ NextPowerOfTwo :: (x: int) -> int #no_aoc {
return x + 1;
}
MemAcquirePageFlags :: enum_flags {
CommitImmediately;
}
MemAcquirePage :: (flags: MemAcquirePageFlags = 0, previous: *void = null) -> *void {
BaseAddress :: 1 * Terabyte;
#if OS == .MACOS {
addr: darwin.mach_vm_address_t = BaseAddress;
if previous != null {
addr = previous.(darwin.mach_vm_address_t) + PageSize;
}
res := darwin.mach_vm_allocate(darwin.mach_task_self(), *addr, PageSize, darwin.VM_FLAGS_ANYWHERE);
if res != 0
{ Panic("jc: MemAcquirePage, failed to request page"); }
ptr := addr.(*void);
if flags & .CommitImmediately
{ MemZero(ptr, PageSize); }
return ptr;
}
else {
Panic("todo: MemAcquirePage not implemented on this OS");
}
return null;
}
MemReleasePage :: (page: *void, size: int) {
#if OS == .MACOS {
res := darwin.mach_vm_deallocate(darwin.mach_task_self(), page.(darwin.mach_vm_address_t), size.(darwin.mach_vm_size_t));
if res != 0
{ Panic("jc: MemReleasePage failed to deallocate page"); }
}
else {
Panic("todo: MemReleasePage not implemented on this OS");
}
}
PageSize :: #run -> uint {
#if OS == .MACOS {
page_size: darwin.mach_vm_size_t;
res := darwin.host_page_size(darwin.host_self(), *page_size);
if res != 0
{ CompileError("failed to get host page size"); }
return page_size.(uint);
}
else {
CompileError("todo: PageSize not implemented on OS");
}
return 0;
};
#scope_module
@ -133,3 +191,11 @@ TrySetAllocator :: (array: *[..]$T) #expand {
array.allocator = context.allocator;
}
} @jc.nodocs
#scope_file
#if OS == {
case .MACOS;
darwin :: #import "jc/ext/darwin";
}

View file

@ -9,6 +9,19 @@ This is what I'm doing day-to-day.
: notes, todos, etc.
/ 09.08.25
+ implemented PagingArena on mac
+ implemented MemAcquirePage/MemReleasePage/PageSize on mac
+ panics have more information now
+ added Terabyte constant
+ deleted old ext/darwin
: u32, s64 -> uint4, sint8?
: ext/darwin needs to just bind frameworks
: ext/darwin needs easy objc interop
/ 09.07.25
+ replaced allocators with custom arenas
@ -17,6 +30,7 @@ This is what I'm doing day-to-day.
+ fixed +internal's weird behavior in the test runner
+ ArrayAppend/ArrayGrow now wrap the current arena as the attached Allocator
+ ArenaMode -> ArenaEvent
+ added pointer alignment procs
/ 09.06.25

View file

@ -1 +0,0 @@
NSObject :: struct {};

View file

@ -1,21 +0,0 @@
NSString :: struct {
// #as super: NSObject;
}
init :: (path: ) -> NSString {
}
sel: struct {
init_withBytes_length_encoding: objc.Sel;
init_contentsOfFile_encoding: objc.Sel;
UTF8String: objc.Sel;
};
init_everything :: () {
sel.init_withBytes_length_encoding = objc.sel_register_name("init:withBytes:length:encoding");
sel.init_contentsOfFile_encoding = objc.sel_register_name("init:contentsOfFile:encoding");
sel.UTF8String = objc.sel_register_name("UTF8String");
}
// #import "jc/meta/init"(init_everything);

View file

@ -1,8 +0,0 @@
NSObject :: struct {
id: u64;
}
NSString :: struct {
#as using isa: NSObject;
}

View file

@ -1,38 +0,0 @@
NSNumber :: struct {
// #as using isa: NSObject;
}
#scope_file;
Sel :: uptr;
sel: struct {
boolValue: Sel; @boolValue
intValue: Sel; @intValue
unsignedIntValue: Sel; @unsignedIntValue
floatValue: Sel; @floatValue
doubleValue: Sel; @doubleValue
}
#add_context InitFoundation :: () {
#import "Basic";
tmp: [512]u8;
base := (*sel).(*u8);
info := type_info(type_of(sel));
for info.members if it.notes.count != 0 {
name := it.notes[0];
memcpy(tmp.data, name.data, name.count);
tmp.data[name.count] = 0;
ptr := base + it.offset_in_bytes;
ptr.* = sel_register_name(tmp.data);
assert(ptr.* != 0, "failed to register selector: %", name);
}
print("%\n", info.*);
}
Foundation :: #library,system,no_dll,link_always "Foundation";

44
ext/darwin/kernel.jai Normal file
View file

@ -0,0 +1,44 @@
kern_return_t :: s32;
natural_t :: u32;
boolean_t :: s32;
vm_prot_t :: s32;
vm_map_t :: mach_port_t;
mach_port_t :: natural_t;
mach_vm_address_t :: u64;
mach_vm_size_t :: u64;
host_t :: *void;
VM_FLAGS_FIXED :: 0x0000;
VM_FLAGS_ANYWHERE :: 0x0001;
VM_FLAGS_PURGABLE :: 0x0002;
VM_FLAGS_4GB_CHUNK :: 0x0004;
VM_FLAGS_RANDOM_ADDR :: 0x0008;
VM_FLAGS_NO_CACHE :: 0x0010;
VM_FLAGS_RESILIENT_CODESIGN :: 0x0020;
VM_FLAGS_RESILIENT_MEDIA :: 0x0040;
VM_FLAGS_OVERWRITE :: 0x4000;
VM_PROT_READ :: 0x1;
VM_PROT_WRITE :: 0x2;
VM_PROT_EXECUTE :: 0x4;
host_self :: () -> host_t #foreign Kernel;
host_page_size :: (host: host_t, page_size: *mach_vm_size_t) -> kern_return_t #foreign Kernel;
mach_task_self :: () -> mach_port_t #foreign Kernel;
mach_vm_allocate :: (target: vm_map_t, address: *mach_vm_address_t, size:mach_vm_size_t , flags: s32) -> kern_return_t #foreign Kernel;
mach_vm_deallocate :: (target: vm_map_t, address: mach_vm_address_t, size:mach_vm_size_t) -> kern_return_t #foreign Kernel;
mach_vm_protect :: (target_task: vm_map_t, address: mach_vm_address_t, size: mach_vm_size_t, set_maximum: boolean_t, new_protection: vm_prot_t) -> kern_return_t #foreign Kernel;
#scope_module
Kernel :: #library,system "libc";

View file

@ -1,47 +1 @@
#scope_export;
UInt :: u64;
Id :: *object;
Class :: *class;
Sel :: *selector;
Bool :: u8;
True : Bool : 1;
False : Bool : 0;
msg_send :: () #foreign objc "objc_msgSend";
msg_send_super :: () #foreign objc "objc_msgSend_super";
msg_send_fpret :: () #foreign objc "objc_msgSend_fpret";
msg_send_stret :: () #foreign objc "objc_msgSend_stret";
get_class :: (name: *u8) -> Class #foreign objc "objc_getClass";
sel_get_name :: (sel: Sel) -> *u8 #foreign objc "sel_getName";
sel_register_name :: (str: *u8) -> Sel #foreign objc "sel_registerName";
sel_get_uid :: (str: *u8) -> Sel #foreign objc "sel_getUid";
obj_get_class :: (obj: Id) -> Class #foreign objc "object_getClass";
obj_set_class :: (obj: Id, cls: Class) -> Class #foreign objc "object_setClass";
obj_is_class :: (obj: Id) -> Bool #foreign objc "object_isClass";
obj_get_class_name :: (obj: Id) -> *u8 #foreign objc "object_getClassName";
obj_copy :: (obj: Id, size: u64) -> Id #foreign objc "object_copy";
obj_dispose :: (obj: Id) -> Id #foreign objc "object_dispose";
class_get_name :: (cls: Class) -> *u8 #foreign objc "class_getName";
class_get_super :: (cls: Class) -> Class #foreign objc "class_getSuperclass";
#scope_module;
class :: struct {};
object :: struct {};
method :: struct {};
ivar :: struct {};
category :: struct {};
protocol :: struct {};
selector :: struct {};
objc :: #library,system,link_always,no_dll "libobjc";
#import "jc";
#load "kernel.jai";