diff --git a/compiler/builtins/bitcode/src/dict.zig b/compiler/builtins/bitcode/src/dict.zig index da03b49e18..9a004c63c6 100644 --- a/compiler/builtins/bitcode/src/dict.zig +++ b/compiler/builtins/bitcode/src/dict.zig @@ -2,7 +2,6 @@ const std = @import("std"); const testing = std.testing; const expectEqual = testing.expectEqual; const mem = std.mem; -const Allocator = mem.Allocator; const assert = std.debug.assert; const utils = @import("utils.zig"); @@ -94,20 +93,18 @@ const Alignment = packed enum(u8) { }; pub fn decref( - allocator: *Allocator, alignment: Alignment, bytes_or_null: ?[*]u8, data_bytes: usize, ) void { - return utils.decref(allocator, alignment.toUsize(), bytes_or_null, data_bytes); + return utils.decref(alignment.toUsize(), bytes_or_null, data_bytes); } pub fn allocateWithRefcount( - allocator: *Allocator, alignment: Alignment, data_bytes: usize, ) [*]u8 { - return utils.allocateWithRefcount(allocator, alignment.toUsize(), data_bytes); + return utils.allocateWithRefcount(alignment.toUsize(), data_bytes); } pub const RocDict = extern struct { @@ -124,7 +121,6 @@ pub const RocDict = extern struct { } pub fn allocate( - allocator: *Allocator, number_of_levels: usize, number_of_entries: usize, alignment: Alignment, @@ -136,7 +132,7 @@ pub const RocDict = extern struct { const data_bytes = number_of_slots * slot_size; return RocDict{ - .dict_bytes = allocateWithRefcount(allocator, alignment, data_bytes), + .dict_bytes = allocateWithRefcount(alignment, data_bytes), .number_of_levels = number_of_levels, .dict_entries_len = number_of_entries, }; @@ -144,7 +140,6 @@ pub const RocDict = extern struct { pub fn reallocate( self: RocDict, - allocator: *Allocator, alignment: Alignment, key_width: usize, value_width: usize, @@ -157,7 +152,7 @@ pub const RocDict = extern struct { const delta_capacity = new_capacity - old_capacity; const data_bytes = new_capacity * slot_size; - const first_slot = allocateWithRefcount(allocator, alignment, data_bytes); + const first_slot = allocateWithRefcount(alignment, data_bytes); // transfer the memory @@ -204,7 +199,7 @@ pub const RocDict = extern struct { }; // NOTE we fuse an increment of all keys/values with a decrement of the input dict - decref(allocator, alignment, self.dict_bytes, self.capacity() * slotSize(key_width, value_width)); + decref(alignment, self.dict_bytes, self.capacity() * slotSize(key_width, value_width)); return result; } @@ -236,7 +231,7 @@ pub const RocDict = extern struct { return totalCapacityAtLevel(self.number_of_levels); } - pub fn makeUnique(self: RocDict, allocator: *Allocator, alignment: Alignment, key_width: usize, value_width: usize) RocDict { + pub fn makeUnique(self: RocDict, alignment: Alignment, key_width: usize, value_width: usize) RocDict { if (self.isEmpty()) { return self; } @@ -246,7 +241,7 @@ pub const RocDict = extern struct { } // unfortunately, we have to clone - var new_dict = RocDict.allocate(allocator, self.number_of_levels, self.dict_entries_len, alignment, key_width, value_width); + var new_dict = RocDict.allocate(self.number_of_levels, self.dict_entries_len, alignment, key_width, value_width); var old_bytes: [*]u8 = @ptrCast([*]u8, self.dict_bytes); var new_bytes: [*]u8 = @ptrCast([*]u8, new_dict.dict_bytes); @@ -256,7 +251,7 @@ pub const RocDict = extern struct { // NOTE we fuse an increment of all keys/values with a decrement of the input dict const data_bytes = self.capacity() * slotSize(key_width, value_width); - decref(allocator, alignment, self.dict_bytes, data_bytes); + decref(alignment, self.dict_bytes, data_bytes); return new_dict; } @@ -420,7 +415,7 @@ const Dec = fn (?[*]u8) callconv(.C) void; pub fn dictInsert(input: RocDict, alignment: Alignment, key: Opaque, key_width: usize, value: Opaque, value_width: usize, hash_fn: HashFn, is_eq: EqFn, dec_key: Dec, dec_value: Dec, output: *RocDict) callconv(.C) void { var seed: u64 = INITIAL_SEED; - var result = input.makeUnique(std.heap.c_allocator, alignment, key_width, value_width); + var result = input.makeUnique(alignment, key_width, value_width); var current_level: usize = 1; var current_level_size: usize = 8; @@ -428,7 +423,7 @@ pub fn dictInsert(input: RocDict, alignment: Alignment, key: Opaque, key_width: while (true) { if (current_level > result.number_of_levels) { - result = result.reallocate(std.heap.c_allocator, alignment, key_width, value_width); + result = result.reallocate(alignment, key_width, value_width); } const hash = hash_fn(seed, key); @@ -484,7 +479,7 @@ pub fn dictRemove(input: RocDict, alignment: Alignment, key: Opaque, key_width: return; }, MaybeIndex.index => |index| { - var dict = input.makeUnique(std.heap.c_allocator, alignment, key_width, value_width); + var dict = input.makeUnique(alignment, key_width, value_width); assert(index < dict.capacity()); @@ -499,7 +494,7 @@ pub fn dictRemove(input: RocDict, alignment: Alignment, key: Opaque, key_width: // if the dict is now completely empty, free its allocation if (dict.dict_entries_len == 0) { const data_bytes = dict.capacity() * slotSize(key_width, value_width); - decref(std.heap.c_allocator, alignment, dict.dict_bytes, data_bytes); + decref(alignment, dict.dict_bytes, data_bytes); output.* = RocDict.empty(); return; } @@ -572,7 +567,7 @@ pub fn dictKeys(dict: RocDict, alignment: Alignment, key_width: usize, value_wid } const data_bytes = length * key_width; - var ptr = allocateWithRefcount(std.heap.c_allocator, alignment, data_bytes); + var ptr = allocateWithRefcount(alignment, data_bytes); var offset = blk: { if (alignment.keyFirst()) { @@ -621,7 +616,7 @@ pub fn dictValues(dict: RocDict, alignment: Alignment, key_width: usize, value_w } const data_bytes = length * value_width; - var ptr = allocateWithRefcount(std.heap.c_allocator, alignment, data_bytes); + var ptr = allocateWithRefcount(alignment, data_bytes); var offset = blk: { if (alignment.keyFirst()) { @@ -655,7 +650,7 @@ fn doNothing(ptr: Opaque) callconv(.C) void { } pub fn dictUnion(dict1: RocDict, dict2: RocDict, alignment: Alignment, key_width: usize, value_width: usize, hash_fn: HashFn, is_eq: EqFn, inc_key: Inc, inc_value: Inc, output: *RocDict) callconv(.C) void { - output.* = dict1.makeUnique(std.heap.c_allocator, alignment, key_width, value_width); + output.* = dict1.makeUnique(alignment, key_width, value_width); var i: usize = 0; while (i < dict2.capacity()) : (i += 1) { @@ -690,7 +685,7 @@ pub fn dictUnion(dict1: RocDict, dict2: RocDict, alignment: Alignment, key_width } pub fn dictIntersection(dict1: RocDict, dict2: RocDict, alignment: Alignment, key_width: usize, value_width: usize, hash_fn: HashFn, is_eq: EqFn, dec_key: Inc, dec_value: Inc, output: *RocDict) callconv(.C) void { - output.* = dict1.makeUnique(std.heap.c_allocator, alignment, key_width, value_width); + output.* = dict1.makeUnique(alignment, key_width, value_width); var i: usize = 0; const size = dict1.capacity(); @@ -715,7 +710,7 @@ pub fn dictIntersection(dict1: RocDict, dict2: RocDict, alignment: Alignment, ke } pub fn dictDifference(dict1: RocDict, dict2: RocDict, alignment: Alignment, key_width: usize, value_width: usize, hash_fn: HashFn, is_eq: EqFn, dec_key: Dec, dec_value: Dec, output: *RocDict) callconv(.C) void { - output.* = dict1.makeUnique(std.heap.c_allocator, alignment, key_width, value_width); + output.* = dict1.makeUnique(alignment, key_width, value_width); var i: usize = 0; const size = dict1.capacity(); @@ -756,16 +751,17 @@ pub fn setFromList(list: RocList, alignment: Alignment, key_width: usize, value_ // NOTE: decref checks for the empty case const data_bytes = size * key_width; - decref(std.heap.c_allocator, alignment, list.bytes, data_bytes); + decref(alignment, list.bytes, data_bytes); } const StepperCaller = fn (?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8, ?[*]u8) callconv(.C) void; 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 { + const alignment_usize = alignment.toUsize(); // allocate space to write the result of the stepper into // experimentally aliasing the accum and output pointers is not a good idea - const alloc: [*]u8 = @ptrCast([*]u8, std.heap.c_allocator.alloc(u8, accum_width) catch unreachable); + const bytes_ptr: [*]u8 = utils.alloc(alignment_usize, accum_width); var b1 = output orelse unreachable; - var b2 = alloc; + var b2 = bytes_ptr; @memcpy(b2, accum orelse unreachable, accum_width); @@ -788,8 +784,8 @@ pub fn dictWalk(dict: RocDict, stepper: Opaque, stepper_caller: StepperCaller, a } @memcpy(output orelse unreachable, b2, accum_width); - std.heap.c_allocator.free(alloc[0..accum_width]); + utils.dealloc(alignment_usize, bytes_ptr); const data_bytes = dict.capacity() * slotSize(key_width, value_width); - decref(std.heap.c_allocator, alignment, dict.dict_bytes, data_bytes); + decref(alignment, dict.dict_bytes, data_bytes); } diff --git a/compiler/builtins/bitcode/src/list.zig b/compiler/builtins/bitcode/src/list.zig index bd31d71951..2080a9b388 100644 --- a/compiler/builtins/bitcode/src/list.zig +++ b/compiler/builtins/bitcode/src/list.zig @@ -2,7 +2,6 @@ const std = @import("std"); const utils = @import("utils.zig"); const RocResult = utils.RocResult; const mem = std.mem; -const Allocator = mem.Allocator; const TAG_WIDTH = 8; @@ -42,7 +41,6 @@ pub const RocList = extern struct { } pub fn allocate( - allocator: *Allocator, alignment: usize, length: usize, element_size: usize, @@ -50,12 +48,12 @@ pub const RocList = extern struct { const data_bytes = length * element_size; return RocList{ - .bytes = utils.allocateWithRefcount(allocator, alignment, data_bytes), + .bytes = utils.allocateWithRefcount(alignment, data_bytes), .length = length, }; } - pub fn makeUnique(self: RocList, allocator: *Allocator, alignment: usize, element_width: usize) RocList { + pub fn makeUnique(self: RocList, alignment: usize, element_width: usize) RocList { if (self.isEmpty()) { return self; } @@ -65,7 +63,7 @@ pub const RocList = extern struct { } // unfortunately, we have to clone - var new_list = RocList.allocate(allocator, alignment, self.length, element_width); + var new_list = RocList.allocate(alignment, self.length, element_width); var old_bytes: [*]u8 = @ptrCast([*]u8, self.bytes); var new_bytes: [*]u8 = @ptrCast([*]u8, new_list.bytes); @@ -75,33 +73,31 @@ pub const RocList = extern struct { // NOTE we fuse an increment of all keys/values with a decrement of the input dict const data_bytes = self.len() * element_width; - utils.decref(allocator, alignment, self.bytes, data_bytes); + utils.decref(alignment, self.bytes, data_bytes); return new_list; } pub fn reallocate( self: RocList, - allocator: *Allocator, alignment: usize, new_length: usize, element_width: usize, ) RocList { if (self.bytes) |source_ptr| { if (self.isUnique()) { - const new_source = utils.unsafeReallocate(source_ptr, allocator, alignment, self.len(), new_length, element_width); + const new_source = utils.unsafeReallocate(source_ptr, alignment, self.len(), new_length, element_width); return RocList{ .bytes = new_source, .length = new_length }; } } - return self.reallocateFresh(allocator, alignment, new_length, element_width); + return self.reallocateFresh(alignment, new_length, element_width); } /// reallocate by explicitly making a new allocation and copying elements over fn reallocateFresh( self: RocList, - allocator: *Allocator, alignment: usize, new_length: usize, element_width: usize, @@ -110,7 +106,7 @@ pub const RocList = extern struct { const delta_length = new_length - old_length; const data_bytes = new_length * element_width; - const first_slot = utils.allocateWithRefcount(allocator, alignment, data_bytes); + const first_slot = utils.allocateWithRefcount(alignment, data_bytes); // transfer the memory @@ -126,7 +122,7 @@ pub const RocList = extern struct { .length = new_length, }; - utils.decref(allocator, alignment, self.bytes, old_length * element_width); + utils.decref(alignment, self.bytes, old_length * element_width); return result; } @@ -155,7 +151,7 @@ pub fn listReverse(list: RocList, alignment: usize, element_width: usize) callco return list; } else { - const output = RocList.allocate(std.heap.c_allocator, alignment, size, element_width); + const output = RocList.allocate(alignment, size, element_width); const target_ptr = output.bytes orelse unreachable; @@ -165,7 +161,7 @@ pub fn listReverse(list: RocList, alignment: usize, element_width: usize) callco @memcpy(target_ptr + (i * element_width), source_ptr + (last_position * element_width), element_width); } - utils.decref(std.heap.c_allocator, alignment, list.bytes, size * element_width); + utils.decref(alignment, list.bytes, size * element_width); return output; } @@ -178,14 +174,14 @@ pub fn listMap(list: RocList, transform: Opaque, caller: Caller1, alignment: usi if (list.bytes) |source_ptr| { const size = list.len(); var i: usize = 0; - const output = RocList.allocate(std.heap.c_allocator, alignment, size, new_element_width); + const output = RocList.allocate(alignment, size, new_element_width); const target_ptr = output.bytes orelse unreachable; while (i < size) : (i += 1) { caller(transform, source_ptr + (i * old_element_width), target_ptr + (i * new_element_width)); } - utils.decref(std.heap.c_allocator, alignment, list.bytes, size * old_element_width); + utils.decref(alignment, list.bytes, size * old_element_width); return output; } else { @@ -197,14 +193,14 @@ pub fn listMapWithIndex(list: RocList, transform: Opaque, caller: Caller2, align if (list.bytes) |source_ptr| { const size = list.len(); var i: usize = 0; - const output = RocList.allocate(std.heap.c_allocator, alignment, size, new_element_width); + const output = RocList.allocate(alignment, size, new_element_width); const target_ptr = output.bytes orelse unreachable; while (i < size) : (i += 1) { caller(transform, @ptrCast(?[*]u8, &i), source_ptr + (i * old_element_width), target_ptr + (i * new_element_width)); } - utils.decref(std.heap.c_allocator, alignment, list.bytes, size * old_element_width); + utils.decref(alignment, list.bytes, size * old_element_width); return output; } else { @@ -217,7 +213,7 @@ pub fn listMap2(list1: RocList, list2: RocList, transform: Opaque, caller: Calle if (list1.bytes) |source_a| { if (list2.bytes) |source_b| { - const output = RocList.allocate(std.heap.c_allocator, alignment, output_length, c_width); + const output = RocList.allocate(alignment, output_length, c_width); const target_ptr = output.bytes orelse unreachable; var i: usize = 0; @@ -242,8 +238,8 @@ pub fn listMap2(list1: RocList, list2: RocList, transform: Opaque, caller: Calle } } - utils.decref(std.heap.c_allocator, alignment, list1.bytes, list1.len() * a_width); - utils.decref(std.heap.c_allocator, alignment, list2.bytes, list2.len() * b_width); + utils.decref(alignment, list1.bytes, list1.len() * a_width); + utils.decref(alignment, list2.bytes, list2.len() * b_width); return output; } else { @@ -253,7 +249,7 @@ pub fn listMap2(list1: RocList, list2: RocList, transform: Opaque, caller: Calle const element_a = source_a + i * a_width; dec_a(element_a); } - utils.decref(std.heap.c_allocator, alignment, list1.bytes, list1.len() * a_width); + utils.decref(alignment, list1.bytes, list1.len() * a_width); return RocList.empty(); } @@ -265,7 +261,7 @@ pub fn listMap2(list1: RocList, list2: RocList, transform: Opaque, caller: Calle const element_b = source_b + i * b_width; dec_b(element_b); } - utils.decref(std.heap.c_allocator, alignment, list2.bytes, list2.len() * b_width); + utils.decref(alignment, list2.bytes, list2.len() * b_width); } return RocList.empty(); @@ -279,7 +275,7 @@ pub fn listMap3(list1: RocList, list2: RocList, list3: RocList, transform: Opaqu if (list1.bytes) |source_a| { if (list2.bytes) |source_b| { if (list3.bytes) |source_c| { - const output = RocList.allocate(std.heap.c_allocator, alignment, output_length, d_width); + const output = RocList.allocate(alignment, output_length, d_width); const target_ptr = output.bytes orelse unreachable; var i: usize = 0; @@ -318,9 +314,9 @@ pub fn listMap3(list1: RocList, list2: RocList, list3: RocList, transform: Opaqu } } - utils.decref(std.heap.c_allocator, alignment, list1.bytes, list1.len() * a_width); - utils.decref(std.heap.c_allocator, alignment, list2.bytes, list2.len() * b_width); - utils.decref(std.heap.c_allocator, alignment, list3.bytes, list3.len() * c_width); + utils.decref(alignment, list1.bytes, list1.len() * a_width); + utils.decref(alignment, list2.bytes, list2.len() * b_width); + utils.decref(alignment, list3.bytes, list3.len() * c_width); return output; } else { @@ -330,7 +326,7 @@ pub fn listMap3(list1: RocList, list2: RocList, list3: RocList, transform: Opaqu const element_a = source_a + i * a_width; dec_a(element_a); } - utils.decref(std.heap.c_allocator, alignment, list1.bytes, list1.len() * a_width); + utils.decref(alignment, list1.bytes, list1.len() * a_width); // consume list2 elements (we know there is at least one because the list1.bytes pointer is non-null i = 0; @@ -338,7 +334,7 @@ pub fn listMap3(list1: RocList, list2: RocList, list3: RocList, transform: Opaqu const element_b = source_b + i * b_width; dec_b(element_b); } - utils.decref(std.heap.c_allocator, alignment, list2.bytes, list2.len() * b_width); + utils.decref(alignment, list2.bytes, list2.len() * b_width); return RocList.empty(); } @@ -350,7 +346,7 @@ pub fn listMap3(list1: RocList, list2: RocList, list3: RocList, transform: Opaqu dec_a(element_a); } - utils.decref(std.heap.c_allocator, alignment, list1.bytes, list1.len() * a_width); + utils.decref(alignment, list1.bytes, list1.len() * a_width); // consume list3 elements (if any) if (list3.bytes) |source_c| { @@ -361,7 +357,7 @@ pub fn listMap3(list1: RocList, list2: RocList, list3: RocList, transform: Opaqu dec_c(element_c); } - utils.decref(std.heap.c_allocator, alignment, list3.bytes, list3.len() * c_width); + utils.decref(alignment, list3.bytes, list3.len() * c_width); } return RocList.empty(); @@ -376,7 +372,7 @@ pub fn listMap3(list1: RocList, list2: RocList, list3: RocList, transform: Opaqu dec_b(element_b); } - utils.decref(std.heap.c_allocator, alignment, list2.bytes, list2.len() * b_width); + utils.decref(alignment, list2.bytes, list2.len() * b_width); } // consume list3 elements (if any) @@ -388,7 +384,7 @@ pub fn listMap3(list1: RocList, list2: RocList, list3: RocList, transform: Opaqu dec_c(element_c); } - utils.decref(std.heap.c_allocator, alignment, list3.bytes, list3.len() * c_width); + utils.decref(alignment, list3.bytes, list3.len() * c_width); } return RocList.empty(); @@ -399,7 +395,7 @@ pub fn listKeepIf(list: RocList, transform: Opaque, caller: Caller1, alignment: if (list.bytes) |source_ptr| { const size = list.len(); var i: usize = 0; - var output = RocList.allocate(std.heap.c_allocator, alignment, list.len(), list.len() * element_width); + var output = RocList.allocate(alignment, list.len(), list.len() * element_width); const target_ptr = output.bytes orelse unreachable; var kept: usize = 0; @@ -419,11 +415,11 @@ pub fn listKeepIf(list: RocList, transform: Opaque, caller: Caller1, alignment: } // consume the input list - utils.decref(std.heap.c_allocator, alignment, list.bytes, size * element_width); + utils.decref(alignment, list.bytes, size * element_width); if (kept == 0) { // if the output is empty, deallocate the space we made for the result - utils.decref(std.heap.c_allocator, alignment, output.bytes, size * element_width); + utils.decref(alignment, output.bytes, size * element_width); return RocList.empty(); } else { output.length = kept; @@ -447,10 +443,10 @@ pub fn listKeepResult(list: RocList, is_good_constructor: fn (RocResult) bool, t if (list.bytes) |source_ptr| { const size = list.len(); var i: usize = 0; - var output = RocList.allocate(std.heap.c_allocator, alignment, list.len(), list.len() * after_width); + var output = RocList.allocate(alignment, list.len(), list.len() * after_width); const target_ptr = output.bytes orelse unreachable; - var temporary = @ptrCast([*]u8, std.heap.c_allocator.alloc(u8, result_width) catch unreachable); + var temporary = @ptrCast([*]u8, utils.alloc(alignment, result_width)); var kept: usize = 0; while (i < size) : (i += 1) { @@ -469,11 +465,11 @@ pub fn listKeepResult(list: RocList, is_good_constructor: fn (RocResult) bool, t } } - utils.decref(std.heap.c_allocator, alignment, list.bytes, size * before_width); - std.heap.c_allocator.free(temporary[0..result_width]); + utils.decref(alignment, list.bytes, size * before_width); + utils.dealloc(alignment, temporary); if (kept == 0) { - utils.decref(std.heap.c_allocator, alignment, output.bytes, size * after_width); + utils.decref(alignment, output.bytes, size * after_width); return RocList.empty(); } else { output.length = kept; @@ -494,9 +490,9 @@ pub fn listWalk(list: RocList, stepper: Opaque, stepper_caller: Caller2, accum: return; } - const alloc: [*]u8 = @ptrCast([*]u8, std.heap.c_allocator.alloc(u8, accum_width) catch unreachable); + const bytes_ptr: [*]u8 = utils.alloc(alignment, accum_width); var b1 = output orelse unreachable; - var b2 = alloc; + var b2 = bytes_ptr; @memcpy(b2, accum orelse unreachable, accum_width); @@ -514,10 +510,10 @@ pub fn listWalk(list: RocList, stepper: Opaque, stepper_caller: Caller2, accum: } @memcpy(output orelse unreachable, b2, accum_width); - std.heap.c_allocator.free(alloc[0..accum_width]); + utils.dealloc(alignment, bytes_ptr); const data_bytes = list.len() * element_width; - utils.decref(std.heap.c_allocator, alignment, list.bytes, data_bytes); + utils.decref(alignment, list.bytes, data_bytes); } pub fn listWalkBackwards(list: RocList, stepper: Opaque, stepper_caller: Caller2, accum: Opaque, alignment: usize, element_width: usize, accum_width: usize, output: Opaque) callconv(.C) void { @@ -530,9 +526,9 @@ pub fn listWalkBackwards(list: RocList, stepper: Opaque, stepper_caller: Caller2 return; } - const alloc: [*]u8 = @ptrCast([*]u8, std.heap.c_allocator.alloc(u8, accum_width) catch unreachable); + const bytes_ptr: [*]u8 = utils.alloc(alignment, accum_width); var b1 = output orelse unreachable; - var b2 = alloc; + var b2 = bytes_ptr; @memcpy(b2, accum orelse unreachable, accum_width); @@ -551,10 +547,10 @@ pub fn listWalkBackwards(list: RocList, stepper: Opaque, stepper_caller: Caller2 } @memcpy(output orelse unreachable, b2, accum_width); - std.heap.c_allocator.free(alloc[0..accum_width]); + utils.dealloc(alignment, bytes_ptr); const data_bytes = list.len() * element_width; - utils.decref(std.heap.c_allocator, alignment, list.bytes, data_bytes); + utils.decref(alignment, list.bytes, data_bytes); } pub fn listWalkUntil(list: RocList, stepper: Opaque, stepper_caller: Caller2, accum: Opaque, alignment: usize, element_width: usize, accum_width: usize, dec: Dec, output: Opaque) callconv(.C) void { @@ -570,18 +566,18 @@ pub fn listWalkUntil(list: RocList, stepper: Opaque, stepper_caller: Caller2, ac return; } - const alloc: [*]u8 = @ptrCast([*]u8, std.heap.c_allocator.alloc(u8, TAG_WIDTH + accum_width) catch unreachable); + const bytes_ptr: [*]u8 = utils.alloc(alignment, TAG_WIDTH + accum_width); - @memcpy(alloc + TAG_WIDTH, accum orelse unreachable, accum_width); + @memcpy(bytes_ptr + TAG_WIDTH, accum orelse unreachable, accum_width); if (list.bytes) |source_ptr| { var i: usize = 0; const size = list.len(); while (i < size) : (i += 1) { const element = source_ptr + i * element_width; - stepper_caller(stepper, element, alloc + TAG_WIDTH, alloc); + stepper_caller(stepper, element, bytes_ptr + TAG_WIDTH, bytes_ptr); - const usizes: [*]usize = @ptrCast([*]usize, @alignCast(8, alloc)); + const usizes: [*]usize = @ptrCast([*]usize, @alignCast(8, bytes_ptr)); if (usizes[0] != 0) { // decrement refcount of the remaining items i += 1; @@ -593,11 +589,11 @@ pub fn listWalkUntil(list: RocList, stepper: Opaque, stepper_caller: Caller2, ac } } - @memcpy(output orelse unreachable, alloc + TAG_WIDTH, accum_width); - std.heap.c_allocator.free(alloc[0 .. TAG_WIDTH + accum_width]); + @memcpy(output orelse unreachable, bytes_ptr + TAG_WIDTH, accum_width); + utils.dealloc(alignment, bytes_ptr); const data_bytes = list.len() * element_width; - utils.decref(std.heap.c_allocator, alignment, list.bytes, data_bytes); + utils.decref(alignment, list.bytes, data_bytes); } // List.contains : List k, k -> Bool @@ -621,8 +617,7 @@ pub fn listRepeat(count: usize, alignment: usize, element: Opaque, element_width return RocList.empty(); } - const allocator = std.heap.c_allocator; - var output = RocList.allocate(allocator, alignment, count, element_width); + var output = RocList.allocate(alignment, count, element_width); if (output.bytes) |target_ptr| { // increment the element's RC N times @@ -641,7 +636,7 @@ pub fn listRepeat(count: usize, alignment: usize, element: Opaque, element_width } pub fn listSingle(alignment: usize, element: Opaque, element_width: usize) callconv(.C) RocList { - var output = RocList.allocate(std.heap.c_allocator, alignment, 1, element_width); + var output = RocList.allocate(alignment, 1, element_width); if (output.bytes) |target| { if (element) |source| { @@ -654,7 +649,7 @@ pub fn listSingle(alignment: usize, element: Opaque, element_width: usize) callc pub fn listAppend(list: RocList, alignment: usize, element: Opaque, element_width: usize) callconv(.C) RocList { const old_length = list.len(); - var output = list.reallocate(std.heap.c_allocator, alignment, old_length + 1, element_width); + var output = list.reallocate(alignment, old_length + 1, element_width); if (output.bytes) |target| { if (element) |source| { @@ -688,12 +683,12 @@ pub fn listDrop( return RocList.empty(); } - const output = RocList.allocate(std.heap.c_allocator, alignment, keep_count, element_width); + const output = RocList.allocate(alignment, keep_count, element_width); const target_ptr = output.bytes orelse unreachable; @memcpy(target_ptr, source_ptr + drop_count * element_width, keep_count * element_width); - utils.decref(std.heap.c_allocator, alignment, list.bytes, size * element_width); + utils.decref(alignment, list.bytes, size * element_width); return output; } else { @@ -702,54 +697,53 @@ pub fn listDrop( } pub fn listRange(width: utils.IntWidth, low: Opaque, high: Opaque) callconv(.C) RocList { - const allocator = std.heap.c_allocator; const IntWidth = utils.IntWidth; switch (width) { IntWidth.U8 => { - return helper1(allocator, u8, low, high); + return helper1(u8, low, high); }, IntWidth.U16 => { - return helper1(allocator, u16, low, high); + return helper1(u16, low, high); }, IntWidth.U32 => { - return helper1(allocator, u32, low, high); + return helper1(u32, low, high); }, IntWidth.U64 => { - return helper1(allocator, u64, low, high); + return helper1(u64, low, high); }, IntWidth.U128 => { - return helper1(allocator, u128, low, high); + return helper1(u128, low, high); }, IntWidth.I8 => { - return helper1(allocator, i8, low, high); + return helper1(i8, low, high); }, IntWidth.I16 => { - return helper1(allocator, i16, low, high); + return helper1(i16, low, high); }, IntWidth.I32 => { - return helper1(allocator, i32, low, high); + return helper1(i32, low, high); }, IntWidth.I64 => { - return helper1(allocator, i64, low, high); + return helper1(i64, low, high); }, IntWidth.I128 => { - return helper1(allocator, i128, low, high); + return helper1(i128, low, high); }, IntWidth.Usize => { - return helper1(allocator, usize, low, high); + return helper1(usize, low, high); }, } } -fn helper1(allocator: *Allocator, comptime T: type, low: Opaque, high: Opaque) RocList { +fn helper1(comptime T: type, low: Opaque, high: Opaque) RocList { const ptr1 = @ptrCast(*T, @alignCast(@alignOf(T), low)); const ptr2 = @ptrCast(*T, @alignCast(@alignOf(T), high)); - return listRangeHelp(allocator, T, ptr1.*, ptr2.*); + return listRangeHelp(T, ptr1.*, ptr2.*); } -fn listRangeHelp(allocator: *Allocator, comptime T: type, low: T, high: T) RocList { +fn listRangeHelp(comptime T: type, low: T, high: T) RocList { const Order = std.math.Order; switch (std.math.order(low, high)) { @@ -758,7 +752,7 @@ fn listRangeHelp(allocator: *Allocator, comptime T: type, low: T, high: T) RocLi }, Order.eq => { - const list = RocList.allocate(allocator, @alignOf(usize), 1, @sizeOf(T)); + const list = RocList.allocate(@alignOf(usize), 1, @sizeOf(T)); const buffer = @ptrCast([*]T, @alignCast(@alignOf(T), list.bytes orelse unreachable)); buffer[0] = low; @@ -768,7 +762,7 @@ fn listRangeHelp(allocator: *Allocator, comptime T: type, low: T, high: T) RocLi Order.lt => { const length: usize = @intCast(usize, high - low); - const list = RocList.allocate(allocator, @alignOf(usize), length, @sizeOf(T)); + const list = RocList.allocate(@alignOf(usize), length, @sizeOf(T)); const buffer = @ptrCast([*]T, @alignCast(@alignOf(T), list.bytes orelse unreachable)); @@ -822,7 +816,7 @@ fn quicksort(source_ptr: [*]u8, transform: Opaque, wrapper: CompareFn, element_w } pub fn listSortWith(input: RocList, transform: Opaque, wrapper: CompareFn, alignment: usize, element_width: usize) callconv(.C) RocList { - var list = input.makeUnique(std.heap.c_allocator, alignment, element_width); + var list = input.makeUnique(alignment, element_width); if (list.bytes) |source_ptr| { const low = 0; @@ -889,7 +883,7 @@ pub fn listJoin(list_of_lists: RocList, alignment: usize, element_width: usize) total_length += slice_of_lists[i].len(); } - const output = RocList.allocate(std.heap.c_allocator, alignment, total_length, element_width); + const output = RocList.allocate(alignment, total_length, element_width); if (output.bytes) |target| { var elements_copied: usize = 0; @@ -918,7 +912,6 @@ pub fn listConcat(list_a: RocList, list_b: RocList, alignment: usize, element_wi if (list_a.bytes) |source| { const new_source = utils.unsafeReallocate( source, - std.heap.c_allocator, alignment, list_a.len(), total_length, @@ -934,7 +927,7 @@ pub fn listConcat(list_a: RocList, list_b: RocList, alignment: usize, element_wi } const total_length: usize = list_a.len() + list_b.len(); - const output = RocList.allocate(std.heap.c_allocator, alignment, total_length, element_width); + const output = RocList.allocate(alignment, total_length, element_width); if (output.bytes) |target| { if (list_a.bytes) |source| { diff --git a/compiler/builtins/bitcode/src/str.zig b/compiler/builtins/bitcode/src/str.zig index 2961d30872..e76084233d 100644 --- a/compiler/builtins/bitcode/src/str.zig +++ b/compiler/builtins/bitcode/src/str.zig @@ -1,9 +1,9 @@ const utils = @import("utils.zig"); +const roc_mem = @import("mem.zig"); const RocList = @import("list.zig").RocList; const std = @import("std"); const mem = std.mem; const always_inline = std.builtin.CallOptions.Modifier.always_inline; -const Allocator = mem.Allocator; const unicode = std.unicode; const testing = std.testing; const expectEqual = testing.expectEqual; @@ -34,6 +34,8 @@ pub const RocStr = extern struct { str_bytes: ?[*]u8, str_len: usize, + pub const alignment = @alignOf(usize); + pub inline fn empty() RocStr { return RocStr{ .str_len = 0, @@ -43,15 +45,15 @@ pub const RocStr = extern struct { // This clones the pointed-to bytes if they won't fit in a // small string, and returns a (pointer, len) tuple which points to them. - pub fn init(allocator: *Allocator, bytes_ptr: [*]const u8, length: usize) RocStr { - var result = RocStr.allocate(allocator, InPlace.Clone, length); + pub fn init(bytes_ptr: [*]const u8, length: usize) RocStr { + var result = RocStr.allocate(InPlace.Clone, length); @memcpy(result.asU8ptr(), bytes_ptr, length); return result; } - pub fn initBig(allocator: *Allocator, in_place: InPlace, number_of_chars: u64) RocStr { - const first_element = utils.allocateWithRefcount(allocator, @sizeOf(usize), number_of_chars); + pub fn initBig(in_place: InPlace, number_of_chars: u64) RocStr { + const first_element = utils.allocateWithRefcount(@sizeOf(usize), number_of_chars); return RocStr{ .str_bytes = first_element, @@ -60,11 +62,11 @@ pub const RocStr = extern struct { } // allocate space for a (big or small) RocStr, but put nothing in it yet - pub fn allocate(allocator: *Allocator, result_in_place: InPlace, number_of_chars: usize) RocStr { + pub fn allocate(result_in_place: InPlace, number_of_chars: usize) RocStr { const result_is_big = number_of_chars >= small_string_size; if (result_is_big) { - return RocStr.initBig(allocator, result_in_place, number_of_chars); + return RocStr.initBig(result_in_place, number_of_chars); } else { var t = blank_small_string; @@ -77,10 +79,9 @@ pub const RocStr = extern struct { } } - pub fn deinit(self: RocStr, allocator: *Allocator) void { + pub fn deinit(self: RocStr) void { if (!self.isSmallStr() and !self.isEmpty()) { - const alignment = @alignOf(usize); - utils.decref(allocator, alignment, self.str_bytes, self.str_len); + utils.decref(RocStr.alignment, self.str_bytes, self.str_len); } } @@ -98,7 +99,7 @@ pub const RocStr = extern struct { if (length < roc_str_size) { return RocStr.empty(); } else { - var new_bytes: []T = allocator.alloc(u8, length) catch unreachable; + var new_bytes: []T = utils.alloc(RocStr.alignment, length) catch unreachable; var new_bytes_ptr: [*]u8 = @ptrCast([*]u8, &new_bytes); @@ -146,12 +147,12 @@ pub const RocStr = extern struct { return true; } - pub fn clone(allocator: *Allocator, in_place: InPlace, str: RocStr) RocStr { + pub fn clone(in_place: InPlace, str: RocStr) RocStr { if (str.isSmallStr() or str.isEmpty()) { // just return the bytes return str; } else { - var new_str = RocStr.initBig(allocator, in_place, str.str_len); + var new_str = RocStr.initBig(in_place, str.str_len); var old_bytes: [*]u8 = @ptrCast([*]u8, str.str_bytes); var new_bytes: [*]u8 = @ptrCast([*]u8, new_str.str_bytes); @@ -164,33 +165,30 @@ pub const RocStr = extern struct { pub fn reallocate( self: RocStr, - allocator: *Allocator, new_length: usize, ) RocStr { - const alignment = 1; const element_width = 1; if (self.bytes) |source_ptr| { if (self.isUnique()) { - const new_source = utils.unsafeReallocate(source_ptr, allocator, alignment, self.len(), new_length, element_width); + const new_source = utils.unsafeReallocate(source_ptr, RocStr.alignment, self.len(), new_length, element_width); return RocStr{ .str_bytes = new_source, .str_len = new_length }; } } - return self.reallocateFresh(allocator, alignment, new_length, element_width); + return self.reallocateFresh(RocStr.alignment, new_length, element_width); } /// reallocate by explicitly making a new allocation and copying elements over pub fn reallocateFresh( self: RocStr, - allocator: *Allocator, new_length: usize, ) RocStr { const old_length = self.len(); const delta_length = new_length - old_length; - const result = RocStr.allocate(allocator, InPlace.Clone, new_length); + const result = RocStr.allocate(InPlace.Clone, new_length); // transfer the memory @@ -200,7 +198,7 @@ pub const RocStr = extern struct { @memcpy(dest_ptr, source_ptr, old_length); @memset(dest_ptr + old_length, 0, delta_length); - self.deinit(allocator); + self.deinit(); return result; } @@ -268,33 +266,33 @@ pub const RocStr = extern struct { const str1_len = 3; var str1: [str1_len]u8 = "abc".*; const str1_ptr: [*]u8 = &str1; - var roc_str1 = RocStr.init(testing.allocator, str1_ptr, str1_len); + var roc_str1 = RocStr.init(str1_ptr, str1_len); const str2_len = 3; var str2: [str2_len]u8 = "abc".*; const str2_ptr: [*]u8 = &str2; - var roc_str2 = RocStr.init(testing.allocator, str2_ptr, str2_len); + var roc_str2 = RocStr.init(str2_ptr, str2_len); expect(roc_str1.eq(roc_str2)); - roc_str1.deinit(testing.allocator); - roc_str2.deinit(testing.allocator); + roc_str1.deinit(); + roc_str2.deinit(); } test "RocStr.eq: not equal different length" { const str1_len = 4; var str1: [str1_len]u8 = "abcd".*; const str1_ptr: [*]u8 = &str1; - var roc_str1 = RocStr.init(testing.allocator, str1_ptr, str1_len); + var roc_str1 = RocStr.init(str1_ptr, str1_len); const str2_len = 3; var str2: [str2_len]u8 = "abc".*; const str2_ptr: [*]u8 = &str2; - var roc_str2 = RocStr.init(testing.allocator, str2_ptr, str2_len); + var roc_str2 = RocStr.init(str2_ptr, str2_len); defer { - roc_str1.deinit(testing.allocator); - roc_str2.deinit(testing.allocator); + roc_str1.deinit(); + roc_str2.deinit(); } expect(!roc_str1.eq(roc_str2)); @@ -304,16 +302,16 @@ pub const RocStr = extern struct { const str1_len = 3; var str1: [str1_len]u8 = "acb".*; const str1_ptr: [*]u8 = &str1; - var roc_str1 = RocStr.init(testing.allocator, str1_ptr, str1_len); + var roc_str1 = RocStr.init(str1_ptr, str1_len); const str2_len = 3; var str2: [str2_len]u8 = "abc".*; const str2_ptr: [*]u8 = &str2; - var roc_str2 = RocStr.init(testing.allocator, str2_ptr, str2_len); + var roc_str2 = RocStr.init(str2_ptr, str2_len); defer { - roc_str1.deinit(testing.allocator); - roc_str2.deinit(testing.allocator); + roc_str1.deinit(); + roc_str2.deinit(); } expect(!roc_str1.eq(roc_str2)); @@ -321,7 +319,7 @@ pub const RocStr = extern struct { }; pub fn init(bytes_ptr: [*]const u8, length: usize) callconv(.C) RocStr { - return @call(.{ .modifier = always_inline }, RocStr.init, .{ std.heap.c_allocator, bytes_ptr, length }); + return @call(.{ .modifier = always_inline }, RocStr.init, .{ bytes_ptr, length }); } // Str.equal @@ -335,17 +333,12 @@ pub fn strNumberOfBytes(string: RocStr) callconv(.C) usize { } // Str.fromInt -// When we actually use this in Roc, libc will be linked so we have access to std.heap.c_allocator pub fn strFromIntC(int: i64) callconv(.C) RocStr { - return strFromInt(std.heap.c_allocator, int); -} - -fn strFromInt(allocator: *Allocator, int: i64) RocStr { // prepare for having multiple integer types in the future - return @call(.{ .modifier = always_inline }, strFromIntHelp, .{ allocator, i64, int }); + return @call(.{ .modifier = always_inline }, strFromIntHelp, .{ i64, int }); } -fn strFromIntHelp(allocator: *Allocator, comptime T: type, int: T) RocStr { +fn strFromIntHelp(comptime T: type, int: T) RocStr { // determine maximum size for this T comptime const size = comptime blk: { // the string representation of the minimum i128 value uses at most 40 characters @@ -357,11 +350,10 @@ fn strFromIntHelp(allocator: *Allocator, comptime T: type, int: T) RocStr { var buf: [size]u8 = undefined; const result = std.fmt.bufPrint(&buf, "{}", .{int}) catch unreachable; - return RocStr.init(allocator, &buf, result.len); + return RocStr.init(&buf, result.len); } // Str.fromFloat -// When we actually use this in Roc, libc will be linked so we have access to std.heap.c_allocator pub fn strFromFloatC(float: f64) callconv(.C) RocStr { // NOTE the compiled zig for float formatting seems to use LLVM11-specific features // hopefully we can use zig instead of snprintf in the future when we upgrade @@ -374,16 +366,15 @@ pub fn strFromFloatC(float: f64) callconv(.C) RocStr { const result = c.snprintf(&buf, 100, "%f", float); - return RocStr.init(std.heap.c_allocator, &buf, @intCast(usize, result)); + return RocStr.init(&buf, @intCast(usize, result)); } // Str.split -// When we actually use this in Roc, libc will be linked so we have access to std.heap.c_allocator pub fn strSplitInPlaceC(array: [*]RocStr, string: RocStr, delimiter: RocStr) callconv(.C) void { - return @call(.{ .modifier = always_inline }, strSplitInPlace, .{ std.heap.c_allocator, array, string, delimiter }); + return @call(.{ .modifier = always_inline }, strSplitInPlace, .{ array, string, delimiter }); } -fn strSplitInPlace(allocator: *Allocator, array: [*]RocStr, string: RocStr, delimiter: RocStr) void { +fn strSplitInPlace(array: [*]RocStr, string: RocStr, delimiter: RocStr) void { var ret_array_index: usize = 0; var slice_start_index: usize = 0; var str_index: usize = 0; @@ -415,7 +406,7 @@ fn strSplitInPlace(allocator: *Allocator, array: [*]RocStr, string: RocStr, deli if (matches_delimiter) { const segment_len: usize = str_index - slice_start_index; - array[ret_array_index] = RocStr.init(allocator, str_bytes + slice_start_index, segment_len); + array[ret_array_index] = RocStr.init(str_bytes + slice_start_index, segment_len); slice_start_index = str_index + delimiter_len; ret_array_index += 1; str_index += delimiter_len; @@ -425,21 +416,21 @@ fn strSplitInPlace(allocator: *Allocator, array: [*]RocStr, string: RocStr, deli } } - array[ret_array_index] = RocStr.init(allocator, str_bytes + slice_start_index, str_len - slice_start_index); + array[ret_array_index] = RocStr.init(str_bytes + slice_start_index, str_len - slice_start_index); } test "strSplitInPlace: no delimiter" { // Str.split "abc" "!" == [ "abc" ] const str_arr = "abc"; - const str = RocStr.init(testing.allocator, str_arr, str_arr.len); + const str = RocStr.init(str_arr, str_arr.len); const delimiter_arr = "!"; - const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len); + const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len); var array: [1]RocStr = undefined; const array_ptr: [*]RocStr = &array; - strSplitInPlace(testing.allocator, array_ptr, str, delimiter); + strSplitInPlace(array_ptr, str, delimiter); var expected = [1]RocStr{ str, @@ -447,15 +438,15 @@ test "strSplitInPlace: no delimiter" { defer { for (array) |roc_str| { - roc_str.deinit(testing.allocator); + roc_str.deinit(); } for (expected) |roc_str| { - roc_str.deinit(testing.allocator); + roc_str.deinit(); } - str.deinit(testing.allocator); - delimiter.deinit(testing.allocator); + str.deinit(); + delimiter.deinit(); } expectEqual(array.len, expected.len); @@ -464,10 +455,10 @@ test "strSplitInPlace: no delimiter" { test "strSplitInPlace: empty end" { const str_arr = "1---- ---- ---- ---- ----2---- ---- ---- ---- ----"; - const str = RocStr.init(testing.allocator, str_arr, str_arr.len); + const str = RocStr.init(str_arr, str_arr.len); const delimiter_arr = "---- ---- ---- ---- ----"; - const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len); + const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len); const array_len: usize = 3; var array: [array_len]RocStr = [_]RocStr{ @@ -477,10 +468,10 @@ test "strSplitInPlace: empty end" { }; const array_ptr: [*]RocStr = &array; - strSplitInPlace(testing.allocator, array_ptr, str, delimiter); + strSplitInPlace(array_ptr, str, delimiter); - const one = RocStr.init(testing.allocator, "1", 1); - const two = RocStr.init(testing.allocator, "2", 1); + const one = RocStr.init("1", 1); + const two = RocStr.init("2", 1); var expected = [3]RocStr{ one, two, RocStr.empty(), @@ -488,15 +479,15 @@ test "strSplitInPlace: empty end" { defer { for (array) |rocStr| { - rocStr.deinit(testing.allocator); + rocStr.deinit(); } for (expected) |rocStr| { - rocStr.deinit(testing.allocator); + rocStr.deinit(); } - str.deinit(testing.allocator); - delimiter.deinit(testing.allocator); + str.deinit(); + delimiter.deinit(); } expectEqual(array.len, expected.len); @@ -507,10 +498,10 @@ test "strSplitInPlace: empty end" { test "strSplitInPlace: delimiter on sides" { const str_arr = "tttghittt"; - const str = RocStr.init(testing.allocator, str_arr, str_arr.len); + const str = RocStr.init(str_arr, str_arr.len); const delimiter_arr = "ttt"; - const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len); + const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len); const array_len: usize = 3; var array: [array_len]RocStr = [_]RocStr{ @@ -519,10 +510,10 @@ test "strSplitInPlace: delimiter on sides" { undefined, }; const array_ptr: [*]RocStr = &array; - strSplitInPlace(testing.allocator, array_ptr, str, delimiter); + strSplitInPlace(array_ptr, str, delimiter); const ghi_arr = "ghi"; - const ghi = RocStr.init(testing.allocator, ghi_arr, ghi_arr.len); + const ghi = RocStr.init(ghi_arr, ghi_arr.len); var expected = [3]RocStr{ RocStr.empty(), ghi, RocStr.empty(), @@ -530,15 +521,15 @@ test "strSplitInPlace: delimiter on sides" { defer { for (array) |rocStr| { - rocStr.deinit(testing.allocator); + rocStr.deinit(); } for (expected) |rocStr| { - rocStr.deinit(testing.allocator); + rocStr.deinit(); } - str.deinit(testing.allocator); - delimiter.deinit(testing.allocator); + str.deinit(); + delimiter.deinit(); } expectEqual(array.len, expected.len); @@ -550,20 +541,20 @@ test "strSplitInPlace: delimiter on sides" { test "strSplitInPlace: three pieces" { // Str.split "a!b!c" "!" == [ "a", "b", "c" ] const str_arr = "a!b!c"; - const str = RocStr.init(testing.allocator, str_arr, str_arr.len); + const str = RocStr.init(str_arr, str_arr.len); const delimiter_arr = "!"; - const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len); + const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len); const array_len: usize = 3; var array: [array_len]RocStr = undefined; const array_ptr: [*]RocStr = &array; - strSplitInPlace(testing.allocator, array_ptr, str, delimiter); + strSplitInPlace(array_ptr, str, delimiter); - const a = RocStr.init(testing.allocator, "a", 1); - const b = RocStr.init(testing.allocator, "b", 1); - const c = RocStr.init(testing.allocator, "c", 1); + const a = RocStr.init("a", 1); + const b = RocStr.init("b", 1); + const c = RocStr.init("c", 1); var expected_array = [array_len]RocStr{ a, b, c, @@ -571,15 +562,15 @@ test "strSplitInPlace: three pieces" { defer { for (array) |roc_str| { - roc_str.deinit(testing.allocator); + roc_str.deinit(); } for (expected_array) |roc_str| { - roc_str.deinit(testing.allocator); + roc_str.deinit(); } - str.deinit(testing.allocator); - delimiter.deinit(testing.allocator); + str.deinit(); + delimiter.deinit(); } expectEqual(expected_array.len, array.len); @@ -637,14 +628,14 @@ test "countSegments: long delimiter" { // Str.split "str" "delimiter" == [ "str" ] // 1 segment const str_arr = "str"; - const str = RocStr.init(testing.allocator, str_arr, str_arr.len); + const str = RocStr.init(str_arr, str_arr.len); const delimiter_arr = "delimiter"; - const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len); + const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len); defer { - str.deinit(testing.allocator); - delimiter.deinit(testing.allocator); + str.deinit(); + delimiter.deinit(); } const segments_count = countSegments(str, delimiter); @@ -655,14 +646,14 @@ test "countSegments: delimiter at start" { // Str.split "hello there" "hello" == [ "", " there" ] // 2 segments const str_arr = "hello there"; - const str = RocStr.init(testing.allocator, str_arr, str_arr.len); + const str = RocStr.init(str_arr, str_arr.len); const delimiter_arr = "hello"; - const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len); + const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len); defer { - str.deinit(testing.allocator); - delimiter.deinit(testing.allocator); + str.deinit(); + delimiter.deinit(); } const segments_count = countSegments(str, delimiter); @@ -674,14 +665,14 @@ test "countSegments: delimiter interspered" { // Str.split "a!b!c" "!" == [ "a", "b", "c" ] // 3 segments const str_arr = "a!b!c"; - const str = RocStr.init(testing.allocator, str_arr, str_arr.len); + const str = RocStr.init(str_arr, str_arr.len); const delimiter_arr = "!"; - const delimiter = RocStr.init(testing.allocator, delimiter_arr, delimiter_arr.len); + const delimiter = RocStr.init(delimiter_arr, delimiter_arr.len); defer { - str.deinit(testing.allocator); - delimiter.deinit(testing.allocator); + str.deinit(); + delimiter.deinit(); } const segments_count = countSegments(str, delimiter); @@ -736,8 +727,8 @@ test "countGraphemeClusters: empty string" { test "countGraphemeClusters: ascii characters" { const bytes_arr = "abcd"; const bytes_len = bytes_arr.len; - const str = RocStr.init(testing.allocator, bytes_arr, bytes_len); - defer str.deinit(testing.allocator); + const str = RocStr.init(bytes_arr, bytes_len); + defer str.deinit(); const count = countGraphemeClusters(str); expectEqual(count, 4); @@ -746,8 +737,8 @@ test "countGraphemeClusters: ascii characters" { test "countGraphemeClusters: utf8 characters" { const bytes_arr = "ãxā"; const bytes_len = bytes_arr.len; - const str = RocStr.init(testing.allocator, bytes_arr, bytes_len); - defer str.deinit(testing.allocator); + const str = RocStr.init(bytes_arr, bytes_len); + defer str.deinit(); const count = countGraphemeClusters(str); expectEqual(count, 3); @@ -756,8 +747,8 @@ test "countGraphemeClusters: utf8 characters" { test "countGraphemeClusters: emojis" { const bytes_arr = "🤔🤔🤔"; const bytes_len = bytes_arr.len; - const str = RocStr.init(testing.allocator, bytes_arr, bytes_len); - defer str.deinit(testing.allocator); + const str = RocStr.init(bytes_arr, bytes_len); + defer str.deinit(); const count = countGraphemeClusters(str); expectEqual(count, 3); @@ -766,8 +757,8 @@ test "countGraphemeClusters: emojis" { test "countGraphemeClusters: emojis and ut8 characters" { const bytes_arr = "🤔å🤔¥🤔ç"; const bytes_len = bytes_arr.len; - const str = RocStr.init(testing.allocator, bytes_arr, bytes_len); - defer str.deinit(testing.allocator); + const str = RocStr.init(bytes_arr, bytes_len); + defer str.deinit(); const count = countGraphemeClusters(str); expectEqual(count, 6); @@ -776,8 +767,8 @@ test "countGraphemeClusters: emojis and ut8 characters" { test "countGraphemeClusters: emojis, ut8, and ascii characters" { const bytes_arr = "6🤔å🤔e¥🤔çpp"; const bytes_len = bytes_arr.len; - const str = RocStr.init(testing.allocator, bytes_arr, bytes_len); - defer str.deinit(testing.allocator); + const str = RocStr.init(bytes_arr, bytes_len); + defer str.deinit(); const count = countGraphemeClusters(str); expectEqual(count, 10); @@ -828,36 +819,36 @@ pub fn startsWithCodePoint(string: RocStr, prefix: u32) callconv(.C) bool { } test "startsWithCodePoint: ascii char" { - const whole = RocStr.init(testing.allocator, "foobar", 6); + const whole = RocStr.init("foobar", 6); const prefix = 'f'; expect(startsWithCodePoint(whole, prefix)); } test "startsWithCodePoint: emoji" { - const yes = RocStr.init(testing.allocator, "💖foobar", 10); - const no = RocStr.init(testing.allocator, "foobar", 6); + const yes = RocStr.init("💖foobar", 10); + const no = RocStr.init("foobar", 6); const prefix = '💖'; expect(startsWithCodePoint(yes, prefix)); expect(!startsWithCodePoint(no, prefix)); } test "startsWith: foo starts with fo" { - const foo = RocStr.init(testing.allocator, "foo", 3); - const fo = RocStr.init(testing.allocator, "fo", 2); + const foo = RocStr.init("foo", 3); + const fo = RocStr.init("fo", 2); expect(startsWith(foo, fo)); } test "startsWith: 123456789123456789 starts with 123456789123456789" { - const str = RocStr.init(testing.allocator, "123456789123456789", 18); - defer str.deinit(testing.allocator); + const str = RocStr.init("123456789123456789", 18); + defer str.deinit(); expect(startsWith(str, str)); } test "startsWith: 12345678912345678910 starts with 123456789123456789" { - const str = RocStr.init(testing.allocator, "12345678912345678910", 20); - defer str.deinit(testing.allocator); - const prefix = RocStr.init(testing.allocator, "123456789123456789", 18); - defer prefix.deinit(testing.allocator); + const str = RocStr.init("12345678912345678910", 20); + defer str.deinit(); + const prefix = RocStr.init("123456789123456789", 18); + defer prefix.deinit(); expect(startsWith(str, prefix)); } @@ -886,63 +877,60 @@ pub fn endsWith(string: RocStr, suffix: RocStr) callconv(.C) bool { } test "endsWith: foo ends with oo" { - const foo = RocStr.init(testing.allocator, "foo", 3); - const oo = RocStr.init(testing.allocator, "oo", 2); - defer foo.deinit(testing.allocator); - defer oo.deinit(testing.allocator); + const foo = RocStr.init("foo", 3); + const oo = RocStr.init("oo", 2); + defer foo.deinit(); + defer oo.deinit(); expect(endsWith(foo, oo)); } test "endsWith: 123456789123456789 ends with 123456789123456789" { - const str = RocStr.init(testing.allocator, "123456789123456789", 18); - defer str.deinit(testing.allocator); + const str = RocStr.init("123456789123456789", 18); + defer str.deinit(); expect(endsWith(str, str)); } test "endsWith: 12345678912345678910 ends with 345678912345678910" { - const str = RocStr.init(testing.allocator, "12345678912345678910", 20); - const suffix = RocStr.init(testing.allocator, "345678912345678910", 18); - defer str.deinit(testing.allocator); - defer suffix.deinit(testing.allocator); + const str = RocStr.init("12345678912345678910", 20); + const suffix = RocStr.init("345678912345678910", 18); + defer str.deinit(); + defer suffix.deinit(); expect(endsWith(str, suffix)); } test "endsWith: hello world ends with world" { - const str = RocStr.init(testing.allocator, "hello world", 11); - const suffix = RocStr.init(testing.allocator, "world", 5); - defer str.deinit(testing.allocator); - defer suffix.deinit(testing.allocator); + const str = RocStr.init("hello world", 11); + const suffix = RocStr.init("world", 5); + defer str.deinit(); + defer suffix.deinit(); expect(endsWith(str, suffix)); } // Str.concat -// When we actually use this in Roc, libc will be linked so we have access to std.heap.c_allocator pub fn strConcatC(result_in_place: InPlace, arg1: RocStr, arg2: RocStr) callconv(.C) RocStr { - return @call(.{ .modifier = always_inline }, strConcat, .{ std.heap.c_allocator, result_in_place, arg1, arg2 }); + return @call(.{ .modifier = always_inline }, strConcat, .{ result_in_place, arg1, arg2 }); } -fn strConcat(allocator: *Allocator, result_in_place: InPlace, arg1: RocStr, arg2: RocStr) RocStr { +fn strConcat(result_in_place: InPlace, arg1: RocStr, arg2: RocStr) RocStr { if (arg1.isEmpty()) { // the second argument is borrowed, so we must increment its refcount before returning - return RocStr.clone(allocator, result_in_place, arg2); + return RocStr.clone(result_in_place, arg2); } else if (arg2.isEmpty()) { // the first argument is owned, so we can return it without cloning - return RocStr.clone(allocator, result_in_place, arg1); + return RocStr.clone(result_in_place, arg1); } else { const combined_length = arg1.len() + arg2.len(); - const alignment = 1; const element_width = 1; if (!arg1.isSmallStr() and arg1.isUnique()) { if (arg1.str_bytes) |source_ptr| { const new_source = utils.unsafeReallocate( source_ptr, - allocator, - alignment, + RocStr.alignment, arg1.len(), combined_length, element_width, @@ -954,16 +942,13 @@ fn strConcat(allocator: *Allocator, result_in_place: InPlace, arg1: RocStr, arg2 } } - var result = arg1.reallocateFresh( - allocator, - combined_length, - ); + var result = arg1.reallocateFresh(combined_length); var result_ptr = result.asU8ptr(); arg1.memcpy(result_ptr); arg2.memcpy(result_ptr + arg1.len()); - arg1.deinit(allocator); + arg1.deinit(); return result; } @@ -973,27 +958,27 @@ test "RocStr.concat: small concat small" { const str1_len = 3; var str1: [str1_len]u8 = "foo".*; const str1_ptr: [*]u8 = &str1; - var roc_str1 = RocStr.init(testing.allocator, str1_ptr, str1_len); + var roc_str1 = RocStr.init(str1_ptr, str1_len); const str2_len = 3; var str2: [str2_len]u8 = "abc".*; const str2_ptr: [*]u8 = &str2; - var roc_str2 = RocStr.init(testing.allocator, str2_ptr, str2_len); + var roc_str2 = RocStr.init(str2_ptr, str2_len); const str3_len = 6; var str3: [str3_len]u8 = "fooabc".*; const str3_ptr: [*]u8 = &str3; - var roc_str3 = RocStr.init(testing.allocator, str3_ptr, str3_len); + var roc_str3 = RocStr.init(str3_ptr, str3_len); defer { - roc_str1.deinit(testing.allocator); - roc_str2.deinit(testing.allocator); - roc_str3.deinit(testing.allocator); + roc_str1.deinit(); + roc_str2.deinit(); + roc_str3.deinit(); } - const result = strConcat(testing.allocator, InPlace.Clone, roc_str1, roc_str2); + const result = strConcat(InPlace.Clone, roc_str1, roc_str2); - defer result.deinit(testing.allocator); + defer result.deinit(); expect(roc_str3.eq(result)); } @@ -1004,12 +989,11 @@ pub const RocListStr = extern struct { }; // Str.joinWith -// When we actually use this in Roc, libc will be linked so we have access to std.heap.c_allocator pub fn strJoinWithC(list: RocListStr, separator: RocStr) callconv(.C) RocStr { - return @call(.{ .modifier = always_inline }, strJoinWith, .{ std.heap.c_allocator, list, separator }); + return @call(.{ .modifier = always_inline }, strJoinWith, .{ list, separator }); } -fn strJoinWith(allocator: *Allocator, list: RocListStr, separator: RocStr) RocStr { +fn strJoinWith(list: RocListStr, separator: RocStr) RocStr { const len = list.list_length; if (len == 0) { @@ -1027,7 +1011,7 @@ fn strJoinWith(allocator: *Allocator, list: RocListStr, separator: RocStr) RocSt // include size of the separator total_size += separator.len() * (len - 1); - var result = RocStr.allocate(allocator, InPlace.Clone, total_size); + var result = RocStr.allocate(InPlace.Clone, total_size); var result_ptr = result.asU8ptr(); var offset: usize = 0; @@ -1050,45 +1034,45 @@ test "RocStr.joinWith: result is big" { const sep_len = 2; var sep: [sep_len]u8 = ", ".*; const sep_ptr: [*]u8 = &sep; - var roc_sep = RocStr.init(testing.allocator, sep_ptr, sep_len); + var roc_sep = RocStr.init(sep_ptr, sep_len); const elem_len = 13; var elem: [elem_len]u8 = "foobarbazspam".*; const elem_ptr: [*]u8 = &elem; - var roc_elem = RocStr.init(testing.allocator, elem_ptr, elem_len); + var roc_elem = RocStr.init(elem_ptr, elem_len); const result_len = 43; var xresult: [result_len]u8 = "foobarbazspam, foobarbazspam, foobarbazspam".*; const result_ptr: [*]u8 = &xresult; - var roc_result = RocStr.init(testing.allocator, result_ptr, result_len); + var roc_result = RocStr.init(result_ptr, result_len); var elements: [3]RocStr = .{ roc_elem, roc_elem, roc_elem }; const list = RocListStr{ .list_length = 3, .list_elements = @ptrCast([*]RocStr, &elements) }; defer { - roc_sep.deinit(testing.allocator); - roc_elem.deinit(testing.allocator); - roc_result.deinit(testing.allocator); + roc_sep.deinit(); + roc_elem.deinit(); + roc_result.deinit(); } - const result = strJoinWith(testing.allocator, list, roc_sep); + const result = strJoinWith(list, roc_sep); - defer result.deinit(testing.allocator); + defer result.deinit(); expect(roc_result.eq(result)); } // Str.toBytes pub fn strToBytesC(arg: RocStr) callconv(.C) RocList { - return @call(.{ .modifier = always_inline }, strToBytes, .{ std.heap.c_allocator, arg }); + return @call(.{ .modifier = always_inline }, strToBytes, .{ arg }); } -fn strToBytes(allocator: *Allocator, arg: RocStr) RocList { +fn strToBytes(arg: RocStr) RocList { if (arg.isEmpty()) { return RocList.empty(); } else if (arg.isSmallStr()) { const length = arg.len(); - const ptr = utils.allocateWithRefcount(allocator, @alignOf(usize), length); + const ptr = utils.allocateWithRefcount(RocStr.alignment, length); @memcpy(ptr, arg.asU8ptr(), length); @@ -1106,25 +1090,25 @@ const FromUtf8Result = extern struct { }; pub fn fromUtf8C(arg: RocList, output: *FromUtf8Result) callconv(.C) void { - output.* = @call(.{ .modifier = always_inline }, fromUtf8, .{ std.heap.c_allocator, arg }); + output.* = @call(.{ .modifier = always_inline }, fromUtf8, .{ arg }); } -fn fromUtf8(allocator: *Allocator, arg: RocList) FromUtf8Result { +fn fromUtf8(arg: RocList) FromUtf8Result { const bytes = @ptrCast([*]const u8, arg.bytes)[0..arg.length]; if (unicode.utf8ValidateSlice(bytes)) { // the output will be correct. Now we need to take ownership of the input if (arg.len() <= SMALL_STR_MAX_LENGTH) { // turn the bytes into a small string - const string = RocStr.init(allocator, @ptrCast([*]u8, arg.bytes), arg.len()); + const string = RocStr.init(@ptrCast([*]u8, arg.bytes), arg.len()); // then decrement the input list const data_bytes = arg.len(); - utils.decref(allocator, @alignOf(usize), arg.bytes, data_bytes); + utils.decref(RocStr.alignment, arg.bytes, data_bytes); return FromUtf8Result{ .is_ok = true, .string = string, .byte_index = 0, .problem_code = Utf8ByteProblem.InvalidStartByte }; } else { - const byte_list = arg.makeUnique(allocator, @alignOf(usize), @sizeOf(u8)); + const byte_list = arg.makeUnique(RocStr.alignment, @sizeOf(u8)); const string = RocStr{ .str_bytes = byte_list.bytes, .str_len = byte_list.length }; @@ -1135,7 +1119,7 @@ fn fromUtf8(allocator: *Allocator, arg: RocList) FromUtf8Result { // consume the input list const data_bytes = arg.len(); - utils.decref(allocator, @alignOf(usize), arg.bytes, data_bytes); + utils.decref(RocStr.alignment, arg.bytes, data_bytes); return FromUtf8Result{ .is_ok = false, .string = RocStr.empty(), .byte_index = temp.index, .problem_code = temp.problem }; } @@ -1202,11 +1186,11 @@ pub const Utf8ByteProblem = packed enum(u8) { }; fn validateUtf8Bytes(bytes: [*]u8, length: usize) FromUtf8Result { - return fromUtf8(std.testing.allocator, RocList{ .bytes = bytes, .length = length }); + return fromUtf8(RocList{ .bytes = bytes, .length = length }); } fn validateUtf8BytesX(str: RocList) FromUtf8Result { - return fromUtf8(std.testing.allocator, str); + return fromUtf8(str); } fn expectOk(result: FromUtf8Result) void { @@ -1214,7 +1198,7 @@ fn expectOk(result: FromUtf8Result) void { } fn sliceHelp(bytes: [*]const u8, length: usize) RocList { - var list = RocList.allocate(testing.allocator, @alignOf(usize), length, @sizeOf(u8)); + var list = RocList.allocate(RocStr.alignment, length, @sizeOf(u8)); @memcpy(list.bytes orelse unreachable, bytes, length); list.length = length; diff --git a/compiler/builtins/bitcode/src/utils.zig b/compiler/builtins/bitcode/src/utils.zig index ab3e6951f7..afbd750ec7 100644 --- a/compiler/builtins/bitcode/src/utils.zig +++ b/compiler/builtins/bitcode/src/utils.zig @@ -1,5 +1,37 @@ const std = @import("std"); -const Allocator = std.mem.Allocator; +const always_inline = std.builtin.CallOptions.Modifier.always_inline; + +// If allocation fails, this must cxa_throw - it must not return a null pointer! +extern fn roc_alloc(alignment: usize, size: usize) callconv(.C) *c_void; + +// This should never be passed a null pointer. +// If allocation fails, this must cxa_throw - it must not return a null pointer! +extern fn roc_realloc(alignment: usize, c_ptr: *c_void, new_size: usize) callconv(.C) *c_void; + +// This should never be passed a null pointer. +extern fn roc_dealloc(alignment: usize, c_ptr: *c_void) callconv(.C) void; + +pub fn alloc(alignment: usize, size: usize) [*]u8 { + return @ptrCast( + [*]u8, + @call(.{ .modifier = always_inline }, roc_alloc, .{ alignment, size }) + ); +} + +pub fn realloc(alignment: usize, c_ptr: [*]u8, new_size: usize) [*]u8 { + return @ptrCast( + [*]u8, + @call(.{ .modifier = always_inline }, roc_realloc, .{ alignment, c_ptr, new_size }) + ); +} + +pub fn dealloc(alignment: usize, c_ptr: [*]u8) void { + return @call( + .{ .modifier = always_inline }, + roc_dealloc, + .{ alignment, c_ptr } + ); +} pub const Inc = fn (?[*]u8) callconv(.C) void; pub const IncN = fn (?[*]u8, u64) callconv(.C) void; @@ -62,7 +94,6 @@ pub fn intWidth(width: IntWidth) anytype { } pub fn decref( - allocator: *Allocator, alignment: usize, bytes_or_null: ?[*]u8, data_bytes: usize, @@ -81,7 +112,7 @@ pub fn decref( switch (alignment) { 16 => { if (refcount == REFCOUNT_ONE_ISIZE) { - allocator.free((bytes - 16)[0 .. 16 + data_bytes]); + dealloc(alignment, bytes - 16); } else if (refcount_isize < 0) { (isizes - 1)[0] = refcount - 1; } @@ -89,7 +120,7 @@ pub fn decref( else => { // NOTE enums can currently have an alignment of < 8 if (refcount == REFCOUNT_ONE_ISIZE) { - allocator.free((bytes - 8)[0 .. 8 + data_bytes]); + dealloc(alignment, bytes - 8); } else if (refcount_isize < 0) { (isizes - 1)[0] = refcount - 1; } @@ -98,7 +129,6 @@ pub fn decref( } pub fn allocateWithRefcount( - allocator: *Allocator, alignment: usize, data_bytes: usize, ) [*]u8 { @@ -108,7 +138,7 @@ pub fn allocateWithRefcount( 16 => { const length = 2 * @sizeOf(usize) + data_bytes; - var new_bytes: []align(16) u8 = allocator.alignedAlloc(u8, 16, length) catch unreachable; + var new_bytes: [*]align(16) u8 = @alignCast(16, alloc(alignment, length)); var as_usize_array = @ptrCast([*]usize, new_bytes); if (result_in_place) { @@ -127,13 +157,13 @@ pub fn allocateWithRefcount( else => { const length = @sizeOf(usize) + data_bytes; - var new_bytes: []align(8) u8 = allocator.alignedAlloc(u8, 8, length) catch unreachable; + var new_bytes: [*]align(8) u8 = @alignCast(8, alloc(alignment, length)); - var as_usize_array = @ptrCast([*]isize, new_bytes); + var as_isize_array = @ptrCast([*]isize, new_bytes); if (result_in_place) { - as_usize_array[0] = @intCast(isize, number_of_slots); + as_isize_array[0] = @intCast(isize, number_of_slots); } else { - as_usize_array[0] = REFCOUNT_ONE_ISIZE; + as_isize_array[0] = REFCOUNT_ONE_ISIZE; } var as_u8_array = @ptrCast([*]u8, new_bytes); @@ -146,7 +176,6 @@ pub fn allocateWithRefcount( pub fn unsafeReallocate( source_ptr: [*]u8, - allocator: *Allocator, alignment: usize, old_length: usize, new_length: usize, @@ -165,8 +194,8 @@ pub fn unsafeReallocate( // TODO handle out of memory // NOTE realloc will dealloc the original allocation - const old_allocation = (source_ptr - align_width)[0..old_width]; - const new_allocation = allocator.realloc(old_allocation, new_width) catch unreachable; + const old_allocation = source_ptr - align_width; + const new_allocation = realloc(alignment, old_allocation, new_width); const new_source = @ptrCast([*]u8, new_allocation) + align_width; return new_source;