authorgravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-04-28 22:22:26-07:00
committergravatar for david@vortan.devDavid Rubin <david@vortan.dev> 2024-06-13 02:19:40-07:00
logc457f35da565e589071db9e55d0866f35cd095dc
tree45994b3380f17d187f65ed08a61bda4cfa99cac1
parentf34dcd067b6e9783a53f007746309bb1f635fbf0
signaturelock-open Commit is signed but in an unrecognized format.

riscv: arbitrary sized arrays


21 files changed, 428 insertions(+), 193 deletions(-)

src/arch/riscv64/CodeGen.zig+396-143
......@@ -666,10 +666,11 @@ fn restoreState(self: *Self, state: State, deaths: []const Air.Inst.Index, compt
666666 if (current_maybe_inst) |current_inst| {
667667 try self.inst_tracking.getPtr(current_inst).?.trackSpill(self, current_inst);
668668 }
669 {
669 blk: {
670 const inst = target_maybe_inst orelse break :blk;
670671 const reg = RegisterManager.regAtTrackedIndex(@intCast(index));
671672 self.register_manager.freeReg(reg);
672 self.register_manager.getRegAssumeFree(reg, target_maybe_inst);
673 self.register_manager.getRegAssumeFree(reg, inst);
673674 }
674675 if (target_maybe_inst) |target_inst| {
675676 self.inst_tracking.getPtr(target_inst).?.trackMaterialize(
......@@ -2272,13 +2273,10 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
22722273 },
22732274 });
22742275
2275 const add_result_frame: FrameAddr = .{
2276 .index = offset.index,
2277 .off = offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(0, zcu))),
2278 };
2279 try self.genSetStack(
2276 try self.genSetMem(
2277 .{ .frame = offset.index },
2278 offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(0, zcu))),
22802279 lhs_ty,
2281 add_result_frame,
22822280 add_result,
22832281 );
22842282
......@@ -2289,14 +2287,10 @@ fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
22892287 .{ .register = add_result_reg },
22902288 lhs_ty,
22912289 );
2292
2293 const overflow_frame: FrameAddr = .{
2294 .index = offset.index,
2295 .off = offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(1, zcu))),
2296 };
2297 try self.genSetStack(
2290 try self.genSetMem(
2291 .{ .frame = offset.index },
2292 offset.off + @as(i32, @intCast(tuple_ty.structFieldOffset(1, zcu))),
22982293 Type.u1,
2299 overflow_frame,
23002294 overflow_mcv,
23012295 );
23022296
......@@ -2340,14 +2334,16 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
23402334
23412335 const tuple_ty = self.typeOfIndex(inst);
23422336
2343 // TODO: optimization, set this to true. needs the other struct access stuff to support
2344 // accessing registers.
2345 const result_mcv = try self.allocRegOrMem(inst, false);
2337 const result_mcv = try self.allocRegOrMem(inst, true);
23462338
23472339 const result_off: i32 = @intCast(tuple_ty.structFieldOffset(0, zcu));
23482340 const overflow_off: i32 = @intCast(tuple_ty.structFieldOffset(1, zcu));
23492341
2350 try self.genSetStack(lhs_ty, result_mcv.offset(result_off).load_frame, dest);
2342 try self.genCopy(
2343 lhs_ty,
2344 result_mcv.offset(result_off),
2345 dest,
2346 );
23512347
23522348 if (int_info.bits >= 8 and math.isPowerOfTwo(int_info.bits)) {
23532349 if (int_info.signedness == .unsigned) {
......@@ -2385,9 +2381,9 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
23852381 lhs_ty,
23862382 );
23872383
2388 try self.genSetStack(
2384 try self.genCopy(
23892385 lhs_ty,
2390 result_mcv.offset(overflow_off).load_frame,
2386 result_mcv.offset(overflow_off),
23912387 overflow_mcv,
23922388 );
23932389
......@@ -2668,9 +2664,9 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
26682664 const frame_index = try self.allocFrameIndex(FrameAlloc.initSpill(eu_ty, zcu));
26692665 const pl_off: i32 = @intCast(errUnionPayloadOffset(pl_ty, zcu));
26702666 const err_off: i32 = @intCast(errUnionErrorOffset(pl_ty, zcu));
2671 try self.genSetStack(pl_ty, .{ .index = frame_index, .off = pl_off }, .undef);
2667 try self.genSetMem(.{ .frame = frame_index }, pl_off, pl_ty, .undef);
26722668 const operand = try self.resolveInst(ty_op.operand);
2673 try self.genSetStack(err_ty, .{ .index = frame_index, .off = err_off }, operand);
2669 try self.genSetMem(.{ .frame = frame_index }, err_off, err_ty, operand);
26742670 break :result .{ .load_frame = .{ .index = frame_index } };
26752671 };
26762672 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
......@@ -2854,7 +2850,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
28542850 switch (array_mcv) {
28552851 .register => {
28562852 const frame_index = try self.allocFrameIndex(FrameAlloc.initType(array_ty, zcu));
2857 try self.genSetStack(array_ty, .{ .index = frame_index }, array_mcv);
2853 try self.genSetMem(.{ .frame = frame_index }, 0, array_ty, array_mcv);
28582854 try self.genSetReg(Type.usize, addr_reg, .{ .lea_frame = .{ .index = frame_index } });
28592855 },
28602856 .load_frame => |frame_addr| {
......@@ -2891,9 +2887,48 @@ fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
28912887}
28922888
28932889fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
2890 const zcu = self.bin_file.comp.module.?;
28942891 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
28952892 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2896 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else return self.fail("TODO implement ptr_elem_ptr for {}", .{self.target.cpu.arch});
2893
2894 const result = result: {
2895 const elem_ptr_ty = self.typeOfIndex(inst);
2896 const base_ptr_ty = self.typeOf(extra.lhs);
2897
2898 const base_ptr_mcv = try self.resolveInst(extra.lhs);
2899 const base_ptr_lock: ?RegisterLock = switch (base_ptr_mcv) {
2900 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
2901 else => null,
2902 };
2903 defer if (base_ptr_lock) |lock| self.register_manager.unlockReg(lock);
2904
2905 if (elem_ptr_ty.ptrInfo(zcu).flags.vector_index != .none) {
2906 break :result if (self.reuseOperand(inst, extra.lhs, 0, base_ptr_mcv))
2907 base_ptr_mcv
2908 else
2909 try self.copyToNewRegister(inst, base_ptr_mcv);
2910 }
2911
2912 const elem_ty = base_ptr_ty.elemType2(zcu);
2913 const elem_abi_size = elem_ty.abiSize(zcu);
2914 const index_ty = self.typeOf(extra.rhs);
2915 const index_mcv = try self.resolveInst(extra.rhs);
2916 const index_lock: ?RegisterLock = switch (index_mcv) {
2917 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
2918 else => null,
2919 };
2920 defer if (index_lock) |lock| self.register_manager.unlockReg(lock);
2921
2922 const offset_reg = try self.elemOffset(index_ty, index_mcv, elem_abi_size);
2923 const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
2924 defer self.register_manager.unlockReg(offset_reg_lock);
2925
2926 if (true) return self.fail("TODO: airPtrElemPtr", .{});
2927
2928 // TODO: something is breaking here dunno
2929
2930 break :result try self.binOp(.ptr_add, base_ptr_mcv, base_ptr_ty, .{ .register = offset_reg }, base_ptr_ty);
2931 };
28972932 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
28982933}
28992934
......@@ -4563,22 +4598,18 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {
45634598 .off = -dst_reg_off.off,
45644599 } },
45654600 }),
4566 .indirect => |ro| {
4567 const src_reg = try self.copyToTmpRegister(ty, src_mcv);
4568
4569 _ = try self.addInst(.{
4570 .tag = .pseudo,
4571 .ops = .pseudo_store_rm,
4572 .data = .{ .rm = .{
4573 .r = src_reg,
4574 .m = .{
4575 .base = .{ .reg = ro.reg },
4576 .mod = .{ .rm = .{ .disp = ro.off, .size = self.memSize(ty) } },
4577 },
4578 } },
4579 });
4580 },
4581 .load_frame => |frame| return self.genSetStack(ty, frame, src_mcv),
4601 .indirect => |reg_off| try self.genSetMem(
4602 .{ .reg = reg_off.reg },
4603 reg_off.off,
4604 ty,
4605 src_mcv,
4606 ),
4607 .load_frame => |frame_addr| try self.genSetMem(
4608 .{ .frame = frame_addr.index },
4609 frame_addr.off,
4610 ty,
4611 src_mcv,
4612 ),
45824613 .memory => return self.fail("TODO: genCopy memory", .{}),
45834614 .register_pair => |dst_regs| {
45844615 const src_info: ?struct { addr_reg: Register, addr_lock: RegisterLock } = switch (src_mcv) {
......@@ -4617,88 +4648,6 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void {
46174648 }
46184649}
46194650
4620fn genSetStack(
4621 self: *Self,
4622 ty: Type,
4623 frame: FrameAddr,
4624 src_mcv: MCValue,
4625) InnerError!void {
4626 const zcu = self.bin_file.comp.module.?;
4627 const abi_size: u32 = @intCast(ty.abiSize(zcu));
4628
4629 switch (src_mcv) {
4630 .none => return,
4631 .dead => unreachable,
4632 .undef => {
4633 if (!self.wantSafety()) return;
4634 try self.genSetStack(ty, frame, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
4635 },
4636 .immediate,
4637 .lea_frame,
4638 => {
4639 // TODO: remove this lock in favor of a copyToTmpRegister when we load 64 bit immediates with
4640 // a register allocation.
4641 const reg, const reg_lock = try self.allocReg();
4642 defer self.register_manager.unlockReg(reg_lock);
4643
4644 try self.genSetReg(ty, reg, src_mcv);
4645
4646 return self.genSetStack(ty, frame, .{ .register = reg });
4647 },
4648 .register => |reg| {
4649 switch (abi_size) {
4650 1, 2, 4, 8 => {
4651 _ = try self.addInst(.{
4652 .tag = .pseudo,
4653 .ops = .pseudo_store_rm,
4654 .data = .{ .rm = .{
4655 .r = reg,
4656 .m = .{
4657 .base = .{ .frame = frame.index },
4658 .mod = .{
4659 .rm = .{
4660 .size = self.memSize(ty),
4661 .disp = frame.off,
4662 },
4663 },
4664 },
4665 } },
4666 });
4667 },
4668 else => unreachable, // register can hold a max of 8 bytes
4669 }
4670 },
4671 .register_pair => |pair| {
4672 var part_disp: i32 = frame.off;
4673 for (try self.splitType(ty), pair) |src_ty, src_reg| {
4674 try self.genSetStack(
4675 src_ty,
4676 .{ .index = frame.index, .off = part_disp },
4677 .{ .register = src_reg },
4678 );
4679 part_disp += @intCast(src_ty.abiSize(zcu));
4680 }
4681 },
4682 .load_frame,
4683 .indirect,
4684 .load_symbol,
4685 => {
4686 if (abi_size <= 8) {
4687 const reg = try self.copyToTmpRegister(ty, src_mcv);
4688 return self.genSetStack(ty, frame, .{ .register = reg });
4689 }
4690
4691 try self.genInlineMemcpy(
4692 .{ .lea_frame = frame },
4693 src_mcv.address(),
4694 .{ .immediate = abi_size },
4695 );
4696 },
4697 .air_ref => |ref| try self.genSetStack(ty, frame, try self.resolveInst(ref)),
4698 else => return self.fail("TODO: genSetStack {s}", .{@tagName(src_mcv)}),
4699 }
4700}
4701
47024651fn genInlineMemcpy(
47034652 self: *Self,
47044653 dst_ptr: MCValue,
......@@ -4805,6 +4754,86 @@ fn genInlineMemcpy(
48054754 });
48064755}
48074756
4757fn genInlineMemset(
4758 self: *Self,
4759 dst_ptr: MCValue,
4760 src_value: MCValue,
4761 len: MCValue,
4762) !void {
4763 const regs = try self.register_manager.allocRegs(3, .{null} ** 3, tp);
4764 const locks = self.register_manager.lockRegsAssumeUnused(3, regs);
4765 defer for (locks) |lock| self.register_manager.unlockReg(lock);
4766
4767 const count = regs[0];
4768 const src = regs[1];
4769 const dst = regs[2];
4770
4771 try self.genSetReg(Type.usize, count, len);
4772 try self.genSetReg(Type.usize, src, src_value);
4773 try self.genSetReg(Type.usize, dst, dst_ptr);
4774
4775 // sb src, 0(dst)
4776 const first_inst = try self.addInst(.{
4777 .tag = .sb,
4778 .ops = .rri,
4779 .data = .{
4780 .i_type = .{
4781 .rd = dst,
4782 .rs1 = src,
4783 .imm12 = Immediate.s(0),
4784 },
4785 },
4786 });
4787
4788 // dec count by 1
4789 _ = try self.addInst(.{
4790 .tag = .addi,
4791 .ops = .rri,
4792 .data = .{
4793 .i_type = .{
4794 .rd = count,
4795 .rs1 = count,
4796 .imm12 = Immediate.s(-1),
4797 },
4798 },
4799 });
4800
4801 // branch if count is 0
4802 _ = try self.addInst(.{
4803 .tag = .beq,
4804 .ops = .rr_inst,
4805 .data = .{
4806 .b_type = .{
4807 .inst = @intCast(self.mir_instructions.len + 4), // points after the last inst
4808 .rs1 = count,
4809 .rs2 = .zero,
4810 },
4811 },
4812 });
4813
4814 // increment the pointers
4815 _ = try self.addInst(.{
4816 .tag = .addi,
4817 .ops = .rri,
4818 .data = .{
4819 .i_type = .{
4820 .rd = dst,
4821 .rs1 = dst,
4822 .imm12 = Immediate.s(1),
4823 },
4824 },
4825 });
4826
4827 // jump back to start of loop
4828 _ = try self.addInst(.{
4829 .tag = .pseudo,
4830 .ops = .pseudo_j,
4831 .data = .{
4832 .inst = first_inst,
4833 },
4834 });
4835}
4836
48084837/// Sets the value of `src_mcv` into `reg`. Assumes you have a lock on it.
48094838fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!void {
48104839 const zcu = self.bin_file.comp.module.?;
......@@ -4965,7 +4994,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
49654994 2 => .lh,
49664995 4 => .lw,
49674996 8 => .ld,
4968 else => return self.fail("TODO: genSetReg for size {d}", .{abi_size}),
4997 else => return std.debug.panic("TODO: genSetReg for size {d}", .{abi_size}),
49694998 };
49704999
49715000 _ = try self.addInst(.{
......@@ -4998,6 +5027,126 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError!
49985027 }
49995028}
50005029
5030fn genSetMem(
5031 self: *Self,
5032 base: Memory.Base,
5033 disp: i32,
5034 ty: Type,
5035 src_mcv: MCValue,
5036) InnerError!void {
5037 const mod = self.bin_file.comp.module.?;
5038 const abi_size: u32 = @intCast(ty.abiSize(mod));
5039 const dst_ptr_mcv: MCValue = switch (base) {
5040 .reg => |base_reg| .{ .register_offset = .{ .reg = base_reg, .off = disp } },
5041 .frame => |base_frame_index| .{ .lea_frame = .{ .index = base_frame_index, .off = disp } },
5042 .reloc => |base_symbol| .{ .lea_symbol = .{ .sym = base_symbol.sym_index, .off = disp } },
5043 };
5044 switch (src_mcv) {
5045 .none,
5046 .unreach,
5047 .dead,
5048 .reserved_frame,
5049 => unreachable,
5050 .undef => try self.genInlineMemset(
5051 dst_ptr_mcv,
5052 src_mcv,
5053 .{ .immediate = abi_size },
5054 ),
5055
5056 .register_offset,
5057 .memory,
5058 .indirect,
5059 .load_frame,
5060 .lea_frame,
5061 .load_symbol,
5062 .lea_symbol,
5063 => switch (abi_size) {
5064 0 => {},
5065 1, 2, 4, 8 => {
5066 const src_reg = try self.copyToTmpRegister(ty, src_mcv);
5067 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg);
5068 defer self.register_manager.unlockReg(src_lock);
5069
5070 try self.genSetMem(base, disp, ty, .{ .register = src_reg });
5071 },
5072 else => try self.genInlineMemcpy(
5073 dst_ptr_mcv,
5074 src_mcv.address(),
5075 .{ .immediate = abi_size },
5076 ),
5077 },
5078 .register => |reg| {
5079 const mem_size = switch (base) {
5080 .frame => |base_fi| mem_size: {
5081 assert(disp >= 0);
5082 const frame_abi_size = self.frame_allocs.items(.abi_size)[@intFromEnum(base_fi)];
5083 const frame_spill_pad = self.frame_allocs.items(.spill_pad)[@intFromEnum(base_fi)];
5084 assert(frame_abi_size - frame_spill_pad - disp >= abi_size);
5085 break :mem_size if (frame_abi_size - frame_spill_pad - disp == abi_size)
5086 frame_abi_size
5087 else
5088 abi_size;
5089 },
5090 else => abi_size,
5091 };
5092 const src_size = math.ceilPowerOfTwoAssert(u32, abi_size);
5093 const src_align = Alignment.fromNonzeroByteUnits(math.ceilPowerOfTwoAssert(u32, src_size));
5094 if (src_size > mem_size) {
5095 const frame_index = try self.allocFrameIndex(FrameAlloc.init(.{
5096 .size = src_size,
5097 .alignment = src_align,
5098 }));
5099 const frame_mcv: MCValue = .{ .load_frame = .{ .index = frame_index } };
5100 _ = try self.addInst(.{
5101 .tag = .pseudo,
5102 .ops = .pseudo_store_rm,
5103 .data = .{
5104 .rm = .{ .r = reg, .m = .{
5105 .base = .{ .frame = frame_index },
5106 .mod = .{ .rm = .{
5107 .size = Memory.Size.fromByteSize(src_size),
5108 } },
5109 } },
5110 },
5111 });
5112 try self.genSetMem(base, disp, ty, frame_mcv);
5113 try self.freeValue(frame_mcv);
5114 } else _ = try self.addInst(.{
5115 .tag = .pseudo,
5116 .ops = .pseudo_store_rm,
5117 .data = .{ .rm = .{
5118 .r = reg,
5119 .m = .{
5120 .base = base,
5121 .mod = .{ .rm = .{
5122 .size = self.memSize(ty),
5123 .disp = disp,
5124 } },
5125 },
5126 } },
5127 });
5128 },
5129 .register_pair => |src_regs| {
5130 var part_disp: i32 = disp;
5131 for (try self.splitType(ty), src_regs) |src_ty, src_reg| {
5132 try self.genSetMem(base, part_disp, src_ty, .{ .register = src_reg });
5133 part_disp += @intCast(src_ty.abiSize(mod));
5134 }
5135 },
5136 .immediate => {
5137 // TODO: remove this lock in favor of a copyToTmpRegister when we load 64 bit immediates with
5138 // a register allocation.
5139 const reg, const reg_lock = try self.allocReg();
5140 defer self.register_manager.unlockReg(reg_lock);
5141
5142 try self.genSetReg(ty, reg, src_mcv);
5143
5144 return self.genSetMem(base, disp, ty, .{ .register = reg });
5145 },
5146 .air_ref => |src_ref| try self.genSetMem(base, disp, ty, try self.resolveInst(src_ref)),
5147 }
5148}
5149
50015150fn airIntFromPtr(self: *Self, inst: Air.Inst.Index) !void {
50025151 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op;
50035152 const result = result: {
......@@ -5099,13 +5248,83 @@ fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOr
50995248}
51005249
51015250fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void {
5102 _ = inst;
5103 if (safety) {
5104 // TODO if the value is undef, write 0xaa bytes to dest
5105 } else {
5106 // TODO if the value is undef, don't lower this instruction
5251 const zcu = self.bin_file.comp.module.?;
5252 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
5253
5254 result: {
5255 if (!safety and (try self.resolveInst(bin_op.rhs)) == .undef) break :result;
5256
5257 const dst_ptr = try self.resolveInst(bin_op.lhs);
5258 const dst_ptr_ty = self.typeOf(bin_op.lhs);
5259 const dst_ptr_lock: ?RegisterLock = switch (dst_ptr) {
5260 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
5261 else => null,
5262 };
5263 defer if (dst_ptr_lock) |lock| self.register_manager.unlockReg(lock);
5264
5265 const src_val = try self.resolveInst(bin_op.rhs);
5266 const elem_ty = self.typeOf(bin_op.rhs);
5267 const src_val_lock: ?RegisterLock = switch (src_val) {
5268 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
5269 else => null,
5270 };
5271 defer if (src_val_lock) |lock| self.register_manager.unlockReg(lock);
5272
5273 const elem_abi_size: u31 = @intCast(elem_ty.abiSize(zcu));
5274
5275 if (elem_abi_size == 1) {
5276 const ptr: MCValue = switch (dst_ptr_ty.ptrSize(zcu)) {
5277 // TODO: this only handles slices stored in the stack
5278 .Slice => dst_ptr,
5279 .One => dst_ptr,
5280 .C, .Many => unreachable,
5281 };
5282 const len: MCValue = switch (dst_ptr_ty.ptrSize(zcu)) {
5283 // TODO: this only handles slices stored in the stack
5284 .Slice => dst_ptr.address().offset(8).deref(),
5285 .One => .{ .immediate = dst_ptr_ty.childType(zcu).arrayLen(zcu) },
5286 .C, .Many => unreachable,
5287 };
5288 const len_lock: ?RegisterLock = switch (len) {
5289 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
5290 else => null,
5291 };
5292 defer if (len_lock) |lock| self.register_manager.unlockReg(lock);
5293
5294 try self.genInlineMemset(ptr, src_val, len);
5295 break :result;
5296 }
5297
5298 // Store the first element, and then rely on memcpy copying forwards.
5299 // Length zero requires a runtime check - so we handle arrays specially
5300 // here to elide it.
5301 switch (dst_ptr_ty.ptrSize(zcu)) {
5302 .Slice => return self.fail("TODO: airMemset Slices", .{}),
5303 .One => {
5304 const elem_ptr_ty = try zcu.singleMutPtrType(elem_ty);
5305
5306 const len = dst_ptr_ty.childType(zcu).arrayLen(zcu);
5307
5308 assert(len != 0); // prevented by Sema
5309 try self.store(dst_ptr, src_val, elem_ptr_ty, elem_ty);
5310
5311 const second_elem_ptr_reg, const second_elem_ptr_lock = try self.allocReg();
5312 defer self.register_manager.unlockReg(second_elem_ptr_lock);
5313
5314 const second_elem_ptr_mcv: MCValue = .{ .register = second_elem_ptr_reg };
5315
5316 try self.genSetReg(Type.usize, second_elem_ptr_reg, .{ .register_offset = .{
5317 .reg = try self.copyToTmpRegister(Type.usize, dst_ptr),
5318 .off = elem_abi_size,
5319 } });
5320
5321 const bytes_to_copy: MCValue = .{ .immediate = elem_abi_size * (len - 1) };
5322 try self.genInlineMemcpy(second_elem_ptr_mcv, dst_ptr, bytes_to_copy);
5323 },
5324 .C, .Many => unreachable,
5325 }
51075326 }
5108 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
5327 return self.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none });
51095328}
51105329
51115330fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
......@@ -5190,32 +5409,66 @@ fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
51905409 const len: usize = @intCast(result_ty.arrayLen(zcu));
51915410 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
51925411 const elements: []const Air.Inst.Ref = @ptrCast(self.air.extra[ty_pl.payload..][0..len]);
5412
51935413 const result: MCValue = result: {
51945414 switch (result_ty.zigTypeTag(zcu)) {
51955415 .Struct => {
51965416 const frame_index = try self.allocFrameIndex(FrameAlloc.initSpill(result_ty, zcu));
5417 if (result_ty.containerLayout(zcu) == .@"packed") {
5418 const struct_obj = zcu.typeToStruct(result_ty).?;
5419 try self.genInlineMemset(
5420 .{ .lea_frame = .{ .index = frame_index } },
5421 .{ .immediate = 0 },
5422 .{ .immediate = result_ty.abiSize(zcu) },
5423 );
5424
5425 for (elements, 0..) |elem, elem_i_usize| {
5426 const elem_i: u32 = @intCast(elem_i_usize);
5427 if ((try result_ty.structFieldValueComptime(zcu, elem_i)) != null) continue;
5428
5429 const elem_ty = result_ty.structFieldType(elem_i, zcu);
5430 const elem_bit_size: u32 = @intCast(elem_ty.bitSize(zcu));
5431 if (elem_bit_size > 64) {
5432 return self.fail(
5433 "TODO airAggregateInit implement packed structs with large fields",
5434 .{},
5435 );
5436 }
51975437
5198 if (result_ty.containerLayout(zcu) == .@"packed") {} else for (elements, 0..) |elem, elem_i| {
5438 const elem_abi_size: u32 = @intCast(elem_ty.abiSize(zcu));
5439 const elem_abi_bits = elem_abi_size * 8;
5440 const elem_off = zcu.structPackedFieldBitOffset(struct_obj, elem_i);
5441 const elem_byte_off: i32 = @intCast(elem_off / elem_abi_bits * elem_abi_size);
5442 const elem_bit_off = elem_off % elem_abi_bits;
5443 const elem_mcv = try self.resolveInst(elem);
5444
5445 _ = elem_byte_off;
5446 _ = elem_bit_off;
5447
5448 const elem_lock = switch (elem_mcv) {
5449 .register => |reg| self.register_manager.lockReg(reg),
5450 .immediate => |imm| lock: {
5451 if (imm == 0) continue;
5452 break :lock null;
5453 },
5454 else => null,
5455 };
5456 defer if (elem_lock) |lock| self.register_manager.unlockReg(lock);
5457
5458 return self.fail("TODO: airAggregateInit packed structs", .{});
5459 }
5460 } else for (elements, 0..) |elem, elem_i| {
51995461 if ((try result_ty.structFieldValueComptime(zcu, elem_i)) != null) continue;
52005462
52015463 const elem_ty = result_ty.structFieldType(elem_i, zcu);
52025464 const elem_off: i32 = @intCast(result_ty.structFieldOffset(elem_i, zcu));
52035465 const elem_mcv = try self.resolveInst(elem);
5204
5205 const elem_frame: FrameAddr = .{
5206 .index = frame_index,
5207 .off = elem_off,
5208 };
5209 try self.genSetStack(
5210 elem_ty,
5211 elem_frame,
5212 elem_mcv,
5213 );
5466 try self.genSetMem(.{ .frame = frame_index }, elem_off, elem_ty, elem_mcv);
52145467 }
5468 break :result .{ .load_frame = .{ .index = frame_index } };
52155469 },
5216 else => return self.fail("TODO: airAggregateInit {}", .{result_ty.fmt(zcu)}),
5470 else => return self.fail("TODO: airAggregate {}", .{result_ty.fmt(zcu)}),
52175471 }
5218 break :result .{ .register = .zero };
52195472 };
52205473
52215474 if (elements.len <= Liveness.bpi - 1) {
src/arch/riscv64/abi.zig+13
......@@ -169,6 +169,19 @@ pub fn classifySystem(ty: Type, zcu: *Module) [8]Class {
169169
170170 return memory_class;
171171 },
172 .Array => {
173 const ty_size = ty.abiSize(zcu);
174 if (ty_size <= 8) {
175 result[0] = .integer;
176 return result;
177 }
178 if (ty_size <= 16) {
179 result[0] = .integer;
180 result[1] = .integer;
181 return result;
182 }
183 return memory_class;
184 },
172185 else => |bad_ty| std.debug.panic("classifySystem {s}", .{@tagName(bad_ty)}),
173186 }
174187}
src/arch/riscv64/bits.zig+19-8
......@@ -35,10 +35,10 @@ pub const Memory = struct {
3535
3636 pub fn fromByteSize(size: u64) Size {
3737 return switch (size) {
38 1 => .byte,
39 2 => .hword,
40 4 => .word,
41 8 => .dword,
38 1...1 => .byte,
39 2...2 => .hword,
40 3...4 => .word,
41 5...8 => .dword,
4242 else => unreachable,
4343 };
4444 }
......@@ -149,10 +149,8 @@ pub const Immediate = union(enum) {
149149
150150pub const Register = enum(u6) {
151151 // zig fmt: off
152 x0, x1, x2, x3, x4, x5, x6, x7,
153 x8, x9, x10, x11, x12, x13, x14, x15,
154 x16, x17, x18, x19, x20, x21, x22, x23,
155 x24, x25, x26, x27, x28, x29, x30, x31,
152
153 // general purpose registers
156154
157155 zero, // zero
158156 ra, // return address. caller saved
......@@ -166,6 +164,13 @@ pub const Register = enum(u6) {
166164 a2, a3, a4, a5, a6, a7, // fn args. caller saved.
167165 s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, // saved registers. callee saved.
168166 t3, t4, t5, t6, // caller saved
167
168 x0, x1, x2, x3, x4, x5, x6, x7,
169 x8, x9, x10, x11, x12, x13, x14, x15,
170 x16, x17, x18, x19, x20, x21, x22, x23,
171 x24, x25, x26, x27, x28, x29, x30, x31,
172
173
169174 // zig fmt: on
170175
171176 /// Returns the unique 5-bit ID of this register which is used in
......@@ -177,6 +182,12 @@ pub const Register = enum(u6) {
177182 pub fn dwarfLocOp(reg: Register) u8 {
178183 return @as(u8, reg.id());
179184 }
185
186 pub fn bitSize(reg: Register) u32 {
187 return switch (@intFromEnum(reg)) {
188 @intFromEnum(Register.zero)...@intFromEnum(Register.x31) => 64,
189 };
190 }
180191};
181192
182193pub const FrameIndex = enum(u32) {
test/behavior/alignof.zig-2
......@@ -29,8 +29,6 @@ test "comparison of @alignOf(T) against zero" {
2929}
3030
3131test "correct alignment for elements and slices of aligned array" {
32 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
33
3432 var buf: [1024]u8 align(64) = undefined;
3533 var start: usize = 1;
3634 var end: usize = undefined;
test/behavior/array.zig-4
......@@ -231,7 +231,6 @@ test "nested arrays of integers" {
231231test "implicit comptime in array type size" {
232232 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
233233 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
234 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
235234
236235 var arr: [plusOne(10)]bool = undefined;
237236 _ = &arr;
......@@ -505,7 +504,6 @@ test "anonymous literal in array" {
505504test "access the null element of a null terminated array" {
506505 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
507506 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
508 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
509507
510508 const S = struct {
511509 fn doTheTest() !void {
......@@ -778,8 +776,6 @@ test "slicing array of zero-sized values" {
778776}
779777
780778test "array init with no result pointer sets field result types" {
781 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
782
783779 const S = struct {
784780 // A function parameter has a result type, but no result pointer.
785781 fn f(arr: [1]u32) u32 {
test/behavior/cast.zig-3
......@@ -1695,7 +1695,6 @@ test "peer type resolution: const sentinel slice and mutable non-sentinel slice"
16951695 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
16961696 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
16971697 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1698 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
16991698
17001699 const S = struct {
17011700 fn doTheTest(comptime T: type, comptime s: T) !void {
......@@ -1866,7 +1865,6 @@ test "peer type resolution: C pointer and @TypeOf(null)" {
18661865 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
18671866 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
18681867 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1869 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
18701868
18711869 var a: [*c]c_int = 0x1000;
18721870 _ = &a;
......@@ -2240,7 +2238,6 @@ test "peer type resolution: C pointer and many pointer" {
22402238 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
22412239 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
22422240 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2243 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
22442241
22452242 var buf = "hello".*;
22462243
test/behavior/eval.zig-1
......@@ -73,7 +73,6 @@ fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 {
7373test "constant expressions" {
7474 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
7575 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
76 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
7776
7877 var array: [array_size]u8 = undefined;
7978 _ = &array;
test/behavior/extern.zig-2
......@@ -20,7 +20,6 @@ test "function extern symbol" {
2020 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
2121 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
2222 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
23 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
2423
2524 const a = @extern(*const fn () callconv(.C) i32, .{ .name = "a_mystery_function" });
2625 try expect(a() == 4567);
......@@ -34,7 +33,6 @@ test "function extern symbol matches extern decl" {
3433 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
3534 if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest;
3635 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
37 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
3836
3937 const S = struct {
4038 extern fn another_mystery_function() u32;
test/behavior/fn.zig-2
......@@ -414,8 +414,6 @@ test "ability to give comptime types and non comptime types to same parameter" {
414414}
415415
416416test "function with inferred error set but returning no error" {
417 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
418
419417 const S = struct {
420418 fn foo() !void {}
421419 };
test/behavior/for.zig-1
......@@ -487,7 +487,6 @@ test "ref counter that starts at zero" {
487487 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
488488 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
489489 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
490 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
491490
492491 for ([_]usize{ 0, 1, 2 }, 0..) |i, j| {
493492 try expectEqual(i, j);
test/behavior/generics.zig-1
......@@ -117,7 +117,6 @@ test "function with return type type" {
117117 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
118118 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
119119 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
120 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
121120
122121 var list: List(i32) = undefined;
123122 var list2: List(i32) = undefined;
test/behavior/maximum_minimum.zig-1
......@@ -300,7 +300,6 @@ test "@min/@max notices bounds from vector types when element of comptime-known
300300 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
301301 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
302302 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
303 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
304303 if (builtin.zig_backend == .stage2_x86_64 and
305304 !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .avx)) return error.SkipZigTest;
306305
test/behavior/member_func.zig-1
......@@ -76,7 +76,6 @@ test "@field field calls" {
7676 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
7777 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
7878 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
79 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
8079
8180 try expect(@field(HasFuncs, "one")(0) == 1);
8281 try expect(@field(HasFuncs, "two")(0) == 2);
test/behavior/memset.zig-3
......@@ -73,7 +73,6 @@ test "memset with bool element" {
7373 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
7474 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
7575 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
76 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
7776
7877 var buf: [5]bool = undefined;
7978 @memset(&buf, true);
......@@ -86,7 +85,6 @@ test "memset with 1-byte struct element" {
8685 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
8786 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
8887 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
89 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
9088
9189 const S = struct { x: bool };
9290 var buf: [5]S = undefined;
......@@ -100,7 +98,6 @@ test "memset with 1-byte array element" {
10098 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
10199 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
102100 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
103 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
104101
105102 const A = [1]bool;
106103 var buf: [5]A = undefined;
test/behavior/packed-struct.zig-1
......@@ -435,7 +435,6 @@ test "nested packed struct field pointers" {
435435 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
436436 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
437437 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // ubsan unaligned pointer access
438 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
439438 if (native_endian != .little) return error.SkipZigTest; // Byte aligned packed struct field pointers have not been implemented yet
440439
441440 const S2 = packed struct {
test/behavior/ptrcast.zig-1
......@@ -58,7 +58,6 @@ fn testReinterpretStructWrappedBytesAsInteger() !void {
5858test "reinterpret bytes of an array into an extern struct" {
5959 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
6060 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
61 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
6261
6362 try testReinterpretBytesAsExternStruct();
6463 try comptime testReinterpretBytesAsExternStruct();
test/behavior/ptrfromint.zig-1
......@@ -47,7 +47,6 @@ test "@ptrFromInt creates allowzero zero pointer" {
4747 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
4848 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
4949 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
50 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
5150
5251 const ptr = @as(*allowzero u32, @ptrFromInt(0));
5352 try expectEqual(@as(usize, 0), @intFromPtr(ptr));
test/behavior/return_address.zig-1
......@@ -10,7 +10,6 @@ test "return address" {
1010 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1111 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1212 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
13 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1413
1514 _ = retAddr();
1615 // TODO: #14938
test/behavior/struct.zig-10
......@@ -111,7 +111,6 @@ fn testMutation(foo: *StructFoo) void {
111111
112112test "struct byval assign" {
113113 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
114 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
115114
116115 var foo1: StructFoo = undefined;
117116 var foo2: StructFoo = undefined;
......@@ -300,7 +299,6 @@ const Val = struct {
300299test "struct point to self" {
301300 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
302301 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
303 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
304302
305303 var root: Node = undefined;
306304 root.val.x = 1;
......@@ -1023,7 +1021,6 @@ test "struct with 0-length union array field" {
10231021 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
10241022 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10251023 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1026 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
10271024
10281025 const U = union {
10291026 a: u32,
......@@ -1732,7 +1729,6 @@ test "extern struct field pointer has correct alignment" {
17321729 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
17331730 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
17341731 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1735 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
17361732
17371733 const S = struct {
17381734 fn doTheTest() !void {
......@@ -1863,8 +1859,6 @@ test "comptimeness of optional and error union payload is analyzed properly" {
18631859}
18641860
18651861test "initializer uses own alignment" {
1866 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1867
18681862 const S = struct {
18691863 x: u32 = @alignOf(@This()) + 1,
18701864 };
......@@ -1876,8 +1870,6 @@ test "initializer uses own alignment" {
18761870}
18771871
18781872test "initializer uses own size" {
1879 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1880
18811873 const S = struct {
18821874 x: u32 = @sizeOf(@This()) + 1,
18831875 };
......@@ -1889,8 +1881,6 @@ test "initializer uses own size" {
18891881}
18901882
18911883test "initializer takes a pointer to a variable inside its struct" {
1892 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1893
18941884 const namespace = struct {
18951885 const S = struct {
18961886 s: *S = &S.instance,
test/behavior/tuple.zig-2
......@@ -579,8 +579,6 @@ test "comptime fields in tuple can be initialized" {
579579}
580580
581581test "tuple default values" {
582 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
583
584582 const T = struct {
585583 usize,
586584 usize = 123,
test/behavior/union.zig-5
......@@ -1744,7 +1744,6 @@ test "union with 128 bit integer" {
17441744
17451745test "memset extern union" {
17461746 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1747 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
17481747
17491748 const U = extern union {
17501749 foo: u8,
......@@ -1766,7 +1765,6 @@ test "memset extern union" {
17661765
17671766test "memset packed union" {
17681767 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
1769 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
17701768
17711769 const U = packed union {
17721770 a: u32,
......@@ -1977,8 +1975,6 @@ test "reinterpret packed union inside packed struct" {
19771975}
19781976
19791977test "inner struct initializer uses union layout" {
1980 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
1981
19821978 const namespace = struct {
19831979 const U = union {
19841980 a: struct {
......@@ -2004,7 +2000,6 @@ test "inner struct initializer uses union layout" {
20042000
20052001test "inner struct initializer uses packed union layout" {
20062002 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
2007 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
20082003
20092004 const namespace = struct {
20102005 const U = packed union {