From 61842da9f7d7fdbfdc76b135335a685da1de8789 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 7 Nov 2022 18:47:52 +0200 Subject: [PATCH] llvm: implement packed unions Closes #13340 --- src/codegen/llvm.zig | 48 +++++++++++++++++++++++++++++++++++++---- test/behavior/union.zig | 22 +++++++++++++++++++ 2 files changed, 66 insertions(+), 4 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index a37098f04b..5331862a14 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -3013,6 +3013,13 @@ pub const DeclGen = struct { const layout = t.unionGetLayout(target); const union_obj = t.cast(Type.Payload.Union).?.data; + if (union_obj.layout == .Packed) { + const bitsize = @intCast(c_uint, t.bitSize(target)); + const int_llvm_ty = dg.context.intType(bitsize); + gop.value_ptr.* = int_llvm_ty; + return int_llvm_ty; + } + if (layout.payload_size == 0) { const enum_tag_llvm_ty = try dg.lowerType(union_obj.tag_ty); gop.value_ptr.* = enum_tag_llvm_ty; @@ -3762,13 +3769,23 @@ pub const DeclGen = struct { const field_index = tv.ty.unionTagFieldIndex(tag_and_val.tag, dg.module).?; assert(union_obj.haveFieldTypes()); + const field_ty = union_obj.fields.values()[field_index].ty; + if (union_obj.layout == .Packed) { + const non_int_val = try lowerValue(dg, .{ .ty = field_ty, .val = tag_and_val.val }); + const ty_bit_size = @intCast(u16, field_ty.bitSize(target)); + const small_int_ty = dg.context.intType(ty_bit_size); + const small_int_val = if (field_ty.isPtrAtRuntime()) + non_int_val.constPtrToInt(small_int_ty) + else + non_int_val.constBitCast(small_int_ty); + return small_int_val.constZExtOrBitCast(llvm_union_ty); + } + // Sometimes we must make an unnamed struct because LLVM does // not support bitcasting our payload struct to the true union payload type. // Instead we use an unnamed struct and every reference to the global // must pointer cast to the expected type before accessing the union. var need_unnamed: bool = layout.most_aligned_field != field_index; - - const field_ty = union_obj.fields.values()[field_index].ty; const payload = p: { if (!field_ty.hasRuntimeBitsIgnoreComptime()) { const padding_len = @intCast(c_uint, layout.payload_size); @@ -3959,6 +3976,9 @@ pub const DeclGen = struct { switch (parent_ty.zigTypeTag()) { .Union => { bitcast_needed = true; + if (parent_ty.containerLayout() == .Packed) { + break :blk parent_llvm_ptr; + } const layout = parent_ty.unionGetLayout(target); if (layout.payload_size == 0) { @@ -5768,7 +5788,21 @@ pub const FuncGen = struct { }, }, .Union => { - return self.todo("airStructFieldVal byval union", .{}); + assert(struct_ty.containerLayout() == .Packed); + const containing_int = struct_llvm_val; + const elem_llvm_ty = try self.dg.lowerType(field_ty); + if (field_ty.zigTypeTag() == .Float) { + const elem_bits = @intCast(c_uint, field_ty.bitSize(target)); + const same_size_int = self.context.intType(elem_bits); + const truncated_int = self.builder.buildTrunc(containing_int, same_size_int, ""); + return self.builder.buildBitCast(truncated_int, elem_llvm_ty, ""); + } else if (field_ty.isPtrAtRuntime()) { + const elem_bits = @intCast(c_uint, field_ty.bitSize(target)); + const same_size_int = self.context.intType(elem_bits); + const truncated_int = self.builder.buildTrunc(containing_int, same_size_int, ""); + return self.builder.buildIntToPtr(truncated_int, elem_llvm_ty, ""); + } + return self.builder.buildTrunc(containing_int, elem_llvm_ty, ""); }, else => unreachable, } @@ -9510,6 +9544,9 @@ pub const FuncGen = struct { if (layout.payload_size == 0) { return self.builder.buildBitCast(union_ptr, result_llvm_ty, ""); } + if (union_ty.containerLayout() == .Packed) { + return self.builder.buildBitCast(union_ptr, result_llvm_ty, ""); + } const payload_index = @boolToInt(layout.tag_align >= layout.payload_align); const union_llvm_ty = try self.dg.lowerType(union_ty); const union_field_ptr = self.builder.buildStructGEP(union_llvm_ty, union_ptr, payload_index, ""); @@ -10667,7 +10704,10 @@ fn isByRef(ty: Type) bool { } return false; }, - .Union => return ty.hasRuntimeBits(), + .Union => switch (ty.containerLayout()) { + .Packed => return false, + else => return ty.hasRuntimeBits(), + }, .ErrorUnion => { const payload_ty = ty.errorUnionPayload(); if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 2540d780a6..d473debe1f 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1366,3 +1366,25 @@ test "union field ptr - zero sized field" { var u: U = .{ .foo = {} }; U.bar(&u.foo); } + +test "packed union in packed struct" { + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO + + const S = packed struct { + nested: packed union { + val: usize, + foo: u32, + }, + bar: u32, + + fn unpack(self: @This()) usize { + return self.nested.foo; + } + }; + const a: S = .{ .nested = .{ .foo = 123 }, .bar = 5 }; + try expect(a.unpack() == 123); +}