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 {
24252425
24262426 fn lowerType(dg: *DeclGen, t: Type) Allocator.Error!*const llvm.Type {
24272427 const llvm_ty = try lowerTypeInner(dg, t);
2428 if (std.debug.runtime_safety) {
2429 if (t.zigTypeTag() != .Opaque and t.hasRuntimeBits() and
2430 !llvm_ty.isOpaqueStruct().toBool())
2431 {
2432 const zig_size = t.abiSize(dg.module.getTarget());
2433 const llvm_size = dg.object.target_data.abiSizeOfType(llvm_ty);
2434 if (llvm_size != zig_size) {
2435 log.err("when lowering {}, Zig ABI size = {d} but LLVM ABI size = {d}", .{
2436 t.fmt(dg.module), zig_size, llvm_size,
2437 });
2438 }
2428 if (std.debug.runtime_safety) 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 });
24392439 }
24402440 }
24412441 return llvm_ty;
......@@ -2537,22 +2537,18 @@ pub const DeclGen = struct {
25372537 return payload_llvm_ty;
25382538 }
25392539
2540 comptime assert(optional_layout_version == 1);
2541 const fields: [2]*const llvm.Type = .{
2542 payload_llvm_ty,
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),
2540 comptime assert(optional_layout_version == 2);
2541 var fields_buf: [3]*const llvm.Type = .{
2542 payload_llvm_ty, dg.context.intType(1), undefined,
25542543 };
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);
25562552 },
25572553 .ErrorUnion => {
25582554 const payload_ty = t.errorUnionPayload();
......@@ -2564,12 +2560,37 @@ pub const DeclGen = struct {
25642560
25652561 const payload_align = payload_ty.abiAlignment(target);
25662562 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;
25672568 if (error_align > payload_align) {
2568 const fields: [2]*const llvm.Type = .{ llvm_error_type, llvm_payload_type };
2569 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);
25702581 } else {
2571 const fields: [2]*const llvm.Type = .{ llvm_payload_type, llvm_error_type };
2572 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);
25732594 }
25742595 },
25752596 .ErrorSet => return dg.context.intType(16),
......@@ -3113,7 +3134,7 @@ pub const DeclGen = struct {
31133134 else => unreachable,
31143135 },
31153136 .Optional => {
3116 comptime assert(optional_layout_version == 1);
3137 comptime assert(optional_layout_version == 2);
31173138 var buf: Type.Payload.ElemType = undefined;
31183139 const payload_ty = tv.ty.optionalChild(&buf);
31193140 const llvm_i1 = dg.context.intType(1);
......@@ -3191,12 +3212,23 @@ pub const DeclGen = struct {
31913212 .ty = payload_type,
31923213 .val = if (tv.val.castTag(.eu_payload)) |pl| pl.data else Value.initTag(.undef),
31933214 });
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
31943224 if (error_align > payload_align) {
3195 const fields: [2]*const llvm.Value = .{ llvm_error_value, llvm_payload_value };
3196 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);
31973228 } else {
3198 const fields: [2]*const llvm.Value = .{ llvm_payload_value, llvm_error_value };
3199 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);
32003232 }
32013233 },
32023234 .Struct => {
......@@ -5883,7 +5915,7 @@ pub const FuncGen = struct {
58835915 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
58845916 const payload_ty = self.air.typeOf(ty_op.operand);
58855917 const non_null_bit = self.context.intType(1).constAllOnes();
5886 comptime assert(optional_layout_version == 1);
5918 comptime assert(optional_layout_version == 2);
58875919 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) return non_null_bit;
58885920 const operand = try self.resolveInst(ty_op.operand);
58895921 const optional_ty = self.air.typeOfIndex(inst);
......@@ -9337,7 +9369,7 @@ fn intrinsicsAllowed(scalar_ty: Type, target: std.Target) bool {
93379369/// We can do this because for all types, Zig ABI alignment >= LLVM ABI
93389370/// alignment.
93399371const struct_layout_version = 2;
9340const optional_layout_version = 1;
9372const optional_layout_version = 2;
93419373
93429374/// We use the least significant bit of the pointer address to tell us
93439375/// 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 {
304304
305305 pub const isOpaqueStruct = LLVMIsOpaqueStruct;
306306 extern fn LLVMIsOpaqueStruct(StructTy: *const Type) Bool;
307
308 pub const isSized = LLVMTypeIsSized;
309 extern fn LLVMTypeIsSized(Ty: *const Type) Bool;
307310};
308311
309312pub const Module = opaque {
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.