authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2021-04-25 16:40:41+02:00
committergravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2021-04-25 16:40:41+02:00
log50a8124f45cd0994b52af3fd166dd3bda48f4b99
tree1e5c4a6bcd6a9b233f9aa198a07663aa9a9ddb37
parent37b05742ff1544bccf7c8ae9b12c6707a5a54df2

stage1: Change how the Frame alignment is computed

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) {
47704770}
47714771
47724772static uint32_t get_async_frame_align_bytes(CodeGen *g) {
4773 uint32_t a = g->pointer_size_bytes * 2;
4774 // promises have at least alignment 8 so that we can have 3 extra bits when doing atomicrmw
4775 if (a < 8) a = 8;
4776 return a;
4773 // Due to how the frame structure is built the minimum alignment is the one
4774 // of a usize (or pointer).
4775 // label (grep this): [fn_frame_struct_layout]
4776 return max(g->builtin_types.entry_usize->abi_align, target_fn_align(g->zig_target));
47774777}
47784778
47794779uint32_t get_ptr_align(CodeGen *g, ZigType *type) {
......@@ -4789,11 +4789,8 @@ uint32_t get_ptr_align(CodeGen *g, ZigType *type) {
47894789 return (ptr_type->data.pointer.explicit_alignment == 0) ?
47904790 get_abi_alignment(g, ptr_type->data.pointer.child_type) : ptr_type->data.pointer.explicit_alignment;
47914791 } else if (ptr_type->id == ZigTypeIdFn) {
4792 // I tried making this use LLVMABIAlignmentOfType but it trips this assertion in LLVM:
4793 // "Cannot getTypeInfo() on a type that is unsized!"
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;
4792 return (ptr_type->data.fn.fn_type_id.alignment == 0) ?
4793 target_fn_ptr_align(g->zig_target) : ptr_type->data.fn.fn_type_id.alignment;
47974794 } else if (ptr_type->id == ZigTypeIdAnyFrame) {
47984795 return get_async_frame_align_bytes(g);
47994796 } else {
src/stage1/ir.cpp+2-2
......@@ -26079,11 +26079,11 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
2607926079 fields[0]->special = ConstValSpecialStatic;
2608026080 fields[0]->type = get_builtin_type(ira->codegen, "CallingConvention");
2608126081 bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.fn.fn_type_id.cc);
26082 // alignment: u29
26082 // alignment: comptime_int
2608326083 ensure_field_index(result->type, "alignment", 1);
2608426084 fields[1]->special = ConstValSpecialStatic;
2608526085 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));
2608726087 // is_generic: bool
2608826088 ensure_field_index(result->type, "is_generic", 2);
2608926089 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) {
12531253 target->arch == ZigLLVM_ppc64le;
12541254}
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.
12561264unsigned 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 }
12581289}
src/stage1/target.hpp+1
......@@ -98,6 +98,7 @@ size_t target_libc_count(void);
9898void target_libc_enum(size_t index, ZigTarget *out_target);
9999bool target_libc_needs_crti_crtn(const ZigTarget *target);
100100
101unsigned target_fn_ptr_align(const ZigTarget *target);
101102unsigned target_fn_align(const ZigTarget *target);
102103
103104#endif
test/stage1/behavior/type_info.zig+1-1
......@@ -306,7 +306,7 @@ test "type info: function type info" {
306306fn testFunction() void {
307307 const fn_info = @typeInfo(@TypeOf(foo));
308308 expect(fn_info == .Fn);
309 expect(fn_info.Fn.alignment == 0);
309 expect(fn_info.Fn.alignment > 0);
310310 expect(fn_info.Fn.calling_convention == .C);
311311 expect(!fn_info.Fn.is_generic);
312312 expect(fn_info.Fn.args.len == 2);