From 6d595473d9f620111f789f1f95686aa9920e61b4 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreply.github.com> Date: Mon, 25 Mar 2024 12:26:07 +1100 Subject: [PATCH 1/5] feat!: update for zig version 0.12.0-dev.3387+cba155510 --- src/Page.zig | 2 +- src/huge_alignment.zig | 16 ++++++++-------- src/libzimalloc.zig | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/Page.zig b/src/Page.zig index ca7267d..427393e 100644 --- a/src/Page.zig +++ b/src/Page.zig @@ -60,7 +60,7 @@ pub fn deinit(self: *Page) !void { segment.init_set.unset(page_index); const page_bytes = segment.pageSlice(page_index); - try std.os.madvise(page_bytes.ptr, page_bytes.len, std.os.MADV.DONTNEED); + try std.posix.madvise(page_bytes.ptr, page_bytes.len, std.posix.MADV.DONTNEED); } pub fn getPtrInFreeSlot(self: *const Page) *align(constants.min_slot_alignment) anyopaque { diff --git a/src/huge_alignment.zig b/src/huge_alignment.zig index a8fecfc..fa023c1 100644 --- a/src/huge_alignment.zig +++ b/src/huge_alignment.zig @@ -4,27 +4,27 @@ pub fn allocateOptions( size: usize, alignment: usize, prot: u32, - flags: std.os.MAP, + flags: std.posix.MAP, ) ?[]align(std.mem.page_size) u8 { assert.withMessage(@src(), alignment > std.mem.page_size, "alignment is not greater than the page size"); assert.withMessage(@src(), std.mem.isValidAlign(alignment), "alignment is not a power of two"); const mmap_length = size + alignment - 1; - const unaligned = std.os.mmap(null, mmap_length, prot, flags, -1, 0) catch return null; + const unaligned = std.posix.mmap(null, mmap_length, prot, flags, -1, 0) catch return null; const unaligned_address = @intFromPtr(unaligned.ptr); const aligned_address = std.mem.alignForward(usize, unaligned_address, alignment); const aligned_size = std.mem.alignForward(usize, size, std.mem.page_size); if (aligned_address == unaligned_address) { - std.os.munmap(@alignCast(unaligned[aligned_size..])); + std.posix.munmap(@alignCast(unaligned[aligned_size..])); return unaligned[0..aligned_size]; } else { const offset = aligned_address - unaligned_address; assert.withMessage(@src(), std.mem.isAligned(offset, std.mem.page_size), "offset is not aligned"); - std.os.munmap(unaligned[0..offset]); - std.os.munmap(@alignCast(unaligned[offset + aligned_size ..])); + std.posix.munmap(unaligned[0..offset]); + std.posix.munmap(@alignCast(unaligned[offset + aligned_size ..])); return @alignCast(unaligned[offset..][0..aligned_size]); } } @@ -35,7 +35,7 @@ pub fn allocate(size: usize, alignment: usize) ?[]align(std.mem.page_size) u8 { return allocateOptions( size, alignment, - std.os.PROT.READ | std.os.PROT.WRITE, + std.posix.PROT.READ | std.posix.PROT.WRITE, .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, ); } @@ -43,7 +43,7 @@ pub fn allocate(size: usize, alignment: usize) ?[]align(std.mem.page_size) u8 { /// Rounds `buf.len` up to a multiple of `std.mem.page_size`. pub fn deallocate(buf: []align(std.mem.page_size) const u8) void { const aligned_len = std.mem.alignForward(usize, buf.len, std.mem.page_size); - std.os.munmap(buf.ptr[0..aligned_len]); + std.posix.munmap(buf.ptr[0..aligned_len]); } pub fn resizeAllocation(buf: []align(std.mem.page_size) u8, new_len: usize) bool { @@ -54,7 +54,7 @@ pub fn resizeAllocation(buf: []align(std.mem.page_size) u8, new_len: usize) bool return true; } else if (new_aligned_len < old_aligned_len) { const trailing_ptr: [*]align(std.mem.page_size) u8 = @alignCast(buf.ptr + new_aligned_len); - std.os.munmap(trailing_ptr[0 .. old_aligned_len - new_aligned_len]); + std.posix.munmap(trailing_ptr[0 .. old_aligned_len - new_aligned_len]); return true; } else { return false; diff --git a/src/libzimalloc.zig b/src/libzimalloc.zig index d109098..aa13965 100644 --- a/src/libzimalloc.zig +++ b/src/libzimalloc.zig @@ -75,7 +75,7 @@ export fn posix_memalign(ptr: *?*anyopaque, alignment: usize, size: usize) c_int } if (@popCount(alignment) != 1 or alignment < @sizeOf(*anyopaque)) { - return @intFromEnum(std.os.E.INVAL); + return @intFromEnum(std.posix.E.INVAL); } if (allocateBytes(size, alignment, @returnAddress(), false, false, false)) |p| { @@ -83,7 +83,7 @@ export fn posix_memalign(ptr: *?*anyopaque, alignment: usize, size: usize) c_int return 0; } - return @intFromEnum(std.os.E.NOMEM); + return @intFromEnum(std.posix.E.NOMEM); } export fn memalign(alignment: usize, size: usize) ?*anyopaque { From 2f81edbeed11633501fe9843283ebb99c16bb232 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreply.github.com> Date: Thu, 17 Aug 2023 18:26:41 +1000 Subject: [PATCH 2/5] refactor: make huge_alignment.allocateOptions private --- src/huge_alignment.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/huge_alignment.zig b/src/huge_alignment.zig index fa023c1..431589d 100644 --- a/src/huge_alignment.zig +++ b/src/huge_alignment.zig @@ -1,6 +1,6 @@ -/// The `size` is rounded up to a multiple of `std.mem.page_size`. +/// The `size` is rounded up to a multiple of `std.mem.page_size`. /// Can be freed with std.os.unmap -pub fn allocateOptions( +fn allocateOptions( size: usize, alignment: usize, prot: u32, From c4e00bada8e8151aa368a93c7cd5d514d7ec071c Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreply.github.com> Date: Thu, 17 Aug 2023 18:27:02 +1000 Subject: [PATCH 3/5] feat: add windows support --- src/Page.zig | 12 ++- src/huge_alignment.zig | 71 ++--------------- src/huge_alignment/linux.zig | 66 +++++++++++++++ src/huge_alignment/windows.zig | 142 +++++++++++++++++++++++++++++++++ 4 files changed, 225 insertions(+), 66 deletions(-) create mode 100644 src/huge_alignment/linux.zig create mode 100644 src/huge_alignment/windows.zig diff --git a/src/Page.zig b/src/Page.zig index 427393e..d2275ae 100644 --- a/src/Page.zig +++ b/src/Page.zig @@ -60,7 +60,17 @@ pub fn deinit(self: *Page) !void { segment.init_set.unset(page_index); const page_bytes = segment.pageSlice(page_index); - try std.posix.madvise(page_bytes.ptr, page_bytes.len, std.posix.MADV.DONTNEED); + switch (@import("builtin").os.tag) { + .linux => try std.posix.madvise(page_bytes.ptr, page_bytes.len, std.posix.MADV.DONTNEED), + // TODO: test this windows impl, presumably it's broken + .windows => _ = try std.os.windows.VirtualAlloc( + page_bytes.ptr, + page_bytes.len, + std.os.windows.MEM_RESET, + std.os.windows.PAGE_NOACCESS, + ), + else => |tag| @compileError(@tagName(tag) ++ " is not supported yet"), + } } pub fn getPtrInFreeSlot(self: *const Page) *align(constants.min_slot_alignment) anyopaque { diff --git a/src/huge_alignment.zig b/src/huge_alignment.zig index 431589d..7bc195f 100644 --- a/src/huge_alignment.zig +++ b/src/huge_alignment.zig @@ -1,66 +1,7 @@ -/// The `size` is rounded up to a multiple of `std.mem.page_size`. -/// Can be freed with std.os.unmap -fn allocateOptions( - size: usize, - alignment: usize, - prot: u32, - flags: std.posix.MAP, -) ?[]align(std.mem.page_size) u8 { - assert.withMessage(@src(), alignment > std.mem.page_size, "alignment is not greater than the page size"); - assert.withMessage(@src(), std.mem.isValidAlign(alignment), "alignment is not a power of two"); +const builtin = @import("builtin"); - const mmap_length = size + alignment - 1; - const unaligned = std.posix.mmap(null, mmap_length, prot, flags, -1, 0) catch return null; - const unaligned_address = @intFromPtr(unaligned.ptr); - const aligned_address = std.mem.alignForward(usize, unaligned_address, alignment); - - const aligned_size = std.mem.alignForward(usize, size, std.mem.page_size); - - if (aligned_address == unaligned_address) { - std.posix.munmap(@alignCast(unaligned[aligned_size..])); - return unaligned[0..aligned_size]; - } else { - const offset = aligned_address - unaligned_address; - assert.withMessage(@src(), std.mem.isAligned(offset, std.mem.page_size), "offset is not aligned"); - - std.posix.munmap(unaligned[0..offset]); - std.posix.munmap(@alignCast(unaligned[offset + aligned_size ..])); - return @alignCast(unaligned[offset..][0..aligned_size]); - } -} - -/// Makes a readable, writeable, anonymous private mapping with size rounded up to -/// a multiple of `std.mem.page_size`. Should be freed with `deallocate()`. -pub fn allocate(size: usize, alignment: usize) ?[]align(std.mem.page_size) u8 { - return allocateOptions( - size, - alignment, - std.posix.PROT.READ | std.posix.PROT.WRITE, - .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, - ); -} - -/// Rounds `buf.len` up to a multiple of `std.mem.page_size`. -pub fn deallocate(buf: []align(std.mem.page_size) const u8) void { - const aligned_len = std.mem.alignForward(usize, buf.len, std.mem.page_size); - std.posix.munmap(buf.ptr[0..aligned_len]); -} - -pub fn resizeAllocation(buf: []align(std.mem.page_size) u8, new_len: usize) bool { - const old_aligned_len = std.mem.alignForward(usize, buf.len, std.mem.page_size); - const new_aligned_len = std.mem.alignForward(usize, new_len, std.mem.page_size); - - if (new_aligned_len == old_aligned_len) { - return true; - } else if (new_aligned_len < old_aligned_len) { - const trailing_ptr: [*]align(std.mem.page_size) u8 = @alignCast(buf.ptr + new_aligned_len); - std.posix.munmap(trailing_ptr[0 .. old_aligned_len - new_aligned_len]); - return true; - } else { - return false; - } -} - -const std = @import("std"); - -const assert = @import("assert.zig"); +pub usingnamespace switch (builtin.os.tag) { + .linux => @import("huge_alignment/linux.zig"), + .windows => @import("huge_alignment/windows.zig"), + else => |tag| @compileError(@tagName(tag) ++ "is not supported yet"), +}; diff --git a/src/huge_alignment/linux.zig b/src/huge_alignment/linux.zig new file mode 100644 index 0000000..3c0ec33 --- /dev/null +++ b/src/huge_alignment/linux.zig @@ -0,0 +1,66 @@ +/// The `size` is rounded up to a multiple of `std.mem.page_size`. +/// Can be freed with std.os.unmap +fn allocateOptions( + size: usize, + alignment: usize, + prot: u32, + flags: std.posix.MAP, +) ?[]align(std.mem.page_size) u8 { + assert.withMessage(@src(), alignment > std.mem.page_size, "alignment is not greater than the page size"); + assert.withMessage(@src(), std.mem.isValidAlign(alignment), "alignment is not a power of two"); + + const mmap_length = size + alignment - 1; + const unaligned = std.posix.mmap(null, mmap_length, prot, flags, -1, 0) catch return null; + const unaligned_address = @intFromPtr(unaligned.ptr); + const aligned_address = std.mem.alignForward(usize, unaligned_address, alignment); + + const aligned_size = std.mem.alignForward(usize, size, std.mem.page_size); + + if (aligned_address == unaligned_address) { + std.posix.munmap(@alignCast(unaligned[aligned_size..])); + return unaligned[0..aligned_size]; + } else { + const offset = aligned_address - unaligned_address; + assert.withMessage(@src(), std.mem.isAligned(offset, std.mem.page_size), "offset is not aligned"); + + std.posix.munmap(unaligned[0..offset]); + std.posix.munmap(@alignCast(unaligned[offset + aligned_size ..])); + return @alignCast(unaligned[offset..][0..aligned_size]); + } +} + +/// Makes a readable, writeable, anonymous private mapping with size rounded up to +/// a multiple of `std.mem.page_size`. Should be freed with `deallocate()`. +pub fn allocate(size: usize, alignment: usize) ?[]align(std.mem.page_size) u8 { + return allocateOptions( + size, + alignment, + std.posix.PROT.READ | std.posix.PROT.WRITE, + .{ .TYPE = .PRIVATE, .ANONYMOUS = true }, + ); +} + +/// Rounds `buf.len` up to a multiple of `std.mem.page_size`. +pub fn deallocate(buf: []align(std.mem.page_size) const u8) void { + const aligned_len = std.mem.alignForward(usize, buf.len, std.mem.page_size); + std.posix.munmap(buf.ptr[0..aligned_len]); +} + +pub fn resizeAllocation(buf: []align(std.mem.page_size) u8, new_len: usize) bool { + const old_aligned_len = std.mem.alignForward(usize, buf.len, std.mem.page_size); + const new_aligned_len = std.mem.alignForward(usize, new_len, std.mem.page_size); + + if (new_aligned_len == old_aligned_len) { + return true; + } else if (new_aligned_len < old_aligned_len) { + const trailing_ptr: [*]align(std.mem.page_size) u8 = @alignCast(buf.ptr + new_aligned_len); + std.posix.munmap(trailing_ptr[0 .. old_aligned_len - new_aligned_len]); + return true; + } else { + return false; + } +} + +const std = @import("std"); + +const assert = @import("../assert.zig"); diff --git a/src/huge_alignment/windows.zig b/src/huge_alignment/windows.zig new file mode 100644 index 0000000..4874aec --- /dev/null +++ b/src/huge_alignment/windows.zig @@ -0,0 +1,142 @@ +/// Makes a readable, writeable, anonymous private mapping with size rounded up to +/// a multiple of `std.mem.page_size`. Should be freed with `deallocate()`. +pub fn allocate(size: usize, alignment: usize) ?[]align(std.mem.page_size) u8 { + return allocateOptions( + size, + alignment, + windows.PAGE_READWRITE, + windows.MEM_COMMIT | windows.MEM_RESERVE, + ); +} + +/// Rounds `buf.len` up to a multiple of `std.mem.page_size`. +pub fn deallocate(buf: []align(std.mem.page_size) u8) void { + windows.VirtualFree(@ptrCast(buf.ptr), 0, windows.MEM_RELEASE); +} + +pub fn resizeAllocation(buf: []align(std.mem.page_size) u8, new_len: usize) bool { + const old_aligned_len = std.mem.alignForward(usize, buf.len, std.mem.page_size); + const new_aligned_len = std.mem.alignForward(usize, new_len, std.mem.page_size); + + if (new_aligned_len == old_aligned_len) { + return true; + } else if (new_aligned_len < old_aligned_len) { + const trailing_ptr: [*]align(std.mem.page_size) u8 = @alignCast(buf.ptr + new_aligned_len); + windows.VirtualFree(trailing_ptr, old_aligned_len - new_aligned_len, windows.MEM_DECOMMIT); + return true; + } else { + return false; + } +} + +const kernel32 = struct { + extern "kernel32" fn VirtualAlloc2(Process: ?HANDLE, BaseAddress: ?PVOID, Size: SIZE_T, AllocationType: ULONG, PageProtection: ULONG, ExtendedParameters: [*]MEM_EXTENDED_PARAMETER, ParameterCount: ULONG) callconv(WINAPI) ?PVOID; +}; + +pub const VirtualAlloc2Error = error{Unexpected}; + +fn VirtualAlloc2( + process: ?HANDLE, + base_address: ?PVOID, + size: SIZE_T, + allocation_type: ULONG, + prot: ULONG, + extended_parameters: []MEM_EXTENDED_PARAMETER, +) VirtualAlloc2Error!PVOID { + return kernel32.VirtualAlloc2( + process, + base_address, + size, + allocation_type, + prot, + extended_parameters.ptr, + @intCast(extended_parameters.len), + ) orelse { + switch (windows.kernel32.GetLastError()) { + // TODO: handle errors that can happen + else => |err| return windows.unexpectedError(err), + } + }; +} + +fn allocateOptions( + size: usize, + alignment: usize, + prot: ULONG, + alloc_type: ULONG, +) ?[]align(std.mem.page_size) u8 { + assert.withMessage(@src(), alignment > std.mem.page_size, "alignment is not greater than the page size"); + assert.withMessage(@src(), std.mem.isValidAlign(alignment), "alignment is not a power of two"); + + var mem_address_requirements = _MEM_ADDRESS_REQUIREMENTS{ + .lowest_address = null, + .highest_address = null, + .alignment = alignment, + }; + + var extended_parameters = [_]MEM_EXTENDED_PARAMETER{ + .{ + .dummy = .{ .Type = .address_requirements }, + .param = .{ .Pointer = &mem_address_requirements }, + }, + }; + + const aligned_size = std.mem.alignForward(usize, size, std.mem.page_size); + const ptr = VirtualAlloc2(null, null, aligned_size, alloc_type, prot, &extended_parameters) catch + return null; + return @alignCast(@as([*]u8, @ptrCast(ptr))[0..aligned_size]); +} + +// TODO: figure out how to deallocate - problem is that VirtualFree with MEM_RELEASE requires +// the originally returned base pointer which does not play well with forcing alignment greater +// than VirtualAlloc guarantees + +const std = @import("std"); + +const windows = std.os.windows; + +const DWORD64 = windows.DWORD64; +const DWORD = windows.DWORD; +const SIZE_T = windows.SIZE_T; +const HANDLE = windows.HANDLE; +const PVOID = windows.PVOID; +const ULONG = windows.ULONG; +const WINAPI = windows.WINAPI; + +const MEM_EXTENDED_PARAMETER_BITS = 8; +const MEM_EXTENDED_PARAMETER = extern struct { + dummy: packed struct(u64) { + Type: MEM_EXTENDED_PARAMETER_TYPE, + RESERVED: std.meta.Int(.unsigned, 64 - MEM_EXTENDED_PARAMETER_BITS) = 0, + }, + param: extern union { + ULong64: DWORD64, + Pointer: PVOID, + Size: SIZE_T, + Handle: HANDLE, + ULong: DWORD, + }, +}; + +const MEM_EXTENDED_PARAMETER_TYPE = enum(std.meta.Int(.unsigned, MEM_EXTENDED_PARAMETER_BITS)) { + invalid, + address_requirements, + numa_node, + partition_handle, + user_physical_handle, + attribute_flags, + image_machine, + max, +}; + +const _MEM_ADDRESS_REQUIREMENTS = extern struct { + lowest_address: ?PVOID, + highest_address: ?PVOID, + alignment: SIZE_T, +}; + +const assert = @import("../assert.zig"); + +comptime { + _ = std.testing.refAllDecls(@This()); +} From 7e2a5e3048bebbf9a7a561adcda527cf0640ff60 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreply.github.com> Date: Mon, 25 Sep 2023 15:17:41 +1000 Subject: [PATCH 4/5] ci: add windows to build-and-test --- .github/workflows/ci.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7fb92f0..89d910d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,15 @@ on: jobs: build-and-test: - runs-on: ubuntu-latest + strategy: + matrix: + os: [ubuntu-latest, windows-latest] + include: + - os: ubuntu-latest + artifact: libzimalloc.so + - os: windows-latest + artifact: zimalloc.* + runs-on: ${{ matrix.os }} steps: - name: Checkout uses: actions/checkout@v4 @@ -24,6 +32,7 @@ jobs: version: master - name: Check formatting + if: runner.os == 'Linux' run: zig fmt --check . - name: Run tests @@ -41,7 +50,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: libzimalloc - path: zig-out-*/lib/libzimalloc.so + path: zig-out-*/lib/${{ matrix.artifact }} mimalloc-bench-smoke-test: runs-on: ubuntu-latest From 9359537136789e2514e797eb5616f64de8106eeb Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreply.github.com> Date: Mon, 25 Mar 2024 12:40:08 +1100 Subject: [PATCH 5/5] build: link onecore on windows --- build.zig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.zig b/build.zig index b9bf165..58653ca 100644 --- a/build.zig +++ b/build.zig @@ -85,6 +85,9 @@ pub fn build(b: *std.Build) void { .link_libc = true, }); tests.root_module.addImport("build_options", zimalloc_options); + if (target.result.os.tag == .windows) { + tests.linkSystemLibrary("onecore.lib"); + } const tests_run = b.addRunArtifact(tests);