Commit 53433cdea2

LemonBoy <thatlemon@gmail.com>
2020-09-22 13:18:40
Implement a fallback mechanism for posix_memalign
Do the alignment dance by ourselves whenever posix_memalign is not available. Don't try to use malloc as it has too many edge cases, figuring out whether a block of memory is manually aligned by the mechanism above or is directly coming from malloc becomes too hard to be valuable.
1 parent 4664eae
lib/std/c/darwin.zig
@@ -45,6 +45,9 @@ pub const _fstatat = if (builtin.arch == .aarch64) fstatat else @"fstatat$INODE6
 pub extern "c" fn mach_absolute_time() u64;
 pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void;
 
+pub extern "c" fn malloc_size(?*const c_void) usize;
+pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
+
 pub extern "c" fn kevent64(
     kq: c_int,
     changelist: [*]const kevent64_s,
lib/std/c/dragonfly.zig
@@ -17,6 +17,8 @@ pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize
 pub const dl_iterate_phdr_callback = fn (info: *dl_phdr_info, size: usize, data: ?*c_void) callconv(.C) c_int;
 pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int;
 
+pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
+
 pub const pthread_mutex_t = extern struct {
     inner: ?*c_void = null,
 };
lib/std/c/freebsd.zig
@@ -13,6 +13,9 @@ pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize;
 pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;
 pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize;
 
+pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
+pub extern "c" fn malloc_usable_size(?*const c_void) usize;
+
 pub const sf_hdtr = extern struct {
     headers: [*]const iovec_const,
     hdr_cnt: c_int,
lib/std/c/linux.zig
@@ -101,6 +101,8 @@ pub extern "c" fn copy_file_range(fd_in: fd_t, off_in: ?*i64, fd_out: fd_t, off_
 pub extern "c" fn signalfd(fd: fd_t, mask: *const sigset_t, flags: c_uint) c_int;
 
 pub extern "c" fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: *const rlimit, old_limit: *rlimit) c_int;
+pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
+pub extern "c" fn malloc_usable_size(?*const c_void) usize;
 
 pub const pthread_attr_t = extern struct {
     __size: [56]u8,
lib/std/c/netbsd.zig
@@ -30,6 +30,9 @@ pub extern "c" fn __getrusage50(who: c_int, usage: *rusage) c_int;
 // libc aliases this as sched_yield
 pub extern "c" fn __libc_thr_yield() c_int;
 
+pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
+pub extern "c" fn malloc_usable_size(?*const c_void) usize;
+
 pub const pthread_mutex_t = extern struct {
     ptm_magic: u32 = 0x33330003,
     ptm_errorcheck: padded_pthread_spin_t = 0,
lib/std/c/openbsd.zig
@@ -32,3 +32,6 @@ pub const pthread_spinlock_t = extern struct {
 pub const pthread_attr_t = extern struct {
     inner: ?*c_void = null,
 };
+
+pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
+pub extern "c" fn malloc_usable_size(?*const c_void) usize;
lib/std/c/windows.zig
@@ -5,6 +5,4 @@
 // and substantial portions of the software.
 pub extern "c" fn _errno() *c_int;
 
-pub extern "c" fn _aligned_free(memblock: ?*c_void) void;
-pub extern "c" fn _aligned_malloc(size: usize, alignment: usize) ?*c_void;
-pub extern "c" fn _aligned_realloc(memblock: ?*c_void, size: usize, alignment: usize) ?*c_void;
+pub extern "c" fn _msize(memblock: ?*c_void) usize;
lib/std/c.zig
@@ -246,20 +246,8 @@ pub extern "c" fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) c_int;
 pub extern "c" fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) c_int;
 
 pub extern "c" fn malloc(usize) ?*c_void;
-
-pub usingnamespace switch (builtin.os.tag) {
-    .linux, .freebsd, .kfreebsd, .netbsd => struct {
-        pub extern "c" fn malloc_usable_size(?*const c_void) usize;
-    },
-    .macos, .ios, .watchos, .tvos => struct {
-        pub extern "c" fn malloc_size(?*const c_void) usize;
-    },
-    else => struct {},
-};
-
 pub extern "c" fn realloc(?*c_void, usize) ?*c_void;
 pub extern "c" fn free(*c_void) void;
-pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
 
 pub extern "c" fn futimes(fd: fd_t, times: *[2]timeval) c_int;
 pub extern "c" fn utimes(path: [*:0]const u8, times: *[2]timeval) c_int;
lib/std/heap.zig
@@ -38,37 +38,63 @@ const CAllocator = struct {
             pub const supports_malloc_size = true;
             pub const malloc_size = c.malloc_usable_size;
         }
+    else if (comptime @hasDecl(c, "_msize"))
+        struct {
+            pub const supports_malloc_size = true;
+            pub const malloc_size = c._msize;
+        }
     else
         struct {
             pub const supports_malloc_size = false;
         };
 
-    // The alignment guaranteed by malloc, the value matches the result of the C
-    // expression `alignof(max_alignment_t)`
-    const min_ptr_alignment = comptime std.math.max(
-        @alignOf(c_longdouble),
-        @alignOf(c_longlong),
-    );
+    pub const supports_posix_memalign = false and @hasDecl(c, "posix_memalign");
 
-    fn aligned_alloc(len: usize, alignment: usize) ?*c_void {
-        // The minimum alignment supported by both APIs is the size of a pointer
-        const eff_alignment = std.math.max(alignment, @sizeOf(usize));
+    fn get_header(ptr: [*]u8) *[*]u8 {
+        return @intToPtr(*[*]u8, @ptrToInt(ptr) - @sizeOf(usize));
+    }
 
-        if (builtin.os.tag == .windows) {
-            return c._aligned_malloc(len, eff_alignment);
+    fn aligned_alloc(len: usize, alignment: usize) ?[*]u8 {
+        if (supports_posix_memalign) {
+            // The minimum alignment supported posix_memalign is the pointer size
+            const eff_alignment = std.math.max(alignment, @sizeOf(usize));
+
+            var aligned_ptr: ?*c_void = undefined;
+            if (c.posix_memalign(&aligned_ptr, eff_alignment, len) != 0)
+                return null;
+
+            return @ptrCast([*]u8, aligned_ptr);
         }
 
-        var aligned_ptr: ?*c_void = undefined;
-        if (c.posix_memalign(&aligned_ptr, eff_alignment, len) != 0)
-            return null;
+        // Thin wrapper around regular malloc, overallocate to account for
+        // alignment padding and store the orignal malloc()'ed pointer before
+        // the aligned address.
+        var unaligned_ptr = @ptrCast([*]u8, c.malloc(len + alignment - 1 + @sizeOf(usize)) orelse return null);
+        const unaligned_addr = @ptrToInt(unaligned_ptr);
+        const aligned_addr = mem.alignForward(unaligned_addr + @sizeOf(usize), alignment);
+        var aligned_ptr = unaligned_ptr + (aligned_addr - unaligned_addr);
+        get_header(aligned_ptr).* = unaligned_ptr;
+
         return aligned_ptr;
     }
 
-    fn aligned_free(ptr: *c_void) void {
-        if (builtin.os.tag == .windows) {
-            return c._aligned_free(ptr);
+    fn aligned_free(ptr: [*]u8) void {
+        if (supports_posix_memalign) {
+            return c.free(ptr);
         }
-        c.free(ptr);
+
+        const unaligned_ptr = get_header(ptr).*;
+        c.free(unaligned_ptr);
+    }
+
+    fn aligned_alloc_size(ptr: [*]u8) usize {
+        if (supports_posix_memalign) {
+            return malloc_size(ptr);
+        }
+
+        const unaligned_ptr = get_header(ptr).*;
+        const delta = @ptrToInt(ptr) - @ptrToInt(unaligned_ptr);
+        return malloc_size(unaligned_ptr) - delta;
     }
 
     fn alloc(
@@ -81,23 +107,18 @@ const CAllocator = struct {
         assert(len > 0);
         assert(std.math.isPowerOfTwo(alignment));
 
-        var ptr = if (alignment <= min_ptr_alignment)
-            @ptrCast([*]u8, c.malloc(len) orelse return error.OutOfMemory)
-        else
-            @ptrCast([*]u8, aligned_alloc(len, alignment) orelse return error.OutOfMemory);
-
-        if (len_align == 0)
+        var ptr = aligned_alloc(len, alignment) orelse return error.OutOfMemory;
+        if (len_align == 0) {
             return ptr[0..len];
-
+        }
         const full_len = init: {
             if (supports_malloc_size) {
-                const s = malloc_size(ptr);
+                const s = aligned_alloc_size(ptr);
                 assert(s >= len);
                 break :init s;
             }
             break :init len;
         };
-
         return ptr[0..mem.alignBackwardAnyAlign(full_len, len_align)];
     }
 
@@ -110,17 +131,14 @@ const CAllocator = struct {
         return_address: usize,
     ) Allocator.Error!usize {
         if (new_len == 0) {
-            if (buf_align <= min_ptr_alignment)
-                c.free(buf.ptr)
-            else
-                aligned_free(buf.ptr);
+            aligned_free(buf.ptr);
             return 0;
         }
         if (new_len <= buf.len) {
             return mem.alignAllocLen(buf.len, new_len, len_align);
         }
         if (supports_malloc_size) {
-            const full_len = malloc_size(buf.ptr);
+            const full_len = aligned_alloc_size(buf.ptr);
             if (new_len <= full_len) {
                 return mem.alignAllocLen(full_len, new_len, len_align);
             }