| ... | ... | @@ -33,11 +33,19 @@ fn cShrink(self: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new |
| 33 | 33 | |
| 34 | 34 | /// This allocator makes a syscall directly for every allocation and free. |
| 35 | 35 | /// Thread-safe and lock-free. |
| 36 | | pub const page_allocator = &page_allocator_state; |
| 36 | pub const page_allocator = if (std.Target.current.isWasm()) |
| 37 | &wasm_page_allocator_state |
| 38 | else |
| 39 | &page_allocator_state; |
| 40 | |
| 37 | 41 | var page_allocator_state = Allocator{ |
| 38 | 42 | .reallocFn = PageAllocator.realloc, |
| 39 | 43 | .shrinkFn = PageAllocator.shrink, |
| 40 | 44 | }; |
| 45 | var wasm_page_allocator_state = Allocator{ |
| 46 | .reallocFn = WasmPageAllocator.realloc, |
| 47 | .shrinkFn = WasmPageAllocator.shrink, |
| 48 | }; |
| 41 | 49 | |
| 42 | 50 | /// Deprecated. Use `page_allocator`. |
| 43 | 51 | pub const direct_allocator = page_allocator; |
| ... | ... | @@ -238,6 +246,88 @@ const PageAllocator = struct { |
| 238 | 246 | } |
| 239 | 247 | }; |
| 240 | 248 | |
| 249 | // TODO Exposed LLVM intrinsics is a bug |
| 250 | // See: https://github.com/ziglang/zig/issues/2291 |
| 251 | extern fn @"llvm.wasm.memory.size.i32"(u32) u32; |
| 252 | extern fn @"llvm.wasm.memory.grow.i32"(u32, u32) i32; |
| 253 | |
| 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() |
| 256 | const WasmPageAllocator = struct { |
| 257 | var start_ptr: [*]u8 = undefined; |
| 258 | var num_pages: usize = 0; |
| 259 | var end_index: usize = 0; |
| 260 | |
| 261 | comptime { |
| 262 | if (builtin.arch != .wasm32) { |
| 263 | @compileError("WasmPageAllocator is only available for wasm32 arch"); |
| 264 | } |
| 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; |
| 272 | |
| 273 | if (new_end_index > num_pages * mem.page_size) { |
| 274 | const required_memory = new_end_index - (num_pages * mem.page_size); |
| 275 | |
| 276 | var num_pages: usize = required_memory / mem.page_size; |
| 277 | if (required_memory % mem.page_size != 0) { |
| 278 | num_pages += 1; |
| 279 | } |
| 280 | |
| 281 | const prev_page = @"llvm.wasm.memory.grow.i32"(0, @intCast(u32, num_pages)); |
| 282 | if (prev_page == -1) { |
| 283 | return error.OutOfMemory; |
| 284 | } |
| 285 | |
| 286 | num_pages += 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); |
| 298 | } |
| 299 | |
| 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; |
| 319 | } else { |
| 320 | const result = try alloc(allocator, new_size, new_align); |
| 321 | mem.copy(u8, result, old_mem); |
| 322 | return result; |
| 323 | } |
| 324 | } |
| 325 | |
| 326 | fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { |
| 327 | return old_mem[0..new_size]; |
| 328 | } |
| 329 | }; |
| 330 | |
| 241 | 331 | pub const HeapAllocator = switch (builtin.os) { |
| 242 | 332 | .windows => struct { |
| 243 | 333 | allocator: Allocator, |
| ... | ... | @@ -487,103 +577,6 @@ pub const FixedBufferAllocator = struct { |
| 487 | 577 | } |
| 488 | 578 | }; |
| 489 | 579 | |
| 490 | | // TODO Exposed LLVM intrinsics is a bug |
| 491 | | // See: https://github.com/ziglang/zig/issues/2291 |
| 492 | | extern fn @"llvm.wasm.memory.size.i32"(u32) u32; |
| 493 | | extern fn @"llvm.wasm.memory.grow.i32"(u32, u32) i32; |
| 494 | | |
| 495 | | pub const wasm_allocator = &wasm_allocator_state.allocator; |
| 496 | | var wasm_allocator_state = WasmAllocator{ |
| 497 | | .allocator = Allocator{ |
| 498 | | .reallocFn = WasmAllocator.realloc, |
| 499 | | .shrinkFn = WasmAllocator.shrink, |
| 500 | | }, |
| 501 | | .start_ptr = undefined, |
| 502 | | .num_pages = 0, |
| 503 | | .end_index = 0, |
| 504 | | }; |
| 505 | | |
| 506 | | const WasmAllocator = struct { |
| 507 | | allocator: Allocator, |
| 508 | | start_ptr: [*]u8, |
| 509 | | num_pages: usize, |
| 510 | | end_index: usize, |
| 511 | | |
| 512 | | comptime { |
| 513 | | if (builtin.arch != .wasm32) { |
| 514 | | @compileError("WasmAllocator is only available for wasm32 arch"); |
| 515 | | } |
| 516 | | } |
| 517 | | |
| 518 | | fn alloc(allocator: *Allocator, size: usize, alignment: u29) ![]u8 { |
| 519 | | const self = @fieldParentPtr(WasmAllocator, "allocator", allocator); |
| 520 | | |
| 521 | | const addr = @ptrToInt(self.start_ptr) + self.end_index; |
| 522 | | const adjusted_addr = mem.alignForward(addr, alignment); |
| 523 | | const adjusted_index = self.end_index + (adjusted_addr - addr); |
| 524 | | const new_end_index = adjusted_index + size; |
| 525 | | |
| 526 | | if (new_end_index > self.num_pages * mem.page_size) { |
| 527 | | const required_memory = new_end_index - (self.num_pages * mem.page_size); |
| 528 | | |
| 529 | | var num_pages: usize = required_memory / mem.page_size; |
| 530 | | if (required_memory % mem.page_size != 0) { |
| 531 | | num_pages += 1; |
| 532 | | } |
| 533 | | |
| 534 | | const prev_page = @"llvm.wasm.memory.grow.i32"(0, @intCast(u32, num_pages)); |
| 535 | | if (prev_page == -1) { |
| 536 | | return error.OutOfMemory; |
| 537 | | } |
| 538 | | |
| 539 | | self.num_pages += num_pages; |
| 540 | | } |
| 541 | | |
| 542 | | const result = self.start_ptr[adjusted_index..new_end_index]; |
| 543 | | self.end_index = new_end_index; |
| 544 | | |
| 545 | | return result; |
| 546 | | } |
| 547 | | |
| 548 | | // Check if memory is the last "item" and is aligned correctly |
| 549 | | fn is_last_item(allocator: *Allocator, memory: []u8, alignment: u29) bool { |
| 550 | | const self = @fieldParentPtr(WasmAllocator, "allocator", allocator); |
| 551 | | return memory.ptr == self.start_ptr + self.end_index - memory.len and mem.alignForward(@ptrToInt(memory.ptr), alignment) == @ptrToInt(memory.ptr); |
| 552 | | } |
| 553 | | |
| 554 | | fn realloc(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) ![]u8 { |
| 555 | | const self = @fieldParentPtr(WasmAllocator, "allocator", allocator); |
| 556 | | |
| 557 | | // Initialize start_ptr at the first realloc |
| 558 | | if (self.num_pages == 0) { |
| 559 | | self.start_ptr = @intToPtr([*]u8, @intCast(usize, @"llvm.wasm.memory.size.i32"(0)) * mem.page_size); |
| 560 | | } |
| 561 | | |
| 562 | | if (is_last_item(allocator, old_mem, new_align)) { |
| 563 | | const start_index = self.end_index - old_mem.len; |
| 564 | | const new_end_index = start_index + new_size; |
| 565 | | |
| 566 | | if (new_end_index > self.num_pages * mem.page_size) { |
| 567 | | _ = try alloc(allocator, new_end_index - self.end_index, new_align); |
| 568 | | } |
| 569 | | const result = self.start_ptr[start_index..new_end_index]; |
| 570 | | |
| 571 | | self.end_index = new_end_index; |
| 572 | | return result; |
| 573 | | } else if (new_size <= old_mem.len and new_align <= old_align) { |
| 574 | | return error.OutOfMemory; |
| 575 | | } else { |
| 576 | | const result = try alloc(allocator, new_size, new_align); |
| 577 | | mem.copy(u8, result, old_mem); |
| 578 | | return result; |
| 579 | | } |
| 580 | | } |
| 581 | | |
| 582 | | fn shrink(allocator: *Allocator, old_mem: []u8, old_align: u29, new_size: usize, new_align: u29) []u8 { |
| 583 | | return old_mem[0..new_size]; |
| 584 | | } |
| 585 | | }; |
| 586 | | |
| 587 | 580 | pub const ThreadSafeFixedBufferAllocator = blk: { |
| 588 | 581 | if (builtin.single_threaded) { |
| 589 | 582 | break :blk FixedBufferAllocator; |