authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-25 19:12:06-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-04-25 19:12:06-04:00
log2fc6b347ec66650cd1702c63104fc45148658b15
treefe102bc1693efb2d3254192ca4f0f10313ed6fd5
parent6f61594692af846226880e5903ad26a922014d55
parent82f1d592fae021fcfc737e3cb6c107b325fcf1ee
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #8616 from LemonBoy/fn-align

Function pointer alignment

12 files changed, 93 insertions(+), 32 deletions(-)

ci/drone/linux_script_test+3-1
...@@ -22,7 +22,9 @@ case "$1" in...@@ -22,7 +22,9 @@ case "$1" in
22 steps="\22 steps="\
23 test-compiler-rt \23 test-compiler-rt \
24 test-minilibc \24 test-minilibc \
25 test-compare-output"25 test-compare-output \
26 test-translate-c \
27 test-run-translated-c"
26 ;;28 ;;
27 '')29 '')
28 echo "error: expecting test group argument"30 echo "error: expecting test group argument"
lib/std/meta.zig+16-3
...@@ -117,10 +117,21 @@ test "std.meta.bitCount" {...@@ -117,10 +117,21 @@ test "std.meta.bitCount" {
117 testing.expect(bitCount(f32) == 32);117 testing.expect(bitCount(f32) == 32);
118}118}
119119
120/// Returns the alignment of type T.
121/// Note that if T is a pointer or function type the result is different than
122/// the one returned by @alignOf(T).
123/// If T is a pointer type the alignment of the type it points to is returned.
124/// If T is a function type the alignment a target-dependent value is returned.
120pub fn alignment(comptime T: type) comptime_int {125pub fn alignment(comptime T: type) comptime_int {
121 //@alignOf works on non-pointer types126 return switch (@typeInfo(T)) {
122 const P = if (comptime trait.is(.Pointer)(T)) T else *T;127 .Optional => |info| switch (@typeInfo(info.child)) {
123 return @typeInfo(P).Pointer.alignment;128 .Pointer, .Fn => alignment(info.child),
129 else => @alignOf(T),
130 },
131 .Pointer => |info| info.alignment,
132 .Fn => |info| info.alignment,
133 else => @alignOf(T),
134 };
124}135}
125136
126test "std.meta.alignment" {137test "std.meta.alignment" {
...@@ -129,6 +140,8 @@ test "std.meta.alignment" {...@@ -129,6 +140,8 @@ test "std.meta.alignment" {
129 testing.expect(alignment(*align(2) u8) == 2);140 testing.expect(alignment(*align(2) u8) == 2);
130 testing.expect(alignment([]align(1) u8) == 1);141 testing.expect(alignment([]align(1) u8) == 1);
131 testing.expect(alignment([]align(2) u8) == 2);142 testing.expect(alignment([]align(2) u8) == 2);
143 testing.expect(alignment(fn () void) > 0);
144 testing.expect(alignment(fn () align(128) void) == 128);
132}145}
133146
134pub fn Child(comptime T: type) type {147pub fn Child(comptime T: type) type {
src/stage1/analyze.cpp+7-10
...@@ -4769,11 +4769,11 @@ Error type_is_nonnull_ptr2(CodeGen *g, ZigType *type, bool *result) {...@@ -4769,11 +4769,11 @@ Error type_is_nonnull_ptr2(CodeGen *g, ZigType *type, bool *result) {
4769 return ErrorNone;4769 return ErrorNone;
4770}4770}
47714771
4772static uint32_t get_async_frame_align_bytes(CodeGen *g) {4772uint32_t get_async_frame_align_bytes(CodeGen *g) {
4773 uint32_t a = g->pointer_size_bytes * 2;4773 // Due to how the frame structure is built the minimum alignment is the one
4774 // promises have at least alignment 8 so that we can have 3 extra bits when doing atomicrmw4774 // of a usize (or pointer).
4775 if (a < 8) a = 8;4775 // label (grep this): [fn_frame_struct_layout]
4776 return a;4776 return max(g->builtin_types.entry_usize->abi_align, target_fn_align(g->zig_target));
4777}4777}
47784778
4779uint32_t get_ptr_align(CodeGen *g, ZigType *type) {4779uint32_t get_ptr_align(CodeGen *g, ZigType *type) {
...@@ -4789,11 +4789,8 @@ uint32_t get_ptr_align(CodeGen *g, ZigType *type) {...@@ -4789,11 +4789,8 @@ uint32_t get_ptr_align(CodeGen *g, ZigType *type) {
4789 return (ptr_type->data.pointer.explicit_alignment == 0) ?4789 return (ptr_type->data.pointer.explicit_alignment == 0) ?
4790 get_abi_alignment(g, ptr_type->data.pointer.child_type) : ptr_type->data.pointer.explicit_alignment;4790 get_abi_alignment(g, ptr_type->data.pointer.child_type) : ptr_type->data.pointer.explicit_alignment;
4791 } else if (ptr_type->id == ZigTypeIdFn) {4791 } else if (ptr_type->id == ZigTypeIdFn) {
4792 // I tried making this use LLVMABIAlignmentOfType but it trips this assertion in LLVM:4792 return (ptr_type->data.fn.fn_type_id.alignment == 0) ?
4793 // "Cannot getTypeInfo() on a type that is unsized!"4793 target_fn_ptr_align(g->zig_target) : ptr_type->data.fn.fn_type_id.alignment;
4794 // when getting the alignment of `?fn() callconv(.C) void`.
4795 // See http://lists.llvm.org/pipermail/llvm-dev/2018-September/126142.html
4796 return (ptr_type->data.fn.fn_type_id.alignment == 0) ? 1 : ptr_type->data.fn.fn_type_id.alignment;
4797 } else if (ptr_type->id == ZigTypeIdAnyFrame) {4794 } else if (ptr_type->id == ZigTypeIdAnyFrame) {
4798 return get_async_frame_align_bytes(g);4795 return get_async_frame_align_bytes(g);
4799 } else {4796 } else {
src/stage1/analyze.hpp+1
...@@ -47,6 +47,7 @@ ZigType *get_test_fn_type(CodeGen *g);...@@ -47,6 +47,7 @@ ZigType *get_test_fn_type(CodeGen *g);
47ZigType *get_any_frame_type(CodeGen *g, ZigType *result_type);47ZigType *get_any_frame_type(CodeGen *g, ZigType *result_type);
48bool handle_is_ptr(CodeGen *g, ZigType *type_entry);48bool handle_is_ptr(CodeGen *g, ZigType *type_entry);
49Error emit_error_unless_callconv_allowed_for_target(CodeGen *g, AstNode *source_node, CallingConvention cc);49Error emit_error_unless_callconv_allowed_for_target(CodeGen *g, AstNode *source_node, CallingConvention cc);
50uint32_t get_async_frame_align_bytes(CodeGen *g);
5051
51bool type_has_bits(CodeGen *g, ZigType *type_entry);52bool type_has_bits(CodeGen *g, ZigType *type_entry);
52Error type_has_bits2(CodeGen *g, ZigType *type_entry, bool *result);53Error type_has_bits2(CodeGen *g, ZigType *type_entry, bool *result);
src/stage1/ir.cpp+8-4
...@@ -20659,8 +20659,12 @@ static IrInstGen *analyze_casted_new_stack(IrAnalyze *ira, IrInst* source_instr,...@@ -20659,8 +20659,12 @@ static IrInstGen *analyze_casted_new_stack(IrAnalyze *ira, IrInst* source_instr,
20659 get_fn_frame_type(ira->codegen, fn_entry), false);20659 get_fn_frame_type(ira->codegen, fn_entry), false);
20660 return ir_implicit_cast(ira, new_stack, needed_frame_type);20660 return ir_implicit_cast(ira, new_stack, needed_frame_type);
20661 } else {20661 } else {
20662 // XXX The stack alignment is hardcoded to 16 here and in
20663 // std.Target.stack_align.
20664 const uint32_t required_align = is_async_call_builtin ?
20665 get_async_frame_align_bytes(ira->codegen) : 16;
20662 ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,20666 ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
20663 false, false, PtrLenUnknown, target_fn_align(ira->codegen->zig_target), 0, 0, false);20667 false, false, PtrLenUnknown, required_align, 0, 0, false);
20664 ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);20668 ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
20665 ira->codegen->need_frame_size_prefix_data = true;20669 ira->codegen->need_frame_size_prefix_data = true;
20666 return ir_implicit_cast2(ira, new_stack_src, new_stack, u8_slice);20670 return ir_implicit_cast2(ira, new_stack_src, new_stack, u8_slice);
...@@ -26079,11 +26083,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy...@@ -26079,11 +26083,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
26079 fields[0]->special = ConstValSpecialStatic;26083 fields[0]->special = ConstValSpecialStatic;
26080 fields[0]->type = get_builtin_type(ira->codegen, "CallingConvention");26084 fields[0]->type = get_builtin_type(ira->codegen, "CallingConvention");
26081 bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.fn.fn_type_id.cc);26085 bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.fn.fn_type_id.cc);
26082 // alignment: u2926086 // alignment: comptime_int
26083 ensure_field_index(result->type, "alignment", 1);26087 ensure_field_index(result->type, "alignment", 1);
26084 fields[1]->special = ConstValSpecialStatic;26088 fields[1]->special = ConstValSpecialStatic;
26085 fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int;26089 fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int;
26086 bigint_init_unsigned(&fields[1]->data.x_bigint, type_entry->data.fn.fn_type_id.alignment);26090 bigint_init_unsigned(&fields[1]->data.x_bigint, get_ptr_align(ira->codegen, type_entry));
26087 // is_generic: bool26091 // is_generic: bool
26088 ensure_field_index(result->type, "is_generic", 2);26092 ensure_field_index(result->type, "is_generic", 2);
26089 bool is_generic = type_entry->data.fn.is_generic;26093 bool is_generic = type_entry->data.fn.is_generic;
...@@ -30095,7 +30099,7 @@ static IrInstGen *ir_align_cast(IrAnalyze *ira, IrInstGen *target, uint32_t alig...@@ -30095,7 +30099,7 @@ static IrInstGen *ir_align_cast(IrAnalyze *ira, IrInstGen *target, uint32_t alig
30095 fn_type_id.alignment = align_bytes;30099 fn_type_id.alignment = align_bytes;
30096 result_type = get_fn_type(ira->codegen, &fn_type_id);30100 result_type = get_fn_type(ira->codegen, &fn_type_id);
30097 } else if (target_type->id == ZigTypeIdAnyFrame) {30101 } else if (target_type->id == ZigTypeIdAnyFrame) {
30098 if (align_bytes >= target_fn_align(ira->codegen->zig_target)) {30102 if (align_bytes >= get_async_frame_align_bytes(ira->codegen)) {
30099 result_type = target_type;30103 result_type = target_type;
30100 } else {30104 } else {
30101 ir_add_error(ira, &target->base, buf_sprintf("sub-aligned anyframe not allowed"));30105 ir_add_error(ira, &target->base, buf_sprintf("sub-aligned anyframe not allowed"));
src/stage1/target.cpp+32-1
...@@ -1253,6 +1253,37 @@ bool target_is_ppc(const ZigTarget *target) {...@@ -1253,6 +1253,37 @@ bool target_is_ppc(const ZigTarget *target) {
1253 target->arch == ZigLLVM_ppc64le;1253 target->arch == ZigLLVM_ppc64le;
1254}1254}
12551255
1256// Returns the minimum alignment for every function pointer on the given
1257// architecture.
1258unsigned target_fn_ptr_align(const ZigTarget *target) {
1259 // TODO This is a pessimization but is always correct.
1260 return 1;
1261}
1262
1263// Returns the minimum alignment for every function on the given architecture.
1256unsigned target_fn_align(const ZigTarget *target) {1264unsigned target_fn_align(const ZigTarget *target) {
1257 return 16;1265 switch (target->arch) {
1266 case ZigLLVM_riscv32:
1267 case ZigLLVM_riscv64:
1268 // TODO If the C extension is not present the value is 4.
1269 return 2;
1270 case ZigLLVM_ppc:
1271 case ZigLLVM_ppcle:
1272 case ZigLLVM_ppc64:
1273 case ZigLLVM_ppc64le:
1274 case ZigLLVM_aarch64:
1275 case ZigLLVM_aarch64_be:
1276 case ZigLLVM_aarch64_32:
1277 case ZigLLVM_sparc:
1278 case ZigLLVM_sparcel:
1279 case ZigLLVM_sparcv9:
1280 case ZigLLVM_mips:
1281 case ZigLLVM_mipsel:
1282 case ZigLLVM_mips64:
1283 case ZigLLVM_mips64el:
1284 return 4;
1285
1286 default:
1287 return 1;
1288 }
1258}1289}
src/stage1/target.hpp+1
...@@ -98,6 +98,7 @@ size_t target_libc_count(void);...@@ -98,6 +98,7 @@ size_t target_libc_count(void);
98void target_libc_enum(size_t index, ZigTarget *out_target);98void target_libc_enum(size_t index, ZigTarget *out_target);
99bool target_libc_needs_crti_crtn(const ZigTarget *target);99bool target_libc_needs_crti_crtn(const ZigTarget *target);
100100
101unsigned target_fn_ptr_align(const ZigTarget *target);
101unsigned target_fn_align(const ZigTarget *target);102unsigned target_fn_align(const ZigTarget *target);
102103
103#endif104#endif
src/translate_c.zig+1-1
...@@ -3540,7 +3540,7 @@ fn transCPtrCast(...@@ -3540,7 +3540,7 @@ fn transCPtrCast(
3540 expr3540 expr
3541 else blk: {3541 else blk: {
3542 const child_type_node = try transQualType(c, scope, child_type, loc);3542 const child_type_node = try transQualType(c, scope, child_type, loc);
3543 const alignof = try Tag.alignof.create(c.arena, child_type_node);3543 const alignof = try Tag.std_meta_alignment.create(c.arena, child_type_node);
3544 const align_cast = try Tag.align_cast.create(c.arena, .{ .lhs = alignof, .rhs = expr });3544 const align_cast = try Tag.align_cast.create(c.arena, .{ .lhs = alignof, .rhs = expr });
3545 break :blk align_cast;3545 break :blk align_cast;
3546 };3546 };
src/translate_c/ast.zig+11-1
...@@ -120,8 +120,11 @@ pub const Node = extern union {...@@ -120,8 +120,11 @@ pub const Node = extern union {
120 std_math_Log2Int,120 std_math_Log2Int,
121 /// @intCast(lhs, rhs)121 /// @intCast(lhs, rhs)
122 int_cast,122 int_cast,
123 /// @rem(lhs, rhs)123 /// @import("std").meta.promoteIntLiteral(value, type, radix)
124 std_meta_promoteIntLiteral,124 std_meta_promoteIntLiteral,
125 /// @import("std").meta.alignment(value)
126 std_meta_alignment,
127 /// @rem(lhs, rhs)
125 rem,128 rem,
126 /// @divTrunc(lhs, rhs)129 /// @divTrunc(lhs, rhs)
127 div_trunc,130 div_trunc,
...@@ -260,6 +263,7 @@ pub const Node = extern union {...@@ -260,6 +263,7 @@ pub const Node = extern union {
260 .switch_else,263 .switch_else,
261 .block_single,264 .block_single,
262 .std_meta_sizeof,265 .std_meta_sizeof,
266 .std_meta_alignment,
263 .bool_to_int,267 .bool_to_int,
264 .sizeof,268 .sizeof,
265 .alignof,269 .alignof,
...@@ -876,6 +880,11 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -876,6 +880,11 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
876 const import_node = try renderStdImport(c, "meta", "promoteIntLiteral");880 const import_node = try renderStdImport(c, "meta", "promoteIntLiteral");
877 return renderCall(c, import_node, &.{ payload.type, payload.value, payload.radix });881 return renderCall(c, import_node, &.{ payload.type, payload.value, payload.radix });
878 },882 },
883 .std_meta_alignment => {
884 const payload = node.castTag(.std_meta_alignment).?.data;
885 const import_node = try renderStdImport(c, "meta", "alignment");
886 return renderCall(c, import_node, &.{payload});
887 },
879 .std_meta_sizeof => {888 .std_meta_sizeof => {
880 const payload = node.castTag(.std_meta_sizeof).?.data;889 const payload = node.castTag(.std_meta_sizeof).?.data;
881 const import_node = try renderStdImport(c, "meta", "sizeof");890 const import_node = try renderStdImport(c, "meta", "sizeof");
...@@ -2144,6 +2153,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {...@@ -2144,6 +2153,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
2144 .typeof,2153 .typeof,
2145 .typeinfo,2154 .typeinfo,
2146 .std_meta_sizeof,2155 .std_meta_sizeof,
2156 .std_meta_alignment,
2147 .std_meta_cast,2157 .std_meta_cast,
2148 .std_meta_promoteIntLiteral,2158 .std_meta_promoteIntLiteral,
2149 .std_meta_vector,2159 .std_meta_vector,
test/compile_errors.zig+3-1
...@@ -2136,7 +2136,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -2136,7 +2136,9 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
2136 \\}2136 \\}
2137 \\fn func() callconv(.Async) void {}2137 \\fn func() callconv(.Async) void {}
2138 , &[_][]const u8{2138 , &[_][]const u8{
2139 "tmp.zig:4:21: error: expected type '[]align(16) u8', found '*[64]u8'",2139 // Split the check in two as the alignment value is target dependent.
2140 "tmp.zig:4:21: error: expected type '[]align(",
2141 ") u8', found '*[64]u8'",
2140 });2142 });
21412143
2142 cases.add("atomic orderings of fence Acquire or stricter",2144 cases.add("atomic orderings of fence Acquire or stricter",
test/stage1/behavior/type_info.zig+1-1
...@@ -306,7 +306,7 @@ test "type info: function type info" {...@@ -306,7 +306,7 @@ test "type info: function type info" {
306fn testFunction() void {306fn testFunction() void {
307 const fn_info = @typeInfo(@TypeOf(foo));307 const fn_info = @typeInfo(@TypeOf(foo));
308 expect(fn_info == .Fn);308 expect(fn_info == .Fn);
309 expect(fn_info.Fn.alignment == 0);309 expect(fn_info.Fn.alignment > 0);
310 expect(fn_info.Fn.calling_convention == .C);310 expect(fn_info.Fn.calling_convention == .C);
311 expect(!fn_info.Fn.is_generic);311 expect(!fn_info.Fn.is_generic);
312 expect(fn_info.Fn.args.len == 2);312 expect(fn_info.Fn.args.len == 2);
test/translate_c.zig+9-9
...@@ -1363,7 +1363,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1363,7 +1363,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1363 , &[_][]const u8{1363 , &[_][]const u8{
1364 \\pub export fn ptrcast() [*c]f32 {1364 \\pub export fn ptrcast() [*c]f32 {
1365 \\ var a: [*c]c_int = undefined;1365 \\ var a: [*c]c_int = undefined;
1366 \\ return @ptrCast([*c]f32, @alignCast(@alignOf(f32), a));1366 \\ return @ptrCast([*c]f32, @alignCast(@import("std").meta.alignment(f32), a));
1367 \\}1367 \\}
1368 });1368 });
13691369
...@@ -1387,16 +1387,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -1387,16 +1387,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
1387 \\pub export fn test_ptr_cast() void {1387 \\pub export fn test_ptr_cast() void {
1388 \\ var p: ?*c_void = undefined;1388 \\ var p: ?*c_void = undefined;
1389 \\ {1389 \\ {
1390 \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p));1390 \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@import("std").meta.alignment(u8), p));
1391 \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p));1391 \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@import("std").meta.alignment(c_short), p));
1392 \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p));1392 \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@import("std").meta.alignment(c_int), p));
1393 \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p));1393 \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@import("std").meta.alignment(c_longlong), p));
1394 \\ }1394 \\ }
1395 \\ {1395 \\ {
1396 \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@alignOf(u8), p));1396 \\ var to_char: [*c]u8 = @ptrCast([*c]u8, @alignCast(@import("std").meta.alignment(u8), p));
1397 \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@alignOf(c_short), p));1397 \\ var to_short: [*c]c_short = @ptrCast([*c]c_short, @alignCast(@import("std").meta.alignment(c_short), p));
1398 \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@alignOf(c_int), p));1398 \\ var to_int: [*c]c_int = @ptrCast([*c]c_int, @alignCast(@import("std").meta.alignment(c_int), p));
1399 \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@alignOf(c_longlong), p));1399 \\ var to_longlong: [*c]c_longlong = @ptrCast([*c]c_longlong, @alignCast(@import("std").meta.alignment(c_longlong), p));
1400 \\ }1400 \\ }
1401 \\}1401 \\}
1402 });1402 });