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 {...@@ -318,48 +318,96 @@ pub const FixedBufferAllocator = struct {
318 }318 }
319};319};
320320
321extern fn @"llvm.wasm.memory.size.i32"(u32) u32;
321extern fn @"llvm.wasm.memory.grow.i32"(u32, u32) i32;322extern 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 internally336pub const WasmAllocator = struct {
324pub const WasmAllocator = blk: {337 allocator: Allocator,
325 if (builtin.arch != builtin.Arch.wasm32) {338 start_ptr: [*]u8,
326 @compileError("only supported in wasm32");339 num_pages: usize,
327 } else {340 end_index: usize,
328 const mem_grow = @"llvm.wasm.memory.grow.i32";
329
330 const WASM_PAGE_SIZE = 64 * 1024; // 64 kilobytes
331341
332 break :blk struct {342 fn alloc(allocator: *Allocator, size: usize, alignment: u29) ![]u8 {
333 allocator: Allocator,343 const self = @fieldParentPtr(WasmAllocator, "allocator", allocator);
334 fb_allocator: FixedBufferAllocator,
335344
336 pub fn init(num_pages: u32) !WasmAllocator {345 const addr = @ptrToInt(self.start_ptr) + self.end_index;
337 const prev_block = mem_grow(0, num_pages);346 const adjusted_addr = mem.alignForward(addr, alignment);
338 if (prev_block == -1) {347 const adjusted_index = self.end_index + (adjusted_addr - addr);
339 return error.OutOfMemory;348 const new_end_index = adjusted_index + size;
340 }
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{352 if (required_memory > 0) {
345 .allocator = Allocator{353 var num_pages: u32 = @divTrunc(required_memory, WASM_PAGE_SIZE);
346 .reallocFn = realloc,354 if (@rem(required_memory, WASM_PAGE_SIZE) != 0) {
347 .shrinkFn = shrink,355 num_pages += 1;
348 },
349 .fb_allocator = FixedBufferAllocator.init(buffer_slice),
350 };
351 }356 }
352357
353 fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 {358 const prev_page = @"llvm.wasm.memory.grow.i32"(0, num_pages);
354 const self = @fieldParentPtr(WasmAllocator, "allocator", allocator);359 if (prev_page == -1) {
355 return FixedBufferAllocator.realloc(&self.fb_allocator.allocator, old_mem, old_align, new_size, new_align);360 return error.OutOfMemory;
356 }361 }
357362
358 fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 {363 self.num_pages += num_pages;
359 const self = @fieldParentPtr(WasmAllocator, "allocator", allocator);364 }
360 return FixedBufferAllocator.shrink(&self.fb_allocator.allocator, old_mem, old_align, new_size, new_align);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);
361 }394 }
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];
363 }411 }
364};412};
365413