| author | |
| committer | |
| log | 50a8124f45cd0994b52af3fd166dd3bda48f4b99 |
| tree | 1e5c4a6bcd6a9b233f9aa198a07663aa9a9ddb37 |
| parent | 37b05742ff1544bccf7c8ae9b12c6707a5a54df2 |
The code would previously assume every function would start at addresses
being multiples of 16, this is not true beside some specific cases.
Moreover LLVM picks different alignment values depending on whether it's
trying to generate dense or fast code.
Let's use the minimum guaranteed alignment as base value, computed
according to how big the opcodes are.
The alignment of function pointers is always 1, a safe value that won't
cause any error at runtime. Note that this was already the case before
this commit, here we're making this choice explicit.
Let the 'alignment' field for TypeInfo of fn types reflect the ABI
alignment used by the compiler, make this field behave similarly to the
'alignment' one for pointers.5 files changed, 42 insertions(+), 13 deletions(-)
src/stage1/analyze.cpp+6-9| ... | @@ -4770,10 +4770,10 @@ Error type_is_nonnull_ptr2(CodeGen *g, ZigType *type, bool *result) { | ... | @@ -4770,10 +4770,10 @@ Error type_is_nonnull_ptr2(CodeGen *g, ZigType *type, bool *result) { |
| 4770 | } | 4770 | } |
| 4771 | 4771 | ||
| 4772 | static uint32_t get_async_frame_align_bytes(CodeGen *g) { | 4772 | static uint32_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 atomicrmw | 4774 | // 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 | } |
| 4778 | 4778 | ||
| 4779 | uint32_t get_ptr_align(CodeGen *g, ZigType *type) { | 4779 | uint32_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/ir.cpp+2-2| ... | @@ -26079,11 +26079,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy | ... | @@ -26079,11 +26079,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy |
| 26079 | fields[0]->special = ConstValSpecialStatic; | 26079 | fields[0]->special = ConstValSpecialStatic; |
| 26080 | fields[0]->type = get_builtin_type(ira->codegen, "CallingConvention"); | 26080 | 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); | 26081 | bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.fn.fn_type_id.cc); |
| 26082 | // alignment: u29 | 26082 | // alignment: comptime_int |
| 26083 | ensure_field_index(result->type, "alignment", 1); | 26083 | ensure_field_index(result->type, "alignment", 1); |
| 26084 | fields[1]->special = ConstValSpecialStatic; | 26084 | fields[1]->special = ConstValSpecialStatic; |
| 26085 | fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int; | 26085 | 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); | 26086 | bigint_init_unsigned(&fields[1]->data.x_bigint, get_ptr_align(ira->codegen, type_entry)); |
| 26087 | // is_generic: bool | 26087 | // is_generic: bool |
| 26088 | ensure_field_index(result->type, "is_generic", 2); | 26088 | ensure_field_index(result->type, "is_generic", 2); |
| 26089 | bool is_generic = type_entry->data.fn.is_generic; | 26089 | bool is_generic = type_entry->data.fn.is_generic; |
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 | } |
| 1255 | 1255 | ||
| 1256 | // Returns the minimum alignment for every function pointer on the given | ||
| 1257 | // architecture. | ||
| 1258 | unsigned 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. | ||
| 1256 | unsigned target_fn_align(const ZigTarget *target) { | 1264 | unsigned 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); |
| 98 | void target_libc_enum(size_t index, ZigTarget *out_target); | 98 | void target_libc_enum(size_t index, ZigTarget *out_target); |
| 99 | bool target_libc_needs_crti_crtn(const ZigTarget *target); | 99 | bool target_libc_needs_crti_crtn(const ZigTarget *target); |
| 100 | 100 | ||
| 101 | unsigned target_fn_ptr_align(const ZigTarget *target); | ||
| 101 | unsigned target_fn_align(const ZigTarget *target); | 102 | unsigned target_fn_align(const ZigTarget *target); |
| 102 | 103 | ||
| 103 | #endif | 104 | #endif |
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" { |
| 306 | fn testFunction() void { | 306 | fn 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); |