authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-02-18 15:18:01+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-02-18 15:18:01+01:00
log5af9d0c603fa5a4e318326a6d2603131f5da4138
tree4a7cd7ec4ee32cbb518fad21c40a689bf8c5f637
parent53241f288e40a9e97a5425cb0e1ac7dbbc9de852
parent0f0bb7e5ea2aa4216dcbec57086d2d5c7a84625e
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #10916 from ziglang/x64-args-stack-debug

stage2,x64: pass all args on stack in debug and if not extern fn

4 files changed, 264 insertions(+), 199 deletions(-)

src/arch/x86_64/CodeGen.zig+238-177
......@@ -883,7 +883,8 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
883883/// Allocates a new register and copies `mcv` into it.
884884/// `reg_owner` is the instruction that gets associated with the register in the register table.
885885/// This can have a side effect of spilling instructions to the stack to free up a register.
886fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, ty: Type, mcv: MCValue) !MCValue {
886/// WARNING make sure that the allocated register matches the returned MCValue from an instruction!
887fn copyToRegisterWithInstTracking(self: *Self, reg_owner: Air.Inst.Index, ty: Type, mcv: MCValue) !MCValue {
887888 const reg = try self.register_manager.allocReg(reg_owner);
888889 try self.genSetReg(ty, reg, mcv);
889890 return MCValue{ .register = reg };
......@@ -939,7 +940,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
939940 operand.freezeIfRegister(&self.register_manager);
940941 defer operand.unfreezeIfRegister(&self.register_manager);
941942
942 break :blk try self.copyToNewRegister(inst, dest_ty, operand);
943 break :blk try self.copyToRegisterWithInstTracking(inst, dest_ty, operand);
943944 };
944945
945946 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
......@@ -970,7 +971,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
970971 break :blk operand.register.to64();
971972 }
972973 }
973 const mcv = try self.copyToNewRegister(inst, src_ty, operand);
974 const mcv = try self.copyToRegisterWithInstTracking(inst, src_ty, operand);
974975 break :blk mcv.register.to64();
975976 };
976977
......@@ -1088,7 +1089,7 @@ fn genPtrBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_r
10881089 if (self.reuseOperand(inst, op_lhs, 0, ptr)) {
10891090 if (ptr.isMemory() or ptr.isRegister()) break :blk ptr;
10901091 }
1091 break :blk try self.copyToNewRegister(inst, dst_ty, ptr);
1092 break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, ptr);
10921093 };
10931094
10941095 const offset_mcv = blk: {
......@@ -1338,7 +1339,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
13381339 if (self.reuseOperand(inst, ty_op.operand, 0, operand)) {
13391340 break :result operand;
13401341 }
1341 break :result try self.copyToNewRegister(inst, self.air.typeOfIndex(inst), operand);
1342 break :result try self.copyToRegisterWithInstTracking(inst, self.air.typeOfIndex(inst), operand);
13421343 };
13431344 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
13441345}
......@@ -1500,52 +1501,60 @@ fn elemOffset(self: *Self, index_ty: Type, index: MCValue, elem_size: u64) !Regi
15001501 return reg;
15011502}
15021503
1504fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue {
1505 const slice_ty = self.air.typeOf(lhs);
1506 const slice_mcv = try self.resolveInst(lhs);
1507 slice_mcv.freezeIfRegister(&self.register_manager);
1508 defer slice_mcv.unfreezeIfRegister(&self.register_manager);
1509
1510 const elem_ty = slice_ty.childType();
1511 const elem_size = elem_ty.abiSize(self.target.*);
1512 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
1513 const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);
1514
1515 const index_ty = self.air.typeOf(rhs);
1516 const index_mcv = try self.resolveInst(rhs);
1517 index_mcv.freezeIfRegister(&self.register_manager);
1518 defer index_mcv.unfreezeIfRegister(&self.register_manager);
1519
1520 const offset_reg = try self.elemOffset(index_ty, index_mcv, elem_size);
1521 self.register_manager.freezeRegs(&.{offset_reg});
1522 defer self.register_manager.unfreezeRegs(&.{offset_reg});
1523
1524 const addr_reg = try self.register_manager.allocReg(null);
1525 switch (slice_mcv) {
1526 .stack_offset => |off| {
1527 // mov reg, [rbp - 8]
1528 _ = try self.addInst(.{
1529 .tag = .mov,
1530 .ops = (Mir.Ops{
1531 .reg1 = addr_reg.to64(),
1532 .reg2 = .rbp,
1533 .flags = 0b01,
1534 }).encode(),
1535 .data = .{ .imm = @bitCast(u32, -@intCast(i32, off)) },
1536 });
1537 },
1538 else => return self.fail("TODO implement slice_elem_ptr when slice is {}", .{slice_mcv}),
1539 }
1540 // TODO we could allocate register here, but need to expect addr register and potentially
1541 // offset register.
1542 try self.genBinMathOpMir(.add, slice_ptr_field_type, .{ .register = addr_reg.to64() }, .{
1543 .register = offset_reg.to64(),
1544 });
1545 return MCValue{ .register = addr_reg.to64() };
1546}
1547
15031548fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
15041549 const is_volatile = false; // TODO
15051550 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
15061551 const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else result: {
15071552 const slice_ty = self.air.typeOf(bin_op.lhs);
1508 const slice_mcv = try self.resolveInst(bin_op.lhs);
1509 slice_mcv.freezeIfRegister(&self.register_manager);
1510 defer slice_mcv.unfreezeIfRegister(&self.register_manager);
1511
1512 const elem_ty = slice_ty.childType();
1513 const elem_size = elem_ty.abiSize(self.target.*);
15141553 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
15151554 const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);
1516
1517 const index_ty = self.air.typeOf(bin_op.rhs);
1518 const index_mcv = try self.resolveInst(bin_op.rhs);
1519 index_mcv.freezeIfRegister(&self.register_manager);
1520 defer index_mcv.unfreezeIfRegister(&self.register_manager);
1521
1522 const offset_reg = try self.elemOffset(index_ty, index_mcv, elem_size);
1523 self.register_manager.freezeRegs(&.{offset_reg});
1524 defer self.register_manager.unfreezeRegs(&.{offset_reg});
1525
1526 const addr_reg = try self.register_manager.allocReg(null);
1527 switch (slice_mcv) {
1528 .stack_offset => |off| {
1529 // mov reg, [rbp - 8]
1530 _ = try self.addInst(.{
1531 .tag = .mov,
1532 .ops = (Mir.Ops{
1533 .reg1 = addr_reg.to64(),
1534 .reg2 = .rbp,
1535 .flags = 0b01,
1536 }).encode(),
1537 .data = .{ .imm = @bitCast(u32, -@intCast(i32, off)) },
1538 });
1539 },
1540 else => return self.fail("TODO implement slice_elem_val when slice is {}", .{slice_mcv}),
1541 }
1542 // TODO we could allocate register here, but need to expect addr register and potentially
1543 // offset register.
1555 const elem_ptr = try self.genSliceElemPtr(bin_op.lhs, bin_op.rhs);
15441556 const dst_mcv = try self.allocRegOrMem(inst, false);
1545 try self.genBinMathOpMir(.add, slice_ptr_field_type, .{ .register = addr_reg.to64() }, .{
1546 .register = offset_reg.to64(),
1547 });
1548 try self.load(dst_mcv, .{ .register = addr_reg.to64() }, slice_ptr_field_type);
1557 try self.load(dst_mcv, elem_ptr, slice_ptr_field_type);
15491558 break :result dst_mcv;
15501559 };
15511560 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
......@@ -1557,7 +1566,7 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
15571566 const result: MCValue = if (self.liveness.isUnused(inst))
15581567 .dead
15591568 else
1560 return self.fail("TODO implement slice_elem_ptr for {}", .{self.target.cpu.arch});
1569 try self.genSliceElemPtr(extra.lhs, extra.rhs);
15611570 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
15621571}
15631572
......@@ -1571,6 +1580,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
15711580
15721581 const elem_ty = array_ty.childType();
15731582 const elem_abi_size = elem_ty.abiSize(self.target.*);
1583
15741584 const index_ty = self.air.typeOf(bin_op.rhs);
15751585 const index = try self.resolveInst(bin_op.rhs);
15761586 index.freezeIfRegister(&self.register_manager);
......@@ -1580,21 +1590,43 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
15801590 self.register_manager.freezeRegs(&.{offset_reg});
15811591 defer self.register_manager.unfreezeRegs(&.{offset_reg});
15821592
1583 const addr_reg = try self.register_manager.allocReg(null);
1584 switch (array) {
1585 .stack_offset => |off| {
1586 // lea reg, [rbp]
1587 _ = try self.addInst(.{
1588 .tag = .lea,
1589 .ops = (Mir.Ops{
1590 .reg1 = addr_reg.to64(),
1591 .reg2 = .rbp,
1592 }).encode(),
1593 .data = .{ .imm = @bitCast(u32, -off) },
1594 });
1595 },
1596 else => return self.fail("TODO implement array_elem_val when array is {}", .{array}),
1597 }
1593 const addr_reg = blk: {
1594 const off = inner: {
1595 switch (array) {
1596 .register => {
1597 const off = @intCast(i32, try self.allocMem(
1598 inst,
1599 @intCast(u32, array_ty.abiSize(self.target.*)),
1600 array_ty.abiAlignment(self.target.*),
1601 ));
1602 try self.genSetStack(array_ty, off, array);
1603 break :inner off;
1604 },
1605 .stack_offset => |off| {
1606 break :inner off;
1607 },
1608 .memory,
1609 .got_load,
1610 .direct_load,
1611 => {
1612 break :blk try self.loadMemPtrIntoRegister(Type.usize, array);
1613 },
1614 else => return self.fail("TODO implement array_elem_val when array is {}", .{array}),
1615 }
1616 };
1617 const addr_reg = try self.register_manager.allocReg(null);
1618 // lea reg, [rbp]
1619 _ = try self.addInst(.{
1620 .tag = .lea,
1621 .ops = (Mir.Ops{
1622 .reg1 = addr_reg.to64(),
1623 .reg2 = .rbp,
1624 }).encode(),
1625 .data = .{ .imm = @bitCast(u32, -off) },
1626 });
1627 break :blk addr_reg.to64();
1628 };
1629
15981630 // TODO we could allocate register here, but need to expect addr register and potentially
15991631 // offset register.
16001632 const dst_mcv = try self.allocRegOrMem(inst, false);
......@@ -1635,7 +1667,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
16351667 self.register_manager.freezeRegs(&.{offset_reg});
16361668 defer self.register_manager.unfreezeRegs(&.{offset_reg});
16371669
1638 const dst_mcv = try self.copyToNewRegister(inst, ptr_ty, ptr);
1670 const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr);
16391671 try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg });
16401672 break :result dst_mcv;
16411673 };
......@@ -1693,7 +1725,13 @@ fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
16931725 return self.finishAir(inst, result, .{ un_op, .none, .none });
16941726}
16951727
1696fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
1728fn reuseOperand(
1729 self: *Self,
1730 inst: Air.Inst.Index,
1731 operand: Air.Inst.Ref,
1732 op_index: Liveness.OperandInt,
1733 mcv: MCValue,
1734) bool {
16971735 if (!self.liveness.operandDies(inst, op_index))
16981736 return false;
16991737
......@@ -1737,6 +1775,10 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
17371775 .immediate => |imm| {
17381776 try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm });
17391777 },
1778 .stack_offset => {
1779 const reg = try self.copyToTmpRegister(ptr_ty, ptr);
1780 try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
1781 },
17401782 .ptr_stack_offset => |off| {
17411783 try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off });
17421784 },
......@@ -1787,9 +1829,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
17871829 const reg = try self.copyToTmpRegister(ptr_ty, ptr);
17881830 try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
17891831 },
1790 .stack_offset => {
1791 return self.fail("TODO implement loading from MCValue.stack_offset", .{});
1792 },
17931832 }
17941833}
17951834
......@@ -1819,6 +1858,42 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
18191858 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
18201859}
18211860
1861fn loadMemPtrIntoRegister(self: *Self, ptr_ty: Type, ptr: MCValue) InnerError!Register {
1862 switch (ptr) {
1863 .got_load,
1864 .direct_load,
1865 => |sym_index| {
1866 const flags: u2 = switch (ptr) {
1867 .got_load => 0b00,
1868 .direct_load => 0b01,
1869 else => unreachable,
1870 };
1871 const reg = try self.register_manager.allocReg(null);
1872 _ = try self.addInst(.{
1873 .tag = .lea_pie,
1874 .ops = (Mir.Ops{
1875 .reg1 = reg.to64(),
1876 .flags = flags,
1877 }).encode(),
1878 .data = .{
1879 .load_reloc = .{
1880 .atom_index = self.mod_fn.owner_decl.link.macho.local_sym_index,
1881 .sym_index = sym_index,
1882 },
1883 },
1884 });
1885 return reg.to64();
1886 },
1887 .memory => |addr| {
1888 // TODO: in case the address fits in an imm32 we can use [ds:imm32]
1889 // instead of wasting an instruction copying the address to a register
1890 const reg = try self.copyToTmpRegister(ptr_ty, .{ .immediate = addr });
1891 return reg.to64();
1892 },
1893 else => unreachable,
1894 }
1895}
1896
18221897fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void {
18231898 _ = ptr_ty;
18241899 const abi_size = value_ty.abiSize(self.target.*);
......@@ -1832,6 +1907,10 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
18321907 .immediate => |imm| {
18331908 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);
18341909 },
1910 .stack_offset => {
1911 const reg = try self.copyToTmpRegister(ptr_ty, ptr);
1912 try self.store(.{ .register = reg }, value, ptr_ty, value_ty);
1913 },
18351914 .ptr_stack_offset => |off| {
18361915 try self.genSetStack(value_ty, off, value);
18371916 },
......@@ -1909,6 +1988,10 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
19091988 .data = .{ .imm = 0 },
19101989 });
19111990 },
1991 .stack_offset => {
1992 const tmp_reg = try self.copyToTmpRegister(value_ty, value);
1993 return self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty);
1994 },
19121995 else => |other| {
19131996 return self.fail("TODO implement set pointee with {}", .{other});
19141997 },
......@@ -1921,41 +2004,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
19212004 value.freezeIfRegister(&self.register_manager);
19222005 defer value.unfreezeIfRegister(&self.register_manager);
19232006
1924 const addr_reg: Register = blk: {
1925 switch (ptr) {
1926 .got_load,
1927 .direct_load,
1928 => |sym_index| {
1929 const flags: u2 = switch (ptr) {
1930 .got_load => 0b00,
1931 .direct_load => 0b01,
1932 else => unreachable,
1933 };
1934 const addr_reg = try self.register_manager.allocReg(null);
1935 _ = try self.addInst(.{
1936 .tag = .lea_pie,
1937 .ops = (Mir.Ops{
1938 .reg1 = addr_reg.to64(),
1939 .flags = flags,
1940 }).encode(),
1941 .data = .{
1942 .load_reloc = .{
1943 .atom_index = self.mod_fn.owner_decl.link.macho.local_sym_index,
1944 .sym_index = sym_index,
1945 },
1946 },
1947 });
1948 break :blk addr_reg;
1949 },
1950 .memory => |addr| {
1951 // TODO: in case the address fits in an imm32 we can use [ds:imm32]
1952 // instead of wasting an instruction copying the address to a register
1953 const addr_reg = try self.copyToTmpRegister(ptr_ty, .{ .immediate = addr });
1954 break :blk addr_reg;
1955 },
1956 else => unreachable,
1957 }
1958 };
2007 const addr_reg = try self.loadMemPtrIntoRegister(ptr_ty, ptr);
19592008
19602009 // to get the actual address of the value we want to modify we have to go through the GOT
19612010 // mov reg, [reg]
......@@ -2020,9 +2069,6 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
20202069 else => return self.fail("TODO implement storing {} to MCValue.memory", .{value}),
20212070 }
20222071 },
2023 .stack_offset => {
2024 return self.fail("TODO implement storing to MCValue.stack_offset", .{});
2025 },
20262072 }
20272073}
20282074
......@@ -2068,7 +2114,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
20682114 self.register_manager.freezeRegs(&.{offset_reg});
20692115 defer self.register_manager.unfreezeRegs(&.{offset_reg});
20702116
2071 const dst_mcv = try self.copyToNewRegister(inst, ptr_ty, mcv);
2117 const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, mcv);
20722118 try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg });
20732119 break :result dst_mcv;
20742120 },
......@@ -2129,7 +2175,9 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
21292175 if (self.reuseOperand(inst, operand, 0, mcv)) {
21302176 break :blk mcv;
21312177 } else {
2132 const dst_mcv = try self.copyToNewRegister(inst, Type.usize, .{ .register = reg.to64() });
2178 const dst_mcv = try self.copyToRegisterWithInstTracking(inst, Type.usize, .{
2179 .register = reg.to64(),
2180 });
21332181 break :blk dst_mcv;
21342182 }
21352183 };
......@@ -2192,7 +2240,7 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:
21922240 // LHS dies; use it as the destination.
21932241 // Both operands cannot be memory.
21942242 if (lhs.isMemory() and rhs.isMemory()) {
2195 dst_mcv = try self.copyToNewRegister(inst, dst_ty, lhs);
2243 dst_mcv = try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs);
21962244 src_mcv = rhs;
21972245 } else {
21982246 dst_mcv = lhs;
......@@ -2202,7 +2250,7 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:
22022250 // RHS dies; use it as the destination.
22032251 // Both operands cannot be memory.
22042252 if (lhs.isMemory() and rhs.isMemory()) {
2205 dst_mcv = try self.copyToNewRegister(inst, dst_ty, rhs);
2253 dst_mcv = try self.copyToRegisterWithInstTracking(inst, dst_ty, rhs);
22062254 src_mcv = lhs;
22072255 } else {
22082256 dst_mcv = rhs;
......@@ -2213,13 +2261,13 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:
22132261 rhs.freezeIfRegister(&self.register_manager);
22142262 defer rhs.unfreezeIfRegister(&self.register_manager);
22152263
2216 dst_mcv = try self.copyToNewRegister(inst, dst_ty, lhs);
2264 dst_mcv = try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs);
22172265 src_mcv = rhs;
22182266 } else {
22192267 lhs.freezeIfRegister(&self.register_manager);
22202268 defer lhs.unfreezeIfRegister(&self.register_manager);
22212269
2222 dst_mcv = try self.copyToNewRegister(inst, dst_ty, rhs);
2270 dst_mcv = try self.copyToRegisterWithInstTracking(inst, dst_ty, rhs);
22232271 src_mcv = lhs;
22242272 }
22252273 }
......@@ -2228,13 +2276,14 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:
22282276 // A potential opportunity for future optimization here would be keeping track
22292277 // of the fact that the instruction is available both as an immediate
22302278 // and as a register.
2279 // TODO consolidate with limitImmediateType() function
22312280 switch (src_mcv) {
22322281 .immediate => |imm| {
22332282 if (imm > math.maxInt(u31)) {
22342283 dst_mcv.freezeIfRegister(&self.register_manager);
22352284 defer dst_mcv.unfreezeIfRegister(&self.register_manager);
22362285
2237 src_mcv = try self.copyToNewRegister(inst, Type.u64, src_mcv);
2286 src_mcv = MCValue{ .register = try self.copyToTmpRegister(Type.usize, src_mcv) };
22382287 }
22392288 },
22402289 else => {},
......@@ -2452,7 +2501,18 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
24522501 return self.genIMulOpMir(dst_ty, dst_mcv, MCValue{ .register = src_reg });
24532502 }
24542503 },
2455 .embedded_in_code, .memory, .stack_offset => {
2504 .stack_offset => |off| {
2505 _ = try self.addInst(.{
2506 .tag = .imul_complex,
2507 .ops = (Mir.Ops{
2508 .reg1 = dst_reg,
2509 .reg2 = .rbp,
2510 .flags = 0b01,
2511 }).encode(),
2512 .data = .{ .imm = @bitCast(u32, -off) },
2513 });
2514 },
2515 .embedded_in_code, .memory => {
24562516 return self.fail("TODO implement x86 multiply source memory", .{});
24572517 },
24582518 .got_load, .direct_load => {
......@@ -2521,16 +2581,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
25212581 self.arg_index += 1;
25222582
25232583 const mcv = self.args[arg_index];
2524 const max_stack = loop: for (self.args) |arg| {
2525 switch (arg) {
2526 .stack_offset => |last| break :loop last,
2527 else => {},
2528 }
2529 } else 0;
25302584 const payload = try self.addExtra(Mir.ArgDbgInfo{
25312585 .air_inst = inst,
25322586 .arg_index = arg_index,
2533 .max_stack = @intCast(u32, max_stack),
2587 .max_stack = self.max_end_stack,
25342588 });
25352589 _ = try self.addInst(.{
25362590 .tag = .arg_dbg_info,
......@@ -2547,7 +2601,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
25472601 break :blk mcv;
25482602 },
25492603 .stack_offset => |off| {
2550 const offset = max_stack - off + 16;
2604 const offset = @intCast(i32, self.max_end_stack) - off + 16;
25512605 break :blk MCValue{ .stack_offset = -offset };
25522606 },
25532607 else => return self.fail("TODO implement arg for {}", .{mcv}),
......@@ -2591,7 +2645,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
25912645 var info = try self.resolveCallingConventionValues(fn_ty);
25922646 defer info.deinit(self);
25932647
2594 var stack_adjustment: ?u32 = null;
25952648 for (args) |arg, arg_i| {
25962649 const mc_arg = info.args[arg_i];
25972650 const arg_ty = self.air.typeOf(arg);
......@@ -2606,9 +2659,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
26062659 },
26072660 .stack_offset => |off| {
26082661 try self.genSetStackArg(arg_ty, off, arg_mcv);
2609 if (stack_adjustment == null) {
2610 stack_adjustment = @intCast(u32, off);
2611 }
26122662 },
26132663 .ptr_stack_offset => {
26142664 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
......@@ -2629,14 +2679,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
26292679 }
26302680 }
26312681
2632 if (stack_adjustment) |off| {
2682 if (info.stack_byte_count > 0) {
26332683 // Adjust the stack
26342684 _ = try self.addInst(.{
26352685 .tag = .sub,
26362686 .ops = (Mir.Ops{
26372687 .reg1 = .rsp,
26382688 }).encode(),
2639 .data = .{ .imm = off },
2689 .data = .{ .imm = info.stack_byte_count },
26402690 });
26412691 }
26422692
......@@ -2764,14 +2814,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
27642814 }
27652815 } else unreachable;
27662816
2767 if (stack_adjustment) |off| {
2817 if (info.stack_byte_count > 0) {
27682818 // Readjust the stack
27692819 _ = try self.addInst(.{
27702820 .tag = .add,
27712821 .ops = (Mir.Ops{
27722822 .reg1 = .rsp,
27732823 }).encode(),
2774 .data = .{ .imm = off },
2824 .data = .{ .imm = info.stack_byte_count },
27752825 });
27762826 }
27772827
......@@ -2780,7 +2830,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
27802830 .register => |reg| {
27812831 if (Register.allocIndex(reg) == null) {
27822832 // Save function return value in a callee saved register
2783 break :result try self.copyToNewRegister(inst, self.air.typeOfIndex(inst), info.return_value);
2833 break :result try self.copyToRegisterWithInstTracking(
2834 inst,
2835 self.air.typeOfIndex(inst),
2836 info.return_value,
2837 );
27842838 }
27852839 },
27862840 else => {},
......@@ -2857,7 +2911,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
28572911 // Either one, but not both, can be a memory operand.
28582912 // Source operand can be an immediate, 8 bits or 32 bits.
28592913 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))
2860 try self.copyToNewRegister(inst, ty, lhs)
2914 MCValue{ .register = try self.copyToTmpRegister(ty, lhs) }
28612915 else
28622916 lhs;
28632917 // This instruction supports only signed 32-bit immediates at most.
......@@ -3520,6 +3574,13 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
35203574 .dead => unreachable,
35213575 .ptr_embedded_in_code => unreachable,
35223576 .unreach, .none => return,
3577 .undef => {
3578 if (abi_size <= 8) {
3579 const reg = try self.copyToTmpRegister(ty, mcv);
3580 return self.genSetStackArg(ty, stack_offset, MCValue{ .register = reg });
3581 }
3582 try self.genInlineMemset(stack_offset, .rsp, ty, .{ .immediate = 0xaa });
3583 },
35233584 .compare_flags_unsigned,
35243585 .compare_flags_signed,
35253586 => {
......@@ -3598,7 +3659,6 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
35983659
35993660 try self.genInlineMemcpy(stack_offset, .rsp, ty, mcv);
36003661 },
3601 else => return self.fail("TODO implement args on stack for {}", .{mcv}),
36023662 }
36033663}
36043664
......@@ -3617,7 +3677,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro
36173677 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
36183678 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
36193679 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
3620 else => return self.genInlineMemset(ty, stack_offset, .{ .immediate = 0xaa }),
3680 else => return self.genInlineMemset(stack_offset, .rbp, ty, .{ .immediate = 0xaa }),
36213681 }
36223682 },
36233683 .compare_flags_unsigned,
......@@ -3783,33 +3843,11 @@ fn genInlineMemcpy(self: *Self, stack_offset: i32, stack_reg: Register, ty: Type
37833843
37843844 const addr_reg: Register = blk: {
37853845 switch (val) {
3786 .memory => |addr| {
3787 const reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = addr });
3788 break :blk reg;
3789 },
3846 .memory,
37903847 .direct_load,
37913848 .got_load,
3792 => |sym_index| {
3793 const flags: u2 = switch (val) {
3794 .got_load => 0b00,
3795 .direct_load => 0b01,
3796 else => unreachable,
3797 };
3798 const addr_reg = (try self.register_manager.allocReg(null)).to64();
3799 _ = try self.addInst(.{
3800 .tag = .lea_pie,
3801 .ops = (Mir.Ops{
3802 .reg1 = addr_reg,
3803 .flags = flags,
3804 }).encode(),
3805 .data = .{
3806 .load_reloc = .{
3807 .atom_index = self.mod_fn.owner_decl.link.macho.local_sym_index,
3808 .sym_index = sym_index,
3809 },
3810 },
3811 });
3812 break :blk addr_reg;
3849 => {
3850 break :blk try self.loadMemPtrIntoRegister(Type.usize, val);
38133851 },
38143852 .stack_offset => |off| {
38153853 const addr_reg = (try self.register_manager.allocReg(null)).to64();
......@@ -3943,12 +3981,20 @@ fn genInlineMemcpy(self: *Self, stack_offset: i32, stack_reg: Register, ty: Type
39433981 try self.performReloc(loop_reloc);
39443982}
39453983
3946fn genInlineMemset(self: *Self, ty: Type, stack_offset: i32, value: MCValue) InnerError!void {
3984fn genInlineMemset(
3985 self: *Self,
3986 stack_offset: i32,
3987 stack_register: Register,
3988 ty: Type,
3989 value: MCValue,
3990) InnerError!void {
39473991 try self.register_manager.getReg(.rax, null);
3992
39483993 const abi_size = ty.abiSize(self.target.*);
39493994 if (stack_offset > 128) {
39503995 return self.fail("TODO inline memset with large stack offset", .{});
39513996 }
3997
39523998 const negative_offset = @bitCast(u32, -stack_offset);
39533999
39544000 // We are actually counting `abi_size` bytes; however, we reuse the index register
......@@ -4005,7 +4051,7 @@ fn genInlineMemset(self: *Self, ty: Type, stack_offset: i32, value: MCValue) Inn
40054051 _ = try self.addInst(.{
40064052 .tag = .mov_mem_index_imm,
40074053 .ops = (Mir.Ops{
4008 .reg1 = .rbp,
4054 .reg1 = stack_register.to64(),
40094055 }).encode(),
40104056 .data = .{ .payload = payload },
40114057 });
......@@ -4731,20 +4777,40 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
47314777 var next_int_reg: usize = 0;
47324778 var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator);
47334779 defer by_reg.deinit();
4734 for (param_types) |ty, i| {
4735 if (!ty.hasRuntimeBits()) continue;
4736 const param_size = @intCast(u32, ty.abiSize(self.target.*));
4737 const pass_in_reg = switch (ty.zigTypeTag()) {
4738 .Bool => true,
4739 .Int, .Enum => param_size <= 8,
4740 .Pointer => ty.ptrSize() != .Slice,
4741 .Optional => ty.isPtrLikeOptional(),
4742 else => false,
4743 };
4744 if (pass_in_reg) {
4745 if (next_int_reg >= c_abi_int_param_regs.len) break;
4746 try by_reg.putNoClobber(i, next_int_reg);
4747 next_int_reg += 1;
4780
4781 // If we want debug output, we store all args on stack for better liveness of args
4782 // in debugging contexts such as previewing the args in the debugger anywhere in
4783 // the procedure. Passing the args via registers can lead to reusing the register
4784 // for local ops thus clobbering the input arg forever.
4785 // This of course excludes C ABI calls.
4786 const omit_args_in_registers = blk: {
4787 if (cc == .C) break :blk false;
4788 switch (self.bin_file.options.optimize_mode) {
4789 .Debug => break :blk true,
4790 else => break :blk false,
4791 }
4792 };
4793 if (!omit_args_in_registers) {
4794 for (param_types) |ty, i| {
4795 if (!ty.hasRuntimeBits()) continue;
4796 const param_size = @intCast(u32, ty.abiSize(self.target.*));
4797 // For simplicity of codegen, slices and other types are always pushed onto the stack.
4798 // TODO: look into optimizing this by passing things as registers sometimes,
4799 // such as ptr and len of slices as separate registers.
4800 // TODO: also we need to honor the C ABI for relevant types rather than passing on
4801 // the stack here.
4802 const pass_in_reg = switch (ty.zigTypeTag()) {
4803 .Bool => true,
4804 .Int, .Enum => param_size <= 8,
4805 .Pointer => ty.ptrSize() != .Slice,
4806 .Optional => ty.isPtrLikeOptional(),
4807 else => false,
4808 };
4809 if (pass_in_reg) {
4810 if (next_int_reg >= c_abi_int_param_regs.len) break;
4811 try by_reg.putNoClobber(i, next_int_reg);
4812 next_int_reg += 1;
4813 }
47484814 }
47494815 }
47504816
......@@ -4765,19 +4831,14 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
47654831 result.args[i] = .{ .register = aliased_reg };
47664832 next_int_reg += 1;
47674833 } else {
4768 // For simplicity of codegen, slices and other types are always pushed onto the stack.
4769 // TODO: look into optimizing this by passing things as registers sometimes,
4770 // such as ptr and len of slices as separate registers.
4771 // TODO: also we need to honor the C ABI for relevant types rather than passing on
4772 // the stack here.
47734834 const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);
47744835 result.args[i] = .{ .stack_offset = @intCast(i32, offset) };
47754836 next_stack_offset = offset;
47764837 }
47774838 }
47784839
4779 result.stack_byte_count = next_stack_offset;
47804840 result.stack_align = 16;
4841 result.stack_byte_count = mem.alignForwardGeneric(u32, next_stack_offset, result.stack_align);
47814842 },
47824843 else => return self.fail("TODO implement function parameters for {} on x86_64", .{cc}),
47834844 }
src/arch/x86_64/Emit.zig+16-1
......@@ -691,11 +691,26 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
691691 0b00 => {
692692 return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
693693 },
694 0b01 => {
695 const imm = emit.mir.instructions.items(.data)[inst].imm;
696 const src_reg: ?Register = if (ops.reg2 == .none) null else ops.reg2;
697 return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{
698 .disp = imm,
699 .base = src_reg,
700 }), emit.code);
701 },
694702 0b10 => {
695703 const imm = emit.mir.instructions.items(.data)[inst].imm;
696704 return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code);
697705 },
698 else => return emit.fail("TODO implement imul", .{}),
706 0b11 => {
707 const payload = emit.mir.instructions.items(.data)[inst].payload;
708 const imm_pair = emit.mir.extraData(Mir.ImmPair, payload).data;
709 return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{
710 .disp = imm_pair.dest_off,
711 .base = ops.reg2,
712 }), imm_pair.operand, emit.code);
713 },
699714 }
700715}
701716
test/behavior/array.zig+10-18
......@@ -8,7 +8,6 @@ const expectEqual = testing.expectEqual;
88test "array to slice" {
99 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1010 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
11 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1211
1312 const a: u32 align(4) = 3;
1413 const b: u32 align(8) = 4;
......@@ -62,7 +61,7 @@ test "array init with mult" {
6261
6362test "array literal with explicit type" {
6463 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
65 if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
64 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
6665
6766 const hex_mult: [4]u16 = .{ 4096, 256, 16, 1 };
6867
......@@ -71,7 +70,7 @@ test "array literal with explicit type" {
7170}
7271
7372test "array literal with inferred length" {
74 if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
73 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
7574
7675 const hex_mult = [_]u16{ 4096, 256, 16, 1 };
7776
......@@ -92,7 +91,7 @@ const some_array = [_]u8{ 0, 1, 2, 3 };
9291
9392test "array literal with specified size" {
9493 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
95 if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
94 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
9695
9796 var array = [2]u8{ 1, 2 };
9897 try expect(array[0] == 1);
......@@ -101,7 +100,7 @@ test "array literal with specified size" {
101100
102101test "array len field" {
103102 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
104 if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
103 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
105104
106105 var arr = [4]u8{ 0, 0, 0, 0 };
107106 var ptr = &arr;
......@@ -143,7 +142,7 @@ test "array with sentinels" {
143142
144143test "void arrays" {
145144 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
146 if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
145 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
147146
148147 var array: [4]void = undefined;
149148 array[0] = void{};
......@@ -154,7 +153,7 @@ test "void arrays" {
154153
155154test "nested arrays" {
156155 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
157 if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
156 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
158157
159158 const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" };
160159 for (array_of_strings) |s, i| {
......@@ -222,7 +221,7 @@ test "implicit cast zero sized array ptr to slice" {
222221
223222test "anonymous list literal syntax" {
224223 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
225 if (builtin.zig_backend == .stage2_x86_64 or builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
224 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
226225
227226 const S = struct {
228227 fn doTheTest() !void {
......@@ -282,7 +281,6 @@ test "read/write through global variable array of struct fields initialized via
282281test "implicit cast single-item pointer" {
283282 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
284283 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
285 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
286284 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
287285
288286 try testImplicitCastSingleItemPtr();
......@@ -303,7 +301,6 @@ fn testArrayByValAtComptime(b: [2]u8) u8 {
303301test "comptime evaluating function that takes array by value" {
304302 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
305303 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
306 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
307304 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
308305
309306 const arr = [_]u8{ 1, 2 };
......@@ -316,7 +313,6 @@ test "comptime evaluating function that takes array by value" {
316313test "runtime initialize array elem and then implicit cast to slice" {
317314 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
318315 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
319 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
320316 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
321317
322318 var two: i32 = 2;
......@@ -327,7 +323,6 @@ test "runtime initialize array elem and then implicit cast to slice" {
327323test "array literal as argument to function" {
328324 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
329325 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
330 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
331326 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
332327
333328 const S = struct {
......@@ -418,7 +413,6 @@ test "double nested array to const slice cast in array literal" {
418413test "anonymous literal in array" {
419414 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
420415 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
421 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
422416 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
423417
424418 const S = struct {
......@@ -444,7 +438,6 @@ test "anonymous literal in array" {
444438test "access the null element of a null terminated array" {
445439 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
446440 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
447 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
448441 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
449442
450443 const S = struct {
......@@ -462,7 +455,6 @@ test "access the null element of a null terminated array" {
462455test "type deduction for array subscript expression" {
463456 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
464457 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
465 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
466458 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
467459
468460 const S = struct {
......@@ -533,8 +525,8 @@ test "zero-sized array with recursive type definition" {
533525test "type coercion of anon struct literal to array" {
534526 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
535527 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
536 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
537528 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
529 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
538530
539531 const S = struct {
540532 const U = union {
......@@ -551,8 +543,8 @@ test "type coercion of anon struct literal to array" {
551543 try expect(arr1[1] == 56);
552544 try expect(arr1[2] == 54);
553545
554 if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
555 if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
546 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
547 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
556548
557549 var x2: U = .{ .a = 42 };
558550 const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } };
test/behavior/slice.zig-3
......@@ -261,7 +261,6 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 {
261261test "C pointer" {
262262 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
263263 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
264 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
265264
266265 var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf";
267266 var len: u32 = 10;
......@@ -302,7 +301,6 @@ fn sliceSum(comptime q: []const u8) i32 {
302301test "slice type with custom alignment" {
303302 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
304303 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
305 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
306304
307305 const LazilyResolvedType = struct {
308306 anything: i32,
......@@ -317,7 +315,6 @@ test "slice type with custom alignment" {
317315test "obtaining a null terminated slice" {
318316 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
319317 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
320 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
321318 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
322319
323320 // here we have a normal array