authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-07-14 23:24:57-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-07-14 23:24:57-07:00
log040cb585e88583cba8a15f30eea07bfd2f135515
tree1a7be73e85d8a92d09f148924b219cac1d59f55d
parent8c14d170b579e078ff961973e4d28a4946df36d7

LLVM: fix ABI size of optional and error union types

Previously, the Zig ABI size and LLVM ABI size of these types disagreed sometimes. This code also corrects the logging messages to not trigger LLVM assertions.

3 files changed, 75 insertions(+), 37 deletions(-)

src/codegen/llvm.zig+69-37
...@@ -2425,17 +2425,17 @@ pub const DeclGen = struct {...@@ -2425,17 +2425,17 @@ pub const DeclGen = struct {
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);2427 const llvm_ty = try lowerTypeInner(dg, t);
2428 if (std.debug.runtime_safety) {2428 if (std.debug.runtime_safety) check: {
2429 if (t.zigTypeTag() != .Opaque and t.hasRuntimeBits() and2429 if (t.zigTypeTag() == .Opaque) break :check;
2430 !llvm_ty.isOpaqueStruct().toBool())2430 if (!t.hasRuntimeBits()) break :check;
2431 {2431 if (!llvm_ty.isSized().toBool()) break :check;
2432 const zig_size = t.abiSize(dg.module.getTarget());2432
2433 const llvm_size = dg.object.target_data.abiSizeOfType(llvm_ty);2433 const zig_size = t.abiSize(dg.module.getTarget());
2434 if (llvm_size != zig_size) {2434 const llvm_size = dg.object.target_data.abiSizeOfType(llvm_ty);
2435 log.err("when lowering {}, Zig ABI size = {d} but LLVM ABI size = {d}", .{2435 if (llvm_size != zig_size) {
2436 t.fmt(dg.module), zig_size, llvm_size,2436 log.err("when lowering {}, Zig ABI size = {d} but LLVM ABI size = {d}", .{
2437 });2437 t.fmt(dg.module), zig_size, llvm_size,
2438 }2438 });
2439 }2439 }
2440 }2440 }
2441 return llvm_ty;2441 return llvm_ty;
...@@ -2537,22 +2537,18 @@ pub const DeclGen = struct {...@@ -2537,22 +2537,18 @@ pub const DeclGen = struct {
2537 return payload_llvm_ty;2537 return payload_llvm_ty;
2538 }2538 }
25392539
2540 comptime assert(optional_layout_version == 1);2540 comptime assert(optional_layout_version == 2);
2541 const fields: [2]*const llvm.Type = .{2541 var fields_buf: [3]*const llvm.Type = .{
2542 payload_llvm_ty,2542 payload_llvm_ty, dg.context.intType(1), undefined,
2543 dg.context.intType(1),
2544 };
2545 const llvm_ty = dg.context.structType(&fields, fields.len, .False);
2546 const llvm_size = dg.object.target_data.abiSizeOfType(llvm_ty);
2547 const zig_size = t.abiSize(target);
2548 const padding = @intCast(c_uint, zig_size - llvm_size);
2549 if (padding == 0) return llvm_ty;
2550 const padded_fields: [3]*const llvm.Type = .{
2551 payload_llvm_ty,
2552 dg.context.intType(1),
2553 dg.context.intType(8).arrayType(padding),
2554 };2543 };
2555 return dg.context.structType(&padded_fields, padded_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);
2556 },2552 },
2557 .ErrorUnion => {2553 .ErrorUnion => {
2558 const payload_ty = t.errorUnionPayload();2554 const payload_ty = t.errorUnionPayload();
...@@ -2564,12 +2560,37 @@ pub const DeclGen = struct {...@@ -2564,12 +2560,37 @@ pub const DeclGen = struct {
25642560
2565 const payload_align = payload_ty.abiAlignment(target);2561 const payload_align = payload_ty.abiAlignment(target);
2566 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;
2567 if (error_align > payload_align) {2568 if (error_align > payload_align) {
2568 const fields: [2]*const llvm.Type = .{ llvm_error_type, llvm_payload_type };2569 fields_buf[0] = llvm_error_type;
2569 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);
2570 } else {2581 } else {
2571 const fields: [2]*const llvm.Type = .{ llvm_payload_type, llvm_error_type };2582 fields_buf[0] = llvm_payload_type;
2572 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);
2573 }2594 }
2574 },2595 },
2575 .ErrorSet => return dg.context.intType(16),2596 .ErrorSet => return dg.context.intType(16),
...@@ -3113,7 +3134,7 @@ pub const DeclGen = struct {...@@ -3113,7 +3134,7 @@ pub const DeclGen = struct {
3113 else => unreachable,3134 else => unreachable,
3114 },3135 },
3115 .Optional => {3136 .Optional => {
3116 comptime assert(optional_layout_version == 1);3137 comptime assert(optional_layout_version == 2);
3117 var buf: Type.Payload.ElemType = undefined;3138 var buf: Type.Payload.ElemType = undefined;
3118 const payload_ty = tv.ty.optionalChild(&buf);3139 const payload_ty = tv.ty.optionalChild(&buf);
3119 const llvm_i1 = dg.context.intType(1);3140 const llvm_i1 = dg.context.intType(1);
...@@ -3191,12 +3212,23 @@ pub const DeclGen = struct {...@@ -3191,12 +3212,23 @@ pub const DeclGen = struct {
3191 .ty = payload_type,3212 .ty = payload_type,
3192 .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),
3193 });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
3194 if (error_align > payload_align) {3224 if (error_align > payload_align) {
3195 const fields: [2]*const llvm.Value = .{ llvm_error_value, llvm_payload_value };3225 fields_buf[0] = llvm_error_value;
3196 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);
3197 } else {3228 } else {
3198 const fields: [2]*const llvm.Value = .{ llvm_payload_value, llvm_error_value };3229 fields_buf[0] = llvm_payload_value;
3199 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);
3200 }3232 }
3201 },3233 },
3202 .Struct => {3234 .Struct => {
...@@ -5883,7 +5915,7 @@ pub const FuncGen = struct {...@@ -5883,7 +5915,7 @@ pub const FuncGen = struct {
5883 const ty_op = self.air.instructions.items(.data)[inst].ty_op;5915 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
5884 const payload_ty = self.air.typeOf(ty_op.operand);5916 const payload_ty = self.air.typeOf(ty_op.operand);
5885 const non_null_bit = self.context.intType(1).constAllOnes();5917 const non_null_bit = self.context.intType(1).constAllOnes();
5886 comptime assert(optional_layout_version == 1);5918 comptime assert(optional_layout_version == 2);
5887 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return non_null_bit;5919 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return non_null_bit;
5888 const operand = try self.resolveInst(ty_op.operand);5920 const operand = try self.resolveInst(ty_op.operand);
5889 const optional_ty = self.air.typeOfIndex(inst);5921 const optional_ty = self.air.typeOfIndex(inst);
...@@ -9337,7 +9369,7 @@ fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool {...@@ -9337,7 +9369,7 @@ fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool {
9337/// 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
9338/// alignment.9370/// alignment.
9339const struct_layout_version = 2;9371const struct_layout_version = 2;
9340const optional_layout_version = 1;9372const optional_layout_version = 2;
93419373
9342/// 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
9343/// 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+3
...@@ -304,6 +304,9 @@ pub const Type = opaque {...@@ -304,6 +304,9 @@ pub const Type = opaque {
304304
305 pub const isOpaqueStruct = LLVMIsOpaqueStruct;305 pub const isOpaqueStruct = LLVMIsOpaqueStruct;
306 extern fn LLVMIsOpaqueStruct(StructTy: *const Type) Bool;306 extern fn LLVMIsOpaqueStruct(StructTy: *const Type) Bool;
307
308 pub const isSized = LLVMTypeIsSized;
309 extern fn LLVMTypeIsSized(Ty: *const Type) Bool;
307};310};
308311
309pub const Module = opaque {312pub const Module = opaque {
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.