authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-07-26 21:39:43-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-07-26 21:39:50-04:00
log7894703ee74a915206143f0b3efea082f999bb86
treef3a5df459e0a043fae677b5d6a178619052ae047
parent69abc945e45af3f447f9ee07d426d1ec40cf3f15

aarch64: implement more optional/error union/union support


13 files changed, 342 insertions(+), 68 deletions(-)

lib/compiler_rt.zig+1-1
......@@ -240,7 +240,7 @@ comptime {
240240 _ = @import("compiler_rt/udivmodti4.zig");
241241
242242 // extra
243 if (builtin.zig_backend != .stage2_aarch64) _ = @import("compiler_rt/os_version_check.zig");
243 _ = @import("compiler_rt/os_version_check.zig");
244244 _ = @import("compiler_rt/emutls.zig");
245245 _ = @import("compiler_rt/arm.zig");
246246 _ = @import("compiler_rt/aulldiv.zig");
src/codegen/aarch64/Select.zig+341-29
......@@ -584,7 +584,7 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
584584
585585 air_body_index += 1;
586586 },
587 .@"try", .try_cold, .try_ptr, .try_ptr_cold => {
587 .@"try", .try_cold => {
588588 const pl_op = air_data[@intFromEnum(air_inst_index)].pl_op;
589589 const extra = isel.air.extraData(Air.Try, pl_op.payload);
590590
......@@ -596,6 +596,18 @@ pub fn analyze(isel: *Select, air_body: []const Air.Inst.Index) !void {
596596 air_inst_index = air_body[air_body_index];
597597 continue :air_tag air_tags[@intFromEnum(air_inst_index)];
598598 },
599 .try_ptr, .try_ptr_cold => {
600 const ty_pl = air_data[@intFromEnum(air_inst_index)].ty_pl;
601 const extra = isel.air.extraData(Air.TryPtr, ty_pl.payload);
602
603 try isel.analyzeUse(extra.data.ptr);
604 try isel.analyze(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len]));
605 try isel.def_order.putNoClobber(gpa, air_inst_index, {});
606
607 air_body_index += 1;
608 air_inst_index = air_body[air_body_index];
609 continue :air_tag air_tags[@intFromEnum(air_inst_index)];
610 },
599611 .ret, .ret_safe, .ret_load => {
600612 const un_op = air_data[@intFromEnum(air_inst_index)].un_op;
601613 isel.returns = true;
......@@ -4760,17 +4772,62 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
47604772 const error_set_part_vi = try error_set_part_it.only(isel);
47614773 const error_set_part_mat = try error_set_part_vi.?.matReg(isel);
47624774 try isel.emit(.cbz(
4763 switch (error_set_part_vi.?.size(isel)) {
4764 else => unreachable,
4765 1...4 => error_set_part_mat.ra.w(),
4766 5...8 => error_set_part_mat.ra.x(),
4767 },
4775 error_set_part_mat.ra.w(),
47684776 @intCast((isel.instructions.items.len + 1 - cont_label) << 2),
47694777 ));
47704778 try error_set_part_mat.finish(isel);
47714779
47724780 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
47734781 },
4782 .try_ptr, .try_ptr_cold => {
4783 const ty_pl = air.data(air.inst_index).ty_pl;
4784 const extra = isel.air.extraData(Air.TryPtr, ty_pl.payload);
4785 const error_union_ty = isel.air.typeOf(extra.data.ptr, ip).childType(zcu);
4786 const error_union_info = ip.indexToKey(error_union_ty.toIntern()).error_union_type;
4787 const payload_ty: ZigType = .fromInterned(error_union_info.payload_type);
4788
4789 const error_union_ptr_vi = try isel.use(extra.data.ptr);
4790 const error_union_ptr_mat = try error_union_ptr_vi.matReg(isel);
4791 if (isel.live_values.fetchRemove(air.inst_index)) |payload_ptr_vi| unused: {
4792 defer payload_ptr_vi.value.deref(isel);
4793 switch (codegen.errUnionPayloadOffset(ty_pl.ty.toType().childType(zcu), zcu)) {
4794 0 => try payload_ptr_vi.value.move(isel, extra.data.ptr),
4795 else => |payload_offset| {
4796 const payload_ptr_ra = try payload_ptr_vi.value.defReg(isel) orelse break :unused;
4797 const lo12: u12 = @truncate(payload_offset >> 0);
4798 const hi12: u12 = @intCast(payload_offset >> 12);
4799 if (hi12 > 0) try isel.emit(.add(
4800 payload_ptr_ra.x(),
4801 if (lo12 > 0) payload_ptr_ra.x() else error_union_ptr_mat.ra.x(),
4802 .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
4803 ));
4804 if (lo12 > 0) try isel.emit(.add(payload_ptr_ra.x(), error_union_ptr_mat.ra.x(), .{ .immediate = lo12 }));
4805 },
4806 }
4807 }
4808
4809 const cont_label = isel.instructions.items.len;
4810 const cont_live_registers = isel.live_registers;
4811 try isel.body(@ptrCast(isel.air.extra.items[extra.end..][0..extra.data.body_len]));
4812 try isel.merge(&cont_live_registers, .{});
4813
4814 const error_set_ra = try isel.allocIntReg();
4815 defer isel.freeReg(error_set_ra);
4816 try isel.loadReg(
4817 error_set_ra,
4818 ZigType.fromInterned(error_union_info.error_set_type).abiSize(zcu),
4819 .unsigned,
4820 error_union_ptr_mat.ra,
4821 codegen.errUnionErrorOffset(payload_ty, zcu),
4822 );
4823 try error_union_ptr_mat.finish(isel);
4824 try isel.emit(.cbz(
4825 error_set_ra.w(),
4826 @intCast((isel.instructions.items.len + 1 - cont_label) << 2),
4827 ));
4828
4829 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
4830 },
47744831 .dbg_stmt => {
47754832 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
47764833 },
......@@ -5403,14 +5460,6 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
54035460 }
54045461 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
54055462 },
5406 .optional_payload_ptr => {
5407 if (isel.live_values.fetchRemove(air.inst_index)) |dst_vi| {
5408 defer dst_vi.value.deref(isel);
5409 const ty_op = air.data(air.inst_index).ty_op;
5410 try dst_vi.value.move(isel, ty_op.operand);
5411 }
5412 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
5413 },
54145463 .optional_payload => {
54155464 if (isel.live_values.fetchRemove(air.inst_index)) |payload_vi| unused: {
54165465 defer payload_vi.value.deref(isel);
......@@ -5429,6 +5478,37 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
54295478 }
54305479 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
54315480 },
5481 .optional_payload_ptr => {
5482 if (isel.live_values.fetchRemove(air.inst_index)) |payload_ptr_vi| {
5483 defer payload_ptr_vi.value.deref(isel);
5484 const ty_op = air.data(air.inst_index).ty_op;
5485 try payload_ptr_vi.value.move(isel, ty_op.operand);
5486 }
5487 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
5488 },
5489 .optional_payload_ptr_set => {
5490 if (isel.live_values.fetchRemove(air.inst_index)) |payload_ptr_vi| {
5491 defer payload_ptr_vi.value.deref(isel);
5492 const ty_op = air.data(air.inst_index).ty_op;
5493 const opt_ty = isel.air.typeOf(ty_op.operand, ip).childType(zcu);
5494 if (!opt_ty.optionalReprIsPayload(zcu)) {
5495 const opt_ptr_vi = try isel.use(ty_op.operand);
5496 const opt_ptr_mat = try opt_ptr_vi.matReg(isel);
5497 const has_value_ra = try isel.allocIntReg();
5498 defer isel.freeReg(has_value_ra);
5499 try isel.storeReg(
5500 has_value_ra,
5501 1,
5502 opt_ptr_mat.ra,
5503 opt_ty.optionalChild(zcu).abiSize(zcu),
5504 );
5505 try opt_ptr_mat.finish(isel);
5506 try isel.emit(.movz(has_value_ra.w(), 1, .{ .lsl = .@"0" }));
5507 }
5508 try payload_ptr_vi.value.move(isel, ty_op.operand);
5509 }
5510 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
5511 },
54325512 .wrap_optional => {
54335513 if (isel.live_values.fetchRemove(air.inst_index)) |opt_vi| unused: {
54345514 defer opt_vi.value.deref(isel);
......@@ -5486,6 +5566,93 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
54865566 }
54875567 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
54885568 },
5569 .unwrap_errunion_payload_ptr => {
5570 if (isel.live_values.fetchRemove(air.inst_index)) |payload_ptr_vi| unused: {
5571 defer payload_ptr_vi.value.deref(isel);
5572 const ty_op = air.data(air.inst_index).ty_op;
5573 switch (codegen.errUnionPayloadOffset(ty_op.ty.toType().childType(zcu), zcu)) {
5574 0 => try payload_ptr_vi.value.move(isel, ty_op.operand),
5575 else => |payload_offset| {
5576 const payload_ptr_ra = try payload_ptr_vi.value.defReg(isel) orelse break :unused;
5577 const error_union_ptr_vi = try isel.use(ty_op.operand);
5578 const error_union_ptr_mat = try error_union_ptr_vi.matReg(isel);
5579 const lo12: u12 = @truncate(payload_offset >> 0);
5580 const hi12: u12 = @intCast(payload_offset >> 12);
5581 if (hi12 > 0) try isel.emit(.add(
5582 payload_ptr_ra.x(),
5583 if (lo12 > 0) payload_ptr_ra.x() else error_union_ptr_mat.ra.x(),
5584 .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
5585 ));
5586 if (lo12 > 0) try isel.emit(.add(payload_ptr_ra.x(), error_union_ptr_mat.ra.x(), .{ .immediate = lo12 }));
5587 try error_union_ptr_mat.finish(isel);
5588 },
5589 }
5590 }
5591 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
5592 },
5593 .unwrap_errunion_err_ptr => {
5594 if (isel.live_values.fetchRemove(air.inst_index)) |error_ptr_vi| unused: {
5595 defer error_ptr_vi.value.deref(isel);
5596 const ty_op = air.data(air.inst_index).ty_op;
5597 switch (codegen.errUnionErrorOffset(
5598 isel.air.typeOf(ty_op.operand, ip).childType(zcu).errorUnionPayload(zcu),
5599 zcu,
5600 )) {
5601 0 => try error_ptr_vi.value.move(isel, ty_op.operand),
5602 else => |error_offset| {
5603 const error_ptr_ra = try error_ptr_vi.value.defReg(isel) orelse break :unused;
5604 const error_union_ptr_vi = try isel.use(ty_op.operand);
5605 const error_union_ptr_mat = try error_union_ptr_vi.matReg(isel);
5606 const lo12: u12 = @truncate(error_offset >> 0);
5607 const hi12: u12 = @intCast(error_offset >> 12);
5608 if (hi12 > 0) try isel.emit(.add(
5609 error_ptr_ra.x(),
5610 if (lo12 > 0) error_ptr_ra.x() else error_union_ptr_mat.ra.x(),
5611 .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
5612 ));
5613 if (lo12 > 0) try isel.emit(.add(error_ptr_ra.x(), error_union_ptr_mat.ra.x(), .{ .immediate = lo12 }));
5614 try error_union_ptr_mat.finish(isel);
5615 },
5616 }
5617 }
5618 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
5619 },
5620 .errunion_payload_ptr_set => {
5621 if (isel.live_values.fetchRemove(air.inst_index)) |payload_ptr_vi| unused: {
5622 defer payload_ptr_vi.value.deref(isel);
5623 const ty_op = air.data(air.inst_index).ty_op;
5624 const payload_ty = ty_op.ty.toType().childType(zcu);
5625 const error_union_ty = isel.air.typeOf(ty_op.operand, ip).childType(zcu);
5626 const error_set_size = error_union_ty.errorUnionSet(zcu).abiSize(zcu);
5627 const error_union_ptr_vi = try isel.use(ty_op.operand);
5628 const error_union_ptr_mat = try error_union_ptr_vi.matReg(isel);
5629 if (error_set_size > 0) try isel.storeReg(
5630 .zr,
5631 error_set_size,
5632 error_union_ptr_mat.ra,
5633 codegen.errUnionErrorOffset(payload_ty, zcu),
5634 );
5635 switch (codegen.errUnionPayloadOffset(payload_ty, zcu)) {
5636 0 => {
5637 try error_union_ptr_mat.finish(isel);
5638 try payload_ptr_vi.value.move(isel, ty_op.operand);
5639 },
5640 else => |payload_offset| {
5641 const payload_ptr_ra = try payload_ptr_vi.value.defReg(isel) orelse break :unused;
5642 const lo12: u12 = @truncate(payload_offset >> 0);
5643 const hi12: u12 = @intCast(payload_offset >> 12);
5644 if (hi12 > 0) try isel.emit(.add(
5645 payload_ptr_ra.x(),
5646 if (lo12 > 0) payload_ptr_ra.x() else error_union_ptr_mat.ra.x(),
5647 .{ .shifted_immediate = .{ .immediate = hi12, .lsl = .@"12" } },
5648 ));
5649 if (lo12 > 0) try isel.emit(.add(payload_ptr_ra.x(), error_union_ptr_mat.ra.x(), .{ .immediate = lo12 }));
5650 try error_union_ptr_mat.finish(isel);
5651 },
5652 }
5653 }
5654 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
5655 },
54895656 .wrap_errunion_payload => {
54905657 if (isel.live_values.fetchRemove(air.inst_index)) |error_union_vi| {
54915658 defer error_union_vi.value.deref(isel);
......@@ -5672,6 +5839,32 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
56725839 }
56735840 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
56745841 },
5842 .set_union_tag => {
5843 const bin_op = air.data(air.inst_index).bin_op;
5844 const union_ty = isel.air.typeOf(bin_op.lhs, ip).childType(zcu);
5845 const union_layout = union_ty.unionGetLayout(zcu);
5846 const tag_vi = try isel.use(bin_op.rhs);
5847 const union_ptr_vi = try isel.use(bin_op.lhs);
5848 const union_ptr_mat = try union_ptr_vi.matReg(isel);
5849 try tag_vi.store(isel, isel.air.typeOf(bin_op.rhs, ip), union_ptr_mat.ra, .{
5850 .offset = union_layout.tagOffset(),
5851 });
5852 try union_ptr_mat.finish(isel);
5853 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
5854 },
5855 .get_union_tag => {
5856 if (isel.live_values.fetchRemove(air.inst_index)) |tag_vi| {
5857 defer tag_vi.value.deref(isel);
5858 const ty_op = air.data(air.inst_index).ty_op;
5859 const union_ty = isel.air.typeOf(ty_op.operand, ip);
5860 const union_layout = union_ty.unionGetLayout(zcu);
5861 const union_vi = try isel.use(ty_op.operand);
5862 var tag_part_it = union_vi.field(union_ty, union_layout.tagOffset(), union_layout.tag_size);
5863 const tag_part_vi = try tag_part_it.only(isel);
5864 try tag_vi.value.copy(isel, ty_op.ty.toType(), tag_part_vi.?);
5865 }
5866 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
5867 },
56755868 .slice => {
56765869 if (isel.live_values.fetchRemove(air.inst_index)) |slice_vi| {
56775870 defer slice_vi.value.deref(isel);
......@@ -6541,8 +6734,8 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
65416734 if (ptr_part_ra == null and len_part_ra == null) break :unused;
65426735
65436736 const un_op = air.data(air.inst_index).un_op;
6544 const err_vi = try isel.use(un_op);
6545 const err_mat = try err_vi.matReg(isel);
6737 const error_vi = try isel.use(un_op);
6738 const error_mat = try error_vi.matReg(isel);
65466739 const ptr_ra = try isel.allocIntReg();
65476740 defer isel.freeReg(ptr_ra);
65486741 const start_ra, const end_ra = range_ras: {
......@@ -6573,7 +6766,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
65736766 if (len_part_ra) |_| try isel.emit(.sub(end_ra.w(), end_ra.w(), .{ .immediate = 1 }));
65746767 try isel.emit(.ldp(start_ra.w(), end_ra.w(), .{ .base = start_ra.x() }));
65756768 try isel.emit(.add(start_ra.x(), ptr_ra.x(), .{ .extended_register = .{
6576 .register = err_mat.ra.w(),
6769 .register = error_mat.ra.w(),
65776770 .extend = switch (zcu.errorSetBits()) {
65786771 else => unreachable,
65796772 1...8 => .{ .uxtb = 2 },
......@@ -6591,7 +6784,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
65916784 .reloc = .{ .label = @intCast(isel.instructions.items.len) },
65926785 });
65936786 try isel.emit(.adrp(ptr_ra.x(), 0));
6594 try err_mat.finish(isel);
6787 try error_mat.finish(isel);
65956788 }
65966789 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
65976790 },
......@@ -6893,11 +7086,11 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
68937086 try isel.emit(.csinc(is_ra.w(), .wzr, .wzr, .invert(.ls)));
68947087
68957088 const un_op = air.data(air.inst_index).un_op;
6896 const err_vi = try isel.use(un_op);
6897 const err_mat = try err_vi.matReg(isel);
7089 const error_vi = try isel.use(un_op);
7090 const error_mat = try error_vi.matReg(isel);
68987091 const ptr_ra = try isel.allocIntReg();
68997092 defer isel.freeReg(ptr_ra);
6900 try isel.emit(.subs(.wzr, err_mat.ra.w(), .{ .register = ptr_ra.w() }));
7093 try isel.emit(.subs(.wzr, error_mat.ra.w(), .{ .register = ptr_ra.w() }));
69017094 try isel.lazy_relocs.append(gpa, .{
69027095 .symbol = .{ .kind = .const_data, .ty = .anyerror_type },
69037096 .reloc = .{ .label = @intCast(isel.instructions.items.len) },
......@@ -6908,7 +7101,7 @@ pub fn body(isel: *Select, air_body: []const Air.Inst.Index) error{ OutOfMemory,
69087101 .reloc = .{ .label = @intCast(isel.instructions.items.len) },
69097102 });
69107103 try isel.emit(.adrp(ptr_ra.x(), 0));
6911 try err_mat.finish(isel);
7104 try error_mat.finish(isel);
69127105 }
69137106 if (air.next()) |next_air_tag| continue :air_tag next_air_tag;
69147107 },
......@@ -9529,8 +9722,14 @@ pub const Value = struct {
95299722 } },
95309723 },
95319724 .struct_type => {
9532 const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
95339725 const loaded_struct = ip.loadStructType(ty.toIntern());
9726 switch (loaded_struct.layout) {
9727 .auto, .@"extern" => {},
9728 .@"packed" => continue :type_key .{
9729 .int_type = ip.indexToKey(loaded_struct.backingIntTypeUnordered(ip)).int_type,
9730 },
9731 }
9732 const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
95349733 if (loaded_struct.field_types.len > Value.max_parts and
95359734 (std.math.divCeil(u64, size, @as(u64, 1) << min_part_log2_stride) catch unreachable) > Value.max_parts)
95369735 return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
......@@ -9638,6 +9837,77 @@ pub const Value = struct {
96389837 if (part.is_vector) subpart_vi.setIsVector(isel);
96399838 }
96409839 },
9840 .union_type => {
9841 const loaded_union = ip.loadUnionType(ty.toIntern());
9842 switch (loaded_union.flagsUnordered(ip).layout) {
9843 .auto, .@"extern" => {},
9844 .@"packed" => continue :type_key .{ .int_type = .{
9845 .signedness = .unsigned,
9846 .bits = @intCast(ty.bitSize(zcu)),
9847 } },
9848 }
9849 const min_part_log2_stride: u5 = if (size > 16) 4 else if (size > 8) 3 else 0;
9850 if ((std.math.divCeil(u64, size, @as(u64, 1) << min_part_log2_stride) catch unreachable) > Value.max_parts)
9851 return isel.fail("Value.FieldPartIterator.next({f})", .{isel.fmtType(ty)});
9852 const union_layout = ZigType.getUnionLayout(loaded_union, zcu);
9853 const alignment = vi.alignment(isel);
9854 const tag_offset = union_layout.tagOffset();
9855 const payload_offset = union_layout.payloadOffset();
9856 const Part = struct { offset: u64, size: u64, signedness: ?std.builtin.Signedness };
9857 var parts: [2]Part = undefined;
9858 var parts_len: Value.PartsLen = 0;
9859 var field_end: u64 = 0;
9860 for (0..2) |field_index| {
9861 const field: enum { tag, payload } = switch (field_index) {
9862 0 => if (tag_offset < payload_offset) .tag else .payload,
9863 1 => if (tag_offset < payload_offset) .payload else .tag,
9864 else => unreachable,
9865 };
9866 const field_size, const field_begin = switch (field) {
9867 .tag => .{ union_layout.tag_size, tag_offset },
9868 .payload => .{ union_layout.payload_size, payload_offset },
9869 };
9870 if (field_begin >= offset + size) break;
9871 if (field_size == 0) continue;
9872 field_end = field_begin + field_size;
9873 if (field_end <= offset) continue;
9874 const field_signedness = field_signedness: switch (field) {
9875 .tag => {
9876 if (offset >= field_begin and offset + size <= field_begin + field_size) {
9877 ty = .fromInterned(loaded_union.enum_tag_ty);
9878 ty_size = field_size;
9879 offset -= field_begin;
9880 continue :type_key ip.indexToKey(loaded_union.enum_tag_ty);
9881 }
9882 break :field_signedness ip.indexToKey(loaded_union.loadTagType(ip).tag_ty).int_type.signedness;
9883 },
9884 .payload => null,
9885 };
9886 if (parts_len > 0) combine: {
9887 const prev_part = &parts[parts_len - 1];
9888 const combined_size = field_end - prev_part.offset;
9889 if (combined_size > @as(u64, 1) << @min(
9890 min_part_log2_stride,
9891 alignment.toLog2Units(),
9892 @ctz(prev_part.offset),
9893 )) break :combine;
9894 prev_part.size = combined_size;
9895 prev_part.signedness = null;
9896 continue;
9897 }
9898 parts[parts_len] = .{
9899 .offset = field_begin,
9900 .size = field_size,
9901 .signedness = field_signedness,
9902 };
9903 parts_len += 1;
9904 }
9905 vi.setParts(isel, parts_len);
9906 for (parts[0..parts_len]) |part| {
9907 const subpart_vi = vi.addPart(isel, part.offset - offset, part.size);
9908 if (part.signedness) |signedness| subpart_vi.setSignedness(isel, signedness);
9909 }
9910 },
96419911 .opaque_type, .func_type => continue :type_key .{ .simple_type = .anyopaque },
96429912 .enum_type => continue :type_key ip.indexToKey(ip.loadEnumType(ty.toIntern()).tag_ty),
96439913 .error_set_type,
......@@ -10075,7 +10345,11 @@ pub const Value = struct {
1007510345 };
1007610346 },
1007710347 .slice => |slice| switch (offset) {
10078 0 => continue :constant_key .{ .ptr = ip.indexToKey(slice.ptr).ptr },
10348 0 => continue :constant_key switch (ip.indexToKey(slice.ptr)) {
10349 else => unreachable,
10350 .undef => |undef| .{ .undef = undef },
10351 .ptr => |ptr| .{ .ptr = ptr },
10352 },
1007910353 else => {
1008010354 assert(offset == @divExact(isel.target.ptrBitWidth(), 8));
1008110355 offset = 0;
......@@ -11128,16 +11402,14 @@ pub const CallAbiIterator = struct {
1112811402 {
1112911403 const error_set_ty: ZigType = .fromInterned(error_union_type.error_set_type);
1113011404 const offset = codegen.errUnionErrorOffset(payload_ty, zcu);
11131 const size = error_set_ty.abiSize(zcu);
11132 const end = offset % 8 + size;
11405 const end = offset % 8 + error_set_ty.abiSize(zcu);
1113311406 const part_index: usize = @intCast(offset / 8);
1113411407 sizes[part_index] = @max(sizes[part_index], @min(end, 8));
1113511408 if (end > 8) sizes[part_index + 1] = @max(sizes[part_index + 1], end - 8);
1113611409 }
1113711410 {
1113811411 const offset = codegen.errUnionPayloadOffset(payload_ty, zcu);
11139 const size = payload_ty.abiSize(zcu);
11140 const end = offset % 8 + size;
11412 const end = offset % 8 + payload_ty.abiSize(zcu);
1114111413 const part_index: usize = @intCast(offset / 8);
1114211414 sizes[part_index] = @max(sizes[part_index], @min(end, 8));
1114311415 if (end > 8) sizes[part_index + 1] = @max(sizes[part_index + 1], end - 8);
......@@ -11181,8 +11453,14 @@ pub const CallAbiIterator = struct {
1118111453 => unreachable,
1118211454 },
1118311455 .struct_type => {
11184 const size = wip_vi.size(isel);
1118511456 const loaded_struct = ip.loadStructType(ty.toIntern());
11457 switch (loaded_struct.layout) {
11458 .auto, .@"extern" => {},
11459 .@"packed" => continue :type_key .{
11460 .int_type = ip.indexToKey(loaded_struct.backingIntTypeUnordered(ip)).int_type,
11461 },
11462 }
11463 const size = wip_vi.size(isel);
1118611464 if (size <= 16 * 4) homogeneous_aggregate: {
1118711465 const fdt = homogeneousStructBaseType(zcu, &loaded_struct) orelse break :homogeneous_aggregate;
1118811466 const parts_len = @shrExact(size, fdt.log2Size());
......@@ -11267,6 +11545,40 @@ pub const CallAbiIterator = struct {
1126711545 else => it.indirect(isel, wip_vi),
1126811546 }
1126911547 },
11548 .union_type => {
11549 const loaded_union = ip.loadUnionType(ty.toIntern());
11550 switch (loaded_union.flagsUnordered(ip).layout) {
11551 .auto, .@"extern" => {},
11552 .@"packed" => continue :type_key .{ .int_type = .{
11553 .signedness = .unsigned,
11554 .bits = @intCast(ty.bitSize(zcu)),
11555 } },
11556 }
11557 switch (wip_vi.size(isel)) {
11558 0 => unreachable,
11559 1...8 => it.integer(isel, wip_vi),
11560 9...16 => {
11561 const union_layout = ZigType.getUnionLayout(loaded_union, zcu);
11562 var sizes: [2]u64 = @splat(0);
11563 {
11564 const offset = union_layout.tagOffset();
11565 const end = offset % 8 + union_layout.tag_size;
11566 const part_index: usize = @intCast(offset / 8);
11567 sizes[part_index] = @max(sizes[part_index], @min(end, 8));
11568 if (end > 8) sizes[part_index + 1] = @max(sizes[part_index + 1], end - 8);
11569 }
11570 {
11571 const offset = union_layout.payloadOffset();
11572 const end = offset % 8 + union_layout.payload_size;
11573 const part_index: usize = @intCast(offset / 8);
11574 sizes[part_index] = @max(sizes[part_index], @min(end, 8));
11575 if (end > 8) sizes[part_index + 1] = @max(sizes[part_index + 1], end - 8);
11576 }
11577 it.integers(isel, wip_vi, sizes);
11578 },
11579 else => it.indirect(isel, wip_vi),
11580 }
11581 },
1127011582 .opaque_type, .func_type => continue :type_key .{ .simple_type = .anyopaque },
1127111583 .enum_type => continue :type_key ip.indexToKey(ip.loadEnumType(ty.toIntern()).tag_ty),
1127211584 .error_set_type,
test/behavior/decl_literals.zig-2
......@@ -33,7 +33,6 @@ test "decl literal with pointer" {
3333}
3434
3535test "call decl literal with optional" {
36 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
3736 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
3837 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
3938 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
......@@ -74,7 +73,6 @@ test "call decl literal" {
7473}
7574
7675test "call decl literal with error union" {
77 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
7876 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; // TODO
7977
8078 const S = struct {
test/behavior/error.zig-1
......@@ -943,7 +943,6 @@ test "optional error set function parameter" {
943943}
944944
945945test "returning an error union containing a type with no runtime bits" {
946 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
947946 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
948947 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
949948
test/behavior/field_parent_ptr.zig-2
......@@ -587,7 +587,6 @@ test "@fieldParentPtr extern struct last zero-bit field" {
587587}
588588
589589test "@fieldParentPtr unaligned packed struct" {
590 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
591590 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
592591 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
593592 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
......@@ -726,7 +725,6 @@ test "@fieldParentPtr unaligned packed struct" {
726725}
727726
728727test "@fieldParentPtr aligned packed struct" {
729 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
730728 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
731729 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
732730 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
test/behavior/inline_switch.zig-1
......@@ -43,7 +43,6 @@ test "inline switch enums" {
4343
4444const U = union(E) { a: void, b: u2, c: u3, d: u4 };
4545test "inline switch unions" {
46 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
4746 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
4847 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
4948
test/behavior/optional.zig-2
......@@ -319,7 +319,6 @@ test "assigning to an unwrapped optional field in an inline loop" {
319319}
320320
321321test "coerce an anon struct literal to optional struct" {
322 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
323322 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
324323 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
325324 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
......@@ -447,7 +446,6 @@ test "optional pointer to zero bit optional payload" {
447446}
448447
449448test "optional pointer to zero bit error union payload" {
450 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
451449 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
452450 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
453451 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
test/behavior/struct.zig-5
......@@ -797,7 +797,6 @@ test "fn with C calling convention returns struct by value" {
797797}
798798
799799test "non-packed struct with u128 entry in union" {
800 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
801800 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
802801 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
803802 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
......@@ -1026,7 +1025,6 @@ test "packed struct with undefined initializers" {
10261025}
10271026
10281027test "for loop over pointers to struct, getting field from struct pointer" {
1029 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
10301028 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
10311029 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10321030 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
......@@ -1093,7 +1091,6 @@ test "anon init through error unions and optionals" {
10931091}
10941092
10951093test "anon init through optional" {
1096 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
10971094 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10981095 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
10991096 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
......@@ -1113,7 +1110,6 @@ test "anon init through optional" {
11131110}
11141111
11151112test "anon init through error union" {
1116 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
11171113 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
11181114 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
11191115 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
......@@ -1398,7 +1394,6 @@ test "struct has only one reference" {
13981394}
13991395
14001396test "no dependency loop on pointer to optional struct" {
1401 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
14021397 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
14031398 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
14041399
test/behavior/switch.zig-4
......@@ -299,7 +299,6 @@ fn switchProngWithVarFn(a: SwitchProngWithVarEnum) !void {
299299}
300300
301301test "switch on enum using pointer capture" {
302 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
303302 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
304303 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
305304
......@@ -360,7 +359,6 @@ fn testSwitchHandleAllCasesRange(x: u8) u8 {
360359}
361360
362361test "switch on union with some prongs capturing" {
363 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
364362 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
365363 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
366364
......@@ -975,8 +973,6 @@ test "switch prong captures range" {
975973}
976974
977975test "prong with inline call to unreachable" {
978 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
979
980976 const U = union(enum) {
981977 void: void,
982978 bool: bool,
test/behavior/switch_on_captured_error.zig-1
......@@ -6,7 +6,6 @@ const expectEqual = std.testing.expectEqual;
66const builtin = @import("builtin");
77
88test "switch on error union catch capture" {
9 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
109 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1110 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1211
test/behavior/union.zig-18
......@@ -160,7 +160,6 @@ test "unions embedded in aggregate types" {
160160}
161161
162162test "constant tagged union with payload" {
163 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
164163 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
165164 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
166165
......@@ -263,7 +262,6 @@ fn testComparison() !void {
263262}
264263
265264test "comparison between union and enum literal" {
266 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
267265 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
268266 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
269267 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
......@@ -279,7 +277,6 @@ const TheUnion = union(TheTag) {
279277 C: i32,
280278};
281279test "cast union to tag type of union" {
282 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
283280 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
284281 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
285282
......@@ -300,7 +297,6 @@ test "union field access gives the enum values" {
300297}
301298
302299test "cast tag type of union to union" {
303 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
304300 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
305301 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
306302
......@@ -316,7 +312,6 @@ const Value2 = union(Letter2) {
316312};
317313
318314test "implicit cast union to its tag type" {
319 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
320315 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
321316 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
322317
......@@ -495,7 +490,6 @@ test "initialize global array of union" {
495490}
496491
497492test "update the tag value for zero-sized unions" {
498 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
499493 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
500494 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
501495
......@@ -734,7 +728,6 @@ test "union with only 1 field casted to its enum type which has enum value speci
734728}
735729
736730test "@intFromEnum works on unions" {
737 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
738731 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
739732 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
740733
......@@ -848,7 +841,6 @@ test "@unionInit stored to a const" {
848841}
849842
850843test "@unionInit can modify a union type" {
851 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
852844 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
853845 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
854846
......@@ -871,7 +863,6 @@ test "@unionInit can modify a union type" {
871863}
872864
873865test "@unionInit can modify a pointer value" {
874 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
875866 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
876867 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
877868
......@@ -990,7 +981,6 @@ test "function call result coerces from tagged union to the tag" {
990981}
991982
992983test "switching on non exhaustive union" {
993 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
994984 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
995985 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
996986
......@@ -1176,7 +1166,6 @@ test "comptime equality of extern unions with same tag" {
11761166}
11771167
11781168test "union tag is set when initiated as a temporary value at runtime" {
1179 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
11801169 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
11811170 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
11821171 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
......@@ -1216,7 +1205,6 @@ test "extern union most-aligned field is smaller" {
12161205}
12171206
12181207test "return an extern union from C calling convention" {
1219 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
12201208 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
12211209 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
12221210 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
......@@ -1248,7 +1236,6 @@ test "return an extern union from C calling convention" {
12481236}
12491237
12501238test "noreturn field in union" {
1251 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
12521239 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
12531240 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
12541241
......@@ -1481,8 +1468,6 @@ test "reinterpreting enum value inside packed union" {
14811468}
14821469
14831470test "access the tag of a global tagged union" {
1484 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1485
14861471 const U = union(enum) {
14871472 a,
14881473 b: u8,
......@@ -2111,7 +2096,6 @@ test "runtime union init, most-aligned field != largest" {
21112096}
21122097
21132098test "copied union field doesn't alias source" {
2114 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
21152099 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
21162100 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
21172101 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
......@@ -2334,8 +2318,6 @@ test "assign global tagged union" {
23342318}
23352319
23362320test "set mutable union by switching on same union" {
2337 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
2338
23392321 const U = union(enum) {
23402322 foo,
23412323 bar: usize,
test/behavior/union_with_members.zig-1
......@@ -17,7 +17,6 @@ const ET = union(enum) {
1717};
1818
1919test "enum with members" {
20 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
2120 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2221 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2322 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
test/behavior/while.zig-1
......@@ -344,7 +344,6 @@ test "else continue outer while" {
344344}
345345
346346test "try terminating an infinite loop" {
347 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
348347 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
349348 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
350349