diff --git a/compiler/build/src/program.rs b/compiler/build/src/program.rs index ab036cd98b..4cd62a6fbc 100644 --- a/compiler/build/src/program.rs +++ b/compiler/build/src/program.rs @@ -109,7 +109,7 @@ pub fn gen_from_mono_module( } if name.starts_with("roc_builtins.dict") || name.starts_with("dict.RocDict") { - function.add_attribute(AttributeLoc::Function, attr); + // function.add_attribute(AttributeLoc::Function, attr); } if name.starts_with("roc_builtins.list") || name.starts_with("list.RocList") { diff --git a/compiler/builtins/bitcode/build.zig b/compiler/builtins/bitcode/build.zig index 9d3b0456a7..6deb1a8859 100644 --- a/compiler/builtins/bitcode/build.zig +++ b/compiler/builtins/bitcode/build.zig @@ -4,8 +4,8 @@ const mem = std.mem; const Builder = std.build.Builder; pub fn build(b: *Builder) void { - // b.setPreferredReleaseMode(builtin.Mode.Debug); - b.setPreferredReleaseMode(builtin.Mode.ReleaseFast); + b.setPreferredReleaseMode(builtin.Mode.Debug); + // b.setPreferredReleaseMode(builtin.Mode.ReleaseFast); const mode = b.standardReleaseOptions(); // Options diff --git a/compiler/builtins/bitcode/src/dict.zig b/compiler/builtins/bitcode/src/dict.zig index fb8d60fb2c..d360faf93d 100644 --- a/compiler/builtins/bitcode/src/dict.zig +++ b/compiler/builtins/bitcode/src/dict.zig @@ -763,9 +763,8 @@ const StepperCaller = fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) v pub fn dictWalk(dict: RocDict, stepper: Opaque, stepper_caller: StepperCaller, accum: Opaque, alignment: Alignment, key_width: usize, value_width: usize, accum_width: usize, inc_key: Inc, inc_value: Inc, output: Opaque) callconv(.C) void { // allocate space to write the result of the stepper into // experimentally aliasing the accum and output pointers is not a good idea - const threshold: comptime usize = 64; - var buffer: [threshold]u8 = undefined; - const buffer_allocator = &std.heap.FixedBufferAllocator.init(&buffer).allocator; + const threshold: comptime usize = 512; + const buffer_allocator = &utils.stackFallback(threshold, std.heap.c_allocator).allocator; const alloc: [*]u8 = @ptrCast([*]u8, buffer_allocator.alloc(u8, accum_width) catch unreachable); var b1 = output orelse unreachable; diff --git a/compiler/builtins/bitcode/src/utils.zig b/compiler/builtins/bitcode/src/utils.zig index eb795271df..e3fe885181 100644 --- a/compiler/builtins/bitcode/src/utils.zig +++ b/compiler/builtins/bitcode/src/utils.zig @@ -163,3 +163,161 @@ pub const Ordering = packed enum(u8) { GT = 1, LT = 2, }; + +// including this code because of fixes introduced in https://github.com/ziglang/zig/commit/d73f46b57c1c407bacd0daed8c69c4f14d14a06a +// Are we using a version of zig bigger than 0.7.1? Then shoud be removed and we can use the version from std.heap! + +const mem = std.mem; +const assert = debug.assert; +const debug = std.debug; + +fn sliceContainsPtr(container: []u8, ptr: [*]u8) bool { + return @ptrToInt(ptr) >= @ptrToInt(container.ptr) and + @ptrToInt(ptr) < (@ptrToInt(container.ptr) + container.len); +} + +fn sliceContainsSlice(container: []u8, slice: []u8) bool { + return @ptrToInt(slice.ptr) >= @ptrToInt(container.ptr) and + (@ptrToInt(slice.ptr) + slice.len) <= (@ptrToInt(container.ptr) + container.len); +} + +pub fn stackFallback(comptime size: usize, fallback_allocator: *Allocator) StackFallbackAllocator(size) { + return StackFallbackAllocator(size){ + .buffer = undefined, + .fallback_allocator = fallback_allocator, + .fixed_buffer_allocator = undefined, + .allocator = Allocator{ + .allocFn = StackFallbackAllocator(size).alloc, + .resizeFn = StackFallbackAllocator(size).resize, + }, + }; +} + +pub fn StackFallbackAllocator(comptime size: usize) type { + return struct { + const Self = @This(); + + buffer: [size]u8, + allocator: Allocator, + fallback_allocator: *Allocator, + fixed_buffer_allocator: FixedBufferAllocator, + + pub fn get(self: *Self) *Allocator { + self.fixed_buffer_allocator = FixedBufferAllocator.init(self.buffer[0..]); + return &self.allocator; + } + + fn alloc( + allocator: *Allocator, + len: usize, + ptr_align: u29, + len_align: u29, + return_address: usize, + ) error{OutOfMemory}![]u8 { + const self = @fieldParentPtr(Self, "allocator", allocator); + return FixedBufferAllocator.alloc(&self.fixed_buffer_allocator.allocator, len, ptr_align, len_align, return_address) catch + return self.fallback_allocator.allocFn(self.fallback_allocator, len, ptr_align, len_align, return_address); + } + + fn resize( + allocator: *Allocator, + buf: []u8, + buf_align: u29, + new_len: usize, + len_align: u29, + return_address: usize, + ) error{OutOfMemory}!usize { + const self = @fieldParentPtr(Self, "allocator", allocator); + if (self.fixed_buffer_allocator.ownsPtr(buf.ptr)) { + return FixedBufferAllocator.resize(&self.fixed_buffer_allocator.allocator, buf, buf_align, new_len, len_align, return_address); + } else { + return self.fallback_allocator.resizeFn(self.fallback_allocator, buf, buf_align, new_len, len_align, return_address); + } + } + }; +} + +pub const FixedBufferAllocator = struct { + allocator: Allocator, + end_index: usize, + buffer: []u8, + + pub fn init(buffer: []u8) FixedBufferAllocator { + return FixedBufferAllocator{ + .allocator = Allocator{ + .allocFn = alloc, + .resizeFn = resize, + }, + .buffer = buffer, + .end_index = 0, + }; + } + + pub fn ownsPtr(self: *FixedBufferAllocator, ptr: [*]u8) bool { + return sliceContainsPtr(self.buffer, ptr); + } + + pub fn ownsSlice(self: *FixedBufferAllocator, slice: []u8) bool { + return sliceContainsSlice(self.buffer, slice); + } + + /// NOTE: this will not work in all cases, if the last allocation had an adjusted_index + /// then we won't be able to determine what the last allocation was. This is because + /// the alignForward operation done in alloc is not reverisible. + pub fn isLastAllocation(self: *FixedBufferAllocator, buf: []u8) bool { + return buf.ptr + buf.len == self.buffer.ptr + self.end_index; + } + + fn alloc(allocator: *Allocator, n: usize, ptr_align: u29, len_align: u29, ra: usize) ![]u8 { + const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator); + + const stdout = std.io.getStdOut().writer(); + stdout.print("Hello, {d} {d}!\n", .{ @ptrToInt(self.buffer.ptr), self.end_index }) catch unreachable; + + const aligned_addr = mem.alignForward(@ptrToInt(self.buffer.ptr) + self.end_index, ptr_align); + const adjusted_index = aligned_addr - @ptrToInt(self.buffer.ptr); + const new_end_index = adjusted_index + n; + if (new_end_index > self.buffer.len) { + return error.OutOfMemory; + } + const result = self.buffer[adjusted_index..new_end_index]; + self.end_index = new_end_index; + + return result; + } + + fn resize( + allocator: *Allocator, + buf: []u8, + buf_align: u29, + new_size: usize, + len_align: u29, + return_address: usize, + ) Allocator.Error!usize { + const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator); + assert(self.ownsSlice(buf)); // sanity check + + if (!self.isLastAllocation(buf)) { + if (new_size > buf.len) + return error.OutOfMemory; + return if (new_size == 0) 0 else mem.alignAllocLen(buf.len, new_size, len_align); + } + + if (new_size <= buf.len) { + const sub = buf.len - new_size; + self.end_index -= sub; + return if (new_size == 0) 0 else mem.alignAllocLen(buf.len - sub, new_size, len_align); + } + + const add = new_size - buf.len; + if (add + self.end_index > self.buffer.len) { + return error.OutOfMemory; + } + self.end_index += add; + return new_size; + } + + pub fn reset(self: *FixedBufferAllocator) void { + self.end_index = 0; + } +};