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 {
24242424 }
24252425
24262426 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 {
24272445 const gpa = dg.gpa;
24282446 const target = dg.module.getTarget();
24292447 switch (t.zigTypeTag()) {
......@@ -2519,10 +2537,18 @@ pub const DeclGen = struct {
25192537 return payload_llvm_ty;
25202538 }
25212539
2522 const fields: [2]*const llvm.Type = .{
2523 payload_llvm_ty, dg.context.intType(1),
2540 comptime assert(optional_layout_version == 2);
2541 var fields_buf: [3]*const llvm.Type = .{
2542 payload_llvm_ty, dg.context.intType(1), undefined,
25242543 };
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);
25262552 },
25272553 .ErrorUnion => {
25282554 const payload_ty = t.errorUnionPayload();
......@@ -2534,12 +2560,37 @@ pub const DeclGen = struct {
25342560
25352561 const payload_align = payload_ty.abiAlignment(target);
25362562 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;
25372568 if (error_align > payload_align) {
2538 const fields: [2]*const llvm.Type = .{ llvm_error_type, llvm_payload_type };
2539 return dg.context.structType(&fields, fields.len, .False);
2569 fields_buf[0] = llvm_error_type;
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);
25402581 } else {
2541 const fields: [2]*const llvm.Type = .{ llvm_payload_type, llvm_error_type };
2542 return dg.context.structType(&fields, fields.len, .False);
2582 fields_buf[0] = llvm_payload_type;
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);
25432594 }
25442595 },
25452596 .ErrorSet => return dg.context.intType(16),
......@@ -2704,7 +2755,7 @@ pub const DeclGen = struct {
27042755 llvm_aligned_field_ty,
27052756 dg.context.intType(8).arrayType(padding_len),
27062757 };
2707 break :t dg.context.structType(&fields, fields.len, .False);
2758 break :t dg.context.structType(&fields, fields.len, .True);
27082759 };
27092760
27102761 if (layout.tag_size == 0) {
......@@ -3020,7 +3071,7 @@ pub const DeclGen = struct {
30203071 return dg.context.constStruct(
30213072 llvm_elems.ptr,
30223073 @intCast(c_uint, llvm_elems.len),
3023 .False,
3074 .True,
30243075 );
30253076 } else {
30263077 const llvm_elem_ty = try dg.lowerType(elem_ty);
......@@ -3057,7 +3108,7 @@ pub const DeclGen = struct {
30573108 return dg.context.constStruct(
30583109 llvm_elems.ptr,
30593110 @intCast(c_uint, llvm_elems.len),
3060 .False,
3111 .True,
30613112 );
30623113 } else {
30633114 const llvm_elem_ty = try dg.lowerType(elem_ty);
......@@ -3074,7 +3125,7 @@ pub const DeclGen = struct {
30743125 const llvm_elems: [1]*const llvm.Value = .{sentinel};
30753126 const need_unnamed = dg.isUnnamedType(elem_ty, llvm_elems[0]);
30763127 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);
30783129 } else {
30793130 const llvm_elem_ty = try dg.lowerType(elem_ty);
30803131 return llvm_elem_ty.constArray(&llvm_elems, llvm_elems.len);
......@@ -3083,6 +3134,7 @@ pub const DeclGen = struct {
30833134 else => unreachable,
30843135 },
30853136 .Optional => {
3137 comptime assert(optional_layout_version == 2);
30863138 var buf: Type.Payload.ElemType = undefined;
30873139 const payload_ty = tv.ty.optionalChild(&buf);
30883140 const llvm_i1 = dg.context.intType(1);
......@@ -3091,25 +3143,30 @@ pub const DeclGen = struct {
30913143 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
30923144 return non_null_bit;
30933145 }
3146 const llvm_ty = try dg.lowerType(tv.ty);
30943147 if (tv.ty.optionalReprIsPayload()) {
30953148 if (tv.val.castTag(.opt_payload)) |payload| {
30963149 return dg.lowerValue(.{ .ty = payload_ty, .val = payload.data });
30973150 } else if (is_pl) {
30983151 return dg.lowerValue(.{ .ty = payload_ty, .val = tv.val });
30993152 } else {
3100 const llvm_ty = try dg.lowerType(tv.ty);
31013153 return llvm_ty.constNull();
31023154 }
31033155 }
31043156 assert(payload_ty.zigTypeTag() != .Fn);
3105 const fields: [2]*const llvm.Value = .{
3106 try dg.lowerValue(.{
3107 .ty = payload_ty,
3108 .val = if (tv.val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),
3109 }),
3110 non_null_bit,
3111 };
3112 return dg.context.constStruct(&fields, fields.len, .False);
3157
3158 const llvm_field_count = llvm_ty.countStructElementTypes();
3159 var fields_buf: [3]*const llvm.Value = undefined;
3160 fields_buf[0] = try dg.lowerValue(.{
3161 .ty = payload_ty,
3162 .val = if (tv.val.castTag(.opt_payload)) |pl| pl.data else Value.initTag(.undef),
3163 });
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);
31133170 },
31143171 .Fn => {
31153172 const fn_decl_index = switch (tv.val.tag()) {
......@@ -3155,12 +3212,23 @@ pub const DeclGen = struct {
31553212 .ty = payload_type,
31563213 .val = if (tv.val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef),
31573214 });
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
31583224 if (error_align > payload_align) {
3159 const fields: [2]*const llvm.Value = .{ llvm_error_value, llvm_payload_value };
3160 return dg.context.constStruct(&fields, fields.len, .False);
3225 fields_buf[0] = llvm_error_value;
3226 fields_buf[1] = llvm_payload_value;
3227 return dg.context.constStruct(&fields_buf, llvm_field_count, .False);
31613228 } else {
3162 const fields: [2]*const llvm.Value = .{ llvm_payload_value, llvm_error_value };
3163 return dg.context.constStruct(&fields, fields.len, .False);
3229 fields_buf[0] = llvm_payload_value;
3230 fields_buf[1] = llvm_error_value;
3231 return dg.context.constStruct(&fields_buf, llvm_field_count, .False);
31643232 }
31653233 },
31663234 .Struct => {
......@@ -3361,7 +3429,7 @@ pub const DeclGen = struct {
33613429 const fields: [2]*const llvm.Value = .{
33623430 field, dg.context.intType(8).arrayType(padding_len).getUndef(),
33633431 };
3364 break :p dg.context.constStruct(&fields, fields.len, .False);
3432 break :p dg.context.constStruct(&fields, fields.len, .True);
33653433 };
33663434
33673435 if (layout.tag_size == 0) {
......@@ -5847,6 +5915,7 @@ pub const FuncGen = struct {
58475915 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
58485916 const payload_ty = self.air.typeOf(ty_op.operand);
58495917 const non_null_bit = self.context.intType(1).constAllOnes();
5918 comptime assert(optional_layout_version == 2);
58505919 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return non_null_bit;
58515920 const operand = try self.resolveInst(ty_op.operand);
58525921 const optional_ty = self.air.typeOfIndex(inst);
......@@ -9300,6 +9369,7 @@ fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool {
93009369/// We can do this because for all types, Zig ABI alignment >= LLVM ABI
93019370/// alignment.
93029371const struct_layout_version = 2;
9372const optional_layout_version = 2;
93039373
93049374/// We use the least significant bit of the pointer address to tell us
93059375/// 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 {
301301
302302 pub const countStructElementTypes = LLVMCountStructElementTypes;
303303 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;
304310};
305311
306312pub const Module = opaque {
......@@ -1032,6 +1038,9 @@ pub const TargetData = opaque {
10321038
10331039 pub const abiAlignmentOfType = LLVMABIAlignmentOfType;
10341040 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;
10351044};
10361045
10371046pub const CodeModel = enum(c_int) {
src/type.zig+3
......@@ -2305,6 +2305,9 @@ pub const Type = extern union {
23052305 /// true if and only if the type takes up space in memory at runtime.
23062306 /// There are two reasons a type will return false:
23072307 /// * 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
23082311 /// * the type has only one possible value, making its ABI size 0.
23092312 /// When `ignore_comptime_only` is true, then types that are comptime only
23102313 /// may return false positives.