authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-11-07 18:47:52+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-11-09 17:14:38+02:00
log61842da9f7d7fdbfdc76b135335a685da1de8789
tree9ecd02b3a86682704d152def3720232b6a697b53
parent95f989a05b4da66790c03f68c99b9fe684fff1ab

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 {
30133013 const layout = t.unionGetLayout(target);
30143014 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
30163023 if (layout.payload_size == 0) {
30173024 const enum_tag_llvm_ty = try dg.lowerType(union_obj.tag_ty);
30183025 gop.value_ptr.* = enum_tag_llvm_ty;
......@@ -3762,13 +3769,23 @@ pub const DeclGen = struct {
37623769 const field_index = tv.ty.unionTagFieldIndex(tag_and_val.tag, dg.module).?;
37633770 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
37653784 // Sometimes we must make an unnamed struct because LLVM does
37663785 // not support bitcasting our payload struct to the true union payload type.
37673786 // Instead we use an unnamed struct and every reference to the global
37683787 // must pointer cast to the expected type before accessing the union.
37693788 var need_unnamed: bool = layout.most_aligned_field != field_index;
3770
3771 const field_ty = union_obj.fields.values()[field_index].ty;
37723789 const payload = p: {
37733790 if (!field_ty.hasRuntimeBitsIgnoreComptime()) {
37743791 const padding_len = @intCast(c_uint, layout.payload_size);
......@@ -3959,6 +3976,9 @@ pub const DeclGen = struct {
39593976 switch (parent_ty.zigTypeTag()) {
39603977 .Union => {
39613978 bitcast_needed = true;
3979 if (parent_ty.containerLayout() == .Packed) {
3980 break :blk parent_llvm_ptr;
3981 }
39623982
39633983 const layout = parent_ty.unionGetLayout(target);
39643984 if (layout.payload_size == 0) {
......@@ -5768,7 +5788,21 @@ pub const FuncGen = struct {
57685788 },
57695789 },
57705790 .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, "");
57725806 },
57735807 else => unreachable,
57745808 }
......@@ -9510,6 +9544,9 @@ pub const FuncGen = struct {
95109544 if (layout.payload_size == 0) {
95119545 return self.builder.buildBitCast(union_ptr, result_llvm_ty, "");
95129546 }
9547 if (union_ty.containerLayout() == .Packed) {
9548 return self.builder.buildBitCast(union_ptr, result_llvm_ty, "");
9549 }
95139550 const payload_index = @boolToInt(layout.tag_align >= layout.payload_align);
95149551 const union_llvm_ty = try self.dg.lowerType(union_ty);
95159552 const union_field_ptr = self.builder.buildStructGEP(union_llvm_ty, union_ptr, payload_index, "");
......@@ -10667,7 +10704,10 @@ fn isByRef(ty: Type) bool {
1066710704 }
1066810705 return false;
1066910706 },
10670 .Union => return ty.hasRuntimeBits(),
10707 .Union => switch (ty.containerLayout()) {
10708 .Packed => return false,
10709 else => return ty.hasRuntimeBits(),
10710 },
1067110711 .ErrorUnion => {
1067210712 const payload_ty = ty.errorUnionPayload();
1067310713 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
test/behavior/union.zig+22
......@@ -1366,3 +1366,25 @@ test "union field ptr - zero sized field" {
13661366 var u: U = .{ .foo = {} };
13671367 U.bar(&u.foo);
13681368}
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}