authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-28 17:42:59+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-02-28 17:42:59+01:00
log331cc810ded2058f2e2767b0485eb18d888a45e5
tree9e5ba37e5b06eaccf5ff36976955a32cda5ddfc4
parent90059a12e0ffe433132450f9f43221a198a22106
parent16f9774d2d6f358c97637e35609dfe0fc14cb501
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11012 from ziglang/x64-union-tag

stage2,x64: basic (un)tagged unions

4 files changed, 217 insertions(+), 76 deletions(-)

src/arch/x86_64/CodeGen.zig+96-46
...@@ -2098,17 +2098,72 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2098,17 +2098,72 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
20982098
2099fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {2099fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
2100 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2100 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2101 _ = bin_op;2101 const ptr_ty = self.air.typeOf(bin_op.lhs);
2102 return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch});2102 const union_ty = ptr_ty.childType();
2103 const tag_ty = self.air.typeOf(bin_op.rhs);
2104 const layout = union_ty.unionGetLayout(self.target.*);
2105
2106 if (layout.tag_size == 0) {
2107 return self.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none });
2108 }
2109
2110 const ptr = try self.resolveInst(bin_op.lhs);
2111 ptr.freezeIfRegister(&self.register_manager);
2112 defer ptr.unfreezeIfRegister(&self.register_manager);
2113
2114 const tag = try self.resolveInst(bin_op.rhs);
2115 tag.freezeIfRegister(&self.register_manager);
2116 defer tag.unfreezeIfRegister(&self.register_manager);
2117
2118 const adjusted_ptr: MCValue = if (layout.payload_size > 0 and layout.tag_align < layout.payload_align) blk: {
2119 // TODO reusing the operand
2120 const reg = try self.copyToTmpRegister(ptr_ty, ptr);
2121 try self.genBinMathOpMir(.add, ptr_ty, .{ .register = reg }, .{ .immediate = layout.payload_size });
2122 break :blk MCValue{ .register = reg };
2123 } else ptr;
2124
2125 try self.store(adjusted_ptr, tag, ptr_ty, tag_ty);
2126
2127 return self.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none });
2103}2128}
21042129
2105fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {2130fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
2106 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2131 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2107 const result: MCValue = if (self.liveness.isUnused(inst))2132 if (self.liveness.isUnused(inst)) {
2108 .dead2133 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
2109 else2134 }
2110 return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch});2135
2111 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });2136 const tag_ty = self.air.typeOfIndex(inst);
2137 const union_ty = self.air.typeOf(ty_op.operand);
2138 const layout = union_ty.unionGetLayout(self.target.*);
2139
2140 if (layout.tag_size == 0) {
2141 return self.finishAir(inst, .none, .{ ty_op.operand, .none, .none });
2142 }
2143
2144 // TODO reusing the operand
2145 const operand = try self.resolveInst(ty_op.operand);
2146 operand.freezeIfRegister(&self.register_manager);
2147 defer operand.unfreezeIfRegister(&self.register_manager);
2148
2149 const tag_abi_size = tag_ty.abiSize(self.target.*);
2150 const offset: i32 = if (layout.tag_align < layout.payload_align) @intCast(i32, layout.payload_size) else 0;
2151 const dst_mcv: MCValue = blk: {
2152 switch (operand) {
2153 .stack_offset => |off| {
2154 if (tag_abi_size <= 8) {
2155 break :blk try self.copyToRegisterWithInstTracking(inst, tag_ty, .{
2156 .stack_offset = off - offset,
2157 });
2158 }
2159
2160 return self.fail("TODO implement get_union_tag for ABI larger than 8 bytes and operand {}", .{operand});
2161 },
2162 else => return self.fail("TODO implement get_union_tag for {}", .{operand}),
2163 }
2164 };
2165
2166 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
2112}2167}
21132168
2114fn airClz(self: *Self, inst: Air.Inst.Index) !void {2169fn airClz(self: *Self, inst: Air.Inst.Index) !void {
...@@ -2429,8 +2484,15 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -2429,8 +2484,15 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
2429 });2484 });
2430 },2485 },
2431 .stack_offset => {2486 .stack_offset => {
2432 const tmp_reg = try self.copyToTmpRegister(value_ty, value);2487 if (abi_size <= 8) {
2433 return self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);2488 const tmp_reg = try self.copyToTmpRegister(value_ty, value);
2489 return self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);
2490 }
2491
2492 try self.genInlineMemcpy(0, value_ty, value, .{
2493 .source_stack_base = .rbp,
2494 .dest_stack_base = reg.to64(),
2495 });
2434 },2496 },
2435 else => |other| {2497 else => |other| {
2436 return self.fail("TODO implement set pointee with {}", .{other});2498 return self.fail("TODO implement set pointee with {}", .{other});
...@@ -3905,36 +3967,22 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u...@@ -3905,36 +3967,22 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u
3905 .dead, .unreach => unreachable,3967 .dead, .unreach => unreachable,
3906 .immediate => |imm| {3968 .immediate => |imm| {
3907 _ = try self.addInst(.{3969 _ = try self.addInst(.{
3908 .tag = .@"test",3970 .tag = .xor,
3909 .ops = (Mir.Ops{3971 .ops = (Mir.Ops{
3910 .reg1 = registerAlias(cond_reg, abi_size),3972 .reg1 = registerAlias(cond_reg, abi_size),
3911 }).encode(),3973 }).encode(),
3912 .data = .{ .imm = @intCast(u32, imm) },3974 .data = .{ .imm = @intCast(u32, imm) },
3913 });3975 });
3914 return self.addInst(.{
3915 .tag = .cond_jmp_eq_ne,
3916 .ops = (Mir.Ops{
3917 .flags = 0b00,
3918 }).encode(),
3919 .data = .{ .inst = undefined },
3920 });
3921 },3976 },
3922 .register => |reg| {3977 .register => |reg| {
3923 _ = try self.addInst(.{3978 _ = try self.addInst(.{
3924 .tag = .@"test",3979 .tag = .xor,
3925 .ops = (Mir.Ops{3980 .ops = (Mir.Ops{
3926 .reg1 = registerAlias(cond_reg, abi_size),3981 .reg1 = registerAlias(cond_reg, abi_size),
3927 .reg2 = registerAlias(reg, abi_size),3982 .reg2 = registerAlias(reg, abi_size),
3928 }).encode(),3983 }).encode(),
3929 .data = undefined,3984 .data = undefined,
3930 });3985 });
3931 return self.addInst(.{
3932 .tag = .cond_jmp_eq_ne,
3933 .ops = (Mir.Ops{
3934 .flags = 0b00,
3935 }).encode(),
3936 .data = .{ .inst = undefined },
3937 });
3938 },3986 },
3939 .stack_offset => {3987 .stack_offset => {
3940 if (abi_size <= 8) {3988 if (abi_size <= 8) {
...@@ -3948,6 +3996,22 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u...@@ -3948,6 +3996,22 @@ fn genCondSwitchMir(self: *Self, ty: Type, condition: MCValue, case: MCValue) !u
3948 return self.fail("TODO implement switch mir when case is {}", .{case});3996 return self.fail("TODO implement switch mir when case is {}", .{case});
3949 },3997 },
3950 }3998 }
3999
4000 _ = try self.addInst(.{
4001 .tag = .@"test",
4002 .ops = (Mir.Ops{
4003 .reg1 = registerAlias(cond_reg, abi_size),
4004 .reg2 = registerAlias(cond_reg, abi_size),
4005 }).encode(),
4006 .data = undefined,
4007 });
4008 return self.addInst(.{
4009 .tag = .cond_jmp_eq_ne,
4010 .ops = (Mir.Ops{
4011 .flags = 0b00,
4012 }).encode(),
4013 .data = .{ .inst = undefined },
4014 });
3951 },4015 },
3952 .stack_offset => {4016 .stack_offset => {
3953 try self.spillCompareFlagsIfOccupied();4017 try self.spillCompareFlagsIfOccupied();
...@@ -5408,24 +5472,14 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {...@@ -5408,24 +5472,14 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
5408 }5472 }
54095473
5410 switch (typed_value.ty.zigTypeTag()) {5474 switch (typed_value.ty.zigTypeTag()) {
5411 .Array => {
5412 return self.lowerUnnamedConst(typed_value);
5413 },
5414 .Pointer => switch (typed_value.ty.ptrSize()) {5475 .Pointer => switch (typed_value.ty.ptrSize()) {
5415 .Slice => {5476 .Slice => {},
5416 return self.lowerUnnamedConst(typed_value);
5417 },
5418 else => {5477 else => {
5419 switch (typed_value.val.tag()) {5478 switch (typed_value.val.tag()) {
5420 .int_u64 => {5479 .int_u64 => {
5421 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };5480 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
5422 },5481 },
5423 .slice => {5482 else => {},
5424 return self.lowerUnnamedConst(typed_value);
5425 },
5426 else => {
5427 return self.fail("TODO codegen more kinds of const pointers: {}", .{typed_value.val.tag()});
5428 },
5429 }5483 }
5430 },5484 },
5431 },5485 },
...@@ -5434,10 +5488,9 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {...@@ -5434,10 +5488,9 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
5434 if (info.bits <= ptr_bits and info.signedness == .signed) {5488 if (info.bits <= ptr_bits and info.signedness == .signed) {
5435 return MCValue{ .immediate = @bitCast(u64, typed_value.val.toSignedInt()) };5489 return MCValue{ .immediate = @bitCast(u64, typed_value.val.toSignedInt()) };
5436 }5490 }
5437 if (info.bits > ptr_bits or info.signedness == .signed) {5491 if (!(info.bits > ptr_bits or info.signedness == .signed)) {
5438 return self.fail("TODO const int bigger than ptr and signed int", .{});5492 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
5439 }5493 }
5440 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
5441 },5494 },
5442 .Bool => {5495 .Bool => {
5443 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };5496 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
...@@ -5457,7 +5510,6 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {...@@ -5457,7 +5510,6 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
5457 } else if (typed_value.ty.abiSize(self.target.*) == 1) {5510 } else if (typed_value.ty.abiSize(self.target.*) == 1) {
5458 return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };5511 return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };
5459 }5512 }
5460 return self.fail("TODO non pointer optionals", .{});
5461 },5513 },
5462 .Enum => {5514 .Enum => {
5463 if (typed_value.val.castTag(.enum_field_index)) |field_index| {5515 if (typed_value.val.castTag(.enum_field_index)) |field_index| {
...@@ -5504,13 +5556,11 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {...@@ -5504,13 +5556,11 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
5504 return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val });5556 return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val });
5505 }5557 }
5506 }5558 }
5507 return self.lowerUnnamedConst(typed_value);
5508 },
5509 .Struct => {
5510 return self.lowerUnnamedConst(typed_value);
5511 },5559 },
5512 else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}),5560 else => {},
5513 }5561 }
5562
5563 return self.lowerUnnamedConst(typed_value);
5514}5564}
55155565
5516const CallMCValues = struct {5566const CallMCValues = struct {
src/arch/x86_64/Emit.zig+6
...@@ -1859,6 +1859,9 @@ fn lowerToRmEnc(...@@ -1859,6 +1859,9 @@ fn lowerToRmEnc(
1859 switch (reg_or_mem) {1859 switch (reg_or_mem) {
1860 .register => |src_reg| {1860 .register => |src_reg| {
1861 const encoder = try Encoder.init(code, 4);1861 const encoder = try Encoder.init(code, 4);
1862 if (reg.size() == 16) {
1863 encoder.prefix16BitMode();
1864 }
1862 encoder.rex(.{1865 encoder.rex(.{
1863 .w = setRexWRegister(reg) or setRexWRegister(src_reg),1866 .w = setRexWRegister(reg) or setRexWRegister(src_reg),
1864 .r = reg.isExtended(),1867 .r = reg.isExtended(),
...@@ -1902,6 +1905,9 @@ fn lowerToMrEnc(...@@ -1902,6 +1905,9 @@ fn lowerToMrEnc(
1902 switch (reg_or_mem) {1905 switch (reg_or_mem) {
1903 .register => |dst_reg| {1906 .register => |dst_reg| {
1904 const encoder = try Encoder.init(code, 3);1907 const encoder = try Encoder.init(code, 3);
1908 if (dst_reg.size() == 16) {
1909 encoder.prefix16BitMode();
1910 }
1905 encoder.rex(.{1911 encoder.rex(.{
1906 .w = setRexWRegister(dst_reg) or setRexWRegister(reg),1912 .w = setRexWRegister(dst_reg) or setRexWRegister(reg),
1907 .r = reg.isExtended(),1913 .r = reg.isExtended(),
src/codegen.zig+67-3
...@@ -466,10 +466,74 @@ pub fn generateSymbol(...@@ -466,10 +466,74 @@ pub fn generateSymbol(
466 return Result{ .appended = {} };466 return Result{ .appended = {} };
467 },467 },
468 .Union => {468 .Union => {
469 // TODO generateSymbol for unions469 // TODO generate debug info for unions
470 const target = bin_file.options.target;470 const target = bin_file.options.target;
471 const abi_size = try math.cast(usize, typed_value.ty.abiSize(target));471 const union_obj = typed_value.val.castTag(.@"union").?.data;
472 try code.writer().writeByteNTimes(0xaa, abi_size);472 const layout = typed_value.ty.unionGetLayout(target);
473
474 if (layout.payload_size == 0) {
475 switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
476 .ty = typed_value.ty.unionTagType().?,
477 .val = union_obj.tag,
478 }, code, debug_output)) {
479 .appended => {},
480 .externally_managed => |external_slice| {
481 code.appendSliceAssumeCapacity(external_slice);
482 },
483 .fail => |em| return Result{ .fail = em },
484 }
485 }
486
487 // Check if we should store the tag first.
488 if (layout.tag_align >= layout.payload_align) {
489 switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
490 .ty = typed_value.ty.unionTagType().?,
491 .val = union_obj.tag,
492 }, code, debug_output)) {
493 .appended => {},
494 .externally_managed => |external_slice| {
495 code.appendSliceAssumeCapacity(external_slice);
496 },
497 .fail => |em| return Result{ .fail = em },
498 }
499 }
500
501 const union_ty = typed_value.ty.cast(Type.Payload.Union).?.data;
502 const field_index = union_ty.tag_ty.enumTagFieldIndex(union_obj.tag).?;
503 assert(union_ty.haveFieldTypes());
504 const field_ty = union_ty.fields.values()[field_index].ty;
505 if (!field_ty.hasRuntimeBits()) {
506 try code.writer().writeByteNTimes(0xaa, try math.cast(usize, layout.payload_size));
507 } else {
508 switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
509 .ty = field_ty,
510 .val = union_obj.val,
511 }, code, debug_output)) {
512 .appended => {},
513 .externally_managed => |external_slice| {
514 code.appendSliceAssumeCapacity(external_slice);
515 },
516 .fail => |em| return Result{ .fail = em },
517 }
518
519 const padding = try math.cast(usize, layout.payload_size - field_ty.abiSize(target));
520 if (padding > 0) {
521 try code.writer().writeByteNTimes(0, padding);
522 }
523 }
524
525 if (layout.tag_size > 0) {
526 switch (try generateSymbol(bin_file, parent_atom_index, src_loc, .{
527 .ty = union_ty.tag_ty,
528 .val = union_obj.tag,
529 }, code, debug_output)) {
530 .appended => {},
531 .externally_managed => |external_slice| {
532 code.appendSliceAssumeCapacity(external_slice);
533 },
534 .fail => |em| return Result{ .fail = em },
535 }
536 }
473537
474 return Result{ .appended = {} };538 return Result{ .appended = {} };
475 },539 },
test/behavior/union.zig+48-27
...@@ -4,68 +4,100 @@ const expect = std.testing.expect;...@@ -4,68 +4,100 @@ const expect = std.testing.expect;
4const expectEqual = std.testing.expectEqual;4const expectEqual = std.testing.expectEqual;
5const Tag = std.meta.Tag;5const Tag = std.meta.Tag;
66
7const Foo = union {7const FooWithFloats = union {
8 float: f64,8 float: f64,
9 int: i32,9 int: i32,
10};10};
1111
12test "basic unions" {12test "basic unions with floats" {
13 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;13 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
14 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;14 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
15 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;15 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1616
17 var foo = Foo{ .int = 1 };17 var foo = FooWithFloats{ .int = 1 };
18 try expect(foo.int == 1);18 try expect(foo.int == 1);
19 foo = Foo{ .float = 12.34 };19 foo = FooWithFloats{ .float = 12.34 };
20 try expect(foo.float == 12.34);20 try expect(foo.float == 12.34);
21}21}
2222
23test "init union with runtime value" {23fn setFloat(foo: *FooWithFloats, x: f64) void {
24 foo.* = FooWithFloats{ .float = x };
25}
26
27test "init union with runtime value - floats" {
24 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;28 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
25 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;29 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
26 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;30 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
2731
28 var foo: Foo = undefined;32 var foo: FooWithFloats = undefined;
2933
30 setFloat(&foo, 12.34);34 setFloat(&foo, 12.34);
31 try expect(foo.float == 12.34);35 try expect(foo.float == 12.34);
36}
37
38test "basic unions" {
39 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
40 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
41
42 var foo = Foo{ .int = 1 };
43 try expect(foo.int == 1);
44 foo = Foo{ .str = .{ .slice = "Hello!" } };
45 try expect(std.mem.eql(u8, foo.str.slice, "Hello!"));
46}
47
48const Foo = union {
49 int: i32,
50 str: struct {
51 slice: []const u8,
52 },
53};
54
55test "init union with runtime value" {
56 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
57 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
58
59 var foo: Foo = undefined;
3260
33 setInt(&foo, 42);61 setInt(&foo, 42);
34 try expect(foo.int == 42);62 try expect(foo.int == 42);
35}
3663
37fn setFloat(foo: *Foo, x: f64) void {64 setStr(&foo, "Hello!");
38 foo.* = Foo{ .float = x };65 try expect(std.mem.eql(u8, foo.str.slice, "Hello!"));
39}66}
4067
41fn setInt(foo: *Foo, x: i32) void {68fn setInt(foo: *Foo, x: i32) void {
42 foo.* = Foo{ .int = x };69 foo.* = Foo{ .int = x };
43}70}
4471
72fn setStr(foo: *Foo, slice: []const u8) void {
73 foo.* = Foo{ .str = .{ .slice = slice } };
74}
75
45test "comptime union field access" {76test "comptime union field access" {
46 comptime {77 comptime {
47 var foo = Foo{ .int = 0 };78 var foo = FooWithFloats{ .int = 0 };
48 try expect(foo.int == 0);79 try expect(foo.int == 0);
4980
50 foo = Foo{ .float = 42.42 };81 foo = FooWithFloats{ .float = 12.34 };
51 try expect(foo.float == 42.42);82 try expect(foo.float == 12.34);
52 }83 }
53}84}
5485
55const FooExtern = extern union {86const FooExtern = extern union {
56 float: f64,
57 int: i32,87 int: i32,
88 str: struct {
89 slice: []const u8,
90 },
58};91};
5992
60test "basic extern unions" {93test "basic extern unions" {
61 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
62 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;94 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
63 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;95 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
6496
65 var foo = FooExtern{ .int = 1 };97 var foo = FooExtern{ .int = 1 };
66 try expect(foo.int == 1);98 try expect(foo.int == 1);
67 foo.float = 12.34;99 foo.str.slice = "Well";
68 try expect(foo.float == 12.34);100 try expect(std.mem.eql(u8, foo.str.slice, "Well"));
69}101}
70102
71const ExternPtrOrInt = extern union {103const ExternPtrOrInt = extern union {
...@@ -129,7 +161,6 @@ test "access a member of tagged union with conflicting enum tag name" {...@@ -129,7 +161,6 @@ test "access a member of tagged union with conflicting enum tag name" {
129}161}
130162
131test "constant tagged union with payload" {163test "constant tagged union with payload" {
132 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
133 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;164 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
134 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;165 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
135166
...@@ -227,7 +258,6 @@ fn testComparison() !void {...@@ -227,7 +258,6 @@ fn testComparison() !void {
227}258}
228259
229test "comparison between union and enum literal" {260test "comparison between union and enum literal" {
230 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
231 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;261 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
232 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;262 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
233263
...@@ -297,7 +327,6 @@ pub const PackThis = union(enum) {...@@ -297,7 +327,6 @@ pub const PackThis = union(enum) {
297};327};
298328
299test "constant packed union" {329test "constant packed union" {
300 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
301 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;330 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
302 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;331 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
303332
...@@ -457,7 +486,6 @@ test "update the tag value for zero-sized unions" {...@@ -457,7 +486,6 @@ test "update the tag value for zero-sized unions" {
457}486}
458487
459test "union initializer generates padding only if needed" {488test "union initializer generates padding only if needed" {
460 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
461 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;489 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
462 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;490 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
463491
...@@ -470,7 +498,6 @@ test "union initializer generates padding only if needed" {...@@ -470,7 +498,6 @@ test "union initializer generates padding only if needed" {
470}498}
471499
472test "runtime tag name with single field" {500test "runtime tag name with single field" {
473 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
474 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;501 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
475 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;502 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
476503
...@@ -483,7 +510,6 @@ test "runtime tag name with single field" {...@@ -483,7 +510,6 @@ test "runtime tag name with single field" {
483}510}
484511
485test "method call on an empty union" {512test "method call on an empty union" {
486 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
487 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;513 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
488 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;514 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
489515
...@@ -547,7 +573,6 @@ test "tagged union type" {...@@ -547,7 +573,6 @@ test "tagged union type" {
547}573}
548574
549test "tagged union as return value" {575test "tagged union as return value" {
550 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
551 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;576 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
552 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;577 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
553578
...@@ -628,7 +653,6 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void {...@@ -628,7 +653,6 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) !void {
628}653}
629654
630test "switch on union with only 1 field" {655test "switch on union with only 1 field" {
631 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
632 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO656 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
633 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO657 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
634658
...@@ -658,7 +682,6 @@ const PartialInstWithPayload = union(enum) {...@@ -658,7 +682,6 @@ const PartialInstWithPayload = union(enum) {
658};682};
659683
660test "union with only 1 field casted to its enum type which has enum value specified" {684test "union with only 1 field casted to its enum type which has enum value specified" {
661 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
662 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;685 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
663 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;686 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
664687
...@@ -796,7 +819,6 @@ test "@unionInit stored to a const" {...@@ -796,7 +819,6 @@ test "@unionInit stored to a const" {
796}819}
797820
798test "@unionInit can modify a union type" {821test "@unionInit can modify a union type" {
799 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
800 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO822 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
801 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO823 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
802824
...@@ -839,7 +861,6 @@ test "@unionInit can modify a pointer value" {...@@ -839,7 +861,6 @@ test "@unionInit can modify a pointer value" {
839}861}
840862
841test "union no tag with struct member" {863test "union no tag with struct member" {
842 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
843 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO864 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
844 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO865 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
845866