zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit ea7e34224a8c3c0551e17a60fd849efee82447f7 (tree)
parent 0f3c883245f27870472978f738d6e81958e15c52
Author: Matthew Lugg <mlugg@mlugg.co.uk>
Date:   Wed,  4 Mar 2026 10:51:45 +0000

compiler: don't call `getUnionLayout` on packed unions

Diffstat:
Msrc/Type.zig | 4++++
Msrc/codegen/llvm.zig | 131++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msrc/codegen/spirv/CodeGen.zig | 72++++++++++++++++++++++++++----------------------------------------------
Msrc/link/Dwarf.zig | 133++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
4 files changed, 168 insertions(+), 172 deletions(-)

diff --git a/src/Type.zig b/src/Type.zig @@ -1571,6 +1571,7 @@ pub fn externUnionBackingType(ty: Type, pt: Zcu.PerThread) !Type { } } +/// Asserts that `ty` is a non-packed union type. pub fn unionGetLayout(ty: Type, zcu: *const Zcu) Zcu.UnionLayout { assertHasLayout(ty, zcu); const union_obj = zcu.intern_pool.loadUnionType(ty.toIntern()); @@ -2689,7 +2690,10 @@ pub fn arrayBase(ty: Type, zcu: *const Zcu) struct { Type, u64 } { return .{ cur_ty, cur_len }; } +/// Asserts that `loaded_union.layout` is not `.@"packed"`. pub fn getUnionLayout(loaded_union: InternPool.LoadedUnionType, zcu: *const Zcu) Zcu.UnionLayout { + assert(loaded_union.layout != .@"packed"); + const ip = &zcu.intern_pool; var most_aligned_field: u32 = 0; var most_aligned_field_align: InternPool.Alignment = .@"1"; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig @@ -2369,6 +2369,30 @@ pub const Object = struct { const line = ty.typeDeclSrcLine(zcu).? + 1; const enum_tag_ty: Type = .fromInterned(union_type.enum_tag_type); + + if (union_type.layout == .@"packed") { + const bitpack_field = try o.builder.debugMemberType( + try o.builder.metadataString("bits"), + null, // file + ty_fwd_ref, + 0, // line + try o.getDebugType(pt, .fromInterned(union_type.packed_backing_int_type)), + ty.abiSize(zcu) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, + 0, // offset + ); + return o.builder.debugStructType( + name, + file, + scope, + line, + null, // underlying type + ty.abiSize(zcu) * 8, + ty.abiAlignment(zcu).toByteUnits().? * 8, + try o.builder.metadataTuple(&.{bitpack_field}), + ); + } + const layout = Type.getUnionLayout(union_type, zcu); if (layout.payload_size == 0) { @@ -2411,10 +2435,7 @@ pub const Object = struct { const field_ty = union_type.field_types.get(ip)[field_index]; const field_size = Type.fromInterned(field_ty).abiSize(zcu); - const field_align: InternPool.Alignment = switch (union_type.layout) { - .@"packed" => .none, - .auto, .@"extern" => ty.explicitFieldAlignment(field_index, zcu), - }; + const field_align: InternPool.Alignment = ty.explicitFieldAlignment(field_index, zcu); const field_name = enum_tag_ty.enumFieldName(field_index, zcu); fields.appendAssumeCapacity(try o.builder.debugMemberType( @@ -3318,7 +3339,6 @@ pub const Object = struct { if (o.type_map.get(t.toIntern())) |value| return value; const union_obj = ip.loadUnionType(t.toIntern()); - const layout = Type.getUnionLayout(union_obj, zcu); if (union_obj.layout == .@"packed") { const int_ty = try o.lowerType(pt, .fromInterned(union_obj.packed_backing_int_type)); @@ -3326,6 +3346,8 @@ pub const Object = struct { return int_ty; } + const layout = Type.getUnionLayout(union_obj, zcu); + if (layout.payload_size == 0) { const enum_tag_ty = try o.lowerType(pt, .fromInterned(union_obj.enum_tag_type)); try o.type_map.put(o.gpa, t.toIntern(), enum_tag_ty); @@ -6760,7 +6782,7 @@ pub const FuncGen = struct { const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data; const struct_ptr = try self.resolveInst(struct_field.struct_operand); const struct_ptr_ty = self.typeOf(struct_field.struct_operand); - return self.fieldPtr(inst, struct_ptr, struct_ptr_ty, struct_field.field_index); + return self.fieldPtr(struct_ptr, struct_ptr_ty, struct_field.field_index); } fn airStructFieldPtrIndex( @@ -6771,7 +6793,7 @@ pub const FuncGen = struct { const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const struct_ptr = try self.resolveInst(ty_op.operand); const struct_ptr_ty = self.typeOf(ty_op.operand); - return self.fieldPtr(inst, struct_ptr, struct_ptr_ty, field_index); + return self.fieldPtr(struct_ptr, struct_ptr_ty, field_index); } fn airStructFieldVal(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { @@ -10704,18 +10726,11 @@ pub const FuncGen = struct { const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; const union_ty = self.typeOfIndex(inst); const union_llvm_ty = try o.lowerType(pt, union_ty); - const layout = union_ty.unionGetLayout(zcu); const union_obj = zcu.typeToUnion(union_ty).?; - if (union_obj.layout == .@"packed") { - const big_bits = union_ty.bitSize(zcu); - const int_llvm_ty = try o.builder.intType(@intCast(big_bits)); - const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[extra.field_index]); - const non_int_val = try self.resolveInst(extra.init); - const small_int_ty = try o.builder.intType(@intCast(field_ty.bitSize(zcu))); - const small_int_val = try self.wip.cast(.bitcast, non_int_val, small_int_ty, ""); - return self.wip.conv(.unsigned, small_int_val, int_llvm_ty, ""); - } + assert(union_obj.layout != .@"packed"); + + const layout = Type.getUnionLayout(union_obj, zcu); const tag_int_val = blk: { const tag_ty = union_ty.unionTagTypeHypothetical(zcu); @@ -11051,65 +11066,45 @@ pub const FuncGen = struct { fn fieldPtr( self: *FuncGen, - inst: Air.Inst.Index, - struct_ptr: Builder.Value, - struct_ptr_ty: Type, + aggregate_ptr: Builder.Value, + aggregate_ptr_ty: Type, field_index: u32, ) !Builder.Value { const o = self.ng.object; const pt = self.ng.pt; const zcu = pt.zcu; - const struct_ty = struct_ptr_ty.childType(zcu); - switch (struct_ty.zigTypeTag(zcu)) { - .@"struct" => switch (struct_ty.containerLayout(zcu)) { - .@"packed" => { - const result_ty = self.typeOfIndex(inst); - const result_ty_info = result_ty.ptrInfo(zcu); - const struct_ptr_ty_info = struct_ptr_ty.ptrInfo(zcu); - const struct_type = zcu.typeToStruct(struct_ty).?; - - if (result_ty_info.packed_offset.host_size != 0) { - // From LLVM's perspective, a pointer to a packed struct and a pointer - // to a field of a packed struct are the same. The difference is in the - // Zig pointer type which provides information for how to mask and shift - // out the relevant bits when accessing the pointee. - return struct_ptr; - } - - // We have a pointer to a packed struct field that happens to be byte-aligned. - // Offset our operand pointer by the correct number of bytes. - const byte_offset = @divExact(zcu.structPackedFieldBitOffset(struct_type, field_index) + struct_ptr_ty_info.packed_offset.bit_offset, 8); - if (byte_offset == 0) return struct_ptr; - const usize_ty = try o.lowerType(pt, Type.usize); - const llvm_index = try o.builder.intValue(usize_ty, byte_offset); - return self.wip.gep(.inbounds, .i8, struct_ptr, &.{llvm_index}, ""); - }, - else => { - if (!struct_ty.hasRuntimeBits(zcu)) { - return struct_ptr; - } - const struct_llvm_ty = try o.lowerType(pt, struct_ty); - if (o.llvmFieldIndex(struct_ty, field_index)) |llvm_field_index| { - return self.wip.gepStruct(struct_llvm_ty, struct_ptr, llvm_field_index, ""); - } else { - // If we found no index then this means this is a zero sized field at the - // end of the struct. Treat our struct pointer as an array of two and get - // the index to the element at index `1` to get a pointer to the end of - // the struct. - const llvm_index = try o.builder.intValue( - try o.lowerType(pt, Type.usize), - @intFromBool(struct_ty.hasRuntimeBits(zcu)), - ); - return self.wip.gep(.inbounds, struct_llvm_ty, struct_ptr, &.{llvm_index}, ""); - } - }, + const aggregate_ty = aggregate_ptr_ty.childType(zcu); + if (aggregate_ty.containerLayout(zcu) == .@"packed") { + // A pointer to a bitpack field is equivalent to a pointer to the whole bitpack; the + // bit offset is represented in the pointer *type*. + return aggregate_ptr; + } + switch (aggregate_ty.zigTypeTag(zcu)) { + .@"struct" => { + if (!aggregate_ty.hasRuntimeBits(zcu)) { + return aggregate_ptr; + } + const struct_llvm_ty = try o.lowerType(pt, aggregate_ty); + if (o.llvmFieldIndex(aggregate_ty, field_index)) |llvm_field_index| { + return self.wip.gepStruct(struct_llvm_ty, aggregate_ptr, llvm_field_index, ""); + } else { + // If we found no index then this means this is a zero sized field at the + // end of the struct. Treat our struct pointer as an array of two and get + // the index to the element at index `1` to get a pointer to the end of + // the struct. + const llvm_index = try o.builder.intValue( + try o.lowerType(pt, Type.usize), + @intFromBool(aggregate_ty.hasRuntimeBits(zcu)), + ); + return self.wip.gep(.inbounds, struct_llvm_ty, aggregate_ptr, &.{llvm_index}, ""); + } }, .@"union" => { - const layout = struct_ty.unionGetLayout(zcu); - if (layout.payload_size == 0 or struct_ty.containerLayout(zcu) == .@"packed") return struct_ptr; + const layout = aggregate_ty.unionGetLayout(zcu); + if (layout.payload_size == 0) return aggregate_ptr; const payload_index = @intFromBool(layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align)); - const union_llvm_ty = try o.lowerType(pt, struct_ty); - return self.wip.gepStruct(union_llvm_ty, struct_ptr, payload_index, ""); + const union_llvm_ty = try o.lowerType(pt, aggregate_ty); + return self.wip.gepStruct(union_llvm_ty, aggregate_ptr, payload_index, ""); }, else => unreachable, } diff --git a/src/codegen/spirv/CodeGen.zig b/src/codegen/spirv/CodeGen.zig @@ -4519,30 +4519,7 @@ fn unionInit( const layout = cg.unionLayout(ty); const payload_ty: Type = .fromInterned(union_ty.field_types.get(ip)[active_field]); - if (union_ty.layout == .@"packed") { - if (!payload_ty.hasRuntimeBits(zcu)) { - const int_ty = try pt.intType(.unsigned, @intCast(ty.bitSize(zcu))); - return cg.constInt(int_ty, 0); - } - - assert(payload != null); - if (payload_ty.isInt(zcu)) { - if (ty.bitSize(zcu) == payload_ty.bitSize(zcu)) { - return cg.bitCast(ty, payload_ty, payload.?); - } - - const trunc = try cg.buildConvert(ty, .{ .ty = payload_ty, .value = .{ .singleton = payload.? } }); - return try trunc.materialize(cg); - } - - const payload_int_ty = try pt.intType(.unsigned, @intCast(payload_ty.bitSize(zcu))); - const payload_int = if (payload_ty.ip_index == .bool_type) - try cg.convertToIndirect(payload_ty, payload.?) - else - try cg.bitCast(payload_int_ty, payload_ty, payload.?); - const trunc = try cg.buildConvert(ty, .{ .ty = payload_int_ty, .value = .{ .singleton = payload_int } }); - return try trunc.materialize(cg); - } + assert(union_ty.layout != .@"packed"); const tag_int = if (layout.tag_size != 0) blk: { const tag_val = try pt.enumValueFieldIndex(tag_ty, active_field); @@ -4761,33 +4738,36 @@ fn structFieldPtr( }, .@"struct" => switch (object_ty.containerLayout(zcu)) { .@"packed" => return cg.todo("implement field access for packed structs", .{}), - else => { + .auto, .@"extern" => { return try cg.accessChain(result_ty_id, object_ptr, &.{field_index}); }, }, - .@"union" => { - const layout = cg.unionLayout(object_ty); - if (!layout.has_payload) { - // Asked to get a pointer to a zero-sized field. Just lower this - // to undefined, there is no reason to make it be a valid pointer. - return try cg.module.constUndef(result_ty_id); - } + .@"union" => switch (object_ty.containerLayout(zcu)) { + .@"packed" => return cg.todo("implement field access for packed unions", .{}), + .auto, .@"extern" => { + const layout = cg.unionLayout(object_ty); + if (!layout.has_payload) { + // Asked to get a pointer to a zero-sized field. Just lower this + // to undefined, there is no reason to make it be a valid pointer. + return try cg.module.constUndef(result_ty_id); + } - const storage_class = cg.module.storageClass(object_ptr_ty.ptrAddressSpace(zcu)); - const layout_payload_ty_id = try cg.resolveType(layout.payload_ty, .indirect); - const pl_ptr_ty_id = try cg.module.ptrType(layout_payload_ty_id, storage_class); - const pl_ptr_id = blk: { - if (object_ty.containerLayout(zcu) == .@"packed") break :blk object_ptr; - break :blk try cg.accessChain(pl_ptr_ty_id, object_ptr, &.{layout.payload_index}); - }; + const storage_class = cg.module.storageClass(object_ptr_ty.ptrAddressSpace(zcu)); + const layout_payload_ty_id = try cg.resolveType(layout.payload_ty, .indirect); + const pl_ptr_ty_id = try cg.module.ptrType(layout_payload_ty_id, storage_class); + const pl_ptr_id = blk: { + if (object_ty.containerLayout(zcu) == .@"packed") break :blk object_ptr; + break :blk try cg.accessChain(pl_ptr_ty_id, object_ptr, &.{layout.payload_index}); + }; - const active_pl_ptr_id = cg.module.allocId(); - try cg.body.emit(cg.module.gpa, .OpBitcast, .{ - .id_result_type = result_ty_id, - .id_result = active_pl_ptr_id, - .operand = pl_ptr_id, - }); - return active_pl_ptr_id; + const active_pl_ptr_id = cg.module.allocId(); + try cg.body.emit(cg.module.gpa, .OpBitcast, .{ + .id_result_type = result_ty_id, + .id_result = active_pl_ptr_id, + .operand = pl_ptr_id, + }); + return active_pl_ptr_id; + }, }, else => unreachable, } diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig @@ -4009,69 +4009,86 @@ fn updateConstInner(dwarf: *Dwarf, pt: Zcu.PerThread, debug_const_index: link.Co .union_type => { const loaded_union = ip.loadUnionType(value_index); const file = loaded_union.zir_index.resolveFile(ip); - const need_terminator: bool = if (loaded_union.name_nav.unwrap()) |nav_index| t: { - const nav = ip.getNav(nav_index); - const decl_inst = nav.srcInst(ip).resolve(ip).?; - const decl = zcu.fileByIndex(file).zir.?.getDeclaration(decl_inst); - try wip_nav.declCommon(.{ - .decl = .decl_union, - .generic_decl = .generic_decl_const, - .decl_instance = .decl_instance_union, - }, &nav, file, &decl); - break :t true; - } else t: { - const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, file); - try wip_nav.abbrevCode(if (loaded_union.field_types.len > 0) .union_type else .empty_union_type); - try diw.writeUleb128(file_gop.index); - try wip_nav.strp(loaded_union.name.toSlice(ip)); - break :t loaded_union.field_types.len > 0; - }; - const union_layout = Type.getUnionLayout(loaded_union, zcu); - try diw.writeUleb128(union_layout.abi_size); - try diw.writeUleb128(union_layout.abi_align.toByteUnits().?); - const loaded_tag = ip.loadEnumType(loaded_union.enum_tag_type); - if (loaded_union.has_runtime_tag) { - try wip_nav.abbrevCode(.tagged_union); - try wip_nav.infoSectionOffset( - .debug_info, - wip_nav.unit, - wip_nav.entry, - @intCast(diw.end + dwarf.sectionOffsetBytes()), - ); - { - try wip_nav.abbrevCode(.generated_field); - try wip_nav.strp("tag"); - try wip_nav.refType(.fromInterned(loaded_union.enum_tag_type)); - try diw.writeUleb128(union_layout.tagOffset()); - - for (0..loaded_union.field_types.len) |field_index| { - try wip_nav.enumConstValue(loaded_tag, .{ - .sdata = .signed_tagged_union_field, - .udata = .unsigned_tagged_union_field, - .block = .big_tagged_union_field, - }, field_index); + switch (loaded_union.layout) { + .auto, .@"extern" => { + const need_terminator: bool = if (loaded_union.name_nav.unwrap()) |nav_index| t: { + const nav = ip.getNav(nav_index); + const decl_inst = nav.srcInst(ip).resolve(ip).?; + const decl = zcu.fileByIndex(file).zir.?.getDeclaration(decl_inst); + try wip_nav.declCommon(.{ + .decl = .decl_union, + .generic_decl = .generic_decl_const, + .decl_instance = .decl_instance_union, + }, &nav, file, &decl); + break :t true; + } else t: { + const file_gop = try dwarf.getModInfo(unit).files.getOrPut(dwarf.gpa, file); + try wip_nav.abbrevCode(if (loaded_union.field_types.len > 0) .union_type else .empty_union_type); + try diw.writeUleb128(file_gop.index); + try wip_nav.strp(loaded_union.name.toSlice(ip)); + break :t loaded_union.field_types.len > 0; + }; + const union_layout = Type.getUnionLayout(loaded_union, zcu); + try diw.writeUleb128(union_layout.abi_size); + try diw.writeUleb128(union_layout.abi_align.toByteUnits().?); + const loaded_tag = ip.loadEnumType(loaded_union.enum_tag_type); + if (loaded_union.has_runtime_tag) { + try wip_nav.abbrevCode(.tagged_union); + try wip_nav.infoSectionOffset( + .debug_info, + wip_nav.unit, + wip_nav.entry, + @intCast(diw.end + dwarf.sectionOffsetBytes()), + ); { - try wip_nav.abbrevCode(.struct_field); - try wip_nav.strp(loaded_tag.field_names.get(ip)[field_index].toSlice(ip)); - const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]); - try wip_nav.refType(field_type); - try diw.writeUleb128(union_layout.payloadOffset()); - try diw.writeUleb128(loaded_union.field_aligns.getOrNone(ip, field_index).toByteUnits() orelse - if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?); + try wip_nav.abbrevCode(.generated_field); + try wip_nav.strp("tag"); + try wip_nav.refType(.fromInterned(loaded_union.enum_tag_type)); + try diw.writeUleb128(union_layout.tagOffset()); + + for (0..loaded_union.field_types.len) |field_index| { + try wip_nav.enumConstValue(loaded_tag, .{ + .sdata = .signed_tagged_union_field, + .udata = .unsigned_tagged_union_field, + .block = .big_tagged_union_field, + }, field_index); + { + try wip_nav.abbrevCode(.struct_field); + try wip_nav.strp(loaded_tag.field_names.get(ip)[field_index].toSlice(ip)); + const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]); + try wip_nav.refType(field_type); + try diw.writeUleb128(union_layout.payloadOffset()); + try diw.writeUleb128(loaded_union.field_aligns.getOrNone(ip, field_index).toByteUnits() orelse + if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?); + } + try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); + } } try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); + } else for (0..loaded_union.field_types.len) |field_index| { + try wip_nav.abbrevCode(.untagged_union_field); + try wip_nav.strp(loaded_tag.field_names.get(ip)[field_index].toSlice(ip)); + const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]); + try wip_nav.refType(field_type); + try diw.writeUleb128(loaded_union.field_aligns.getOrNone(ip, field_index).toByteUnits() orelse + if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?); } - } - try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); - } else for (0..loaded_union.field_types.len) |field_index| { - try wip_nav.abbrevCode(.untagged_union_field); - try wip_nav.strp(loaded_tag.field_names.get(ip)[field_index].toSlice(ip)); - const field_type: Type = .fromInterned(loaded_union.field_types.get(ip)[field_index]); - try wip_nav.refType(field_type); - try diw.writeUleb128(loaded_union.field_aligns.getOrNone(ip, field_index).toByteUnits() orelse - if (field_type.isNoReturn(zcu)) 1 else field_type.abiAlignment(zcu).toByteUnits().?); + if (need_terminator) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); + }, + .@"packed" => { + // TODO: debug info for packed unions + try wip_nav.abbrevCode(.numeric_type); + try wip_nav.strp(loaded_union.name.toSlice(ip)); + const backing_int_ty: Type = .fromInterned(loaded_union.packed_backing_int_type); + const int_info = backing_int_ty.intInfo(zcu); + try diw.writeByte(switch (int_info.signedness) { + inline .signed, .unsigned => |signedness| @field(DW.ATE, @tagName(signedness)), + }); + try diw.writeUleb128(int_info.bits); + try diw.writeUleb128(backing_int_ty.abiSize(zcu)); + try diw.writeUleb128(backing_int_ty.abiAlignment(zcu).toByteUnits().?); + }, } - if (need_terminator) try diw.writeUleb128(@intFromEnum(AbbrevCode.null)); }, .enum_type => { const loaded_enum = ip.loadEnumType(value_index);