authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-07-15 14:48:21-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-07-15 14:48:21-04:00
loge867127a6cc3e0148101f08d846c876f62e839c1
treec823cd6e8e57cd4b939ab997e512de166a00a136
parent6ba2fb3db7d411818c73defa028a471d1ec2b7b9
parent9329b93b8896e552812d68a542b296cb897584d8
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12127 from ziglang/llvm-abi-size

Audit LLVM ABI size

3 files changed, 107 insertions(+), 25 deletions(-)

src/codegen/llvm.zig+95-25
...@@ -2424,6 +2424,24 @@ pub const DeclGen = struct {...@@ -2424,6 +2424,24 @@ pub const DeclGen = struct {
2424 }2424 }
24252425
2426 fn lowerType(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type {2426 fn lowerType(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type {
2427 const llvm_ty = try lowerTypeInner(dg, t);
2428 if (std.debug.runtime_safety and false) check: {
2429 if (t.zigTypeTag() == .Opaque) break :check;
2430 if (!t.hasRuntimeBits()) break :check;
2431 if (!llvm_ty.isSized().toBool()) break :check;
2432
2433 const zig_size = t.abiSize(dg.module.getTarget());
2434 const llvm_size = dg.object.target_data.abiSizeOfType(llvm_ty);
2435 if (llvm_size != zig_size) {
2436 log.err("when lowering {}, Zig ABI size = {d} but LLVM ABI size = {d}", .{
2437 t.fmt(dg.module), zig_size, llvm_size,
2438 });
2439 }
2440 }
2441 return llvm_ty;
2442 }
2443
2444 fn lowerTypeInner(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type {
2427 const gpa = dg.gpa;2445 const gpa = dg.gpa;
2428 const target = dg.module.getTarget();2446 const target = dg.module.getTarget();
2429 switch (t.zigTypeTag()) {2447 switch (t.zigTypeTag()) {
...@@ -2519,10 +2537,18 @@ pub const DeclGen = struct {...@@ -2519,10 +2537,18 @@ pub const DeclGen = struct {
2519 return payload_llvm_ty;2537 return payload_llvm_ty;
2520 }2538 }
25212539
2522 const fields: [2]*const llvm.Type = .{2540 comptime assert(optional_layout_version == 2);
2523 payload_llvm_ty, dg.context.intType(1),2541 var fields_buf: [3]*const llvm.Type = .{
2542 payload_llvm_ty, dg.context.intType(1), undefined,
2524 };2543 };
2525 return dg.context.structType(&fields, fields.len, .False);2544 const offset = child_ty.abiSize(target) + 1;
2545 const abi_size = t.abiSize(target);
2546 const padding = @intCast(c_uint, abi_size - offset);
2547 if (padding == 0) {
2548 return dg.context.structType(&fields_buf, 2, .False);
2549 }
2550 fields_buf[2] = dg.context.intType(8).arrayType(padding);
2551 return dg.context.structType(&fields_buf, 3, .False);
2526 },2552 },
2527 .ErrorUnion => {2553 .ErrorUnion => {
2528 const payload_ty = t.errorUnionPayload();2554 const payload_ty = t.errorUnionPayload();
...@@ -2534,12 +2560,37 @@ pub const DeclGen = struct {...@@ -2534,12 +2560,37 @@ pub const DeclGen = struct {
25342560
2535 const payload_align = payload_ty.abiAlignment(target);2561 const payload_align = payload_ty.abiAlignment(target);
2536 const error_align = Type.anyerror.abiAlignment(target);2562 const error_align = Type.anyerror.abiAlignment(target);
2563
2564 const payload_size = payload_ty.abiSize(target);
2565 const error_size = Type.anyerror.abiSize(target);
2566
2567 var fields_buf: [3]*const llvm.Type = undefined;
2537 if (error_align > payload_align) {2568 if (error_align > payload_align) {
2538 const fields: [2]*const llvm.Type = .{ llvm_error_type, llvm_payload_type };2569 fields_buf[0] = llvm_error_type;
2539 return dg.context.structType(&fields, fields.len, .False);2570 fields_buf[1] = llvm_payload_type;
2571 const payload_end =
2572 std.mem.alignForwardGeneric(u64, error_size, payload_align) +
2573 payload_size;
2574 const abi_size = std.mem.alignForwardGeneric(u64, payload_end, error_align);
2575 const padding = @intCast(c_uint, abi_size - payload_end);
2576 if (padding == 0) {
2577 return dg.context.structType(&fields_buf, 2, .False);
2578 }
2579 fields_buf[2] = dg.context.intType(8).arrayType(padding);
2580 return dg.context.structType(&fields_buf, 3, .False);
2540 } else {2581 } else {
2541 const fields: [2]*const llvm.Type = .{ llvm_payload_type, llvm_error_type };2582 fields_buf[0] = llvm_payload_type;
2542 return dg.context.structType(&fields, fields.len, .False);2583 fields_buf[1] = llvm_error_type;
2584 const error_end =
2585 std.mem.alignForwardGeneric(u64, payload_size, error_align) +
2586 error_size;
2587 const abi_size = std.mem.alignForwardGeneric(u64, error_end, payload_align);
2588 const padding = @intCast(c_uint, abi_size - error_end);
2589 if (padding == 0) {
2590 return dg.context.structType(&fields_buf, 2, .False);
2591 }
2592 fields_buf[2] = dg.context.intType(8).arrayType(padding);
2593 return dg.context.structType(&fields_buf, 3, .False);
2543 }2594 }
2544 },2595 },
2545 .ErrorSet => return dg.context.intType(16),2596 .ErrorSet => return dg.context.intType(16),
...@@ -2704,7 +2755,7 @@ pub const DeclGen = struct {...@@ -2704,7 +2755,7 @@ pub const DeclGen = struct {
2704 llvm_aligned_field_ty,2755 llvm_aligned_field_ty,
2705 dg.context.intType(8).arrayType(padding_len),2756 dg.context.intType(8).arrayType(padding_len),
2706 };2757 };
2707 break :t dg.context.structType(&fields, fields.len, .False);2758 break :t dg.context.structType(&fields, fields.len, .True);
2708 };2759 };
27092760
2710 if (layout.tag_size == 0) {2761 if (layout.tag_size == 0) {
...@@ -3020,7 +3071,7 @@ pub const DeclGen = struct {...@@ -3020,7 +3071,7 @@ pub const DeclGen = struct {
3020 return dg.context.constStruct(3071 return dg.context.constStruct(
3021 llvm_elems.ptr,3072 llvm_elems.ptr,
3022 @intCast(c_uint, llvm_elems.len),3073 @intCast(c_uint, llvm_elems.len),
3023 .False,3074 .True,
3024 );3075 );
3025 } else {3076 } else {
3026 const llvm_elem_ty = try dg.lowerType(elem_ty);3077 const llvm_elem_ty = try dg.lowerType(elem_ty);
...@@ -3057,7 +3108,7 @@ pub const DeclGen = struct {...@@ -3057,7 +3108,7 @@ pub const DeclGen = struct {
3057 return dg.context.constStruct(3108 return dg.context.constStruct(
3058 llvm_elems.ptr,3109 llvm_elems.ptr,
3059 @intCast(c_uint, llvm_elems.len),3110 @intCast(c_uint, llvm_elems.len),
3060 .False,3111 .True,
3061 );3112 );
3062 } else {3113 } else {
3063 const llvm_elem_ty = try dg.lowerType(elem_ty);3114 const llvm_elem_ty = try dg.lowerType(elem_ty);
...@@ -3074,7 +3125,7 @@ pub const DeclGen = struct {...@@ -3074,7 +3125,7 @@ pub const DeclGen = struct {
3074 const llvm_elems: [1]*const llvm.Value = .{sentinel};3125 const llvm_elems: [1]*const llvm.Value = .{sentinel};
3075 const need_unnamed = dg.isUnnamedType(elem_ty, llvm_elems[0]);3126 const need_unnamed = dg.isUnnamedType(elem_ty, llvm_elems[0]);
3076 if (need_unnamed) {3127 if (need_unnamed) {
3077 return dg.context.constStruct(&llvm_elems, llvm_elems.len, .False);3128 return dg.context.constStruct(&llvm_elems, llvm_elems.len, .True);
3078 } else {3129 } else {
3079 const llvm_elem_ty = try dg.lowerType(elem_ty);3130 const llvm_elem_ty = try dg.lowerType(elem_ty);
3080 return llvm_elem_ty.constArray(&llvm_elems, llvm_elems.len);3131 return llvm_elem_ty.constArray(&llvm_elems, llvm_elems.len);
...@@ -3083,6 +3134,7 @@ pub const DeclGen = struct {...@@ -3083,6 +3134,7 @@ pub const DeclGen = struct {
3083 else => unreachable,3134 else => unreachable,
3084 },3135 },
3085 .Optional => {3136 .Optional => {
3137 comptime assert(optional_layout_version == 2);
3086 var buf: Type.Payload.ElemType = undefined;3138 var buf: Type.Payload.ElemType = undefined;
3087 const payload_ty = tv.ty.optionalChild(&buf);3139 const payload_ty = tv.ty.optionalChild(&buf);
3088 const llvm_i1 = dg.context.intType(1);3140 const llvm_i1 = dg.context.intType(1);
...@@ -3091,25 +3143,30 @@ pub const DeclGen = struct {...@@ -3091,25 +3143,30 @@ pub const DeclGen = struct {
3091 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {3143 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
3092 return non_null_bit;3144 return non_null_bit;
3093 }3145 }
3146 const llvm_ty = try dg.lowerType(tv.ty);
3094 if (tv.ty.optionalReprIsPayload()) {3147 if (tv.ty.optionalReprIsPayload()) {
3095 if (tv.val.castTag(.opt_payload)) |payload| {3148 if (tv.val.castTag(.opt_payload)) |payload| {
3096 return dg.lowerValue(.{ .ty = payload_ty, .val = payload.data });3149 return dg.lowerValue(.{ .ty = payload_ty, .val = payload.data });
3097 } else if (is_pl) {3150 } else if (is_pl) {
3098 return dg.lowerValue(.{ .ty = payload_ty, .val = tv.val });3151 return dg.lowerValue(.{ .ty = payload_ty, .val = tv.val });
3099 } else {3152 } else {
3100 const llvm_ty = try dg.lowerType(tv.ty);
3101 return llvm_ty.constNull();3153 return llvm_ty.constNull();
3102 }3154 }
3103 }3155 }
3104 assert(payload_ty.zigTypeTag() != .Fn);3156 assert(payload_ty.zigTypeTag() != .Fn);
3105 const fields: [2]*const llvm.Value = .{3157
3106 try dg.lowerValue(.{3158 const llvm_field_count = llvm_ty.countStructElementTypes();
3107 .ty = payload_ty,3159 var fields_buf: [3]*const llvm.Value = undefined;
3108 .val = if (tv.val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),3160 fields_buf[0] = try dg.lowerValue(.{
3109 }),3161 .ty = payload_ty,
3110 non_null_bit,3162 .val = if (tv.val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),
3111 };3163 });
3112 return dg.context.constStruct(&fields, fields.len, .False);3164 fields_buf[1] = non_null_bit;
3165 if (llvm_field_count > 2) {
3166 assert(llvm_field_count == 3);
3167 fields_buf[2] = llvm_ty.structGetTypeAtIndex(2).getUndef();
3168 }
3169 return dg.context.constStruct(&fields_buf, llvm_field_count, .False);
3113 },3170 },
3114 .Fn => {3171 .Fn => {
3115 const fn_decl_index = switch (tv.val.tag()) {3172 const fn_decl_index = switch (tv.val.tag()) {
...@@ -3155,12 +3212,23 @@ pub const DeclGen = struct {...@@ -3155,12 +3212,23 @@ pub const DeclGen = struct {
3155 .ty = payload_type,3212 .ty = payload_type,
3156 .val = if (tv.val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef),3213 .val = if (tv.val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef),
3157 });3214 });
3215 var fields_buf: [3]*const llvm.Value = undefined;
3216
3217 const llvm_ty = try dg.lowerType(tv.ty);
3218 const llvm_field_count = llvm_ty.countStructElementTypes();
3219 if (llvm_field_count > 2) {
3220 assert(llvm_field_count == 3);
3221 fields_buf[2] = llvm_ty.structGetTypeAtIndex(2).getUndef();
3222 }
3223
3158 if (error_align > payload_align) {3224 if (error_align > payload_align) {
3159 const fields: [2]*const llvm.Value = .{ llvm_error_value, llvm_payload_value };3225 fields_buf[0] = llvm_error_value;
3160 return dg.context.constStruct(&fields, fields.len, .False);3226 fields_buf[1] = llvm_payload_value;
3227 return dg.context.constStruct(&fields_buf, llvm_field_count, .False);
3161 } else {3228 } else {
3162 const fields: [2]*const llvm.Value = .{ llvm_payload_value, llvm_error_value };3229 fields_buf[0] = llvm_payload_value;
3163 return dg.context.constStruct(&fields, fields.len, .False);3230 fields_buf[1] = llvm_error_value;
3231 return dg.context.constStruct(&fields_buf, llvm_field_count, .False);
3164 }3232 }
3165 },3233 },
3166 .Struct => {3234 .Struct => {
...@@ -3361,7 +3429,7 @@ pub const DeclGen = struct {...@@ -3361,7 +3429,7 @@ pub const DeclGen = struct {
3361 const fields: [2]*const llvm.Value = .{3429 const fields: [2]*const llvm.Value = .{
3362 field, dg.context.intType(8).arrayType(padding_len).getUndef(),3430 field, dg.context.intType(8).arrayType(padding_len).getUndef(),
3363 };3431 };
3364 break :p dg.context.constStruct(&fields, fields.len, .False);3432 break :p dg.context.constStruct(&fields, fields.len, .True);
3365 };3433 };
33663434
3367 if (layout.tag_size == 0) {3435 if (layout.tag_size == 0) {
...@@ -5847,6 +5915,7 @@ pub const FuncGen = struct {...@@ -5847,6 +5915,7 @@ pub const FuncGen = struct {
5847 const ty_op = self.air.instructions.items(.data)[inst].ty_op;5915 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
5848 const payload_ty = self.air.typeOf(ty_op.operand);5916 const payload_ty = self.air.typeOf(ty_op.operand);
5849 const non_null_bit = self.context.intType(1).constAllOnes();5917 const non_null_bit = self.context.intType(1).constAllOnes();
5918 comptime assert(optional_layout_version == 2);
5850 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return non_null_bit;5919 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return non_null_bit;
5851 const operand = try self.resolveInst(ty_op.operand);5920 const operand = try self.resolveInst(ty_op.operand);
5852 const optional_ty = self.air.typeOfIndex(inst);5921 const optional_ty = self.air.typeOfIndex(inst);
...@@ -9300,6 +9369,7 @@ fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool {...@@ -9300,6 +9369,7 @@ fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool {
9300/// We can do this because for all types, Zig ABI alignment >= LLVM ABI9369/// We can do this because for all types, Zig ABI alignment >= LLVM ABI
9301/// alignment.9370/// alignment.
9302const struct_layout_version = 2;9371const struct_layout_version = 2;
9372const optional_layout_version = 2;
93039373
9304/// We use the least significant bit of the pointer address to tell us9374/// We use the least significant bit of the pointer address to tell us
9305/// whether the type is fully resolved. Types that are only fwd declared9375/// whether the type is fully resolved. Types that are only fwd declared
src/codegen/llvm/bindings.zig+9
...@@ -301,6 +301,12 @@ pub const Type = opaque {...@@ -301,6 +301,12 @@ pub const Type = opaque {
301301
302 pub const countStructElementTypes = LLVMCountStructElementTypes;302 pub const countStructElementTypes = LLVMCountStructElementTypes;
303 extern fn LLVMCountStructElementTypes(StructTy: *const Type) c_uint;303 extern fn LLVMCountStructElementTypes(StructTy: *const Type) c_uint;
304
305 pub const isOpaqueStruct = LLVMIsOpaqueStruct;
306 extern fn LLVMIsOpaqueStruct(StructTy: *const Type) Bool;
307
308 pub const isSized = LLVMTypeIsSized;
309 extern fn LLVMTypeIsSized(Ty: *const Type) Bool;
304};310};
305311
306pub const Module = opaque {312pub const Module = opaque {
...@@ -1032,6 +1038,9 @@ pub const TargetData = opaque {...@@ -1032,6 +1038,9 @@ pub const TargetData = opaque {
10321038
1033 pub const abiAlignmentOfType = LLVMABIAlignmentOfType;1039 pub const abiAlignmentOfType = LLVMABIAlignmentOfType;
1034 extern fn LLVMABIAlignmentOfType(TD: *const TargetData, Ty: *const Type) c_uint;1040 extern fn LLVMABIAlignmentOfType(TD: *const TargetData, Ty: *const Type) c_uint;
1041
1042 pub const abiSizeOfType = LLVMABISizeOfType;
1043 extern fn LLVMABISizeOfType(TD: *const TargetData, Ty: *const Type) c_ulonglong;
1035};1044};
10361045
1037pub const CodeModel = enum(c_int) {1046pub const CodeModel = enum(c_int) {
src/type.zig+3
...@@ -2305,6 +2305,9 @@ pub const Type = extern union {...@@ -2305,6 +2305,9 @@ pub const Type = extern union {
2305 /// true if and only if the type takes up space in memory at runtime.2305 /// true if and only if the type takes up space in memory at runtime.
2306 /// There are two reasons a type will return false:2306 /// There are two reasons a type will return false:
2307 /// * the type is a comptime-only type. For example, the type `type` itself.2307 /// * the type is a comptime-only type. For example, the type `type` itself.
2308 /// - note, however, that a struct can have mixed fields and only the non-comptime-only
2309 /// fields will count towards the ABI size. For example, `struct {T: type, x: i32}`
2310 /// hasRuntimeBits()=true and abiSize()=4
2308 /// * the type has only one possible value, making its ABI size 0.2311 /// * the type has only one possible value, making its ABI size 0.
2309 /// When `ignore_comptime_only` is true, then types that are comptime only2312 /// When `ignore_comptime_only` is true, then types that are comptime only
2310 /// may return false positives.2313 /// may return false positives.