authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-09-22 13:18:40+02:00
committergravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2020-11-05 16:10:15+01:00
log53433cdea2cee73caea52a6baa10dc7bc7f6da4d
treebe054df879f9fd491f0a2b9fc87f1af3ccff9d29
parent4664eae1e40813fb50bab83247cc7391312e38cb

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.

9 files changed, 67 insertions(+), 47 deletions(-)

lib/std/c.zig-12
...@@ -246,20 +246,8 @@ pub extern "c" fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) c_int;...@@ -246,20 +246,8 @@ pub extern "c" fn setresuid(ruid: uid_t, euid: uid_t, suid: uid_t) c_int;
246pub extern "c" fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) c_int;246pub extern "c" fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) c_int;
247247
248pub extern "c" fn malloc(usize) ?*c_void;248pub extern "c" fn malloc(usize) ?*c_void;
249
250pub usingnamespace switch (builtin.os.tag) {
251 .linux, .freebsd, .kfreebsd, .netbsd => struct {
252 pub extern "c" fn malloc_usable_size(?*const c_void) usize;
253 },
254 .macos, .ios, .watchos, .tvos => struct {
255 pub extern "c" fn malloc_size(?*const c_void) usize;
256 },
257 else => struct {},
258};
259
260pub extern "c" fn realloc(?*c_void, usize) ?*c_void;249pub extern "c" fn realloc(?*c_void, usize) ?*c_void;
261pub extern "c" fn free(*c_void) void;250pub extern "c" fn free(*c_void) void;
262pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
263251
264pub extern "c" fn futimes(fd: fd_t, times: *[2]timeval) c_int;252pub extern "c" fn futimes(fd: fd_t, times: *[2]timeval) c_int;
265pub extern "c" fn utimes(path: [*:0]const u8, times: *[2]timeval) c_int;253pub extern "c" fn utimes(path: [*:0]const u8, times: *[2]timeval) c_int;
lib/std/c/darwin.zig+3
...@@ -45,6 +45,9 @@ pub const _fstatat = if (builtin.arch == .aarch64) fstatat else @"fstatat$INODE6...@@ -45,6 +45,9 @@ pub const _fstatat = if (builtin.arch == .aarch64) fstatat else @"fstatat$INODE6
45pub extern "c" fn mach_absolute_time() u64;45pub extern "c" fn mach_absolute_time() u64;
46pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void;46pub extern "c" fn mach_timebase_info(tinfo: ?*mach_timebase_info_data) void;
4747
48pub extern "c" fn malloc_size(?*const c_void) usize;
49pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
50
48pub extern "c" fn kevent64(51pub extern "c" fn kevent64(
49 kq: c_int,52 kq: c_int,
50 changelist: [*]const kevent64_s,53 changelist: [*]const kevent64_s,
lib/std/c/dragonfly.zig+2
...@@ -17,6 +17,8 @@ pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize...@@ -17,6 +17,8 @@ pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize
17pub const dl_iterate_phdr_callback = fn (info: *dl_phdr_info, size: usize, data: ?*c_void) callconv(.C) c_int;17pub const dl_iterate_phdr_callback = fn (info: *dl_phdr_info, size: usize, data: ?*c_void) callconv(.C) c_int;
18pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int;18pub extern "c" fn dl_iterate_phdr(callback: dl_iterate_phdr_callback, data: ?*c_void) c_int;
1919
20pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
21
20pub const pthread_mutex_t = extern struct {22pub const pthread_mutex_t = extern struct {
21 inner: ?*c_void = null,23 inner: ?*c_void = null,
22};24};
lib/std/c/freebsd.zig+3
...@@ -13,6 +13,9 @@ pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize;...@@ -13,6 +13,9 @@ pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize;
13pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;13pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;
14pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize;14pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize;
1515
16pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
17pub extern "c" fn malloc_usable_size(?*const c_void) usize;
18
16pub const sf_hdtr = extern struct {19pub const sf_hdtr = extern struct {
17 headers: [*]const iovec_const,20 headers: [*]const iovec_const,
18 hdr_cnt: c_int,21 hdr_cnt: c_int,
lib/std/c/linux.zig+2
...@@ -101,6 +101,8 @@ pub extern "c" fn copy_file_range(fd_in: fd_t, off_in: ?*i64, fd_out: fd_t, off_...@@ -101,6 +101,8 @@ pub extern "c" fn copy_file_range(fd_in: fd_t, off_in: ?*i64, fd_out: fd_t, off_
101pub extern "c" fn signalfd(fd: fd_t, mask: *const sigset_t, flags: c_uint) c_int;101pub extern "c" fn signalfd(fd: fd_t, mask: *const sigset_t, flags: c_uint) c_int;
102102
103pub extern "c" fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: *const rlimit, old_limit: *rlimit) c_int;103pub extern "c" fn prlimit(pid: pid_t, resource: rlimit_resource, new_limit: *const rlimit, old_limit: *rlimit) c_int;
104pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
105pub extern "c" fn malloc_usable_size(?*const c_void) usize;
104106
105pub const pthread_attr_t = extern struct {107pub const pthread_attr_t = extern struct {
106 __size: [56]u8,108 __size: [56]u8,
lib/std/c/netbsd.zig+3
...@@ -30,6 +30,9 @@ pub extern "c" fn __getrusage50(who: c_int, usage: *rusage) c_int;...@@ -30,6 +30,9 @@ pub extern "c" fn __getrusage50(who: c_int, usage: *rusage) c_int;
30// libc aliases this as sched_yield30// libc aliases this as sched_yield
31pub extern "c" fn __libc_thr_yield() c_int;31pub extern "c" fn __libc_thr_yield() c_int;
3232
33pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
34pub extern "c" fn malloc_usable_size(?*const c_void) usize;
35
33pub const pthread_mutex_t = extern struct {36pub const pthread_mutex_t = extern struct {
34 ptm_magic: u32 = 0x33330003,37 ptm_magic: u32 = 0x33330003,
35 ptm_errorcheck: padded_pthread_spin_t = 0,38 ptm_errorcheck: padded_pthread_spin_t = 0,
lib/std/c/openbsd.zig+3
...@@ -32,3 +32,6 @@ pub const pthread_spinlock_t = extern struct {...@@ -32,3 +32,6 @@ pub const pthread_spinlock_t = extern struct {
32pub const pthread_attr_t = extern struct {32pub const pthread_attr_t = extern struct {
33 inner: ?*c_void = null,33 inner: ?*c_void = null,
34};34};
35
36pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
37pub extern "c" fn malloc_usable_size(?*const c_void) usize;
lib/std/c/windows.zig+1-3
...@@ -5,6 +5,4 @@...@@ -5,6 +5,4 @@
5// and substantial portions of the software.5// and substantial portions of the software.
6pub extern "c" fn _errno() *c_int;6pub extern "c" fn _errno() *c_int;
77
8pub extern "c" fn _aligned_free(memblock: ?*c_void) void;8pub extern "c" fn _msize(memblock: ?*c_void) usize;
9pub extern "c" fn _aligned_malloc(size: usize, alignment: usize) ?*c_void;
10pub extern "c" fn _aligned_realloc(memblock: ?*c_void, size: usize, alignment: usize) ?*c_void;
lib/std/heap.zig+50-32
...@@ -38,37 +38,63 @@ const CAllocator = struct {...@@ -38,37 +38,63 @@ const CAllocator = struct {
38 pub const supports_malloc_size = true;38 pub const supports_malloc_size = true;
39 pub const malloc_size = c.malloc_usable_size;39 pub const malloc_size = c.malloc_usable_size;
40 }40 }
41 else if (comptime @hasDecl(c, "_msize"))
42 struct {
43 pub const supports_malloc_size = true;
44 pub const malloc_size = c._msize;
45 }
41 else46 else
42 struct {47 struct {
43 pub const supports_malloc_size = false;48 pub const supports_malloc_size = false;
44 };49 };
4550
46 // The alignment guaranteed by malloc, the value matches the result of the C51 pub const supports_posix_memalign = false and @hasDecl(c, "posix_memalign");
47 // expression `alignof(max_alignment_t)`
48 const min_ptr_alignment = comptime std.math.max(
49 @alignOf(c_longdouble),
50 @alignOf(c_longlong),
51 );
5252
53 fn aligned_alloc(len: usize, alignment: usize) ?*c_void {53 fn get_header(ptr: [*]u8) *[*]u8 {
54 // The minimum alignment supported by both APIs is the size of a pointer54 return @intToPtr(*[*]u8, @ptrToInt(ptr) - @sizeOf(usize));
55 const eff_alignment = std.math.max(alignment, @sizeOf(usize));55 }
5656
57 if (builtin.os.tag == .windows) {57 fn aligned_alloc(len: usize, alignment: usize) ?[*]u8 {
58 return c._aligned_malloc(len, eff_alignment);58 if (supports_posix_memalign) {
59 // The minimum alignment supported posix_memalign is the pointer size
60 const eff_alignment = std.math.max(alignment, @sizeOf(usize));
61
62 var aligned_ptr: ?*c_void = undefined;
63 if (c.posix_memalign(&aligned_ptr, eff_alignment, len) != 0)
64 return null;
65
66 return @ptrCast([*]u8, aligned_ptr);
59 }67 }
6068
61 var aligned_ptr: ?*c_void = undefined;69 // Thin wrapper around regular malloc, overallocate to account for
62 if (c.posix_memalign(&aligned_ptr, eff_alignment, len) != 0)70 // alignment padding and store the orignal malloc()'ed pointer before
63 return null;71 // the aligned address.
72 var unaligned_ptr = @ptrCast([*]u8, c.malloc(len + alignment - 1 + @sizeOf(usize)) orelse return null);
73 const unaligned_addr = @ptrToInt(unaligned_ptr);
74 const aligned_addr = mem.alignForward(unaligned_addr + @sizeOf(usize), alignment);
75 var aligned_ptr = unaligned_ptr + (aligned_addr - unaligned_addr);
76 get_header(aligned_ptr).* = unaligned_ptr;
77
64 return aligned_ptr;78 return aligned_ptr;
65 }79 }
6680
67 fn aligned_free(ptr: *c_void) void {81 fn aligned_free(ptr: [*]u8) void {
68 if (builtin.os.tag == .windows) {82 if (supports_posix_memalign) {
69 return c._aligned_free(ptr);83 return c.free(ptr);
70 }84 }
71 c.free(ptr);85
86 const unaligned_ptr = get_header(ptr).*;
87 c.free(unaligned_ptr);
88 }
89
90 fn aligned_alloc_size(ptr: [*]u8) usize {
91 if (supports_posix_memalign) {
92 return malloc_size(ptr);
93 }
94
95 const unaligned_ptr = get_header(ptr).*;
96 const delta = @ptrToInt(ptr) - @ptrToInt(unaligned_ptr);
97 return malloc_size(unaligned_ptr) - delta;
72 }98 }
7399
74 fn alloc(100 fn alloc(
...@@ -81,23 +107,18 @@ const CAllocator = struct {...@@ -81,23 +107,18 @@ const CAllocator = struct {
81 assert(len > 0);107 assert(len > 0);
82 assert(std.math.isPowerOfTwo(alignment));108 assert(std.math.isPowerOfTwo(alignment));
83109
84 var ptr = if (alignment <= min_ptr_alignment)110 var ptr = aligned_alloc(len, alignment) orelse return error.OutOfMemory;
85 @ptrCast([*]u8, c.malloc(len) orelse return error.OutOfMemory)111 if (len_align == 0) {
86 else
87 @ptrCast([*]u8, aligned_alloc(len, alignment) orelse return error.OutOfMemory);
88
89 if (len_align == 0)
90 return ptr[0..len];112 return ptr[0..len];
91113 }
92 const full_len = init: {114 const full_len = init: {
93 if (supports_malloc_size) {115 if (supports_malloc_size) {
94 const s = malloc_size(ptr);116 const s = aligned_alloc_size(ptr);
95 assert(s >= len);117 assert(s >= len);
96 break :init s;118 break :init s;
97 }119 }
98 break :init len;120 break :init len;
99 };121 };
100
101 return ptr[0..mem.alignBackwardAnyAlign(full_len, len_align)];122 return ptr[0..mem.alignBackwardAnyAlign(full_len, len_align)];
102 }123 }
103124
...@@ -110,17 +131,14 @@ const CAllocator = struct {...@@ -110,17 +131,14 @@ const CAllocator = struct {
110 return_address: usize,131 return_address: usize,
111 ) Allocator.Error!usize {132 ) Allocator.Error!usize {
112 if (new_len == 0) {133 if (new_len == 0) {
113 if (buf_align <= min_ptr_alignment)134 aligned_free(buf.ptr);
114 c.free(buf.ptr)
115 else
116 aligned_free(buf.ptr);
117 return 0;135 return 0;
118 }136 }
119 if (new_len <= buf.len) {137 if (new_len <= buf.len) {
120 return mem.alignAllocLen(buf.len, new_len, len_align);138 return mem.alignAllocLen(buf.len, new_len, len_align);
121 }139 }
122 if (supports_malloc_size) {140 if (supports_malloc_size) {
123 const full_len = malloc_size(buf.ptr);141 const full_len = aligned_alloc_size(buf.ptr);
124 if (new_len <= full_len) {142 if (new_len <= full_len) {
125 return mem.alignAllocLen(full_len, new_len, len_align);143 return mem.alignAllocLen(full_len, new_len, len_align);
126 }144 }