Commit 61842da9f7

Veikka Tuominen <git@vexu.eu>
2022-11-07 17:47:52
llvm: implement packed unions
Closes #13340
1 parent 95f989a
Changed files (2)
src
codegen
test
behavior
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()) {
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);
+}