| ... | @@ -246,84 +246,72 @@ const PageAllocator = struct { | ... | @@ -246,84 +246,72 @@ const PageAllocator = struct { |
| 246 | } | 246 | } |
| 247 | }; | 247 | }; |
| 248 | | 248 | |
| | 249 | extern const __heap_base: [*]u8; |
| 249 | // TODO Exposed LLVM intrinsics is a bug | 250 | // TODO Exposed LLVM intrinsics is a bug |
| 250 | // See: https://github.com/ziglang/zig/issues/2291 | 251 | // See: https://github.com/ziglang/zig/issues/2291 |
| 251 | extern fn @"llvm.wasm.memory.size.i32"(u32) u32; | 252 | extern fn @"llvm.wasm.memory.size.i32"(u32) u32; |
| 252 | extern fn @"llvm.wasm.memory.grow.i32"(u32, u32) i32; | 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 | test "" { |
| 255 | /// by better utilizing the return value of grow() | 256 | _ = WasmPageAllocator.alloc; |
| | 257 | } |
| | 258 | |
| 256 | const WasmPageAllocator = struct { | 259 | const WasmPageAllocator = struct { |
| 257 | var start_ptr: [*]u8 = undefined; | 260 | const FreeBlock = struct { |
| 258 | var num_pages: usize = 0; | 261 | offset: usize = 0, |
| 259 | var end_index: usize = 0; | 262 | data: []u8 = &[_]u8{}, |
| 260 | | 263 | |
| 261 | comptime { | 264 | fn alloc(self: FreeBlock, num_pages: usize) ?[]u8 { |
| 262 | if (builtin.arch != .wasm32) { | 265 | return null; |
| 263 | @compileError("WasmPageAllocator is only available for wasm32 arch"); | | |
| 264 | } | 266 | } |
| 265 | } | 267 | }; |
| 266 | | 268 | var base = FreeBlock{}; |
| 267 | fn alloc(allocator: *Allocator, size: usize, alignment: u29) ![]u8 { | 269 | var additional = FreeBlock{}; |
| 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; | | |
| 272 | | 270 | |
| 273 | if (new_end_index > num_pages * mem.page_size) { | 271 | fn nPages(memsize: usize) usize { |
| 274 | const required_memory = new_end_index - (num_pages * mem.page_size); | 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; | 275 | fn alloc(allocator: *Allocator, n: usize, alignment: u29) error{OutOfMemory}![]u8 { |
| 277 | if (required_memory % mem.page_size != 0) { | 276 | if (alignment > std.mem.page_size) { |
| 278 | inner_num_pages += 1; | 277 | return error.OutOfMemory; |
| 279 | } | 278 | } |
| 280 | | 279 | |
| 281 | const prev_page = @"llvm.wasm.memory.grow.i32"(0, @intCast(u32, inner_num_pages)); | 280 | const n_pages = nPages(n); |
| 282 | if (prev_page == -1) { | 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 | return error.OutOfMemory; | 284 | return error.OutOfMemory; |
| 284 | } | 285 | } |
| 285 | | 286 | |
| 286 | num_pages += inner_num_pages; | 287 | const start_ptr = @intToPtr([*]u8, @intCast(usize, prev_page_count) * std.mem.page_size); |
| 287 | } | 288 | return start_ptr[0..n]; |
| 288 | | 289 | }; |
| 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); | | |
| 298 | } | 290 | } |
| 299 | | 291 | |
| 300 | fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { | 292 | pub fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) Allocator.Error![]u8 { |
| 301 | // Initialize start_ptr at the first realloc | 293 | if (old_mem.len == 0) { |
| 302 | if (num_pages == 0) { | 294 | return alloc(allocator, new_size, new_align); |
| 303 | start_ptr = @intToPtr([*]u8, @intCast(usize, @"llvm.wasm.memory.size.i32"(0)) * mem.page_size); | 295 | } else if (new_size < old_mem.len) { |
| 304 | } | 296 | return shrink(allocator, old_mem, old_align, new_size, new_align); |
| 305 | | 297 | } else if (nPages(new_size) == nPages(old_mem.len)) { |
| 306 | if (is_last_item(old_mem, new_align)) { | 298 | return old_mem.ptr[0..new_size]; |
| 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; | | |
| 319 | } else { | 299 | } else { |
| 320 | const result = try alloc(allocator, new_size, new_align); | 300 | const new_mem = try alloc(allocator, new_size, new_align); |
| 321 | mem.copy(u8, result, old_mem); | 301 | std.mem.copy(u8, new_mem, old_mem); |
| 322 | return result; | 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 | return old_mem[0..new_size]; | 315 | return old_mem[0..new_size]; |
| 328 | } | 316 | } |
| 329 | }; | 317 | }; |