diff --git a/+internal/arenas.jai b/+internal/arenas.jai index 2ae79e2..27557a8 100644 --- a/+internal/arenas.jai +++ b/+internal/arenas.jai @@ -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 diff --git a/+internal/arrays.jai b/+internal/arrays.jai index a01935f..75128c1 100644 --- a/+internal/arrays.jai +++ b/+internal/arrays.jai @@ -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); diff --git a/+internal/builtin.jai b/+internal/builtin.jai index 4a74dfc..0568b99 100644 --- a/+internal/builtin.jai +++ b/+internal/builtin.jai @@ -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. diff --git a/+internal/memory.jai b/+internal/memory.jai index a0d14a8..b3f9b09 100644 --- a/+internal/memory.jai +++ b/+internal/memory.jai @@ -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"; +} diff --git a/PLAN.Judah b/PLAN.Judah index efd5550..bdc69c9 100644 --- a/PLAN.Judah +++ b/PLAN.Judah @@ -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 diff --git a/ext/darwin/Foundation/NSObject.jai b/ext/darwin/Foundation/NSObject.jai deleted file mode 100644 index a811b4f..0000000 --- a/ext/darwin/Foundation/NSObject.jai +++ /dev/null @@ -1 +0,0 @@ -NSObject :: struct {}; diff --git a/ext/darwin/Foundation/NSString.jai b/ext/darwin/Foundation/NSString.jai deleted file mode 100644 index aaf294e..0000000 --- a/ext/darwin/Foundation/NSString.jai +++ /dev/null @@ -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); diff --git a/ext/darwin/Foundation/module.jai b/ext/darwin/Foundation/module.jai deleted file mode 100644 index 868ef67..0000000 --- a/ext/darwin/Foundation/module.jai +++ /dev/null @@ -1,8 +0,0 @@ -NSObject :: struct { - id: u64; -} - -NSString :: struct { - #as using isa: NSObject; -} - diff --git a/ext/darwin/foundation.jai b/ext/darwin/foundation.jai deleted file mode 100644 index f8a13c5..0000000 --- a/ext/darwin/foundation.jai +++ /dev/null @@ -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"; diff --git a/ext/darwin/kernel.jai b/ext/darwin/kernel.jai new file mode 100644 index 0000000..0c9716a --- /dev/null +++ b/ext/darwin/kernel.jai @@ -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"; diff --git a/ext/darwin/module.jai b/ext/darwin/module.jai index 5788182..00ecd78 100644 --- a/ext/darwin/module.jai +++ b/ext/darwin/module.jai @@ -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";