| ... | ... | @@ -246,84 +246,72 @@ const PageAllocator = struct { |
| 246 | 246 | } |
| 247 | 247 | }; |
| 248 | 248 | |
| 249 | extern const __heap_base: [*]u8; |
| 249 | 250 | // TODO Exposed LLVM intrinsics is a bug |
| 250 | 251 | // See: https://github.com/ziglang/zig/issues/2291 |
| 251 | 252 | extern fn @"llvm.wasm.memory.size.i32"(u32) u32; |
| 252 | 253 | extern fn @"llvm.wasm.memory.grow.i32"(u32, u32) i32; |
| 253 | 254 | |
| 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() |
| 255 | test "" { |
| 256 | _ = WasmPageAllocator.alloc; |
| 257 | } |
| 258 | |
| 256 | 259 | const 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{}, |
| 260 | 263 | |
| 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; |
| 264 | 266 | } |
| 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{}; |
| 272 | 270 | |
| 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 | } |
| 275 | 274 | |
| 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 | } |
| 280 | 279 | |
| 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) { |
| 283 | 284 | return error.OutOfMemory; |
| 284 | 285 | } |
| 285 | 286 | |
| 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 | }; |
| 298 | 290 | } |
| 299 | 291 | |
| 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]; |
| 319 | 299 | } 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]; |
| 323 | 304 | } |
| 324 | 305 | } |
| 325 | 306 | |
| 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 | } |
| 327 | 315 | return old_mem[0..new_size]; |
| 328 | 316 | } |
| 329 | 317 | }; |