| ... | @@ -43,14 +43,13 @@ var frees = [1]usize{0} ** size_class_count; | ... | @@ -43,14 +43,13 @@ var frees = [1]usize{0} ** size_class_count; |
| 43 | /// For each big size class, points to the freed pointer. | 43 | /// For each big size class, points to the freed pointer. |
| 44 | var big_frees = [1]usize{0} ** big_size_class_count; | 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 | _ = ctx; | 47 | _ = ctx; |
| 48 | _ = len_align; | 48 | _ = return_address; |
| 49 | _ = ra; | | |
| 50 | if (alignment > wasm.page_size) return error.OutOfMemory; // calm down | | |
| 51 | // Make room for the freelist next pointer. | 49 | // Make room for the freelist next pointer. |
| | 50 | const alignment = @as(usize, 1) << @intCast(Allocator.Log2Align, log2_align); |
| 52 | const actual_len = @max(len +| @sizeOf(usize), alignment); | 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 | const class = math.log2(slot_size) - min_class; | 53 | const class = math.log2(slot_size) - min_class; |
| 55 | if (class < size_class_count) { | 54 | if (class < size_class_count) { |
| 56 | const addr = a: { | 55 | const addr = a: { |
| ... | @@ -63,7 +62,8 @@ fn alloc(ctx: *anyopaque, len: usize, alignment: u29, len_align: u29, ra: usize) | ... | @@ -63,7 +62,8 @@ fn alloc(ctx: *anyopaque, len: usize, alignment: u29, len_align: u29, ra: usize) |
| 63 | | 62 | |
| 64 | const next_addr = next_addrs[class]; | 63 | const next_addr = next_addrs[class]; |
| 65 | if (next_addr % wasm.page_size == 0) { | 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 | //std.debug.print("allocated fresh slot_size={d} class={d} addr=0x{x}\n", .{ | 67 | //std.debug.print("allocated fresh slot_size={d} class={d} addr=0x{x}\n", .{ |
| 68 | // slot_size, class, addr, | 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,77 +74,50 @@ fn alloc(ctx: *anyopaque, len: usize, alignment: u29, len_align: u29, ra: usize) |
| 74 | break :a next_addr; | 74 | break :a next_addr; |
| 75 | } | 75 | } |
| 76 | }; | 76 | }; |
| 77 | return @intToPtr([*]u8, addr)[0..len]; | 77 | return @intToPtr([*]u8, addr); |
| 78 | } | 78 | } |
| 79 | const bigpages_needed = bigPagesNeeded(actual_len); | 79 | const bigpages_needed = bigPagesNeeded(actual_len); |
| 80 | const addr = try allocBigPages(bigpages_needed); | 80 | const addr = allocBigPages(bigpages_needed); |
| 81 | return @intToPtr([*]u8, addr)[0..len]; | 81 | return @intToPtr([*]u8, addr); |
| 82 | } | 82 | } |
| 83 | | 83 | |
| 84 | fn resize( | 84 | fn resize( |
| 85 | ctx: *anyopaque, | 85 | ctx: *anyopaque, |
| 86 | buf: []u8, | 86 | buf: []u8, |
| 87 | buf_align: u29, | 87 | log2_buf_align: u8, |
| 88 | new_len: usize, | 88 | new_len: usize, |
| 89 | len_align: u29, | 89 | return_address: usize, |
| 90 | ra: usize, | 90 | ) bool { |
| 91 | ) ?usize { | | |
| 92 | _ = ctx; | 91 | _ = ctx; |
| 93 | _ = len_align; | 92 | _ = return_address; |
| 94 | _ = ra; | 93 | // We don't want to move anything from one size class to another, but we |
| 95 | // We don't want to move anything from one size class to another. But we can recover bytes | 94 | // can recover bytes in between powers of two. |
| 96 | // in between powers of two. | 95 | const buf_align = @as(usize, 1) << @intCast(Allocator.Log2Align, log2_buf_align); |
| 97 | const old_actual_len = @max(buf.len + @sizeOf(usize), buf_align); | 96 | const old_actual_len = @max(buf.len + @sizeOf(usize), buf_align); |
| 98 | const new_actual_len = @max(new_len +| @sizeOf(usize), buf_align); | 97 | const new_actual_len = @max(new_len +| @sizeOf(usize), buf_align); |
| 99 | const old_small_slot_size = math.ceilPowerOfTwoAssert(usize, old_actual_len); | 98 | const old_small_slot_size = math.ceilPowerOfTwoAssert(usize, old_actual_len); |
| 100 | const old_small_class = math.log2(old_small_slot_size) - min_class; | 99 | const old_small_class = math.log2(old_small_slot_size) - min_class; |
| 101 | if (old_small_class < size_class_count) { | 100 | if (old_small_class < size_class_count) { |
| 102 | const new_small_slot_size = math.ceilPowerOfTwo(usize, new_actual_len) catch return null; | 101 | const new_small_slot_size = math.ceilPowerOfTwo(usize, new_actual_len) catch return false; |
| 103 | if (old_small_slot_size == new_small_slot_size) return new_len; | 102 | return old_small_slot_size == new_small_slot_size; |
| 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 | } | | |
| 118 | } else { | 103 | } else { |
| 119 | const old_bigpages_needed = bigPagesNeeded(old_actual_len); | 104 | const old_bigpages_needed = bigPagesNeeded(old_actual_len); |
| 120 | const old_big_slot_pages = math.ceilPowerOfTwoAssert(usize, old_bigpages_needed); | 105 | const old_big_slot_pages = math.ceilPowerOfTwoAssert(usize, old_bigpages_needed); |
| 121 | const new_bigpages_needed = bigPagesNeeded(new_actual_len); | 106 | const new_bigpages_needed = bigPagesNeeded(new_actual_len); |
| 122 | const new_big_slot_pages = math.ceilPowerOfTwo(usize, new_bigpages_needed) catch return null; | 107 | const new_big_slot_pages = math.ceilPowerOfTwo(usize, new_bigpages_needed) catch return false; |
| 123 | if (old_big_slot_pages == new_big_slot_pages) return new_len; | 108 | return old_big_slot_pages == new_big_slot_pages; |
| 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 | } | | |
| 136 | } | 109 | } |
| 137 | return new_len; | | |
| 138 | } | 110 | } |
| 139 | | 111 | |
| 140 | fn free( | 112 | fn free( |
| 141 | ctx: *anyopaque, | 113 | ctx: *anyopaque, |
| 142 | buf: []u8, | 114 | buf: []u8, |
| 143 | buf_align: u29, | 115 | log2_buf_align: u8, |
| 144 | return_address: usize, | 116 | return_address: usize, |
| 145 | ) void { | 117 | ) void { |
| 146 | _ = ctx; | 118 | _ = ctx; |
| 147 | _ = return_address; | 119 | _ = return_address; |
| | 120 | const buf_align = @as(usize, 1) << @intCast(Allocator.Log2Align, log2_buf_align); |
| 148 | const actual_len = @max(buf.len + @sizeOf(usize), buf_align); | 121 | const actual_len = @max(buf.len + @sizeOf(usize), buf_align); |
| 149 | const slot_size = math.ceilPowerOfTwoAssert(usize, actual_len); | 122 | const slot_size = math.ceilPowerOfTwoAssert(usize, actual_len); |
| 150 | const class = math.log2(slot_size) - min_class; | 123 | const class = math.log2(slot_size) - min_class; |
| ... | @@ -168,7 +141,7 @@ inline fn bigPagesNeeded(byte_count: usize) usize { | ... | @@ -168,7 +141,7 @@ inline fn bigPagesNeeded(byte_count: usize) usize { |
| 168 | return (byte_count + (bigpage_size + (@sizeOf(usize) - 1))) / bigpage_size; | 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 | const pow2_pages = math.ceilPowerOfTwoAssert(usize, n); | 145 | const pow2_pages = math.ceilPowerOfTwoAssert(usize, n); |
| 173 | const slot_size_bytes = pow2_pages * bigpage_size; | 146 | const slot_size_bytes = pow2_pages * bigpage_size; |
| 174 | const class = math.log2(pow2_pages); | 147 | const class = math.log2(pow2_pages); |
| ... | @@ -181,7 +154,7 @@ fn allocBigPages(n: usize) !usize { | ... | @@ -181,7 +154,7 @@ fn allocBigPages(n: usize) !usize { |
| 181 | } | 154 | } |
| 182 | | 155 | |
| 183 | const page_index = @wasmMemoryGrow(0, pow2_pages * pages_per_bigpage); | 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 | const addr = @intCast(u32, page_index) * wasm.page_size; | 158 | const addr = @intCast(u32, page_index) * wasm.page_size; |
| 186 | return addr; | 159 | return addr; |
| 187 | } | 160 | } |
| ... | @@ -259,13 +232,15 @@ test "shrink" { | ... | @@ -259,13 +232,15 @@ test "shrink" { |
| 259 | | 232 | |
| 260 | mem.set(u8, slice, 0x11); | 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 | for (slice) |b| { | 238 | for (slice) |b| { |
| 265 | try std.testing.expect(b == 0x11); | 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 | for (slice) |b| { | 245 | for (slice) |b| { |
| 271 | try std.testing.expect(b == 0x11); | 246 | try std.testing.expect(b == 0x11); |
| ... | @@ -303,11 +278,12 @@ test "shrink large object to large object" { | ... | @@ -303,11 +278,12 @@ test "shrink large object to large object" { |
| 303 | slice[0] = 0x12; | 278 | slice[0] = 0x12; |
| 304 | slice[60] = 0x34; | 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 | try std.testing.expect(slice[0] == 0x12); | 283 | try std.testing.expect(slice[0] == 0x12); |
| 308 | try std.testing.expect(slice[60] == 0x34); | 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 | try std.testing.expect(slice[0] == 0x12); | 287 | try std.testing.expect(slice[0] == 0x12); |
| 312 | try std.testing.expect(slice[60] == 0x34); | 288 | try std.testing.expect(slice[60] == 0x34); |
| 313 | | 289 | |