authorgravatar for shritesh@shritesh.comShritesh Bhattarai <shritesh@shritesh.com> 2019-04-16 01:40:16-05:00
committergravatar for shritesh@shritesh.comShritesh Bhattarai <shritesh@shritesh.com> 2019-04-16 01:40:16-05:00
logaa3f31ac0a986f780831697b6d4b0074dbce9ced
treebff14587f50d6931e44a19d135222d5468d61565
parentf2119f9961f6b419f5cf204aba20e2aa2810b36a

wasm: add WasmAllocator


1 files changed, 79 insertions(+), 31 deletions(-)

std/heap.zig+79-31
......@@ -318,48 +318,96 @@ pub const FixedBufferAllocator = struct {
318318 }
319319};
320320
321extern fn @"llvm.wasm.memory.size.i32"(u32) u32;
321322extern fn @"llvm.wasm.memory.grow.i32"(u32, u32) i32;
323const WASM_PAGE_SIZE = 64 * 1024; // 64 kilobytes
324
325pub const wasm_allocator = &wasm_allocator_state.allocator;
326var wasm_allocator_state = WasmAllocator{
327 .allocator = Allocator{
328 .reallocFn = WasmAllocator.realloc,
329 .shrinkFn = WasmAllocator.shrink,
330 },
331 .start_ptr = undefined,
332 .num_pages = 0,
333 .end_index = 0,
334};
322335
323/// This allocator tries to allocate the specified number of 64 KB pages and uses FixedBufferAllocator internally
324pub const WasmAllocator = blk: {
325 if (builtin.arch != builtin.Arch.wasm32) {
326 @compileError("only supported in wasm32");
327 } else {
328 const mem_grow = @"llvm.wasm.memory.grow.i32";
329
330 const WASM_PAGE_SIZE = 64 * 1024; // 64 kilobytes
336pub const WasmAllocator = struct {
337 allocator: Allocator,
338 start_ptr: [*]u8,
339 num_pages: usize,
340 end_index: usize,
331341
332 break :blk struct {
333 allocator: Allocator,
334 fb_allocator: FixedBufferAllocator,
342 fn alloc(allocator: *Allocator, size: usize, alignment: u29) ![]u8 {
343 const self = @fieldParentPtr(WasmAllocator, "allocator", allocator);
335344
336 pub fn init(num_pages: u32) !WasmAllocator {
337 const prev_block = mem_grow(0, num_pages);
338 if (prev_block == -1) {
339 return error.OutOfMemory;
340 }
345 const addr = @ptrToInt(self.start_ptr) + self.end_index;
346 const adjusted_addr = mem.alignForward(addr, alignment);
347 const adjusted_index = self.end_index + (adjusted_addr - addr);
348 const new_end_index = adjusted_index + size;
341349
342 const buffer_slice = @intToPtr([*]u8, @intCast(usize, prev_block) * WASM_PAGE_SIZE)[0..(WASM_PAGE_SIZE * num_pages)];
350 const required_memory = new_end_index - (self.num_pages * WASM_PAGE_SIZE);
343351
344 return WasmAllocator{
345 .allocator = Allocator{
346 .reallocFn = realloc,
347 .shrinkFn = shrink,
348 },
349 .fb_allocator = FixedBufferAllocator.init(buffer_slice),
350 };
352 if (required_memory > 0) {
353 var num_pages: u32 = @divTrunc(required_memory, WASM_PAGE_SIZE);
354 if (@rem(required_memory, WASM_PAGE_SIZE) != 0) {
355 num_pages += 1;
351356 }
352357
353 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
354 const self = @fieldParentPtr(WasmAllocator, "allocator", allocator);
355 return FixedBufferAllocator.realloc(&self.fb_allocator.allocator, old_mem, old_align, new_size, new_align);
358 const prev_page = @"llvm.wasm.memory.grow.i32"(0, num_pages);
359 if (prev_page == -1) {
360 return error.OutOfMemory;
356361 }
357362
358 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
359 const self = @fieldParentPtr(WasmAllocator, "allocator", allocator);
360 return FixedBufferAllocator.shrink(&self.fb_allocator.allocator, old_mem, old_align, new_size, new_align);
363 self.num_pages += num_pages;
364 }
365
366 const result = self.start_ptr[adjusted_index..new_end_index];
367 self.end_index = new_end_index;
368
369
370
371 return result;
372 }
373
374 // Check if memory is the last "item" and it aligns. That lets us expand or reclaim memory
375 fn is_last_item(allocator: *Allocator, memory: []u8, alignment: u29) bool {
376 const self = @fieldParentPtr(WasmAllocator, "allocator", allocator);
377 return memory.ptr == self.start_ptr + self.end_index - memory.len and mem.alignForward(@ptrToInt(memory.ptr), alignment) == @ptrToInt(memory.ptr);
378 }
379
380 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {
381 const self = @fieldParentPtr(WasmAllocator, "allocator", allocator);
382
383 // Initialize start_ptr at the first realloc
384 if (self.num_pages == 0) {
385 self.start_ptr = @intToPtr([*]u8, @intCast(usize, @"llvm.wasm.memory.size.i32"(0)) * WASM_PAGE_SIZE);
386 }
387
388 if (is_last_item(allocator, old_mem, new_align)) {
389 const start_index = self.end_index - old_mem.len;
390 const new_end_index = start_index + new_size;
391
392 if (new_end_index > self.num_pages * WASM_PAGE_SIZE) {
393 _ = try alloc(allocator, new_end_index - self.end_index, new_align);
361394 }
362 };
395 const result = self.start_ptr[start_index..new_end_index];
396
397 self.end_index = new_end_index;
398 return result;
399 } else if (new_size <= old_mem.len and new_align <= old_align) {
400 return error.OutOfMemory;
401 } else {
402 const result = try alloc(allocator, new_size, new_align);
403 mem.copy(u8, result, old_mem);
404 return result;
405 }
406 }
407
408 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {
409 // TODO: Use is_last_item or other heuristic here
410 return old_mem[0..new_size];
363411 }
364412};
365413