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
signaturebadge-question-mark 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 {...@@ -883,7 +883,8 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
883/// Allocates a new register and copies `mcv` into it.883/// Allocates a new register and copies `mcv` into it.
884/// `reg_owner` is the instruction that gets associated with the register in the register table.884/// `reg_owner` is the instruction that gets associated with the register in the register table.
885/// This can have a side effect of spilling instructions to the stack to free up a register.885/// 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 {
887 const reg = try self.register_manager.allocReg(reg_owner);888 const reg = try self.register_manager.allocReg(reg_owner);
888 try self.genSetReg(ty, reg, mcv);889 try self.genSetReg(ty, reg, mcv);
889 return MCValue{ .register = reg };890 return MCValue{ .register = reg };
...@@ -939,7 +940,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {...@@ -939,7 +940,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
939 operand.freezeIfRegister(&self.register_manager);940 operand.freezeIfRegister(&self.register_manager);
940 defer operand.unfreezeIfRegister(&self.register_manager);941 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);
943 };944 };
944945
945 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });946 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none });
...@@ -970,7 +971,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {...@@ -970,7 +971,7 @@ fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
970 break :blk operand.register.to64();971 break :blk operand.register.to64();
971 }972 }
972 }973 }
973 const mcv = try self.copyToNewRegister(inst, src_ty, operand);974 const mcv = try self.copyToRegisterWithInstTracking(inst, src_ty, operand);
974 break :blk mcv.register.to64();975 break :blk mcv.register.to64();
975 };976 };
976977
...@@ -1088,7 +1089,7 @@ fn genPtrBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_r...@@ -1088,7 +1089,7 @@ fn genPtrBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_r
1088 if (self.reuseOperand(inst, op_lhs, 0, ptr)) {1089 if (self.reuseOperand(inst, op_lhs, 0, ptr)) {
1089 if (ptr.isMemory() or ptr.isRegister()) break :blk ptr;1090 if (ptr.isMemory() or ptr.isRegister()) break :blk ptr;
1090 }1091 }
1091 break :blk try self.copyToNewRegister(inst, dst_ty, ptr);1092 break :blk try self.copyToRegisterWithInstTracking(inst, dst_ty, ptr);
1092 };1093 };
10931094
1094 const offset_mcv = blk: {1095 const offset_mcv = blk: {
...@@ -1338,7 +1339,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {...@@ -1338,7 +1339,7 @@ fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
1338 if (self.reuseOperand(inst, ty_op.operand, 0, operand)) {1339 if (self.reuseOperand(inst, ty_op.operand, 0, operand)) {
1339 break :result operand;1340 break :result operand;
1340 }1341 }
1341 break :result try self.copyToNewRegister(inst, self.air.typeOfIndex(inst), operand);1342 break :result try self.copyToRegisterWithInstTracking(inst, self.air.typeOfIndex(inst), operand);
1342 };1343 };
1343 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1344 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1344}1345}
...@@ -1500,52 +1501,60 @@ fn elemOffset(self: *Self, index_ty: Type, index: MCValue, elem_size: u64) !Regi...@@ -1500,52 +1501,60 @@ fn elemOffset(self: *Self, index_ty: Type, index: MCValue, elem_size: u64) !Regi
1500 return reg;1501 return reg;
1501}1502}
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
1503fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {1548fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
1504 const is_volatile = false; // TODO1549 const is_volatile = false; // TODO
1505 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1550 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1506 const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else result: {1551 const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else result: {
1507 const slice_ty = self.air.typeOf(bin_op.lhs);1552 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.*);
1514 var buf: Type.SlicePtrFieldTypeBuffer = undefined;1553 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
1515 const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);1554 const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);
15161555 const elem_ptr = try self.genSliceElemPtr(bin_op.lhs, bin_op.rhs);
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.
1544 const dst_mcv = try self.allocRegOrMem(inst, false);1556 const dst_mcv = try self.allocRegOrMem(inst, false);
1545 try self.genBinMathOpMir(.add, slice_ptr_field_type, .{ .register = addr_reg.to64() }, .{1557 try self.load(dst_mcv, elem_ptr, slice_ptr_field_type);
1546 .register = offset_reg.to64(),
1547 });
1548 try self.load(dst_mcv, .{ .register = addr_reg.to64() }, slice_ptr_field_type);
1549 break :result dst_mcv;1558 break :result dst_mcv;
1550 };1559 };
1551 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1560 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 {...@@ -1557,7 +1566,7 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
1557 const result: MCValue = if (self.liveness.isUnused(inst))1566 const result: MCValue = if (self.liveness.isUnused(inst))
1558 .dead1567 .dead
1559 else1568 else
1560 return self.fail("TODO implement slice_elem_ptr for {}", .{self.target.cpu.arch});1569 try self.genSliceElemPtr(extra.lhs, extra.rhs);
1561 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });1570 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
1562}1571}
15631572
...@@ -1571,6 +1580,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -1571,6 +1580,7 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
15711580
1572 const elem_ty = array_ty.childType();1581 const elem_ty = array_ty.childType();
1573 const elem_abi_size = elem_ty.abiSize(self.target.*);1582 const elem_abi_size = elem_ty.abiSize(self.target.*);
1583
1574 const index_ty = self.air.typeOf(bin_op.rhs);1584 const index_ty = self.air.typeOf(bin_op.rhs);
1575 const index = try self.resolveInst(bin_op.rhs);1585 const index = try self.resolveInst(bin_op.rhs);
1576 index.freezeIfRegister(&self.register_manager);1586 index.freezeIfRegister(&self.register_manager);
...@@ -1580,21 +1590,43 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -1580,21 +1590,43 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
1580 self.register_manager.freezeRegs(&.{offset_reg});1590 self.register_manager.freezeRegs(&.{offset_reg});
1581 defer self.register_manager.unfreezeRegs(&.{offset_reg});1591 defer self.register_manager.unfreezeRegs(&.{offset_reg});
15821592
1583 const addr_reg = try self.register_manager.allocReg(null);1593 const addr_reg = blk: {
1584 switch (array) {1594 const off = inner: {
1585 .stack_offset => |off| {1595 switch (array) {
1586 // lea reg, [rbp]1596 .register => {
1587 _ = try self.addInst(.{1597 const off = @intCast(i32, try self.allocMem(
1588 .tag = .lea,1598 inst,
1589 .ops = (Mir.Ops{1599 @intCast(u32, array_ty.abiSize(self.target.*)),
1590 .reg1 = addr_reg.to64(),1600 array_ty.abiAlignment(self.target.*),
1591 .reg2 = .rbp,1601 ));
1592 }).encode(),1602 try self.genSetStack(array_ty, off, array);
1593 .data = .{ .imm = @bitCast(u32, -off) },1603 break :inner off;
1594 });1604 },
1595 },1605 .stack_offset => |off| {
1596 else => return self.fail("TODO implement array_elem_val when array is {}", .{array}),1606 break :inner off;
1597 }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
1598 // TODO we could allocate register here, but need to expect addr register and potentially1630 // TODO we could allocate register here, but need to expect addr register and potentially
1599 // offset register.1631 // offset register.
1600 const dst_mcv = try self.allocRegOrMem(inst, false);1632 const dst_mcv = try self.allocRegOrMem(inst, false);
...@@ -1635,7 +1667,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -1635,7 +1667,7 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
1635 self.register_manager.freezeRegs(&.{offset_reg});1667 self.register_manager.freezeRegs(&.{offset_reg});
1636 defer self.register_manager.unfreezeRegs(&.{offset_reg});1668 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);
1639 try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg });1671 try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg });
1640 break :result dst_mcv;1672 break :result dst_mcv;
1641 };1673 };
...@@ -1693,7 +1725,13 @@ fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {...@@ -1693,7 +1725,13 @@ fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
1693 return self.finishAir(inst, result, .{ un_op, .none, .none });1725 return self.finishAir(inst, result, .{ un_op, .none, .none });
1694}1726}
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 {
1697 if (!self.liveness.operandDies(inst, op_index))1735 if (!self.liveness.operandDies(inst, op_index))
1698 return false;1736 return false;
16991737
...@@ -1737,6 +1775,10 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo...@@ -1737,6 +1775,10 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
1737 .immediate => |imm| {1775 .immediate => |imm| {
1738 try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm });1776 try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm });
1739 },1777 },
1778 .stack_offset => {
1779 const reg = try self.copyToTmpRegister(ptr_ty, ptr);
1780 try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
1781 },
1740 .ptr_stack_offset => |off| {1782 .ptr_stack_offset => |off| {
1741 try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off });1783 try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off });
1742 },1784 },
...@@ -1787,9 +1829,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo...@@ -1787,9 +1829,6 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
1787 const reg = try self.copyToTmpRegister(ptr_ty, ptr);1829 const reg = try self.copyToTmpRegister(ptr_ty, ptr);
1788 try self.load(dst_mcv, .{ .register = reg }, ptr_ty);1830 try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
1789 },1831 },
1790 .stack_offset => {
1791 return self.fail("TODO implement loading from MCValue.stack_offset", .{});
1792 },
1793 }1832 }
1794}1833}
17951834
...@@ -1819,6 +1858,42 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {...@@ -1819,6 +1858,42 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
1819 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1858 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1820}1859}
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
1822fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void {1897fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void {
1823 _ = ptr_ty;1898 _ = ptr_ty;
1824 const abi_size = value_ty.abiSize(self.target.*);1899 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...@@ -1832,6 +1907,10 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
1832 .immediate => |imm| {1907 .immediate => |imm| {
1833 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);1908 try self.setRegOrMem(value_ty, .{ .memory = imm }, value);
1834 },1909 },
1910 .stack_offset => {
1911 const reg = try self.copyToTmpRegister(ptr_ty, ptr);
1912 try self.store(.{ .register = reg }, value, ptr_ty, value_ty);
1913 },
1835 .ptr_stack_offset => |off| {1914 .ptr_stack_offset => |off| {
1836 try self.genSetStack(value_ty, off, value);1915 try self.genSetStack(value_ty, off, value);
1837 },1916 },
...@@ -1909,6 +1988,10 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -1909,6 +1988,10 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
1909 .data = .{ .imm = 0 },1988 .data = .{ .imm = 0 },
1910 });1989 });
1911 },1990 },
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 },
1912 else => |other| {1995 else => |other| {
1913 return self.fail("TODO implement set pointee with {}", .{other});1996 return self.fail("TODO implement set pointee with {}", .{other});
1914 },1997 },
...@@ -1921,41 +2004,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -1921,41 +2004,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
1921 value.freezeIfRegister(&self.register_manager);2004 value.freezeIfRegister(&self.register_manager);
1922 defer value.unfreezeIfRegister(&self.register_manager);2005 defer value.unfreezeIfRegister(&self.register_manager);
19232006
1924 const addr_reg: Register = blk: {2007 const addr_reg = try self.loadMemPtrIntoRegister(ptr_ty, ptr);
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 };
19592008
1960 // to get the actual address of the value we want to modify we have to go through the GOT2009 // to get the actual address of the value we want to modify we have to go through the GOT
1961 // mov reg, [reg]2010 // mov reg, [reg]
...@@ -2020,9 +2069,6 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type...@@ -2020,9 +2069,6 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
2020 else => return self.fail("TODO implement storing {} to MCValue.memory", .{value}),2069 else => return self.fail("TODO implement storing {} to MCValue.memory", .{value}),
2021 }2070 }
2022 },2071 },
2023 .stack_offset => {
2024 return self.fail("TODO implement storing to MCValue.stack_offset", .{});
2025 },
2026 }2072 }
2027}2073}
20282074
...@@ -2068,7 +2114,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde...@@ -2068,7 +2114,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
2068 self.register_manager.freezeRegs(&.{offset_reg});2114 self.register_manager.freezeRegs(&.{offset_reg});
2069 defer self.register_manager.unfreezeRegs(&.{offset_reg});2115 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);
2072 try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg });2118 try self.genBinMathOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg });
2073 break :result dst_mcv;2119 break :result dst_mcv;
2074 },2120 },
...@@ -2129,7 +2175,9 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -2129,7 +2175,9 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
2129 if (self.reuseOperand(inst, operand, 0, mcv)) {2175 if (self.reuseOperand(inst, operand, 0, mcv)) {
2130 break :blk mcv;2176 break :blk mcv;
2131 } else {2177 } 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 });
2133 break :blk dst_mcv;2181 break :blk dst_mcv;
2134 }2182 }
2135 };2183 };
...@@ -2192,7 +2240,7 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:...@@ -2192,7 +2240,7 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:
2192 // LHS dies; use it as the destination.2240 // LHS dies; use it as the destination.
2193 // Both operands cannot be memory.2241 // Both operands cannot be memory.
2194 if (lhs.isMemory() and rhs.isMemory()) {2242 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);
2196 src_mcv = rhs;2244 src_mcv = rhs;
2197 } else {2245 } else {
2198 dst_mcv = lhs;2246 dst_mcv = lhs;
...@@ -2202,7 +2250,7 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:...@@ -2202,7 +2250,7 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:
2202 // RHS dies; use it as the destination.2250 // RHS dies; use it as the destination.
2203 // Both operands cannot be memory.2251 // Both operands cannot be memory.
2204 if (lhs.isMemory() and rhs.isMemory()) {2252 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);
2206 src_mcv = lhs;2254 src_mcv = lhs;
2207 } else {2255 } else {
2208 dst_mcv = rhs;2256 dst_mcv = rhs;
...@@ -2213,13 +2261,13 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:...@@ -2213,13 +2261,13 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:
2213 rhs.freezeIfRegister(&self.register_manager);2261 rhs.freezeIfRegister(&self.register_manager);
2214 defer rhs.unfreezeIfRegister(&self.register_manager);2262 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);
2217 src_mcv = rhs;2265 src_mcv = rhs;
2218 } else {2266 } else {
2219 lhs.freezeIfRegister(&self.register_manager);2267 lhs.freezeIfRegister(&self.register_manager);
2220 defer lhs.unfreezeIfRegister(&self.register_manager);2268 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);
2223 src_mcv = lhs;2271 src_mcv = lhs;
2224 }2272 }
2225 }2273 }
...@@ -2228,13 +2276,14 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:...@@ -2228,13 +2276,14 @@ fn genBinMathOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs:
2228 // A potential opportunity for future optimization here would be keeping track2276 // A potential opportunity for future optimization here would be keeping track
2229 // of the fact that the instruction is available both as an immediate2277 // of the fact that the instruction is available both as an immediate
2230 // and as a register.2278 // and as a register.
2279 // TODO consolidate with limitImmediateType() function
2231 switch (src_mcv) {2280 switch (src_mcv) {
2232 .immediate => |imm| {2281 .immediate => |imm| {
2233 if (imm > math.maxInt(u31)) {2282 if (imm > math.maxInt(u31)) {
2234 dst_mcv.freezeIfRegister(&self.register_manager);2283 dst_mcv.freezeIfRegister(&self.register_manager);
2235 defer dst_mcv.unfreezeIfRegister(&self.register_manager);2284 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) };
2238 }2287 }
2239 },2288 },
2240 else => {},2289 else => {},
...@@ -2452,7 +2501,18 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !...@@ -2452,7 +2501,18 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
2452 return self.genIMulOpMir(dst_ty, dst_mcv, MCValue{ .register = src_reg });2501 return self.genIMulOpMir(dst_ty, dst_mcv, MCValue{ .register = src_reg });
2453 }2502 }
2454 },2503 },
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 => {
2456 return self.fail("TODO implement x86 multiply source memory", .{});2516 return self.fail("TODO implement x86 multiply source memory", .{});
2457 },2517 },
2458 .got_load, .direct_load => {2518 .got_load, .direct_load => {
...@@ -2521,16 +2581,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -2521,16 +2581,10 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
2521 self.arg_index += 1;2581 self.arg_index += 1;
25222582
2523 const mcv = self.args[arg_index];2583 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;
2530 const payload = try self.addExtra(Mir.ArgDbgInfo{2584 const payload = try self.addExtra(Mir.ArgDbgInfo{
2531 .air_inst = inst,2585 .air_inst = inst,
2532 .arg_index = arg_index,2586 .arg_index = arg_index,
2533 .max_stack = @intCast(u32, max_stack),2587 .max_stack = self.max_end_stack,
2534 });2588 });
2535 _ = try self.addInst(.{2589 _ = try self.addInst(.{
2536 .tag = .arg_dbg_info,2590 .tag = .arg_dbg_info,
...@@ -2547,7 +2601,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -2547,7 +2601,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
2547 break :blk mcv;2601 break :blk mcv;
2548 },2602 },
2549 .stack_offset => |off| {2603 .stack_offset => |off| {
2550 const offset = max_stack - off + 16;2604 const offset = @intCast(i32, self.max_end_stack) - off + 16;
2551 break :blk MCValue{ .stack_offset = -offset };2605 break :blk MCValue{ .stack_offset = -offset };
2552 },2606 },
2553 else => return self.fail("TODO implement arg for {}", .{mcv}),2607 else => return self.fail("TODO implement arg for {}", .{mcv}),
...@@ -2591,7 +2645,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -2591,7 +2645,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
2591 var info = try self.resolveCallingConventionValues(fn_ty);2645 var info = try self.resolveCallingConventionValues(fn_ty);
2592 defer info.deinit(self);2646 defer info.deinit(self);
25932647
2594 var stack_adjustment: ?u32 = null;
2595 for (args) |arg, arg_i| {2648 for (args) |arg, arg_i| {
2596 const mc_arg = info.args[arg_i];2649 const mc_arg = info.args[arg_i];
2597 const arg_ty = self.air.typeOf(arg);2650 const arg_ty = self.air.typeOf(arg);
...@@ -2606,9 +2659,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -2606,9 +2659,6 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
2606 },2659 },
2607 .stack_offset => |off| {2660 .stack_offset => |off| {
2608 try self.genSetStackArg(arg_ty, off, arg_mcv);2661 try self.genSetStackArg(arg_ty, off, arg_mcv);
2609 if (stack_adjustment == null) {
2610 stack_adjustment = @intCast(u32, off);
2611 }
2612 },2662 },
2613 .ptr_stack_offset => {2663 .ptr_stack_offset => {
2614 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});2664 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 {...@@ -2629,14 +2679,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
2629 }2679 }
2630 }2680 }
26312681
2632 if (stack_adjustment) |off| {2682 if (info.stack_byte_count > 0) {
2633 // Adjust the stack2683 // Adjust the stack
2634 _ = try self.addInst(.{2684 _ = try self.addInst(.{
2635 .tag = .sub,2685 .tag = .sub,
2636 .ops = (Mir.Ops{2686 .ops = (Mir.Ops{
2637 .reg1 = .rsp,2687 .reg1 = .rsp,
2638 }).encode(),2688 }).encode(),
2639 .data = .{ .imm = off },2689 .data = .{ .imm = info.stack_byte_count },
2640 });2690 });
2641 }2691 }
26422692
...@@ -2764,14 +2814,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -2764,14 +2814,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
2764 }2814 }
2765 } else unreachable;2815 } else unreachable;
27662816
2767 if (stack_adjustment) |off| {2817 if (info.stack_byte_count > 0) {
2768 // Readjust the stack2818 // Readjust the stack
2769 _ = try self.addInst(.{2819 _ = try self.addInst(.{
2770 .tag = .add,2820 .tag = .add,
2771 .ops = (Mir.Ops{2821 .ops = (Mir.Ops{
2772 .reg1 = .rsp,2822 .reg1 = .rsp,
2773 }).encode(),2823 }).encode(),
2774 .data = .{ .imm = off },2824 .data = .{ .imm = info.stack_byte_count },
2775 });2825 });
2776 }2826 }
27772827
...@@ -2780,7 +2830,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {...@@ -2780,7 +2830,11 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void {
2780 .register => |reg| {2830 .register => |reg| {
2781 if (Register.allocIndex(reg) == null) {2831 if (Register.allocIndex(reg) == null) {
2782 // Save function return value in a callee saved register2832 // 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 );
2784 }2838 }
2785 },2839 },
2786 else => {},2840 else => {},
...@@ -2857,7 +2911,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {...@@ -2857,7 +2911,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
2857 // Either one, but not both, can be a memory operand.2911 // Either one, but not both, can be a memory operand.
2858 // Source operand can be an immediate, 8 bits or 32 bits.2912 // Source operand can be an immediate, 8 bits or 32 bits.
2859 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))2913 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) }
2861 else2915 else
2862 lhs;2916 lhs;
2863 // This instruction supports only signed 32-bit immediates at most.2917 // 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...@@ -3520,6 +3574,13 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
3520 .dead => unreachable,3574 .dead => unreachable,
3521 .ptr_embedded_in_code => unreachable,3575 .ptr_embedded_in_code => unreachable,
3522 .unreach, .none => return,3576 .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 },
3523 .compare_flags_unsigned,3584 .compare_flags_unsigned,
3524 .compare_flags_signed,3585 .compare_flags_signed,
3525 => {3586 => {
...@@ -3598,7 +3659,6 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE...@@ -3598,7 +3659,6 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
35983659
3599 try self.genInlineMemcpy(stack_offset, .rsp, ty, mcv);3660 try self.genInlineMemcpy(stack_offset, .rsp, ty, mcv);
3600 },3661 },
3601 else => return self.fail("TODO implement args on stack for {}", .{mcv}),
3602 }3662 }
3603}3663}
36043664
...@@ -3617,7 +3677,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro...@@ -3617,7 +3677,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerErro
3617 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),3677 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
3618 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),3678 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
3619 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),3679 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 }),
3621 }3681 }
3622 },3682 },
3623 .compare_flags_unsigned,3683 .compare_flags_unsigned,
...@@ -3783,33 +3843,11 @@ fn genInlineMemcpy(self: *Self, stack_offset: i32, stack_reg: Register, ty: Type...@@ -3783,33 +3843,11 @@ fn genInlineMemcpy(self: *Self, stack_offset: i32, stack_reg: Register, ty: Type
37833843
3784 const addr_reg: Register = blk: {3844 const addr_reg: Register = blk: {
3785 switch (val) {3845 switch (val) {
3786 .memory => |addr| {3846 .memory,
3787 const reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = addr });
3788 break :blk reg;
3789 },
3790 .direct_load,3847 .direct_load,
3791 .got_load,3848 .got_load,
3792 => |sym_index| {3849 => {
3793 const flags: u2 = switch (val) {3850 break :blk try self.loadMemPtrIntoRegister(Type.usize, 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;
3813 },3851 },
3814 .stack_offset => |off| {3852 .stack_offset => |off| {
3815 const addr_reg = (try self.register_manager.allocReg(null)).to64();3853 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...@@ -3943,12 +3981,20 @@ fn genInlineMemcpy(self: *Self, stack_offset: i32, stack_reg: Register, ty: Type
3943 try self.performReloc(loop_reloc);3981 try self.performReloc(loop_reloc);
3944}3982}
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 {
3947 try self.register_manager.getReg(.rax, null);3991 try self.register_manager.getReg(.rax, null);
3992
3948 const abi_size = ty.abiSize(self.target.*);3993 const abi_size = ty.abiSize(self.target.*);
3949 if (stack_offset > 128) {3994 if (stack_offset > 128) {
3950 return self.fail("TODO inline memset with large stack offset", .{});3995 return self.fail("TODO inline memset with large stack offset", .{});
3951 }3996 }
3997
3952 const negative_offset = @bitCast(u32, -stack_offset);3998 const negative_offset = @bitCast(u32, -stack_offset);
39533999
3954 // We are actually counting `abi_size` bytes; however, we reuse the index register4000 // 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...@@ -4005,7 +4051,7 @@ fn genInlineMemset(self: *Self, ty: Type, stack_offset: i32, value: MCValue) Inn
4005 _ = try self.addInst(.{4051 _ = try self.addInst(.{
4006 .tag = .mov_mem_index_imm,4052 .tag = .mov_mem_index_imm,
4007 .ops = (Mir.Ops{4053 .ops = (Mir.Ops{
4008 .reg1 = .rbp,4054 .reg1 = stack_register.to64(),
4009 }).encode(),4055 }).encode(),
4010 .data = .{ .payload = payload },4056 .data = .{ .payload = payload },
4011 });4057 });
...@@ -4731,20 +4777,40 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -4731,20 +4777,40 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
4731 var next_int_reg: usize = 0;4777 var next_int_reg: usize = 0;
4732 var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator);4778 var by_reg = std.AutoHashMap(usize, usize).init(self.bin_file.allocator);
4733 defer by_reg.deinit();4779 defer by_reg.deinit();
4734 for (param_types) |ty, i| {4780
4735 if (!ty.hasRuntimeBits()) continue;4781 // If we want debug output, we store all args on stack for better liveness of args
4736 const param_size = @intCast(u32, ty.abiSize(self.target.*));4782 // in debugging contexts such as previewing the args in the debugger anywhere in
4737 const pass_in_reg = switch (ty.zigTypeTag()) {4783 // the procedure. Passing the args via registers can lead to reusing the register
4738 .Bool => true,4784 // for local ops thus clobbering the input arg forever.
4739 .Int, .Enum => param_size <= 8,4785 // This of course excludes C ABI calls.
4740 .Pointer => ty.ptrSize() != .Slice,4786 const omit_args_in_registers = blk: {
4741 .Optional => ty.isPtrLikeOptional(),4787 if (cc == .C) break :blk false;
4742 else => false,4788 switch (self.bin_file.options.optimize_mode) {
4743 };4789 .Debug => break :blk true,
4744 if (pass_in_reg) {4790 else => break :blk false,
4745 if (next_int_reg >= c_abi_int_param_regs.len) break;4791 }
4746 try by_reg.putNoClobber(i, next_int_reg);4792 };
4747 next_int_reg += 1;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 }
4748 }4814 }
4749 }4815 }
47504816
...@@ -4765,19 +4831,14 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {...@@ -4765,19 +4831,14 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
4765 result.args[i] = .{ .register = aliased_reg };4831 result.args[i] = .{ .register = aliased_reg };
4766 next_int_reg += 1;4832 next_int_reg += 1;
4767 } else {4833 } 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.
4773 const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);4834 const offset = mem.alignForwardGeneric(u32, next_stack_offset + param_size, param_align);
4774 result.args[i] = .{ .stack_offset = @intCast(i32, offset) };4835 result.args[i] = .{ .stack_offset = @intCast(i32, offset) };
4775 next_stack_offset = offset;4836 next_stack_offset = offset;
4776 }4837 }
4777 }4838 }
47784839
4779 result.stack_byte_count = next_stack_offset;
4780 result.stack_align = 16;4840 result.stack_align = 16;
4841 result.stack_byte_count = mem.alignForwardGeneric(u32, next_stack_offset, result.stack_align);
4781 },4842 },
4782 else => return self.fail("TODO implement function parameters for {} on x86_64", .{cc}),4843 else => return self.fail("TODO implement function parameters for {} on x86_64", .{cc}),
4783 }4844 }
src/arch/x86_64/Emit.zig+16-1
...@@ -691,11 +691,26 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -691,11 +691,26 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
691 0b00 => {691 0b00 => {
692 return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);692 return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
693 },693 },
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 },
694 0b10 => {702 0b10 => {
695 const imm = emit.mir.instructions.items(.data)[inst].imm;703 const imm = emit.mir.instructions.items(.data)[inst].imm;
696 return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code);704 return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code);
697 },705 },
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 },
699 }714 }
700}715}
701716
test/behavior/array.zig+10-18
...@@ -8,7 +8,6 @@ const expectEqual = testing.expectEqual;...@@ -8,7 +8,6 @@ const expectEqual = testing.expectEqual;
8test "array to slice" {8test "array to slice" {
9 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;9 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
10 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;10 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
11 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1211
13 const a: u32 align(4) = 3;12 const a: u32 align(4) = 3;
14 const b: u32 align(8) = 4;13 const b: u32 align(8) = 4;
...@@ -62,7 +61,7 @@ test "array init with mult" {...@@ -62,7 +61,7 @@ test "array init with mult" {
6261
63test "array literal with explicit type" {62test "array literal with explicit type" {
64 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;63 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
67 const hex_mult: [4]u16 = .{ 4096, 256, 16, 1 };66 const hex_mult: [4]u16 = .{ 4096, 256, 16, 1 };
6867
...@@ -71,7 +70,7 @@ test "array literal with explicit type" {...@@ -71,7 +70,7 @@ test "array literal with explicit type" {
71}70}
7271
73test "array literal with inferred length" {72test "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
76 const hex_mult = [_]u16{ 4096, 256, 16, 1 };75 const hex_mult = [_]u16{ 4096, 256, 16, 1 };
7776
...@@ -92,7 +91,7 @@ const some_array = [_]u8{ 0, 1, 2, 3 };...@@ -92,7 +91,7 @@ const some_array = [_]u8{ 0, 1, 2, 3 };
9291
93test "array literal with specified size" {92test "array literal with specified size" {
94 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;93 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
97 var array = [2]u8{ 1, 2 };96 var array = [2]u8{ 1, 2 };
98 try expect(array[0] == 1);97 try expect(array[0] == 1);
...@@ -101,7 +100,7 @@ test "array literal with specified size" {...@@ -101,7 +100,7 @@ test "array literal with specified size" {
101100
102test "array len field" {101test "array len field" {
103 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;102 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
106 var arr = [4]u8{ 0, 0, 0, 0 };105 var arr = [4]u8{ 0, 0, 0, 0 };
107 var ptr = &arr;106 var ptr = &arr;
...@@ -143,7 +142,7 @@ test "array with sentinels" {...@@ -143,7 +142,7 @@ test "array with sentinels" {
143142
144test "void arrays" {143test "void arrays" {
145 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;144 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
148 var array: [4]void = undefined;147 var array: [4]void = undefined;
149 array[0] = void{};148 array[0] = void{};
...@@ -154,7 +153,7 @@ test "void arrays" {...@@ -154,7 +153,7 @@ test "void arrays" {
154153
155test "nested arrays" {154test "nested arrays" {
156 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;155 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
159 const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" };158 const array_of_strings = [_][]const u8{ "hello", "this", "is", "my", "thing" };
160 for (array_of_strings) |s, i| {159 for (array_of_strings) |s, i| {
...@@ -222,7 +221,7 @@ test "implicit cast zero sized array ptr to slice" {...@@ -222,7 +221,7 @@ test "implicit cast zero sized array ptr to slice" {
222221
223test "anonymous list literal syntax" {222test "anonymous list literal syntax" {
224 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;223 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
227 const S = struct {226 const S = struct {
228 fn doTheTest() !void {227 fn doTheTest() !void {
...@@ -282,7 +281,6 @@ test "read/write through global variable array of struct fields initialized via...@@ -282,7 +281,6 @@ test "read/write through global variable array of struct fields initialized via
282test "implicit cast single-item pointer" {281test "implicit cast single-item pointer" {
283 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;282 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
284 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO283 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
285 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
286 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO284 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
287285
288 try testImplicitCastSingleItemPtr();286 try testImplicitCastSingleItemPtr();
...@@ -303,7 +301,6 @@ fn testArrayByValAtComptime(b: [2]u8) u8 {...@@ -303,7 +301,6 @@ fn testArrayByValAtComptime(b: [2]u8) u8 {
303test "comptime evaluating function that takes array by value" {301test "comptime evaluating function that takes array by value" {
304 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;302 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
305 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO303 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
306 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
307 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO304 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
308305
309 const arr = [_]u8{ 1, 2 };306 const arr = [_]u8{ 1, 2 };
...@@ -316,7 +313,6 @@ test "comptime evaluating function that takes array by value" {...@@ -316,7 +313,6 @@ test "comptime evaluating function that takes array by value" {
316test "runtime initialize array elem and then implicit cast to slice" {313test "runtime initialize array elem and then implicit cast to slice" {
317 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;314 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
318 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO315 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
319 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
320 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO316 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
321317
322 var two: i32 = 2;318 var two: i32 = 2;
...@@ -327,7 +323,6 @@ test "runtime initialize array elem and then implicit cast to slice" {...@@ -327,7 +323,6 @@ test "runtime initialize array elem and then implicit cast to slice" {
327test "array literal as argument to function" {323test "array literal as argument to function" {
328 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;324 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
329 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO325 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
330 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
331 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO326 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
332327
333 const S = struct {328 const S = struct {
...@@ -418,7 +413,6 @@ test "double nested array to const slice cast in array literal" {...@@ -418,7 +413,6 @@ test "double nested array to const slice cast in array literal" {
418test "anonymous literal in array" {413test "anonymous literal in array" {
419 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;414 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
420 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO415 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
421 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
422 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO416 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
423417
424 const S = struct {418 const S = struct {
...@@ -444,7 +438,6 @@ test "anonymous literal in array" {...@@ -444,7 +438,6 @@ test "anonymous literal in array" {
444test "access the null element of a null terminated array" {438test "access the null element of a null terminated array" {
445 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;439 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
446 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO440 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
447 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
448 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO441 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
449442
450 const S = struct {443 const S = struct {
...@@ -462,7 +455,6 @@ test "access the null element of a null terminated array" {...@@ -462,7 +455,6 @@ test "access the null element of a null terminated array" {
462test "type deduction for array subscript expression" {455test "type deduction for array subscript expression" {
463 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;456 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
464 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO457 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
465 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
466 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO458 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
467459
468 const S = struct {460 const S = struct {
...@@ -533,8 +525,8 @@ test "zero-sized array with recursive type definition" {...@@ -533,8 +525,8 @@ test "zero-sized array with recursive type definition" {
533test "type coercion of anon struct literal to array" {525test "type coercion of anon struct literal to array" {
534 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;526 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
535 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO527 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
536 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
537 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO528 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
529 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
538530
539 const S = struct {531 const S = struct {
540 const U = union {532 const U = union {
...@@ -551,8 +543,8 @@ test "type coercion of anon struct literal to array" {...@@ -551,8 +543,8 @@ test "type coercion of anon struct literal to array" {
551 try expect(arr1[1] == 56);543 try expect(arr1[1] == 56);
552 try expect(arr1[2] == 54);544 try expect(arr1[2] == 54);
553545
554 if (@import("builtin").zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO546 if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest; // TODO
555 if (@import("builtin").zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO547 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
556548
557 var x2: U = .{ .a = 42 };549 var x2: U = .{ .a = 42 };
558 const t2 = .{ x2, .{ .b = true }, .{ .c = "hello" } };550 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 {...@@ -261,7 +261,6 @@ fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) []u8 {
261test "C pointer" {261test "C pointer" {
262 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;262 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
263 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;263 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
264 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
265264
266 var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf";265 var buf: [*c]const u8 = "kjdhfkjdhfdkjhfkfjhdfkjdhfkdjhfdkjhf";
267 var len: u32 = 10;266 var len: u32 = 10;
...@@ -302,7 +301,6 @@ fn sliceSum(comptime q: []const u8) i32 {...@@ -302,7 +301,6 @@ fn sliceSum(comptime q: []const u8) i32 {
302test "slice type with custom alignment" {301test "slice type with custom alignment" {
303 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;302 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
304 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;303 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
305 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
306304
307 const LazilyResolvedType = struct {305 const LazilyResolvedType = struct {
308 anything: i32,306 anything: i32,
...@@ -317,7 +315,6 @@ test "slice type with custom alignment" {...@@ -317,7 +315,6 @@ test "slice type with custom alignment" {
317test "obtaining a null terminated slice" {315test "obtaining a null terminated slice" {
318 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;316 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
319 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;317 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
320 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
321 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO318 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
322319
323 // here we have a normal array320 // here we have a normal array