memory stuff
This commit is contained in:
parent
5f27b720ea
commit
d409969867
11 changed files with 257 additions and 126 deletions
|
|
@ -77,7 +77,7 @@ ArenaToAllocator :: (arena: *Arena) -> Allocator {
|
||||||
return .{ proc = JaiAllocatorProc, data = arena };
|
return .{ proc = JaiAllocatorProc, data = arena };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// PanicArena is an Arena that panics when used.
|
||||||
PanicArena :: () -> Arena {
|
PanicArena :: () -> Arena {
|
||||||
return .{ proc = PanicArenaProc };
|
return .{ proc = PanicArenaProc };
|
||||||
}
|
}
|
||||||
|
|
@ -119,12 +119,12 @@ PanicArenaProc :: (
|
||||||
if event == {
|
if event == {
|
||||||
case .Acquire;
|
case .Acquire;
|
||||||
if new_size > 0
|
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;
|
case .Save;
|
||||||
Panic("jc: cannot save using the PanicArean", loc = caller);
|
Panic("jc: PanicArena, saving is not allowed", loc = caller);
|
||||||
case .Restore;
|
case .Restore;
|
||||||
Panic("jc: cannot restore using the PanicArean", loc = caller);
|
Panic("jc: PanicArena, restoring is not allowed", loc = caller);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
|
@ -143,7 +143,7 @@ BumpArenaProc :: (
|
||||||
if event == {
|
if event == {
|
||||||
case .Setup;
|
case .Setup;
|
||||||
if bump.memory.data == null
|
if bump.memory.data == null
|
||||||
{ Panic("jc: BumpArena has no memory", loc = caller); }
|
{ Panic("jc: BumpArena, no backing memory", loc = caller); }
|
||||||
return arena_data;
|
return arena_data;
|
||||||
|
|
||||||
case .Teardown;
|
case .Teardown;
|
||||||
|
|
@ -157,7 +157,7 @@ BumpArenaProc :: (
|
||||||
ptr := MemAlignForward(end, align);
|
ptr := MemAlignForward(end, align);
|
||||||
size := new_size + (ptr - end);
|
size := new_size + (ptr - end);
|
||||||
if bump.offset + size > bump.memory.count
|
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;
|
bump.offset += size;
|
||||||
return ptr;
|
return ptr;
|
||||||
|
|
@ -169,7 +169,7 @@ BumpArenaProc :: (
|
||||||
case .Restore;
|
case .Restore;
|
||||||
wm := old_ptr.(int);
|
wm := old_ptr.(int);
|
||||||
if wm < 0 || wm >= bump.memory.count
|
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;
|
bump.offset = wm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -185,7 +185,128 @@ PagingArenaProc :: (
|
||||||
|
|
||||||
caller: Source_Code_Location
|
caller: Source_Code_Location
|
||||||
) -> *void {
|
) -> *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;
|
return null;
|
||||||
} @jc.nodocs
|
} @jc.nodocs
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,8 @@
|
||||||
ArrayAppend :: inline (arr: *[..]$T, values: ..T) -> *T {
|
ArrayAppend :: inline (arr: *[..]$T, values: ..T) -> *T {
|
||||||
TrySetAllocator(arr,, allocator = ArenaToAllocator(*context.arena));
|
TrySetAllocator(arr,, allocator = ArenaToAllocator(*context.arena));
|
||||||
|
|
||||||
if values.count == 0 {
|
if values.count == 0
|
||||||
return basic.array_add(arr);
|
{ return basic.array_add(arr); }
|
||||||
}
|
|
||||||
|
|
||||||
count := arr.count;
|
count := arr.count;
|
||||||
basic.array_add(arr, ..values);
|
basic.array_add(arr, ..values);
|
||||||
|
|
|
||||||
|
|
@ -189,6 +189,7 @@ WriteStderrNumber :: #bake_arguments write_number(to_standard_error = true); //
|
||||||
|
|
||||||
#scope_file
|
#scope_file
|
||||||
|
|
||||||
|
// @todo(judah): this probably needs to be exposed...
|
||||||
DebugBuild :: #run -> bool {
|
DebugBuild :: #run -> bool {
|
||||||
// @note(judah): there's not really a good way to detect opt level/build type,
|
// @note(judah): there's not really a good way to detect opt level/build type,
|
||||||
// so just check if debug info is being emitted.
|
// so just check if debug info is being emitted.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
Kilobyte :: 1024;
|
Kilobyte :: 1024;
|
||||||
Megabyte :: 1024 * Kilobyte;
|
Megabyte :: 1024 * Kilobyte;
|
||||||
Gigabyte :: 1024 * Megabyte;
|
Gigabyte :: 1024 * Megabyte;
|
||||||
|
Terabyte :: 1024 * Gigabyte;
|
||||||
|
|
||||||
DefaultAlign :: size_of(*void);
|
DefaultAlign :: size_of(*void);
|
||||||
|
|
||||||
|
|
@ -106,6 +107,63 @@ NextPowerOfTwo :: (x: int) -> int #no_aoc {
|
||||||
return x + 1;
|
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
|
#scope_module
|
||||||
|
|
||||||
|
|
@ -133,3 +191,11 @@ TrySetAllocator :: (array: *[..]$T) #expand {
|
||||||
array.allocator = context.allocator;
|
array.allocator = context.allocator;
|
||||||
}
|
}
|
||||||
} @jc.nodocs
|
} @jc.nodocs
|
||||||
|
|
||||||
|
|
||||||
|
#scope_file
|
||||||
|
|
||||||
|
#if OS == {
|
||||||
|
case .MACOS;
|
||||||
|
darwin :: #import "jc/ext/darwin";
|
||||||
|
}
|
||||||
|
|
|
||||||
14
PLAN.Judah
14
PLAN.Judah
|
|
@ -9,6 +9,19 @@ This is what I'm doing day-to-day.
|
||||||
: notes, todos, etc.
|
: 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
|
/ 09.07.25
|
||||||
|
|
||||||
+ replaced allocators with custom arenas
|
+ 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
|
+ fixed +internal's weird behavior in the test runner
|
||||||
+ ArrayAppend/ArrayGrow now wrap the current arena as the attached Allocator
|
+ ArrayAppend/ArrayGrow now wrap the current arena as the attached Allocator
|
||||||
+ ArenaMode -> ArenaEvent
|
+ ArenaMode -> ArenaEvent
|
||||||
|
+ added pointer alignment procs
|
||||||
|
|
||||||
/ 09.06.25
|
/ 09.06.25
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
NSObject :: struct {};
|
|
||||||
|
|
@ -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);
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
NSObject :: struct {
|
|
||||||
id: u64;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString :: struct {
|
|
||||||
#as using isa: NSObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -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
44
ext/darwin/kernel.jai
Normal 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";
|
||||||
|
|
@ -1,47 +1 @@
|
||||||
#scope_export;
|
#load "kernel.jai";
|
||||||
|
|
||||||
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";
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue