authorgravatar for contact@fengb.meBenjamin Feng <contact@fengb.me> 2019-11-27 18:46:42-06:00
committergravatar for contact@fengb.meBenjamin Feng <contact@fengb.me> 2019-12-02 09:59:20-06:00
logeff926b4545fea9f87559d2e1d7372d6cbeba780
tree8d40211ce69ef934c0133a03be063efbf5f1015a
parentfc1373a85d55698575f361100f0867e15d6ec1d3

Brain dump new wasm allocator


1 files changed, 46 insertions(+), 58 deletions(-)

lib/std/heap.zig+46-58
......@@ -246,84 +246,72 @@ const PageAllocator = struct {
246246 }
247247};
248248
249extern const __heap_base: [*]u8;
249250// TODO Exposed LLVM intrinsics is a bug
250251// See: https://github.com/ziglang/zig/issues/2291
251252extern fn @"llvm.wasm.memory.size.i32"(u32) u32;
252253extern fn @"llvm.wasm.memory.grow.i32"(u32, u32) i32;
253254
254/// TODO: make this re-use freed pages, and cooperate with other callers of these global intrinsics
255/// by better utilizing the return value of grow()
255test "" {
256 _ = WasmPageAllocator.alloc;
257}
258
256259const WasmPageAllocator = struct {
257 var start_ptr: [*]u8 = undefined;
258 var num_pages: usize = 0;
259 var end_index: usize = 0;
260 const FreeBlock = struct {
261 offset: usize = 0,
262 data: []u8 = &[_]u8{},
260263
261 comptime {
262 if (builtin.arch != .wasm32) {
263 @compileError("WasmPageAllocator is only available for wasm32 arch");
264 fn alloc(self: FreeBlock, num_pages: usize) ?[]u8 {
265 return null;
264266 }
265 }
266
267 fn alloc(allocator: *Allocator, size: usize, alignment: u29) ![]u8 {
268 const addr = @ptrToInt(start_ptr) + end_index;
269 const adjusted_addr = mem.alignForward(addr, alignment);
270 const adjusted_index = end_index + (adjusted_addr - addr);
271 const new_end_index = adjusted_index + size;
267 };
268 var base = FreeBlock{};
269 var additional = FreeBlock{};
272270
273 if (new_end_index > num_pages * mem.page_size) {
274 const required_memory = new_end_index - (num_pages * mem.page_size);
271 fn nPages(memsize: usize) usize {
272 return std.mem.alignForward(memsize, std.mem.page_size) / std.mem.page_size;
273 }
275274
276 var inner_num_pages: usize = required_memory / mem.page_size;
277 if (required_memory % mem.page_size != 0) {
278 inner_num_pages += 1;
279 }
275 fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 {
276 if (alignment > std.mem.page_size) {
277 return error.OutOfMemory;
278 }
280279
281 const prev_page = @"llvm.wasm.memory.grow.i32"(0, @intCast(u32, inner_num_pages));
282 if (prev_page == -1) {
280 const n_pages = nPages(n);
281 return base.alloc(n_pages) orelse additional.alloc(n_pages) orelse {
282 const prev_page_count = @"llvm.wasm.memory.grow.i32"(0, @intCast(u32, n_pages));
283 if (prev_page_count < 0) {
283284 return error.OutOfMemory;
284285 }
285286
286 num_pages += inner_num_pages;
287 }
288
289 const result = start_ptr[adjusted_index..new_end_index];
290 end_index = new_end_index;
291
292 return result;
293 }
294
295 // Check if memory is the last "item" and is aligned correctly
296 fn is_last_item(memory: []u8, alignment: u29) bool {
297 return memory.ptr == start_ptr + end_index - memory.len and mem.alignForward(@ptrToInt(memory.ptr), alignment) == @ptrToInt(memory.ptr);
287 const start_ptr = @intToPtr([*]u8, @intCast(usize, prev_page_count) * std.mem.page_size);
288 return start_ptr[0..n];
289 };
298290 }
299291
300 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
301 // Initialize start_ptr at the first realloc
302 if (num_pages == 0) {
303 start_ptr = @intToPtr([*]u8, @intCast(usize, @"llvm.wasm.memory.size.i32"(0)) * mem.page_size);
304 }
305
306 if (is_last_item(old_mem, new_align)) {
307 const start_index = end_index - old_mem.len;
308 const new_end_index = start_index + new_size;
309
310 if (new_end_index > num_pages * mem.page_size) {
311 _ = try alloc(allocator, new_end_index - end_index, new_align);
312 }
313 const result = start_ptr[start_index..new_end_index];
314
315 end_index = new_end_index;
316 return result;
317 } else if (new_size <= old_mem.len and new_align <= old_align) {
318 return error.OutOfMemory;
292 pub fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) Allocator.Error![]u8 {
293 if (old_mem.len == 0) {
294 return alloc(allocator, new_size, new_align);
295 } else if (new_size < old_mem.len) {
296 return shrink(allocator, old_mem, old_align, new_size, new_align);
297 } else if (nPages(new_size) == nPages(old_mem.len)) {
298 return old_mem.ptr[0..new_size];
319299 } else {
320 const result = try alloc(allocator, new_size, new_align);
321 mem.copy(u8, result, old_mem);
322 return result;
300 const new_mem = try alloc(allocator, new_size, new_align);
301 std.mem.copy(u8, new_mem, old_mem);
302 _ = shrink(allocator, old_mem, old_align, 0, 0);
303 return new_mem[0..new_size];
323304 }
324305 }
325306
326 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
307 pub fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
308 var shrinkage = nPages(old_mem.len) - nPages(new_size);
309 if (shrinkage > 0) {
310 const success = base.recycle(old_mem[new_size..old_mem.len]);
311 if (!success) {
312 std.debug.assert(additional.recycle(old_mem[new_size..old_mem.len]));
313 }
314 }
327315 return old_mem[0..new_size];
328316 }
329317};