adjust allocator spec to use slices as return type
optimise raw allocator make allocators use new spec remove redundant `lily.` prefix from lily tests kill SimpleAllocator (nobody liked it)
This commit is contained in:
parent
1c2b7f8e83
commit
d1334ada04
|
@ -13,14 +13,14 @@ Allocator := struct {
|
|||
deinit := fn(self: ^Self): void
|
||||
/// should return null on failure.
|
||||
/// should dealloc any intermediate allocations on failure.
|
||||
alloc := fn(self: ^Self, $T: type, count: uint): ?^T
|
||||
alloc := fn(self: ^Self, $T: type, count: uint): ?[]T
|
||||
/// same behaviour as alloc, except:
|
||||
/// must be zeroed.
|
||||
alloc_zeroed := fn(self: ^Self, $T: type, count: uint): ?^T
|
||||
alloc_zeroed := fn(self: ^Self, $T: type, count: uint): ?[]T
|
||||
/// same behaviour as alloc, except:
|
||||
/// must move data to new allocation,
|
||||
/// must ensure the old allocation is freed at some point.
|
||||
realloc := fn(self: ^Self, $T: type, ptr: ^T, new_count: uint): ?^T
|
||||
realloc := fn(self: ^Self, $T: type, ptr: ^T, new_count: uint): ?[]T
|
||||
/// must dealloc or schedule the freeing of the given allocation
|
||||
dealloc := fn(self: ^Self, $T: type, ptr: ^T): void
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ ArenaAllocator := struct {
|
|||
self.last_alloc_size = 0
|
||||
log.debug("deinit: arena allocator")
|
||||
}
|
||||
alloc := fn(self: ^Self, $T: type, count: uint): ?^T {
|
||||
alloc := fn(self: ^Self, $T: type, count: uint): ?[]T {
|
||||
size := @sizeof(T) * count
|
||||
if Config.debug_assertions() & size == 0 {
|
||||
log.error("arena: zero sized allocation")
|
||||
|
@ -72,13 +72,14 @@ ArenaAllocator := struct {
|
|||
self.last_alloc_size = size
|
||||
self.offset = aligned + size
|
||||
log.debug("arena: allocated")
|
||||
return @bitcast(ptr)
|
||||
return @as(^T, @bitcast(ptr))[0..count]
|
||||
}
|
||||
alloc_zeroed := Self.alloc
|
||||
realloc := fn(self: ^Self, $T: type, ptr: ^T, new_count: uint): ?^T {
|
||||
realloc := fn(self: ^Self, $T: type, ptr: ^T, new_count: uint): ?[]T {
|
||||
r0 := @as(^u8, @bitcast(ptr)) != self.current_block.ptr + self.last_alloc_start
|
||||
r1 := self.last_alloc_start + self.last_alloc_size != self.offset
|
||||
if r0 | r1 {
|
||||
// ! (libc) (compiler) bug: checking r0 | r1 here gives "not yet implemented: bool"
|
||||
if Target.current() != .LibC if r0 | r1 {
|
||||
if Config.debug_assertions() {
|
||||
log.error("arena: realloc only supports last allocation")
|
||||
}
|
||||
|
@ -89,13 +90,13 @@ ArenaAllocator := struct {
|
|||
if Config.debug_assertions() {
|
||||
log.warn("arena: useless reallocation (new_size <= old_size)")
|
||||
}
|
||||
return ptr
|
||||
return ptr[0..new_count]
|
||||
}
|
||||
additional := size - self.last_alloc_size
|
||||
if self.offset + additional <= self.current_block.len {
|
||||
self.offset += additional
|
||||
self.last_alloc_size = size
|
||||
return ptr
|
||||
return ptr[0..new_count]
|
||||
}
|
||||
new_size := alloc_size(size)
|
||||
// ! (libc) (compiler) bug: null check broken. unwrapping.
|
||||
|
@ -108,7 +109,7 @@ ArenaAllocator := struct {
|
|||
self.last_alloc_start = 0
|
||||
self.last_alloc_size = size
|
||||
log.debug("arena: reallocated")
|
||||
return @bitcast(new_ptr)
|
||||
return @as(^T, @bitcast(new_ptr))[0..new_count]
|
||||
}
|
||||
$dealloc := fn(self: ^Self, $T: type, ptr: ^T): void {
|
||||
if Config.debug_assertions() log.error("arena: dealloc called. (makes no sense)")
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
.{RawAllocator} := @use("raw.hb");
|
||||
.{SimpleAllocator} := @use("simple.hb");
|
||||
.{ArenaAllocator} := @use("arena.hb");
|
||||
.{ArenaAllocator} := @use("arena.hb")
|
|
@ -1,57 +1,58 @@
|
|||
.{Config, Target, Type, log, collections: .{Vec}} := @use("../lib.hb");
|
||||
|
||||
RawAllocator := struct {
|
||||
ptr: ^u8,
|
||||
size: uint,
|
||||
$new := fn(): Self return .(Type(^u8).uninit(), 0)
|
||||
deinit := fn(self: ^Self): void {
|
||||
self.dealloc(u8, Type(^u8).uninit());
|
||||
*self = Self.new()
|
||||
log.debug("deinit: raw allocator")
|
||||
slice: []u8,
|
||||
$new := fn(): Self return .(Type([]u8).uninit())
|
||||
$deinit := fn(self: ^Self): void {
|
||||
self.dealloc(void, Type(^void).uninit())
|
||||
}
|
||||
alloc := fn(self: ^Self, $T: type, count: uint): ?^T {
|
||||
if Target.calculate_pages(self.size) == Target.calculate_pages(count * @sizeof(T)) {
|
||||
self.size = count * @sizeof(T)
|
||||
return @bitcast(self.ptr)
|
||||
}
|
||||
// ! (libc) (compiler) bug: null check broken. unwrapping.
|
||||
ptr := @unwrap(Target.alloc(count * @sizeof(T)))
|
||||
self.ptr = ptr
|
||||
self.size = count * @sizeof(T)
|
||||
$alloc_zeroed := fn(self: ^Self, $T: type, count: uint): ?[]T {
|
||||
return Self._alloc_common(self, T, count, true)
|
||||
}
|
||||
$alloc := fn(self: ^Self, $T: type, count: uint): ?[]T {
|
||||
return Self._alloc_common(self, T, count, false)
|
||||
}
|
||||
realloc := fn(self: ^Self, $T: type, ptr: ^T, count: uint): ?[]T {
|
||||
size := count * @sizeof(T);
|
||||
if size == 0 return Type([]T).uninit();
|
||||
if self.slice.len == 0 return null;
|
||||
|
||||
log.debug("allocated: raw")
|
||||
return @bitcast(ptr)
|
||||
}
|
||||
alloc_zeroed := fn(self: ^Self, $T: type, count: uint): ?^T {
|
||||
if Target.calculate_pages(self.size) == Target.calculate_pages(count * @sizeof(T)) {
|
||||
self.size = count * @sizeof(T)
|
||||
return @bitcast(self.ptr)
|
||||
if Target.calculate_pages(self.slice.len) >= Target.calculate_pages(size) {
|
||||
return @as(^T, @bitcast(self.slice.ptr))[0..count]
|
||||
}
|
||||
// ! (libc) (compiler) bug: null check broken. unwrapping.
|
||||
ptr := @unwrap(Target.alloc_zeroed(count * @sizeof(T)))
|
||||
self.ptr = ptr
|
||||
self.size = count * @sizeof(T)
|
||||
|
||||
log.debug("allocated: raw")
|
||||
return @bitcast(ptr)
|
||||
}
|
||||
realloc := fn(self: ^Self, $T: type, ptr: ^T, count: uint): ?^T {
|
||||
if self.size == 0 return null
|
||||
if Target.calculate_pages(self.size) == Target.calculate_pages(count * @sizeof(T)) {
|
||||
self.size = count * @sizeof(T)
|
||||
return @bitcast(self.ptr)
|
||||
}
|
||||
log.debug("reallocated: raw")
|
||||
// ! (libc) (compiler) bug: null check broken. unwrapping.
|
||||
new_ptr := @unwrap(Target.realloc(self.ptr, self.size, count * @sizeof(T)))
|
||||
self.ptr = new_ptr
|
||||
self.size = count * @sizeof(T)
|
||||
return @bitcast(new_ptr)
|
||||
new_ptr := @unwrap(Target.realloc(self.slice.ptr, self.slice.len, size));
|
||||
self.slice = new_ptr[0..size];
|
||||
log.debug("reallocated: raw");
|
||||
return @as(^T, @bitcast(new_ptr))[0..count]
|
||||
}
|
||||
// ! INLINING THIS FUNCTION CAUSES MISCOMPILATION!! DO NOT INLINE IT!! :) :) :)
|
||||
dealloc := fn(self: ^Self, $T: type, ptr: ^T): void {
|
||||
if self.size == 0 return;
|
||||
Target.dealloc(self.ptr, self.size)
|
||||
log.debug("deallocated: raw")
|
||||
if self.slice.len > 0 {
|
||||
Target.dealloc(self.slice.ptr, self.slice.len)
|
||||
log.debug("deallocated: raw")
|
||||
};
|
||||
*self = Self.new()
|
||||
}
|
||||
_alloc_common := fn(self: ^Self, $T: type, count: uint, zeroed: bool): ?[]T {
|
||||
size := count * @sizeof(T);
|
||||
if size == 0 return Type([]T).uninit();
|
||||
|
||||
if Target.calculate_pages(self.slice.len) >= Target.calculate_pages(size) {
|
||||
return @as(^T, @bitcast(self.slice.ptr))[0..count]
|
||||
}
|
||||
|
||||
if self.slice.len > 0 {
|
||||
Target.dealloc(self.slice.ptr, self.slice.len)
|
||||
}
|
||||
ptr := Type(^u8).uninit()
|
||||
if zeroed {
|
||||
ptr = @unwrap(Target.alloc_zeroed(size))
|
||||
} else {
|
||||
ptr = @unwrap(Target.alloc(size))
|
||||
}
|
||||
self.slice = ptr[0..size]
|
||||
log.debug("allocated: raw")
|
||||
return @as(^T, @bitcast(ptr))[0..count]
|
||||
}
|
||||
}
|
|
@ -1,91 +0,0 @@
|
|||
.{Config, Target, Type, log, collections: .{Vec}} := @use("../lib.hb");
|
||||
.{RawAllocator} := @use("lib.hb")
|
||||
|
||||
Allocation := struct {
|
||||
ptr: ^u8,
|
||||
len: uint,
|
||||
}
|
||||
|
||||
// ! THIS ALLOCATOR IS TEMPORARY
|
||||
SimpleAllocator := struct {
|
||||
allocations: Vec(Allocation, RawAllocator),
|
||||
raw: RawAllocator,
|
||||
|
||||
$new := fn(): Self {
|
||||
raw := RawAllocator.new()
|
||||
return .(Vec(Allocation, RawAllocator).new(&raw), raw)
|
||||
}
|
||||
deinit := fn(self: ^Self): void {
|
||||
loop if self.allocations.len() == 0 break else {
|
||||
alloced := self.allocations.pop_unchecked()
|
||||
match Target.current() {
|
||||
.LibC => Target.dealloc(alloced.ptr),
|
||||
.AbleOS => Target.dealloc(alloced.ptr, alloced.len),
|
||||
}
|
||||
}
|
||||
|
||||
self.allocations.deinit()
|
||||
self.raw.deinit()
|
||||
log.debug("deinit: allocator")
|
||||
}
|
||||
alloc := fn(self: ^Self, $T: type, count: uint): ?^T {
|
||||
ptr := Target.alloc(count * @sizeof(T))
|
||||
if Target.current() == .AbleOS {
|
||||
if ptr != null self.allocations.push(.(ptr, count * @sizeof(T)))
|
||||
}
|
||||
log.debug("allocated")
|
||||
|
||||
return @bitcast(ptr)
|
||||
}
|
||||
alloc_zeroed := fn(self: ^Self, $T: type, count: uint): ?^T {
|
||||
ptr := Target.alloc_zeroed(count * @sizeof(T))
|
||||
if Target.current() == .AbleOS {
|
||||
if ptr != null self.allocations.push(.(ptr, count * @sizeof(T)))
|
||||
}
|
||||
return @bitcast(ptr)
|
||||
}
|
||||
realloc := fn(self: ^Self, $T: type, ptr: ^T, count: uint): ?^T {
|
||||
match Target.current() {
|
||||
.AbleOS => {
|
||||
alloced := self._find_and_remove(@bitcast(ptr))
|
||||
if alloced == null return null
|
||||
new_ptr := Target.realloc(@bitcast(ptr), alloced.len, count * @sizeof(T))
|
||||
if new_ptr != null {
|
||||
self.allocations.push(.(new_ptr, count * @sizeof(T)))
|
||||
} else {
|
||||
self.allocations.push(alloced)
|
||||
}
|
||||
return @bitcast(new_ptr)
|
||||
},
|
||||
.LibC => {
|
||||
new_ptr := Target.realloc(@bitcast(ptr), count * @sizeof(T))
|
||||
log.debug("reallocated")
|
||||
return @bitcast(new_ptr)
|
||||
},
|
||||
}
|
||||
}
|
||||
dealloc := fn(self: ^Self, $T: type, ptr: ^T): void {
|
||||
match Target.current() {
|
||||
.AbleOS => {
|
||||
alloced := self._find_and_remove(@bitcast(ptr))
|
||||
if alloced != null Target.dealloc(@bitcast(ptr), alloced.len)
|
||||
},
|
||||
.LibC => {
|
||||
Target.dealloc(@bitcast(ptr))
|
||||
},
|
||||
}
|
||||
log.debug("freed")
|
||||
}
|
||||
_find_and_remove := fn(self: ^Self, ptr: ^u8): ?Allocation {
|
||||
i := 0
|
||||
loop if i == self.allocations.len() break else {
|
||||
defer i += 1
|
||||
alloced := self.allocations.get_unchecked(i)
|
||||
if alloced.ptr == ptr {
|
||||
_ = self.allocations.swap_remove(i)
|
||||
return alloced
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
|
@ -34,13 +34,13 @@ Vec := fn($T: type, $Allocator: type): type return struct {
|
|||
if self.cap == 0 {
|
||||
// ! (libc) (compiler) bug: null check broken, so unwrapping (unsafe!)
|
||||
new_alloc := @unwrap(self.allocator.alloc(T, 1))
|
||||
self.slice.ptr = new_alloc
|
||||
self.cap = 1
|
||||
self.slice.ptr = new_alloc.ptr
|
||||
self.cap = new_alloc.len
|
||||
} else {
|
||||
// ! (libc) (compiler) bug: null check broken, so unwrapping (unsafe!)
|
||||
new_alloc := @unwrap(self.allocator.realloc(T, self.slice.ptr, self.cap * 2))
|
||||
self.slice.ptr = new_alloc
|
||||
self.cap *= 2
|
||||
self.slice.ptr = new_alloc.ptr
|
||||
self.cap = new_alloc.len
|
||||
}
|
||||
}
|
||||
self.slice[self.slice.len] = value
|
||||
|
|
24
src/main.hb
24
src/main.hb
|
@ -70,14 +70,26 @@ main := fn(argc: uint, argv: []^void): uint {
|
|||
// lily.print(map.get("asdfasdf!"))
|
||||
// lily.print(map.get("Hello, World!"))
|
||||
|
||||
id := lily.process.fork()
|
||||
lily.print("hello, world")
|
||||
// id := lily.process.fork()
|
||||
// lily.print("hello, world")
|
||||
|
||||
if id == 0 {
|
||||
lily.print("child")
|
||||
} else {
|
||||
lily.print("parent")
|
||||
// if id == 0 {
|
||||
// lily.print("child")
|
||||
// } else {
|
||||
// lily.print("parent")
|
||||
// }
|
||||
|
||||
Allocator := lily.alloc.ArenaAllocator
|
||||
allocator := Allocator.new()
|
||||
defer allocator.deinit()
|
||||
vec := lily.collections.Vec(uint, Allocator).new(&allocator)
|
||||
i := 0
|
||||
// ! (libc) (compiler) bug: i > 512 causes SIGSEGV
|
||||
loop if i == 1024 break else {
|
||||
defer i += 1
|
||||
vec.push(i)
|
||||
}
|
||||
lily.print(vec.slice.len)
|
||||
|
||||
return 0
|
||||
}
|
|
@ -10,8 +10,8 @@ main := fn(): u8 {
|
|||
if b == null return 1
|
||||
c := allocator.alloc(u8, 100)
|
||||
if c == null return 1
|
||||
d := allocator.realloc(u8, c, 100)
|
||||
d := allocator.realloc(u8, c.ptr, 100)
|
||||
if d == null return 1
|
||||
if d != c return 1
|
||||
if d.ptr != c.ptr return 1
|
||||
return 0
|
||||
}
|
|
@ -11,8 +11,8 @@ main := fn(): u8 {
|
|||
if b == null return 1
|
||||
c := allocator.alloc(u8, 100)
|
||||
if c == null return 1
|
||||
d := allocator.realloc(u8, c, 100)
|
||||
d := allocator.realloc(u8, c.ptr, 100)
|
||||
if d == null return 1
|
||||
if d != c return 1
|
||||
if d.ptr != c.ptr return 1
|
||||
return 0
|
||||
}
|
Loading…
Reference in a new issue