authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-11-07 18:47:52+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-01-09 13:56:14-07:00
logc1fc15f9138610353bf53e0c10c27619c7dbab70
tree776248dd17b2f8e97b201f44f1b620f5f759f475
parenta0dd11c479a93a32cdaad4a5aca69c1021d6b195

llvm: implement packed unions

Closes #13340

2 files changed, 66 insertions(+), 4 deletions(-)

src/codegen/llvm.zig+44-4
...@@ -3013,6 +3013,13 @@ pub const DeclGen = struct {...@@ -3013,6 +3013,13 @@ pub const DeclGen = struct {
3013 const layout = t.unionGetLayout(target);3013 const layout = t.unionGetLayout(target);
3014 const union_obj = t.cast(Type.Payload.Union).?.data;3014 const union_obj = t.cast(Type.Payload.Union).?.data;
30153015
3016 if (union_obj.layout == .Packed) {
3017 const bitsize = @intCast(c_uint, t.bitSize(target));
3018 const int_llvm_ty = dg.context.intType(bitsize);
3019 gop.value_ptr.* = int_llvm_ty;
3020 return int_llvm_ty;
3021 }
3022
3016 if (layout.payload_size == 0) {3023 if (layout.payload_size == 0) {
3017 const enum_tag_llvm_ty = try dg.lowerType(union_obj.tag_ty);3024 const enum_tag_llvm_ty = try dg.lowerType(union_obj.tag_ty);
3018 gop.value_ptr.* = enum_tag_llvm_ty;3025 gop.value_ptr.* = enum_tag_llvm_ty;
...@@ -3762,13 +3769,23 @@ pub const DeclGen = struct {...@@ -3762,13 +3769,23 @@ pub const DeclGen = struct {
3762 const field_index = tv.ty.unionTagFieldIndex(tag_and_val.tag, dg.module).?;3769 const field_index = tv.ty.unionTagFieldIndex(tag_and_val.tag, dg.module).?;
3763 assert(union_obj.haveFieldTypes());3770 assert(union_obj.haveFieldTypes());
37643771
3772 const field_ty = union_obj.fields.values()[field_index].ty;
3773 if (union_obj.layout == .Packed) {
3774 const non_int_val = try lowerValue(dg, .{ .ty = field_ty, .val = tag_and_val.val });
3775 const ty_bit_size = @intCast(u16, field_ty.bitSize(target));
3776 const small_int_ty = dg.context.intType(ty_bit_size);
3777 const small_int_val = if (field_ty.isPtrAtRuntime())
3778 non_int_val.constPtrToInt(small_int_ty)
3779 else
3780 non_int_val.constBitCast(small_int_ty);
3781 return small_int_val.constZExtOrBitCast(llvm_union_ty);
3782 }
3783
3765 // Sometimes we must make an unnamed struct because LLVM does3784 // Sometimes we must make an unnamed struct because LLVM does
3766 // not support bitcasting our payload struct to the true union payload type.3785 // not support bitcasting our payload struct to the true union payload type.
3767 // Instead we use an unnamed struct and every reference to the global3786 // Instead we use an unnamed struct and every reference to the global
3768 // must pointer cast to the expected type before accessing the union.3787 // must pointer cast to the expected type before accessing the union.
3769 var need_unnamed: bool = layout.most_aligned_field != field_index;3788 var need_unnamed: bool = layout.most_aligned_field != field_index;
3770
3771 const field_ty = union_obj.fields.values()[field_index].ty;
3772 const payload = p: {3789 const payload = p: {
3773 if (!field_ty.hasRuntimeBitsIgnoreComptime()) {3790 if (!field_ty.hasRuntimeBitsIgnoreComptime()) {
3774 const padding_len = @intCast(c_uint, layout.payload_size);3791 const padding_len = @intCast(c_uint, layout.payload_size);
...@@ -3959,6 +3976,9 @@ pub const DeclGen = struct {...@@ -3959,6 +3976,9 @@ pub const DeclGen = struct {
3959 switch (parent_ty.zigTypeTag()) {3976 switch (parent_ty.zigTypeTag()) {
3960 .Union => {3977 .Union => {
3961 bitcast_needed = true;3978 bitcast_needed = true;
3979 if (parent_ty.containerLayout() == .Packed) {
3980 break :blk parent_llvm_ptr;
3981 }
39623982
3963 const layout = parent_ty.unionGetLayout(target);3983 const layout = parent_ty.unionGetLayout(target);
3964 if (layout.payload_size == 0) {3984 if (layout.payload_size == 0) {
...@@ -5768,7 +5788,21 @@ pub const FuncGen = struct {...@@ -5768,7 +5788,21 @@ pub const FuncGen = struct {
5768 },5788 },
5769 },5789 },
5770 .Union => {5790 .Union => {
5771 return self.todo("airStructFieldVal byval union", .{});5791 assert(struct_ty.containerLayout() == .Packed);
5792 const containing_int = struct_llvm_val;
5793 const elem_llvm_ty = try self.dg.lowerType(field_ty);
5794 if (field_ty.zigTypeTag() == .Float) {
5795 const elem_bits = @intCast(c_uint, field_ty.bitSize(target));
5796 const same_size_int = self.context.intType(elem_bits);
5797 const truncated_int = self.builder.buildTrunc(containing_int, same_size_int, "");
5798 return self.builder.buildBitCast(truncated_int, elem_llvm_ty, "");
5799 } else if (field_ty.isPtrAtRuntime()) {
5800 const elem_bits = @intCast(c_uint, field_ty.bitSize(target));
5801 const same_size_int = self.context.intType(elem_bits);
5802 const truncated_int = self.builder.buildTrunc(containing_int, same_size_int, "");
5803 return self.builder.buildIntToPtr(truncated_int, elem_llvm_ty, "");
5804 }
5805 return self.builder.buildTrunc(containing_int, elem_llvm_ty, "");
5772 },5806 },
5773 else => unreachable,5807 else => unreachable,
5774 }5808 }
...@@ -9510,6 +9544,9 @@ pub const FuncGen = struct {...@@ -9510,6 +9544,9 @@ pub const FuncGen = struct {
9510 if (layout.payload_size == 0) {9544 if (layout.payload_size == 0) {
9511 return self.builder.buildBitCast(union_ptr, result_llvm_ty, "");9545 return self.builder.buildBitCast(union_ptr, result_llvm_ty, "");
9512 }9546 }
9547 if (union_ty.containerLayout() == .Packed) {
9548 return self.builder.buildBitCast(union_ptr, result_llvm_ty, "");
9549 }
9513 const payload_index = @boolToInt(layout.tag_align >= layout.payload_align);9550 const payload_index = @boolToInt(layout.tag_align >= layout.payload_align);
9514 const union_llvm_ty = try self.dg.lowerType(union_ty);9551 const union_llvm_ty = try self.dg.lowerType(union_ty);
9515 const union_field_ptr = self.builder.buildStructGEP(union_llvm_ty, union_ptr, payload_index, "");9552 const union_field_ptr = self.builder.buildStructGEP(union_llvm_ty, union_ptr, payload_index, "");
...@@ -10667,7 +10704,10 @@ fn isByRef(ty: Type) bool {...@@ -10667,7 +10704,10 @@ fn isByRef(ty: Type) bool {
10667 }10704 }
10668 return false;10705 return false;
10669 },10706 },
10670 .Union => return ty.hasRuntimeBits(),10707 .Union => switch (ty.containerLayout()) {
10708 .Packed => return false,
10709 else => return ty.hasRuntimeBits(),
10710 },
10671 .ErrorUnion => {10711 .ErrorUnion => {
10672 const payload_ty = ty.errorUnionPayload();10712 const payload_ty = ty.errorUnionPayload();
10673 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {10713 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
test/behavior/union.zig+22
...@@ -1366,3 +1366,25 @@ test "union field ptr - zero sized field" {...@@ -1366,3 +1366,25 @@ test "union field ptr - zero sized field" {
1366 var u: U = .{ .foo = {} };1366 var u: U = .{ .foo = {} };
1367 U.bar(&u.foo);1367 U.bar(&u.foo);
1368}1368}
1369
1370test "packed union in packed struct" {
1371 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1372 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1373 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1374 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1375 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1376
1377 const S = packed struct {
1378 nested: packed union {
1379 val: usize,
1380 foo: u32,
1381 },
1382 bar: u32,
1383
1384 fn unpack(self: @This()) usize {
1385 return self.nested.foo;
1386 }
1387 };
1388 const a: S = .{ .nested = .{ .foo = 123 }, .bar = 5 };
1389 try expect(a.unpack() == 123);
1390}