| ... | ... | @@ -43,14 +43,13 @@ var frees = [1]usize{0} ** size_class_count; |
| 43 | 43 | /// For each big size class, points to the freed pointer. |
| 44 | 44 | var big_frees = [1]usize{0} ** big_size_class_count; |
| 45 | 45 | |
| 46 | | fn alloc(ctx: *anyopaque, len: usize, alignment: u29, len_align: u29, ra: usize) Error![]u8 { |
| 46 | fn alloc(ctx: *anyopaque, len: usize, log2_align: u8, return_address: usize) ?[*]u8 { |
| 47 | 47 | _ = ctx; |
| 48 | | _ = len_align; |
| 49 | | _ = ra; |
| 50 | | if (alignment > wasm.page_size) return error.OutOfMemory; // calm down |
| 48 | _ = return_address; |
| 51 | 49 | // Make room for the freelist next pointer. |
| 50 | const alignment = @as(usize, 1) << @intCast(Allocator.Log2Align, log2_align); |
| 52 | 51 | const actual_len = @max(len +| @sizeOf(usize), alignment); |
| 53 | | const slot_size = math.ceilPowerOfTwo(usize, actual_len) catch return error.OutOfMemory; |
| 52 | const slot_size = math.ceilPowerOfTwo(usize, actual_len) catch return null; |
| 54 | 53 | const class = math.log2(slot_size) - min_class; |
| 55 | 54 | if (class < size_class_count) { |
| 56 | 55 | const addr = a: { |
| ... | ... | @@ -63,7 +62,8 @@ fn alloc(ctx: *anyopaque, len: usize, alignment: u29, len_align: u29, ra: usize) |
| 63 | 62 | |
| 64 | 63 | const next_addr = next_addrs[class]; |
| 65 | 64 | if (next_addr % wasm.page_size == 0) { |
| 66 | | const addr = try allocBigPages(1); |
| 65 | const addr = allocBigPages(1); |
| 66 | if (addr == 0) return null; |
| 67 | 67 | //std.debug.print("allocated fresh slot_size={d} class={d} addr=0x{x}\n", .{ |
| 68 | 68 | // slot_size, class, addr, |
| 69 | 69 | //}); |
| ... | ... | @@ -74,77 +74,50 @@ fn alloc(ctx: *anyopaque, len: usize, alignment: u29, len_align: u29, ra: usize) |
| 74 | 74 | break :a next_addr; |
| 75 | 75 | } |
| 76 | 76 | }; |
| 77 | | return @intToPtr([*]u8, addr)[0..len]; |
| 77 | return @intToPtr([*]u8, addr); |
| 78 | 78 | } |
| 79 | 79 | const bigpages_needed = bigPagesNeeded(actual_len); |
| 80 | | const addr = try allocBigPages(bigpages_needed); |
| 81 | | return @intToPtr([*]u8, addr)[0..len]; |
| 80 | const addr = allocBigPages(bigpages_needed); |
| 81 | return @intToPtr([*]u8, addr); |
| 82 | 82 | } |
| 83 | 83 | |
| 84 | 84 | fn resize( |
| 85 | 85 | ctx: *anyopaque, |
| 86 | 86 | buf: []u8, |
| 87 | | buf_align: u29, |
| 87 | log2_buf_align: u8, |
| 88 | 88 | new_len: usize, |
| 89 | | len_align: u29, |
| 90 | | ra: usize, |
| 91 | | ) ?usize { |
| 89 | return_address: usize, |
| 90 | ) bool { |
| 92 | 91 | _ = ctx; |
| 93 | | _ = len_align; |
| 94 | | _ = ra; |
| 95 | | // We don't want to move anything from one size class to another. But we can recover bytes |
| 96 | | // in between powers of two. |
| 92 | _ = return_address; |
| 93 | // We don't want to move anything from one size class to another, but we |
| 94 | // can recover bytes in between powers of two. |
| 95 | const buf_align = @as(usize, 1) << @intCast(Allocator.Log2Align, log2_buf_align); |
| 97 | 96 | const old_actual_len = @max(buf.len + @sizeOf(usize), buf_align); |
| 98 | 97 | const new_actual_len = @max(new_len +| @sizeOf(usize), buf_align); |
| 99 | 98 | const old_small_slot_size = math.ceilPowerOfTwoAssert(usize, old_actual_len); |
| 100 | 99 | const old_small_class = math.log2(old_small_slot_size) - min_class; |
| 101 | 100 | if (old_small_class < size_class_count) { |
| 102 | | const new_small_slot_size = math.ceilPowerOfTwo(usize, new_actual_len) catch return null; |
| 103 | | if (old_small_slot_size == new_small_slot_size) return new_len; |
| 104 | | if (new_actual_len >= old_actual_len) return null; |
| 105 | | const new_small_class = math.log2(new_small_slot_size) - min_class; |
| 106 | | assert(new_small_class < old_small_class); |
| 107 | | // Split the small allocation into frees. |
| 108 | | var class = old_small_class - 1; |
| 109 | | while (true) { |
| 110 | | const slot_size = @as(usize, 1) << @intCast(ushift, class + min_class); |
| 111 | | const upper_addr = @ptrToInt(buf.ptr) + slot_size; |
| 112 | | const node = @intToPtr(*usize, upper_addr + (slot_size - @sizeOf(usize))); |
| 113 | | node.* = frees[class]; |
| 114 | | frees[class] = upper_addr; |
| 115 | | if (class == new_small_class) break; |
| 116 | | class -= 1; |
| 117 | | } |
| 101 | const new_small_slot_size = math.ceilPowerOfTwo(usize, new_actual_len) catch return false; |
| 102 | return old_small_slot_size == new_small_slot_size; |
| 118 | 103 | } else { |
| 119 | 104 | const old_bigpages_needed = bigPagesNeeded(old_actual_len); |
| 120 | 105 | const old_big_slot_pages = math.ceilPowerOfTwoAssert(usize, old_bigpages_needed); |
| 121 | 106 | const new_bigpages_needed = bigPagesNeeded(new_actual_len); |
| 122 | | const new_big_slot_pages = math.ceilPowerOfTwo(usize, new_bigpages_needed) catch return null; |
| 123 | | if (old_big_slot_pages == new_big_slot_pages) return new_len; |
| 124 | | if (new_actual_len >= old_actual_len) return null; |
| 125 | | |
| 126 | | const new_small_slot_size = math.ceilPowerOfTwoAssert(usize, new_actual_len); |
| 127 | | if (new_small_slot_size < size_class_count) { |
| 128 | | const new_small_class = math.log2(new_small_slot_size) - min_class; |
| 129 | | // TODO: push the big allocation into the free list |
| 130 | | _ = new_small_class; |
| 131 | | } else { |
| 132 | | const new_big_class = math.log2(new_big_slot_pages); |
| 133 | | // TODO: push the upper area into the free list |
| 134 | | _ = new_big_class; |
| 135 | | } |
| 107 | const new_big_slot_pages = math.ceilPowerOfTwo(usize, new_bigpages_needed) catch return false; |
| 108 | return old_big_slot_pages == new_big_slot_pages; |
| 136 | 109 | } |
| 137 | | return new_len; |
| 138 | 110 | } |
| 139 | 111 | |
| 140 | 112 | fn free( |
| 141 | 113 | ctx: *anyopaque, |
| 142 | 114 | buf: []u8, |
| 143 | | buf_align: u29, |
| 115 | log2_buf_align: u8, |
| 144 | 116 | return_address: usize, |
| 145 | 117 | ) void { |
| 146 | 118 | _ = ctx; |
| 147 | 119 | _ = return_address; |
| 120 | const buf_align = @as(usize, 1) << @intCast(Allocator.Log2Align, log2_buf_align); |
| 148 | 121 | const actual_len = @max(buf.len + @sizeOf(usize), buf_align); |
| 149 | 122 | const slot_size = math.ceilPowerOfTwoAssert(usize, actual_len); |
| 150 | 123 | const class = math.log2(slot_size) - min_class; |
| ... | ... | @@ -168,7 +141,7 @@ inline fn bigPagesNeeded(byte_count: usize) usize { |
| 168 | 141 | return (byte_count + (bigpage_size + (@sizeOf(usize) - 1))) / bigpage_size; |
| 169 | 142 | } |
| 170 | 143 | |
| 171 | | fn allocBigPages(n: usize) !usize { |
| 144 | fn allocBigPages(n: usize) usize { |
| 172 | 145 | const pow2_pages = math.ceilPowerOfTwoAssert(usize, n); |
| 173 | 146 | const slot_size_bytes = pow2_pages * bigpage_size; |
| 174 | 147 | const class = math.log2(pow2_pages); |
| ... | ... | @@ -181,7 +154,7 @@ fn allocBigPages(n: usize) !usize { |
| 181 | 154 | } |
| 182 | 155 | |
| 183 | 156 | const page_index = @wasmMemoryGrow(0, pow2_pages * pages_per_bigpage); |
| 184 | | if (page_index <= 0) return error.OutOfMemory; |
| 157 | if (page_index <= 0) return 0; |
| 185 | 158 | const addr = @intCast(u32, page_index) * wasm.page_size; |
| 186 | 159 | return addr; |
| 187 | 160 | } |
| ... | ... | @@ -259,13 +232,15 @@ test "shrink" { |
| 259 | 232 | |
| 260 | 233 | mem.set(u8, slice, 0x11); |
| 261 | 234 | |
| 262 | | slice = test_ally.shrink(slice, 17); |
| 235 | try std.testing.expect(test_ally.resize(slice, 17)); |
| 236 | slice = slice[0..17]; |
| 263 | 237 | |
| 264 | 238 | for (slice) |b| { |
| 265 | 239 | try std.testing.expect(b == 0x11); |
| 266 | 240 | } |
| 267 | 241 | |
| 268 | | slice = test_ally.shrink(slice, 16); |
| 242 | try std.testing.expect(test_ally.resize(slice, 16)); |
| 243 | slice = slice[0..16]; |
| 269 | 244 | |
| 270 | 245 | for (slice) |b| { |
| 271 | 246 | try std.testing.expect(b == 0x11); |
| ... | ... | @@ -303,11 +278,12 @@ test "shrink large object to large object" { |
| 303 | 278 | slice[0] = 0x12; |
| 304 | 279 | slice[60] = 0x34; |
| 305 | 280 | |
| 306 | | slice = test_ally.resize(slice, bigpage_size * 2 + 1) orelse return; |
| 281 | try std.testing.expect(test_ally.resize(slice, bigpage_size * 2 + 1)); |
| 282 | slice = slice[0 .. bigpage_size * 2 + 1]; |
| 307 | 283 | try std.testing.expect(slice[0] == 0x12); |
| 308 | 284 | try std.testing.expect(slice[60] == 0x34); |
| 309 | 285 | |
| 310 | | slice = test_ally.shrink(slice, bigpage_size * 2 + 1); |
| 286 | try std.testing.expect(test_ally.resize(slice, bigpage_size * 2 + 1)); |
| 311 | 287 | try std.testing.expect(slice[0] == 0x12); |
| 312 | 288 | try std.testing.expect(slice[60] == 0x34); |
| 313 | 289 | |