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;
246246pub extern "c" fn setresgid(rgid: gid_t, egid: gid_t, sgid: gid_t) c_int;
247247
248248pub 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
260249pub extern "c" fn realloc(?*c_void, usize) ?*c_void;
261250pub extern "c" fn free(*c_void) void;
262pub extern "c" fn posix_memalign(memptr: *?*c_void, alignment: usize, size: usize) c_int;
263251
264252pub extern "c" fn futimes(fd: fd_t, times: *[2]timeval) c_int;
265253pub 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
4545pub extern "c" fn mach_absolute_time() u64;
4646pub 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
4851pub extern "c" fn kevent64(
4952 kq: c_int,
5053 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
1717pub const dl_iterate_phdr_callback = fn (info: *dl_phdr_info, size: usize, data: ?*c_void) callconv(.C) c_int;
1818pub 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
2022pub const pthread_mutex_t = extern struct {
2123 inner: ?*c_void = null,
2224};
lib/std/c/freebsd.zig+3
......@@ -13,6 +13,9 @@ pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize;
1313pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;
1414pub 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
1619pub const sf_hdtr = extern struct {
1720 headers: [*]const iovec_const,
1821 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_
101101pub extern "c" fn signalfd(fd: fd_t, mask: *const sigset_t, flags: c_uint) c_int;
102102
103103pub 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
105107pub const pthread_attr_t = extern struct {
106108 __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;
3030// libc aliases this as sched_yield
3131pub 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
3336pub const pthread_mutex_t = extern struct {
3437 ptm_magic: u32 = 0x33330003,
3538 ptm_errorcheck: padded_pthread_spin_t = 0,
lib/std/c/openbsd.zig+3
......@@ -32,3 +32,6 @@ pub const pthread_spinlock_t = extern struct {
3232pub const pthread_attr_t = extern struct {
3333 inner: ?*c_void = null,
3434};
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 @@
55// and substantial portions of the software.
66pub extern "c" fn _errno() *c_int;
77
8pub extern "c" fn _aligned_free(memblock: ?*c_void) void;
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;
8pub extern "c" fn _msize(memblock: ?*c_void) usize;
lib/std/heap.zig+50-32
......@@ -38,37 +38,63 @@ const CAllocator = struct {
3838 pub const supports_malloc_size = true;
3939 pub const malloc_size = c.malloc_usable_size;
4040 }
41 else if (comptime @hasDecl(c, "_msize"))
42 struct {
43 pub const supports_malloc_size = true;
44 pub const malloc_size = c._msize;
45 }
4146 else
4247 struct {
4348 pub const supports_malloc_size = false;
4449 };
4550
46 // The alignment guaranteed by malloc, the value matches the result of the C
47 // expression `alignof(max_alignment_t)`
48 const min_ptr_alignment = comptime std.math.max(
49 @alignOf(c_longdouble),
50 @alignOf(c_longlong),
51 );
51 pub const supports_posix_memalign = false and @hasDecl(c, "posix_memalign");
5252
53 fn aligned_alloc(len: usize, alignment: usize) ?*c_void {
54 // The minimum alignment supported by both APIs is the size of a pointer
55 const eff_alignment = std.math.max(alignment, @sizeOf(usize));
53 fn get_header(ptr: [*]u8) *[*]u8 {
54 return @intToPtr(*[*]u8, @ptrToInt(ptr) - @sizeOf(usize));
55 }
5656
57 if (builtin.os.tag == .windows) {
58 return c._aligned_malloc(len, eff_alignment);
57 fn aligned_alloc(len: usize, alignment: usize) ?[*]u8 {
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);
5967 }
6068
61 var aligned_ptr: ?*c_void = undefined;
62 if (c.posix_memalign(&aligned_ptr, eff_alignment, len) != 0)
63 return null;
69 // Thin wrapper around regular malloc, overallocate to account for
70 // alignment padding and store the orignal malloc()'ed pointer before
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
6478 return aligned_ptr;
6579 }
6680
67 fn aligned_free(ptr: *c_void) void {
68 if (builtin.os.tag == .windows) {
69 return c._aligned_free(ptr);
81 fn aligned_free(ptr: [*]u8) void {
82 if (supports_posix_memalign) {
83 return c.free(ptr);
7084 }
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;
7298 }
7399
74100 fn alloc(
......@@ -81,23 +107,18 @@ const CAllocator = struct {
81107 assert(len > 0);
82108 assert(std.math.isPowerOfTwo(alignment));
83109
84 var ptr = if (alignment <= min_ptr_alignment)
85 @ptrCast([*]u8, c.malloc(len) orelse return error.OutOfMemory)
86 else
87 @ptrCast([*]u8, aligned_alloc(len, alignment) orelse return error.OutOfMemory);
88
89 if (len_align == 0)
110 var ptr = aligned_alloc(len, alignment) orelse return error.OutOfMemory;
111 if (len_align == 0) {
90112 return ptr[0..len];
91
113 }
92114 const full_len = init: {
93115 if (supports_malloc_size) {
94 const s = malloc_size(ptr);
116 const s = aligned_alloc_size(ptr);
95117 assert(s >= len);
96118 break :init s;
97119 }
98120 break :init len;
99121 };
100
101122 return ptr[0..mem.alignBackwardAnyAlign(full_len, len_align)];
102123 }
103124
......@@ -110,17 +131,14 @@ const CAllocator = struct {
110131 return_address: usize,
111132 ) Allocator.Error!usize {
112133 if (new_len == 0) {
113 if (buf_align <= min_ptr_alignment)
114 c.free(buf.ptr)
115 else
116 aligned_free(buf.ptr);
134 aligned_free(buf.ptr);
117135 return 0;
118136 }
119137 if (new_len <= buf.len) {
120138 return mem.alignAllocLen(buf.len, new_len, len_align);
121139 }
122140 if (supports_malloc_size) {
123 const full_len = malloc_size(buf.ptr);
141 const full_len = aligned_alloc_size(buf.ptr);
124142 if (new_len <= full_len) {
125143 return mem.alignAllocLen(full_len, new_len, len_align);
126144 }