| ... | @@ -20,6 +20,8 @@ const build_options = @import("build_options"); | ... | @@ -20,6 +20,8 @@ const build_options = @import("build_options"); |
| 20 | const LazySrcLoc = Module.LazySrcLoc; | 20 | const LazySrcLoc = Module.LazySrcLoc; |
| 21 | const RegisterManager = @import("register_manager.zig").RegisterManager; | 21 | const RegisterManager = @import("register_manager.zig").RegisterManager; |
| 22 | | 22 | |
| | 23 | const X8664Encoder = @import("codegen/x86_64.zig").Encoder; |
| | 24 | |
| 23 | /// The codegen-related data that is stored in `ir.Inst.Block` instructions. | 25 | /// The codegen-related data that is stored in `ir.Inst.Block` instructions. |
| 24 | pub const BlockData = struct { | 26 | pub const BlockData = struct { |
| 25 | relocs: std.ArrayListUnmanaged(Reloc) = undefined, | 27 | relocs: std.ArrayListUnmanaged(Reloc) = undefined, |
| ... | @@ -1038,7 +1040,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1038,7 +1040,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1038 | }, | 1040 | }, |
| 1039 | .val = Value.initTag(.bool_true), | 1041 | .val = Value.initTag(.bool_true), |
| 1040 | }; | 1042 | }; |
| 1041 | return try self.genX8664BinMath(&inst.base, inst.operand, &imm.base, 6, 0x30); | 1043 | return try self.genX8664BinMath(&inst.base, inst.operand, &imm.base); |
| 1042 | }, | 1044 | }, |
| 1043 | .arm, .armeb => { | 1045 | .arm, .armeb => { |
| 1044 | var imm = ir.Inst.Constant{ | 1046 | var imm = ir.Inst.Constant{ |
| ... | @@ -1062,7 +1064,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1062,7 +1064,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1062 | return MCValue.dead; | 1064 | return MCValue.dead; |
| 1063 | switch (arch) { | 1065 | switch (arch) { |
| 1064 | .x86_64 => { | 1066 | .x86_64 => { |
| 1065 | return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 0, 0x00); | 1067 | return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs); |
| 1066 | }, | 1068 | }, |
| 1067 | .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .add), | 1069 | .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .add), |
| 1068 | else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}), | 1070 | else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}), |
| ... | @@ -1083,6 +1085,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1083,6 +1085,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1083 | if (inst.base.isUnused()) | 1085 | if (inst.base.isUnused()) |
| 1084 | return MCValue.dead; | 1086 | return MCValue.dead; |
| 1085 | switch (arch) { | 1087 | switch (arch) { |
| | 1088 | .x86_64 => return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs), |
| 1086 | .arm, .armeb => return try self.genArmMul(&inst.base, inst.lhs, inst.rhs), | 1089 | .arm, .armeb => return try self.genArmMul(&inst.base, inst.lhs, inst.rhs), |
| 1087 | else => return self.fail(inst.base.src, "TODO implement mul for {}", .{self.target.cpu.arch}), | 1090 | else => return self.fail(inst.base.src, "TODO implement mul for {}", .{self.target.cpu.arch}), |
| 1088 | } | 1091 | } |
| ... | @@ -1361,7 +1364,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1361,7 +1364,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1361 | return MCValue.dead; | 1364 | return MCValue.dead; |
| 1362 | switch (arch) { | 1365 | switch (arch) { |
| 1363 | .x86_64 => { | 1366 | .x86_64 => { |
| 1364 | return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 5, 0x28); | 1367 | return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs); |
| 1365 | }, | 1368 | }, |
| 1366 | .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .sub), | 1369 | .arm, .armeb => return try self.genArmBinOp(&inst.base, inst.lhs, inst.rhs, .sub), |
| 1367 | else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}), | 1370 | else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}), |
| ... | @@ -1506,8 +1509,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1506,8 +1509,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1506 | return dst_mcv; | 1509 | return dst_mcv; |
| 1507 | } | 1510 | } |
| 1508 | | 1511 | |
| | 1512 | /// Perform "binary" operators, excluding comparisons. |
| | 1513 | /// Currently, the following ops are supported: |
| 1509 | /// ADD, SUB, XOR, OR, AND | 1514 | /// ADD, SUB, XOR, OR, AND |
| 1510 | fn genX8664BinMath(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue { | 1515 | fn genX8664BinMath(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst) !MCValue { |
| | 1516 | // We'll handle these ops in two steps. |
| | 1517 | // 1) Prepare an output location (register or memory) |
| | 1518 | // This location will be the location of the operand that dies (if one exists) |
| | 1519 | // or just a temporary register (if one doesn't exist) |
| | 1520 | // 2) Perform the op with the other argument |
| | 1521 | // 3) Sometimes, the output location is memory but the op doesn't support it. |
| | 1522 | // In this case, copy that location to a register, then perform the op to that register instead. |
| | 1523 | // |
| | 1524 | // TODO: make this algorithm less bad |
| | 1525 | |
| 1511 | try self.code.ensureCapacity(self.code.items.len + 8); | 1526 | try self.code.ensureCapacity(self.code.items.len + 8); |
| 1512 | | 1527 | |
| 1513 | const lhs = try self.resolveInst(op_lhs); | 1528 | const lhs = try self.resolveInst(op_lhs); |
| ... | @@ -1568,18 +1583,109 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1568,18 +1583,109 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1568 | else => {}, | 1583 | else => {}, |
| 1569 | } | 1584 | } |
| 1570 | | 1585 | |
| 1571 | try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, opx, mr); | 1586 | // Now for step 2, we perform the actual op |
| | 1587 | switch (inst.tag) { |
| | 1588 | // TODO: Generate wrapping and non-wrapping versions separately |
| | 1589 | .add, .addwrap => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 0, 0x00), |
| | 1590 | .bool_or, .bit_or => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 1, 0x08), |
| | 1591 | .bool_and, .bit_and => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 4, 0x20), |
| | 1592 | .sub, .subwrap => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 5, 0x28), |
| | 1593 | .xor, .not => try self.genX8664BinMathCode(inst.src, inst.ty, dst_mcv, src_mcv, 6, 0x30), |
| | 1594 | |
| | 1595 | .mul, .mulwrap => try self.genX8664Imul(inst.src, inst.ty, dst_mcv, src_mcv), |
| | 1596 | else => unreachable, |
| | 1597 | } |
| 1572 | | 1598 | |
| 1573 | return dst_mcv; | 1599 | return dst_mcv; |
| 1574 | } | 1600 | } |
| 1575 | | 1601 | |
| | 1602 | /// Wrap over Instruction.encodeInto to translate errors |
| | 1603 | fn encodeX8664Instruction( |
| | 1604 | self: *Self, |
| | 1605 | src: LazySrcLoc, |
| | 1606 | inst: Instruction, |
| | 1607 | ) !void { |
| | 1608 | inst.encodeInto(self.code) catch |err| { |
| | 1609 | if (err == error.OutOfMemory) |
| | 1610 | return error.OutOfMemory |
| | 1611 | else |
| | 1612 | return self.fail(src, "Instruction.encodeInto failed because {s}", .{@errorName(err)}); |
| | 1613 | }; |
| | 1614 | } |
| | 1615 | |
| | 1616 | /// This function encodes a binary operation for x86_64 |
| | 1617 | /// intended for use with the following opcode ranges |
| | 1618 | /// because they share the same structure. |
| | 1619 | /// |
| | 1620 | /// Thus not all binary operations can be used here |
| | 1621 | /// -- multiplication needs to be done with imul, |
| | 1622 | /// which doesn't have as convenient an interface. |
| | 1623 | /// |
| | 1624 | /// "opx"-style instructions use the opcode extension field to indicate which instruction to execute: |
| | 1625 | /// |
| | 1626 | /// opx = /0: add |
| | 1627 | /// opx = /1: or |
| | 1628 | /// opx = /2: adc |
| | 1629 | /// opx = /3: sbb |
| | 1630 | /// opx = /4: and |
| | 1631 | /// opx = /5: sub |
| | 1632 | /// opx = /6: xor |
| | 1633 | /// opx = /7: cmp |
| | 1634 | /// |
| | 1635 | /// opcode | operand shape |
| | 1636 | /// --------+---------------------- |
| | 1637 | /// 80 /opx | *r/m8*, imm8 |
| | 1638 | /// 81 /opx | *r/m16/32/64*, imm16/32 |
| | 1639 | /// 83 /opx | *r/m16/32/64*, imm8 |
| | 1640 | /// |
| | 1641 | /// "mr"-style instructions use the low bits of opcode to indicate shape of instruction: |
| | 1642 | /// |
| | 1643 | /// mr = 00: add |
| | 1644 | /// mr = 08: or |
| | 1645 | /// mr = 10: adc |
| | 1646 | /// mr = 18: sbb |
| | 1647 | /// mr = 20: and |
| | 1648 | /// mr = 28: sub |
| | 1649 | /// mr = 30: xor |
| | 1650 | /// mr = 38: cmp |
| | 1651 | /// |
| | 1652 | /// opcode | operand shape |
| | 1653 | /// -------+------------------------- |
| | 1654 | /// mr + 0 | *r/m8*, r8 |
| | 1655 | /// mr + 1 | *r/m16/32/64*, r16/32/64 |
| | 1656 | /// mr + 2 | *r8*, r/m8 |
| | 1657 | /// mr + 3 | *r16/32/64*, r/m16/32/64 |
| | 1658 | /// mr + 4 | *AL*, imm8 |
| | 1659 | /// mr + 5 | *rAX*, imm16/32 |
| | 1660 | /// |
| | 1661 | /// TODO: rotates and shifts share the same structure, so we can potentially implement them |
| | 1662 | /// at a later date with very similar code. |
| | 1663 | /// They have "opx"-style instructions, but no "mr"-style instructions. |
| | 1664 | /// |
| | 1665 | /// opx = /0: rol, |
| | 1666 | /// opx = /1: ror, |
| | 1667 | /// opx = /2: rcl, |
| | 1668 | /// opx = /3: rcr, |
| | 1669 | /// opx = /4: shl sal, |
| | 1670 | /// opx = /5: shr, |
| | 1671 | /// opx = /6: sal shl, |
| | 1672 | /// opx = /7: sar, |
| | 1673 | /// |
| | 1674 | /// opcode | operand shape |
| | 1675 | /// --------+------------------ |
| | 1676 | /// c0 /opx | *r/m8*, imm8 |
| | 1677 | /// c1 /opx | *r/m16/32/64*, imm8 |
| | 1678 | /// d0 /opx | *r/m8*, 1 |
| | 1679 | /// d1 /opx | *r/m16/32/64*, 1 |
| | 1680 | /// d2 /opx | *r/m8*, CL (for context, CL is register 1) |
| | 1681 | /// d3 /opx | *r/m16/32/64*, CL (for context, CL is register 1) |
| 1576 | fn genX8664BinMathCode( | 1682 | fn genX8664BinMathCode( |
| 1577 | self: *Self, | 1683 | self: *Self, |
| 1578 | src: LazySrcLoc, | 1684 | src: LazySrcLoc, |
| 1579 | dst_ty: Type, | 1685 | dst_ty: Type, |
| 1580 | dst_mcv: MCValue, | 1686 | dst_mcv: MCValue, |
| 1581 | src_mcv: MCValue, | 1687 | src_mcv: MCValue, |
| 1582 | opx: u8, | 1688 | opx: u3, |
| 1583 | mr: u8, | 1689 | mr: u8, |
| 1584 | ) !void { | 1690 | ) !void { |
| 1585 | switch (dst_mcv) { | 1691 | switch (dst_mcv) { |
| ... | @@ -1598,31 +1704,85 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1598,31 +1704,85 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1598 | .ptr_stack_offset => unreachable, | 1704 | .ptr_stack_offset => unreachable, |
| 1599 | .ptr_embedded_in_code => unreachable, | 1705 | .ptr_embedded_in_code => unreachable, |
| 1600 | .register => |src_reg| { | 1706 | .register => |src_reg| { |
| 1601 | self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 }); | 1707 | // for register, register use mr + 1 |
| 1602 | self.code.appendSliceAssumeCapacity(&[_]u8{ mr + 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) }); | 1708 | // addressing mode: *r/m16/32/64*, r16/32/64 |
| | 1709 | const abi_size = dst_ty.abiSize(self.target.*); |
| | 1710 | const encoder = try X8664Encoder.init(self.code, 3); |
| | 1711 | encoder.rex(.{ |
| | 1712 | .w = abi_size == 8, |
| | 1713 | .r = src_reg.isExtended(), |
| | 1714 | .b = dst_reg.isExtended(), |
| | 1715 | }); |
| | 1716 | encoder.opcode_1byte(mr + 1); |
| | 1717 | encoder.modRm_direct( |
| | 1718 | src_reg.low_id(), |
| | 1719 | dst_reg.low_id(), |
| | 1720 | ); |
| 1603 | }, | 1721 | }, |
| 1604 | .immediate => |imm| { | 1722 | .immediate => |imm| { |
| 1605 | const imm32 = @intCast(u31, imm); // This case must be handled before calling genX8664BinMathCode. | 1723 | // register, immediate use opx = 81 or 83 addressing modes: |
| 1606 | // 81 /opx id | 1724 | // opx = 81: r/m16/32/64, imm16/32 |
| 1607 | if (imm32 <= math.maxInt(u7)) { | 1725 | // opx = 83: r/m16/32/64, imm8 |
| 1608 | self.rex(.{ .b = dst_reg.isExtended(), .w = dst_reg.size() == 64 }); | 1726 | const imm32 = @intCast(i32, imm); // This case must be handled before calling genX8664BinMathCode. |
| 1609 | self.code.appendSliceAssumeCapacity(&[_]u8{ | 1727 | if (imm32 <= math.maxInt(i8)) { |
| 1610 | 0x83, | 1728 | const abi_size = dst_ty.abiSize(self.target.*); |
| 1611 | 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()), | 1729 | const encoder = try X8664Encoder.init(self.code, 4); |
| 1612 | @intCast(u8, imm32), | 1730 | encoder.rex(.{ |
| | 1731 | .w = abi_size == 8, |
| | 1732 | .b = dst_reg.isExtended(), |
| 1613 | }); | 1733 | }); |
| | 1734 | encoder.opcode_1byte(0x83); |
| | 1735 | encoder.modRm_direct( |
| | 1736 | opx, |
| | 1737 | dst_reg.low_id(), |
| | 1738 | ); |
| | 1739 | encoder.imm8(@intCast(i8, imm32)); |
| 1614 | } else { | 1740 | } else { |
| 1615 | self.rex(.{ .r = dst_reg.isExtended(), .w = dst_reg.size() == 64 }); | 1741 | const abi_size = dst_ty.abiSize(self.target.*); |
| 1616 | self.code.appendSliceAssumeCapacity(&[_]u8{ | 1742 | const encoder = try X8664Encoder.init(self.code, 7); |
| 1617 | 0x81, | 1743 | encoder.rex(.{ |
| 1618 | 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()), | 1744 | .w = abi_size == 8, |
| | 1745 | .b = dst_reg.isExtended(), |
| 1619 | }); | 1746 | }); |
| 1620 | std.mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), imm32); | 1747 | encoder.opcode_1byte(0x81); |
| | 1748 | encoder.modRm_direct( |
| | 1749 | opx, |
| | 1750 | dst_reg.low_id(), |
| | 1751 | ); |
| | 1752 | encoder.imm32(@intCast(i32, imm32)); |
| 1621 | } | 1753 | } |
| 1622 | }, | 1754 | }, |
| 1623 | .embedded_in_code, .memory, .stack_offset => { | 1755 | .embedded_in_code, .memory => { |
| 1624 | return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{}); | 1756 | return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{}); |
| 1625 | }, | 1757 | }, |
| | 1758 | .stack_offset => |off| { |
| | 1759 | // register, indirect use mr + 3 |
| | 1760 | // addressing mode: *r16/32/64*, r/m16/32/64 |
| | 1761 | const abi_size = dst_ty.abiSize(self.target.*); |
| | 1762 | const adj_off = off + abi_size; |
| | 1763 | if (off > math.maxInt(i32)) { |
| | 1764 | return self.fail(src, "stack offset too large", .{}); |
| | 1765 | } |
| | 1766 | const encoder = try X8664Encoder.init(self.code, 7); |
| | 1767 | encoder.rex(.{ |
| | 1768 | .w = abi_size == 8, |
| | 1769 | .r = dst_reg.isExtended(), |
| | 1770 | }); |
| | 1771 | encoder.opcode_1byte(mr + 3); |
| | 1772 | if (adj_off <= std.math.maxInt(i8)) { |
| | 1773 | encoder.modRm_indirectDisp8( |
| | 1774 | dst_reg.low_id(), |
| | 1775 | Register.ebp.low_id(), |
| | 1776 | ); |
| | 1777 | encoder.disp8(-@intCast(i8, adj_off)); |
| | 1778 | } else { |
| | 1779 | encoder.modRm_indirectDisp32( |
| | 1780 | dst_reg.low_id(), |
| | 1781 | Register.ebp.low_id(), |
| | 1782 | ); |
| | 1783 | encoder.disp32(-@intCast(i32, adj_off)); |
| | 1784 | } |
| | 1785 | }, |
| 1626 | .compare_flags_unsigned => { | 1786 | .compare_flags_unsigned => { |
| 1627 | return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{}); | 1787 | return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{}); |
| 1628 | }, | 1788 | }, |
| ... | @@ -1661,27 +1821,183 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -1661,27 +1821,183 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1661 | } | 1821 | } |
| 1662 | } | 1822 | } |
| 1663 | | 1823 | |
| | 1824 | /// Performs integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv. |
| | 1825 | fn genX8664Imul( |
| | 1826 | self: *Self, |
| | 1827 | src: LazySrcLoc, |
| | 1828 | dst_ty: Type, |
| | 1829 | dst_mcv: MCValue, |
| | 1830 | src_mcv: MCValue, |
| | 1831 | ) !void { |
| | 1832 | switch (dst_mcv) { |
| | 1833 | .none => unreachable, |
| | 1834 | .undef => unreachable, |
| | 1835 | .dead, .unreach, .immediate => unreachable, |
| | 1836 | .compare_flags_unsigned => unreachable, |
| | 1837 | .compare_flags_signed => unreachable, |
| | 1838 | .ptr_stack_offset => unreachable, |
| | 1839 | .ptr_embedded_in_code => unreachable, |
| | 1840 | .register => |dst_reg| { |
| | 1841 | switch (src_mcv) { |
| | 1842 | .none => unreachable, |
| | 1843 | .undef => try self.genSetReg(src, dst_ty, dst_reg, .undef), |
| | 1844 | .dead, .unreach => unreachable, |
| | 1845 | .ptr_stack_offset => unreachable, |
| | 1846 | .ptr_embedded_in_code => unreachable, |
| | 1847 | .register => |src_reg| { |
| | 1848 | // register, register |
| | 1849 | // |
| | 1850 | // Use the following imul opcode |
| | 1851 | // 0F AF /r: IMUL r32/64, r/m32/64 |
| | 1852 | const abi_size = dst_ty.abiSize(self.target.*); |
| | 1853 | const encoder = try X8664Encoder.init(self.code, 4); |
| | 1854 | encoder.rex(.{ |
| | 1855 | .w = abi_size == 8, |
| | 1856 | .r = dst_reg.isExtended(), |
| | 1857 | .b = src_reg.isExtended(), |
| | 1858 | }); |
| | 1859 | encoder.opcode_2byte(0x0f, 0xaf); |
| | 1860 | encoder.modRm_direct( |
| | 1861 | dst_reg.low_id(), |
| | 1862 | src_reg.low_id(), |
| | 1863 | ); |
| | 1864 | }, |
| | 1865 | .immediate => |imm| { |
| | 1866 | // register, immediate: |
| | 1867 | // depends on size of immediate. |
| | 1868 | // |
| | 1869 | // immediate fits in i8: |
| | 1870 | // 6B /r ib: IMUL r32/64, r/m32/64, imm8 |
| | 1871 | // |
| | 1872 | // immediate fits in i32: |
| | 1873 | // 69 /r id: IMUL r32/64, r/m32/64, imm32 |
| | 1874 | // |
| | 1875 | // immediate is huge: |
| | 1876 | // split into 2 instructions |
| | 1877 | // 1) copy the 64 bit immediate into a tmp register |
| | 1878 | // 2) perform register,register mul |
| | 1879 | // 0F AF /r: IMUL r32/64, r/m32/64 |
| | 1880 | if (math.minInt(i8) <= imm and imm <= math.maxInt(i8)) { |
| | 1881 | const abi_size = dst_ty.abiSize(self.target.*); |
| | 1882 | const encoder = try X8664Encoder.init(self.code, 4); |
| | 1883 | encoder.rex(.{ |
| | 1884 | .w = abi_size == 8, |
| | 1885 | .r = dst_reg.isExtended(), |
| | 1886 | .b = dst_reg.isExtended(), |
| | 1887 | }); |
| | 1888 | encoder.opcode_1byte(0x6B); |
| | 1889 | encoder.modRm_direct( |
| | 1890 | dst_reg.low_id(), |
| | 1891 | dst_reg.low_id(), |
| | 1892 | ); |
| | 1893 | encoder.imm8(@intCast(i8, imm)); |
| | 1894 | } else if (math.minInt(i32) <= imm and imm <= math.maxInt(i32)) { |
| | 1895 | const abi_size = dst_ty.abiSize(self.target.*); |
| | 1896 | const encoder = try X8664Encoder.init(self.code, 7); |
| | 1897 | encoder.rex(.{ |
| | 1898 | .w = abi_size == 8, |
| | 1899 | .r = dst_reg.isExtended(), |
| | 1900 | .b = dst_reg.isExtended(), |
| | 1901 | }); |
| | 1902 | encoder.opcode_1byte(0x69); |
| | 1903 | encoder.modRm_direct( |
| | 1904 | dst_reg.low_id(), |
| | 1905 | dst_reg.low_id(), |
| | 1906 | ); |
| | 1907 | encoder.imm32(@intCast(i32, imm)); |
| | 1908 | } else { |
| | 1909 | const src_reg = try self.copyToTmpRegister(src, dst_ty, src_mcv); |
| | 1910 | return self.genX8664Imul(src, dst_ty, dst_mcv, MCValue{ .register = src_reg }); |
| | 1911 | } |
| | 1912 | }, |
| | 1913 | .embedded_in_code, .memory, .stack_offset => { |
| | 1914 | return self.fail(src, "TODO implement x86 multiply source memory", .{}); |
| | 1915 | }, |
| | 1916 | .compare_flags_unsigned => { |
| | 1917 | return self.fail(src, "TODO implement x86 multiply source compare flag (unsigned)", .{}); |
| | 1918 | }, |
| | 1919 | .compare_flags_signed => { |
| | 1920 | return self.fail(src, "TODO implement x86 multiply source compare flag (signed)", .{}); |
| | 1921 | }, |
| | 1922 | } |
| | 1923 | }, |
| | 1924 | .stack_offset => |off| { |
| | 1925 | switch (src_mcv) { |
| | 1926 | .none => unreachable, |
| | 1927 | .undef => return self.genSetStack(src, dst_ty, off, .undef), |
| | 1928 | .dead, .unreach => unreachable, |
| | 1929 | .ptr_stack_offset => unreachable, |
| | 1930 | .ptr_embedded_in_code => unreachable, |
| | 1931 | .register => |src_reg| { |
| | 1932 | // copy dst to a register |
| | 1933 | const dst_reg = try self.copyToTmpRegister(src, dst_ty, dst_mcv); |
| | 1934 | // multiply into dst_reg |
| | 1935 | // register, register |
| | 1936 | // Use the following imul opcode |
| | 1937 | // 0F AF /r: IMUL r32/64, r/m32/64 |
| | 1938 | const abi_size = dst_ty.abiSize(self.target.*); |
| | 1939 | const encoder = try X8664Encoder.init(self.code, 4); |
| | 1940 | encoder.rex(.{ |
| | 1941 | .w = abi_size == 8, |
| | 1942 | .r = dst_reg.isExtended(), |
| | 1943 | .b = src_reg.isExtended(), |
| | 1944 | }); |
| | 1945 | encoder.opcode_2byte(0x0f, 0xaf); |
| | 1946 | encoder.modRm_direct( |
| | 1947 | dst_reg.low_id(), |
| | 1948 | src_reg.low_id(), |
| | 1949 | ); |
| | 1950 | // copy dst_reg back out |
| | 1951 | return self.genSetStack(src, dst_ty, off, MCValue{ .register = dst_reg }); |
| | 1952 | }, |
| | 1953 | .immediate => |imm| { |
| | 1954 | return self.fail(src, "TODO implement x86 multiply source immediate", .{}); |
| | 1955 | }, |
| | 1956 | .embedded_in_code, .memory, .stack_offset => { |
| | 1957 | return self.fail(src, "TODO implement x86 multiply source memory", .{}); |
| | 1958 | }, |
| | 1959 | .compare_flags_unsigned => { |
| | 1960 | return self.fail(src, "TODO implement x86 multiply source compare flag (unsigned)", .{}); |
| | 1961 | }, |
| | 1962 | .compare_flags_signed => { |
| | 1963 | return self.fail(src, "TODO implement x86 multiply source compare flag (signed)", .{}); |
| | 1964 | }, |
| | 1965 | } |
| | 1966 | }, |
| | 1967 | .embedded_in_code, .memory => { |
| | 1968 | return self.fail(src, "TODO implement x86 multiply destination memory", .{}); |
| | 1969 | }, |
| | 1970 | } |
| | 1971 | } |
| | 1972 | |
| 1664 | fn genX8664ModRMRegToStack(self: *Self, src: LazySrcLoc, ty: Type, off: u32, reg: Register, opcode: u8) !void { | 1973 | fn genX8664ModRMRegToStack(self: *Self, src: LazySrcLoc, ty: Type, off: u32, reg: Register, opcode: u8) !void { |
| 1665 | const abi_size = ty.abiSize(self.target.*); | 1974 | const abi_size = ty.abiSize(self.target.*); |
| 1666 | const adj_off = off + abi_size; | 1975 | const adj_off = off + abi_size; |
| 1667 | try self.code.ensureCapacity(self.code.items.len + 7); | 1976 | if (off > math.maxInt(i32)) { |
| 1668 | self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended() }); | 1977 | return self.fail(src, "stack offset too large", .{}); |
| 1669 | const reg_id: u8 = @truncate(u3, reg.id()); | 1978 | } |
| 1670 | if (adj_off <= 128) { | 1979 | |
| | 1980 | const i_adj_off = -@intCast(i32, adj_off); |
| | 1981 | const encoder = try X8664Encoder.init(self.code, 7); |
| | 1982 | encoder.rex(.{ |
| | 1983 | .w = abi_size == 8, |
| | 1984 | .r = reg.isExtended(), |
| | 1985 | }); |
| | 1986 | encoder.opcode_1byte(opcode); |
| | 1987 | if (i_adj_off < std.math.maxInt(i8)) { |
| 1671 | // example: 48 89 55 7f mov QWORD PTR [rbp+0x7f],rdx | 1988 | // example: 48 89 55 7f mov QWORD PTR [rbp+0x7f],rdx |
| 1672 | const RM = @as(u8, 0b01_000_101) | (reg_id << 3); | 1989 | encoder.modRm_indirectDisp8( |
| 1673 | const negative_offset = @intCast(i8, -@intCast(i32, adj_off)); | 1990 | reg.low_id(), |
| 1674 | const twos_comp = @bitCast(u8, negative_offset); | 1991 | Register.ebp.low_id(), |
| 1675 | self.code.appendSliceAssumeCapacity(&[_]u8{ opcode, RM, twos_comp }); | 1992 | ); |
| 1676 | } else if (adj_off <= 2147483648) { | 1993 | encoder.disp8(@intCast(i8, i_adj_off)); |
| 1677 | // example: 48 89 95 80 00 00 00 mov QWORD PTR [rbp+0x80],rdx | | |
| 1678 | const RM = @as(u8, 0b10_000_101) | (reg_id << 3); | | |
| 1679 | const negative_offset = @intCast(i32, -@intCast(i33, adj_off)); | | |
| 1680 | const twos_comp = @bitCast(u32, negative_offset); | | |
| 1681 | self.code.appendSliceAssumeCapacity(&[_]u8{ opcode, RM }); | | |
| 1682 | mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), twos_comp); | | |
| 1683 | } else { | 1994 | } else { |
| 1684 | return self.fail(src, "stack offset too large", .{}); | 1995 | // example: 48 89 95 80 00 00 00 mov QWORD PTR [rbp+0x80],rdx |
| | 1996 | encoder.modRm_indirectDisp32( |
| | 1997 | reg.low_id(), |
| | 1998 | Register.ebp.low_id(), |
| | 1999 | ); |
| | 2000 | encoder.disp32(i_adj_off); |
| 1685 | } | 2001 | } |
| 1686 | } | 2002 | } |
| 1687 | | 2003 | |
| ... | @@ -2126,12 +2442,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2126,12 +2442,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2126 | log.debug("got_addr = 0x{x}", .{got_addr}); | 2442 | log.debug("got_addr = 0x{x}", .{got_addr}); |
| 2127 | switch (arch) { | 2443 | switch (arch) { |
| 2128 | .x86_64 => { | 2444 | .x86_64 => { |
| 2129 | try self.genSetReg(inst.base.src, Type.initTag(.u32), .rax, .{ .memory = got_addr }); | 2445 | try self.genSetReg(inst.base.src, Type.initTag(.u64), .rax, .{ .memory = got_addr }); |
| 2130 | // callq *%rax | 2446 | // callq *%rax |
| | 2447 | try self.code.ensureCapacity(self.code.items.len + 2); |
| 2131 | self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 }); | 2448 | self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 }); |
| 2132 | }, | 2449 | }, |
| 2133 | .aarch64 => { | 2450 | .aarch64 => { |
| 2134 | try self.genSetReg(inst.base.src, Type.initTag(.u32), .x30, .{ .memory = got_addr }); | 2451 | try self.genSetReg(inst.base.src, Type.initTag(.u64), .x30, .{ .memory = got_addr }); |
| 2135 | // blr x30 | 2452 | // blr x30 |
| 2136 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32()); | 2453 | writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32()); |
| 2137 | }, | 2454 | }, |
| ... | @@ -2355,15 +2672,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2355,15 +2672,19 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2355 | .register => |reg| blk: { | 2672 | .register => |reg| blk: { |
| 2356 | // test reg, 1 | 2673 | // test reg, 1 |
| 2357 | // TODO detect al, ax, eax | 2674 | // TODO detect al, ax, eax |
| 2358 | try self.code.ensureCapacity(self.code.items.len + 4); | 2675 | const encoder = try X8664Encoder.init(self.code, 4); |
| 2359 | // TODO audit this codegen: we force w = true here to make | 2676 | encoder.rex(.{ |
| 2360 | // the value affect the big register | 2677 | // TODO audit this codegen: we force w = true here to make |
| 2361 | self.rex(.{ .b = reg.isExtended(), .w = true }); | 2678 | // the value affect the big register |
| 2362 | self.code.appendSliceAssumeCapacity(&[_]u8{ | 2679 | .w = true, |
| 2363 | 0xf6, | 2680 | .b = reg.isExtended(), |
| 2364 | @as(u8, 0xC0) | (0 << 3) | @truncate(u3, reg.id()), | | |
| 2365 | 0x01, | | |
| 2366 | }); | 2681 | }); |
| | 2682 | encoder.opcode_1byte(0xf6); |
| | 2683 | encoder.modRm_direct( |
| | 2684 | 0, |
| | 2685 | reg.low_id(), |
| | 2686 | ); |
| | 2687 | encoder.disp8(1); |
| 2367 | break :blk 0x84; | 2688 | break :blk 0x84; |
| 2368 | }, | 2689 | }, |
| 2369 | else => return self.fail(inst.base.src, "TODO implement condbr {s} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }), | 2690 | else => return self.fail(inst.base.src, "TODO implement condbr {s} when condition is {s}", .{ self.target.cpu.arch, @tagName(cond) }), |
| ... | @@ -2673,9 +2994,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2673,9 +2994,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2673 | switch (arch) { | 2994 | switch (arch) { |
| 2674 | .x86_64 => switch (inst.base.tag) { | 2995 | .x86_64 => switch (inst.base.tag) { |
| 2675 | // lhs AND rhs | 2996 | // lhs AND rhs |
| 2676 | .bool_and => return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 4, 0x20), | 2997 | .bool_and => return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs), |
| 2677 | // lhs OR rhs | 2998 | // lhs OR rhs |
| 2678 | .bool_or => return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs, 1, 0x08), | 2999 | .bool_or => return try self.genX8664BinMath(&inst.base, inst.lhs, inst.rhs), |
| 2679 | else => unreachable, // Not a boolean operation | 3000 | else => unreachable, // Not a boolean operation |
| 2680 | }, | 3001 | }, |
| 2681 | .arm, .armeb => switch (inst.base.tag) { | 3002 | .arm, .armeb => switch (inst.base.tag) { |
| ... | @@ -2882,39 +3203,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -2882,39 +3203,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2882 | } | 3203 | } |
| 2883 | } | 3204 | } |
| 2884 | | 3205 | |
| 2885 | /// Encodes a REX prefix as specified, and appends it to the instruction | | |
| 2886 | /// stream. This only modifies the instruction stream if at least one bit | | |
| 2887 | /// is set true, which has a few implications: | | |
| 2888 | /// | | |
| 2889 | /// * The length of the instruction buffer will be modified *if* the | | |
| 2890 | /// resulting REX is meaningful, but will remain the same if it is not. | | |
| 2891 | /// * Deliberately inserting a "meaningless REX" requires explicit usage of | | |
| 2892 | /// 0x40, and cannot be done via this function. | | |
| 2893 | /// W => 64 bit mode | | |
| 2894 | /// R => extension to the MODRM.reg field | | |
| 2895 | /// X => extension to the SIB.index field | | |
| 2896 | /// B => extension to the MODRM.rm field or the SIB.base field | | |
| 2897 | fn rex(self: *Self, arg: struct { b: bool = false, w: bool = false, x: bool = false, r: bool = false }) void { | | |
| 2898 | comptime assert(arch == .x86_64); | | |
| 2899 | // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB. | | |
| 2900 | var value: u8 = 0x40; | | |
| 2901 | if (arg.b) { | | |
| 2902 | value |= 0x1; | | |
| 2903 | } | | |
| 2904 | if (arg.x) { | | |
| 2905 | value |= 0x2; | | |
| 2906 | } | | |
| 2907 | if (arg.r) { | | |
| 2908 | value |= 0x4; | | |
| 2909 | } | | |
| 2910 | if (arg.w) { | | |
| 2911 | value |= 0x8; | | |
| 2912 | } | | |
| 2913 | if (value != 0x40) { | | |
| 2914 | self.code.appendAssumeCapacity(value); | | |
| 2915 | } | | |
| 2916 | } | | |
| 2917 | | | |
| 2918 | /// Sets the value without any modifications to register allocation metadata or stack allocation metadata. | 3206 | /// Sets the value without any modifications to register allocation metadata or stack allocation metadata. |
| 2919 | fn setRegOrMem(self: *Self, src: LazySrcLoc, ty: Type, loc: MCValue, val: MCValue) !void { | 3207 | fn setRegOrMem(self: *Self, src: LazySrcLoc, ty: Type, loc: MCValue, val: MCValue) !void { |
| 2920 | switch (loc) { | 3208 | switch (loc) { |
| ... | @@ -3462,20 +3750,25 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -3462,20 +3750,25 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3462 | } | 3750 | } |
| 3463 | }, | 3751 | }, |
| 3464 | .compare_flags_unsigned => |op| { | 3752 | .compare_flags_unsigned => |op| { |
| 3465 | try self.code.ensureCapacity(self.code.items.len + 3); | 3753 | const encoder = try X8664Encoder.init(self.code, 7); |
| 3466 | // TODO audit this codegen: we force w = true here to make | 3754 | // TODO audit this codegen: we force w = true here to make |
| 3467 | // the value affect the big register | 3755 | // the value affect the big register |
| 3468 | self.rex(.{ .b = reg.isExtended(), .w = true }); | 3756 | encoder.rex(.{ |
| 3469 | const opcode: u8 = switch (op) { | 3757 | .w = true, |
| | 3758 | .b = reg.isExtended(), |
| | 3759 | }); |
| | 3760 | encoder.opcode_2byte(0x0f, switch (op) { |
| 3470 | .gte => 0x93, | 3761 | .gte => 0x93, |
| 3471 | .gt => 0x97, | 3762 | .gt => 0x97, |
| 3472 | .neq => 0x95, | 3763 | .neq => 0x95, |
| 3473 | .lt => 0x92, | 3764 | .lt => 0x92, |
| 3474 | .lte => 0x96, | 3765 | .lte => 0x96, |
| 3475 | .eq => 0x94, | 3766 | .eq => 0x94, |
| 3476 | }; | 3767 | }); |
| 3477 | const id = @as(u8, reg.id() & 0b111); | 3768 | encoder.modRm_direct( |
| 3478 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode, 0xC0 | id }); | 3769 | 0, |
| | 3770 | reg.low_id(), |
| | 3771 | ); |
| 3479 | }, | 3772 | }, |
| 3480 | .compare_flags_signed => |op| { | 3773 | .compare_flags_signed => |op| { |
| 3481 | return self.fail(src, "TODO set register with compare flags value (signed)", .{}); | 3774 | return self.fail(src, "TODO set register with compare flags value (signed)", .{}); |
| ... | @@ -3485,40 +3778,43 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -3485,40 +3778,43 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3485 | // register is the fastest way to zero a register. | 3778 | // register is the fastest way to zero a register. |
| 3486 | if (x == 0) { | 3779 | if (x == 0) { |
| 3487 | // The encoding for `xor r32, r32` is `0x31 /r`. | 3780 | // The encoding for `xor r32, r32` is `0x31 /r`. |
| 3488 | // Section 3.1.1.1 of the Intel x64 Manual states that "/r indicates that the | 3781 | const encoder = try X8664Encoder.init(self.code, 3); |
| 3489 | // ModR/M byte of the instruction contains a register operand and an r/m operand." | 3782 | |
| 3490 | // | | |
| 3491 | // R/M bytes are composed of two bits for the mode, then three bits for the register, | | |
| 3492 | // then three bits for the operand. Since we're zeroing a register, the two three-bit | | |
| 3493 | // values will be identical, and the mode is three (the raw register value). | | |
| 3494 | // | | |
| 3495 | // If we're accessing e.g. r8d, we need to use a REX prefix before the actual operation. Since | 3783 | // If we're accessing e.g. r8d, we need to use a REX prefix before the actual operation. Since |
| 3496 | // this is a 32-bit operation, the W flag is set to zero. X is also zero, as we're not using a SIB. | 3784 | // this is a 32-bit operation, the W flag is set to zero. X is also zero, as we're not using a SIB. |
| 3497 | // Both R and B are set, as we're extending, in effect, the register bits *and* the operand. | 3785 | // Both R and B are set, as we're extending, in effect, the register bits *and* the operand. |
| 3498 | try self.code.ensureCapacity(self.code.items.len + 3); | 3786 | encoder.rex(.{ |
| 3499 | self.rex(.{ .r = reg.isExtended(), .b = reg.isExtended() }); | 3787 | .r = reg.isExtended(), |
| 3500 | const id = @as(u8, reg.id() & 0b111); | 3788 | .b = reg.isExtended(), |
| 3501 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x31, 0xC0 | id << 3 | id }); | 3789 | }); |
| | 3790 | encoder.opcode_1byte(0x31); |
| | 3791 | // Section 3.1.1.1 of the Intel x64 Manual states that "/r indicates that the |
| | 3792 | // ModR/M byte of the instruction contains a register operand and an r/m operand." |
| | 3793 | encoder.modRm_direct( |
| | 3794 | reg.low_id(), |
| | 3795 | reg.low_id(), |
| | 3796 | ); |
| | 3797 | |
| 3502 | return; | 3798 | return; |
| 3503 | } | 3799 | } |
| 3504 | if (x <= math.maxInt(u32)) { | 3800 | if (x <= math.maxInt(i32)) { |
| 3505 | // Next best case: if we set the lower four bytes, the upper four will be zeroed. | 3801 | // Next best case: if we set the lower four bytes, the upper four will be zeroed. |
| 3506 | // | 3802 | // |
| 3507 | // The encoding for `mov IMM32 -> REG` is (0xB8 + R) IMM. | 3803 | // The encoding for `mov IMM32 -> REG` is (0xB8 + R) IMM. |
| 3508 | if (reg.isExtended()) { | 3804 | |
| 3509 | // Just as with XORing, we need a REX prefix. This time though, we only | 3805 | const encoder = try X8664Encoder.init(self.code, 6); |
| 3510 | // need the B bit set, as we're extending the opcode's register field, | 3806 | // Just as with XORing, we need a REX prefix. This time though, we only |
| 3511 | // and there is no Mod R/M byte. | 3807 | // need the B bit set, as we're extending the opcode's register field, |
| 3512 | // | 3808 | // and there is no Mod R/M byte. |
| 3513 | // Thus, we need b01000001, or 0x41. | 3809 | encoder.rex(.{ |
| 3514 | try self.code.resize(self.code.items.len + 6); | 3810 | .b = reg.isExtended(), |
| 3515 | self.code.items[self.code.items.len - 6] = 0x41; | 3811 | }); |
| 3516 | } else { | 3812 | encoder.opcode_withReg(0xB8, reg.low_id()); |
| 3517 | try self.code.resize(self.code.items.len + 5); | 3813 | |
| 3518 | } | 3814 | // no ModR/M byte |
| 3519 | self.code.items[self.code.items.len - 5] = 0xB8 | @as(u8, reg.id() & 0b111); | 3815 | |
| 3520 | const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4]; | 3816 | // IMM |
| 3521 | mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x)); | 3817 | encoder.imm32(@intCast(i32, x)); |
| 3522 | return; | 3818 | return; |
| 3523 | } | 3819 | } |
| 3524 | // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls | 3820 | // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls |
| ... | @@ -3528,79 +3824,98 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -3528,79 +3824,98 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3528 | // This encoding is, in fact, the *same* as the one used for 32-bit loads. The only | 3824 | // This encoding is, in fact, the *same* as the one used for 32-bit loads. The only |
| 3529 | // difference is that we set REX.W before the instruction, which extends the load to | 3825 | // difference is that we set REX.W before the instruction, which extends the load to |
| 3530 | // 64-bit and uses the full bit-width of the register. | 3826 | // 64-bit and uses the full bit-width of the register. |
| 3531 | // | 3827 | { |
| 3532 | // Since we always need a REX here, let's just check if we also need to set REX.B. | 3828 | const encoder = try X8664Encoder.init(self.code, 10); |
| 3533 | // | 3829 | encoder.rex(.{ |
| 3534 | // In this case, the encoding of the REX byte is 0b0100100B | 3830 | .w = true, |
| 3535 | try self.code.ensureCapacity(self.code.items.len + 10); | 3831 | .b = reg.isExtended(), |
| 3536 | self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() }); | 3832 | }); |
| 3537 | self.code.items.len += 9; | 3833 | encoder.opcode_withReg(0xB8, reg.low_id()); |
| 3538 | self.code.items[self.code.items.len - 9] = 0xB8 | @as(u8, reg.id() & 0b111); | 3834 | encoder.imm64(x); |
| 3539 | const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8]; | 3835 | } |
| 3540 | mem.writeIntLittle(u64, imm_ptr, x); | | |
| 3541 | }, | 3836 | }, |
| 3542 | .embedded_in_code => |code_offset| { | 3837 | .embedded_in_code => |code_offset| { |
| 3543 | // We need the offset from RIP in a signed i32 twos complement. | 3838 | // We need the offset from RIP in a signed i32 twos complement. |
| 3544 | // The instruction is 7 bytes long and RIP points to the next instruction. | 3839 | // The instruction is 7 bytes long and RIP points to the next instruction. |
| 3545 | try self.code.ensureCapacity(self.code.items.len + 7); | 3840 | |
| 3546 | // 64-bit LEA is encoded as REX.W 8D /r. If the register is extended, the REX byte is modified, | 3841 | // 64-bit LEA is encoded as REX.W 8D /r. |
| 3547 | // but the operation size is unchanged. Since we're using a disp32, we want mode 0 and lower three | 3842 | const rip = self.code.items.len + 7; |
| 3548 | // bits as five. | | |
| 3549 | // REX 0x8D 0b00RRR101, where RRR is the lower three bits of the id. | | |
| 3550 | self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() }); | | |
| 3551 | self.code.items.len += 6; | | |
| 3552 | const rip = self.code.items.len; | | |
| 3553 | const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip); | 3843 | const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip); |
| 3554 | const offset = @intCast(i32, big_offset); | 3844 | const offset = @intCast(i32, big_offset); |
| 3555 | self.code.items[self.code.items.len - 6] = 0x8D; | 3845 | const encoder = try X8664Encoder.init(self.code, 7); |
| 3556 | self.code.items[self.code.items.len - 5] = 0b101 | (@as(u8, reg.id() & 0b111) << 3); | 3846 | |
| 3557 | const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4]; | 3847 | // byte 1, always exists because w = true |
| 3558 | mem.writeIntLittle(i32, imm_ptr, offset); | 3848 | encoder.rex(.{ |
| | 3849 | .w = true, |
| | 3850 | .r = reg.isExtended(), |
| | 3851 | }); |
| | 3852 | // byte 2 |
| | 3853 | encoder.opcode_1byte(0x8D); |
| | 3854 | // byte 3 |
| | 3855 | encoder.modRm_RIPDisp32(reg.low_id()); |
| | 3856 | // byte 4-7 |
| | 3857 | encoder.disp32(offset); |
| | 3858 | |
| | 3859 | // Double check that we haven't done any math errors |
| | 3860 | assert(rip == self.code.items.len); |
| 3559 | }, | 3861 | }, |
| 3560 | .register => |src_reg| { | 3862 | .register => |src_reg| { |
| 3561 | // If the registers are the same, nothing to do. | 3863 | // If the registers are the same, nothing to do. |
| 3562 | if (src_reg.id() == reg.id()) | 3864 | if (src_reg.id() == reg.id()) |
| 3563 | return; | 3865 | return; |
| 3564 | | 3866 | |
| 3565 | // This is a variant of 8B /r. Since we're using 64-bit moves, we require a REX. | 3867 | // This is a variant of 8B /r. |
| 3566 | // This is thus three bytes: REX 0x8B R/M. | 3868 | const abi_size = ty.abiSize(self.target.*); |
| 3567 | // If the destination is extended, the R field must be 1. | 3869 | const encoder = try X8664Encoder.init(self.code, 3); |
| 3568 | // If the *source* is extended, the B field must be 1. | 3870 | encoder.rex(.{ |
| 3569 | // Since the register is being accessed directly, the R/M mode is three. The reg field (the middle | 3871 | .w = abi_size == 8, |
| 3570 | // three bits) contain the destination, and the R/M field (the lower three bits) contain the source. | 3872 | .r = reg.isExtended(), |
| 3571 | try self.code.ensureCapacity(self.code.items.len + 3); | 3873 | .b = src_reg.isExtended(), |
| 3572 | self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended(), .b = src_reg.isExtended() }); | 3874 | }); |
| 3573 | const R = 0xC0 | (@as(u8, reg.id() & 0b111) << 3) | @as(u8, src_reg.id() & 0b111); | 3875 | encoder.opcode_1byte(0x8B); |
| 3574 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, R }); | 3876 | encoder.modRm_direct(reg.low_id(), src_reg.low_id()); |
| 3575 | }, | 3877 | }, |
| 3576 | .memory => |x| { | 3878 | .memory => |x| { |
| 3577 | if (self.bin_file.options.pie) { | 3879 | if (self.bin_file.options.pie) { |
| 3578 | // RIP-relative displacement to the entry in the GOT table. | 3880 | // RIP-relative displacement to the entry in the GOT table. |
| | 3881 | const abi_size = ty.abiSize(self.target.*); |
| | 3882 | const encoder = try X8664Encoder.init(self.code, 10); |
| | 3883 | |
| | 3884 | // LEA reg, [<offset>] |
| | 3885 | |
| | 3886 | // We encode the instruction FIRST because prefixes may or may not appear. |
| | 3887 | // After we encode the instruction, we will know that the displacement bytes |
| | 3888 | // for [<offset>] will be at self.code.items.len - 4. |
| | 3889 | encoder.rex(.{ |
| | 3890 | .w = true, // force 64 bit because loading an address (to the GOT) |
| | 3891 | .r = reg.isExtended(), |
| | 3892 | }); |
| | 3893 | encoder.opcode_1byte(0x8D); |
| | 3894 | encoder.modRm_RIPDisp32(reg.low_id()); |
| | 3895 | encoder.disp32(0); |
| | 3896 | |
| 3579 | // TODO we should come up with our own, backend independent relocation types | 3897 | // TODO we should come up with our own, backend independent relocation types |
| 3580 | // which each backend (Elf, MachO, etc.) would then translate into an actual | 3898 | // which each backend (Elf, MachO, etc.) would then translate into an actual |
| 3581 | // fixup when linking. | 3899 | // fixup when linking. |
| 3582 | if (self.bin_file.cast(link.File.MachO)) |macho_file| { | 3900 | if (self.bin_file.cast(link.File.MachO)) |macho_file| { |
| 3583 | try macho_file.pie_fixups.append(self.bin_file.allocator, .{ | 3901 | try macho_file.pie_fixups.append(self.bin_file.allocator, .{ |
| 3584 | .target_addr = x, | 3902 | .target_addr = x, |
| 3585 | .offset = self.code.items.len + 3, | 3903 | .offset = self.code.items.len - 4, |
| 3586 | .size = 4, | 3904 | .size = 4, |
| 3587 | }); | 3905 | }); |
| 3588 | } else { | 3906 | } else { |
| 3589 | return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{}); | 3907 | return self.fail(src, "TODO implement genSetReg for PIE GOT indirection on this platform", .{}); |
| 3590 | } | 3908 | } |
| 3591 | try self.code.ensureCapacity(self.code.items.len + 7); | | |
| 3592 | self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended() }); | | |
| 3593 | self.code.appendSliceAssumeCapacity(&[_]u8{ | | |
| 3594 | 0x8D, | | |
| 3595 | 0x05 | (@as(u8, reg.id() & 0b111) << 3), | | |
| 3596 | }); | | |
| 3597 | mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), 0); | | |
| 3598 | | 3909 | |
| 3599 | try self.code.ensureCapacity(self.code.items.len + 3); | 3910 | // MOV reg, [reg] |
| 3600 | self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended(), .r = reg.isExtended() }); | 3911 | encoder.rex(.{ |
| 3601 | const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id()); | 3912 | .w = abi_size == 8, |
| 3602 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, RM }); | 3913 | .r = reg.isExtended(), |
| 3603 | } else if (x <= math.maxInt(u32)) { | 3914 | .b = reg.isExtended(), |
| | 3915 | }); |
| | 3916 | encoder.opcode_1byte(0x8B); |
| | 3917 | encoder.modRm_indirectDisp0(reg.low_id(), reg.low_id()); |
| | 3918 | } else if (x <= math.maxInt(i32)) { |
| 3604 | // Moving from memory to a register is a variant of `8B /r`. | 3919 | // Moving from memory to a register is a variant of `8B /r`. |
| 3605 | // Since we're using 64-bit moves, we require a REX. | 3920 | // Since we're using 64-bit moves, we require a REX. |
| 3606 | // This variant also requires a SIB, as it would otherwise be RIP-relative. | 3921 | // This variant also requires a SIB, as it would otherwise be RIP-relative. |
| ... | @@ -3608,14 +3923,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -3608,14 +3923,18 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3608 | // The SIB must be 0x25, to indicate a disp32 with no scaled index. | 3923 | // The SIB must be 0x25, to indicate a disp32 with no scaled index. |
| 3609 | // 0b00RRR100, where RRR is the lower three bits of the register ID. | 3924 | // 0b00RRR100, where RRR is the lower three bits of the register ID. |
| 3610 | // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32. | 3925 | // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32. |
| 3611 | try self.code.ensureCapacity(self.code.items.len + 8); | 3926 | const abi_size = ty.abiSize(self.target.*); |
| 3612 | self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended() }); | 3927 | const encoder = try X8664Encoder.init(self.code, 8); |
| 3613 | self.code.appendSliceAssumeCapacity(&[_]u8{ | 3928 | encoder.rex(.{ |
| 3614 | 0x8B, | 3929 | .w = abi_size == 8, |
| 3615 | 0x04 | (@as(u8, reg.id() & 0b111) << 3), // R | 3930 | .r = reg.isExtended(), |
| 3616 | 0x25, | | |
| 3617 | }); | 3931 | }); |
| 3618 | mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), @intCast(u32, x)); | 3932 | encoder.opcode_1byte(0x8B); |
| | 3933 | // effective address = [SIB] |
| | 3934 | encoder.modRm_SIBDisp0(reg.low_id()); |
| | 3935 | // SIB = disp32 |
| | 3936 | encoder.sib_disp32(); |
| | 3937 | encoder.disp32(@intCast(i32, x)); |
| 3619 | } else { | 3938 | } else { |
| 3620 | // If this is RAX, we can use a direct load; otherwise, we need to load the address, then indirectly load | 3939 | // If this is RAX, we can use a direct load; otherwise, we need to load the address, then indirectly load |
| 3621 | // the value. | 3940 | // the value. |
| ... | @@ -3623,12 +3942,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -3623,12 +3942,13 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3623 | // REX.W 0xA1 moffs64* | 3942 | // REX.W 0xA1 moffs64* |
| 3624 | // moffs64* is a 64-bit offset "relative to segment base", which really just means the | 3943 | // moffs64* is a 64-bit offset "relative to segment base", which really just means the |
| 3625 | // absolute address for all practical purposes. | 3944 | // absolute address for all practical purposes. |
| 3626 | try self.code.resize(self.code.items.len + 10); | 3945 | |
| 3627 | // REX.W == 0x48 | 3946 | const encoder = try X8664Encoder.init(self.code, 10); |
| 3628 | self.code.items[self.code.items.len - 10] = 0x48; | 3947 | encoder.rex(.{ |
| 3629 | self.code.items[self.code.items.len - 9] = 0xA1; | 3948 | .w = true, |
| 3630 | const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8]; | 3949 | }); |
| 3631 | mem.writeIntLittle(u64, imm_ptr, x); | 3950 | encoder.opcode_1byte(0xA1); |
| | 3951 | encoder.writeIntLittle(u64, x); |
| 3632 | } else { | 3952 | } else { |
| 3633 | // This requires two instructions; a move imm as used above, followed by an indirect load using the register | 3953 | // This requires two instructions; a move imm as used above, followed by an indirect load using the register |
| 3634 | // as the address and the register as the destination. | 3954 | // as the address and the register as the destination. |
| ... | @@ -3645,40 +3965,41 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -3645,40 +3965,41 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 3645 | // Now, the register contains the address of the value to load into it | 3965 | // Now, the register contains the address of the value to load into it |
| 3646 | // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant. | 3966 | // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant. |
| 3647 | // TODO: determine whether to allow other sized registers, and if so, handle them properly. | 3967 | // TODO: determine whether to allow other sized registers, and if so, handle them properly. |
| 3648 | // This operation requires three bytes: REX 0x8B R/M | 3968 | |
| 3649 | try self.code.ensureCapacity(self.code.items.len + 3); | 3969 | // mov reg, [reg] |
| 3650 | // For this operation, we want R/M mode *zero* (use register indirectly), and the two register | 3970 | const abi_size = ty.abiSize(self.target.*); |
| 3651 | // values must match. Thus, it's 00ABCABC where ABC is the lower three bits of the register ID. | 3971 | const encoder = try X8664Encoder.init(self.code, 3); |
| 3652 | // | 3972 | encoder.rex(.{ |
| 3653 | // Furthermore, if this is an extended register, both B and R must be set in the REX byte, as *both* | 3973 | .w = abi_size == 8, |
| 3654 | // register operands need to be marked as extended. | 3974 | .r = reg.isExtended(), |
| 3655 | self.rex(.{ .w = reg.size() == 64, .b = reg.isExtended(), .r = reg.isExtended() }); | 3975 | .b = reg.isExtended(), |
| 3656 | const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id()); | 3976 | }); |
| 3657 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, RM }); | 3977 | encoder.opcode_1byte(0x8B); |
| | 3978 | encoder.modRm_indirectDisp0(reg.low_id(), reg.low_id()); |
| 3658 | } | 3979 | } |
| 3659 | } | 3980 | } |
| 3660 | }, | 3981 | }, |
| 3661 | .stack_offset => |unadjusted_off| { | 3982 | .stack_offset => |unadjusted_off| { |
| 3662 | try self.code.ensureCapacity(self.code.items.len + 7); | 3983 | const abi_size = ty.abiSize(self.target.*); |
| 3663 | const size_bytes = @divExact(reg.size(), 8); | 3984 | const off = unadjusted_off + abi_size; |
| 3664 | const off = unadjusted_off + size_bytes; | 3985 | if (off < std.math.minInt(i32) or off > std.math.maxInt(i32)) { |
| 3665 | self.rex(.{ .w = reg.size() == 64, .r = reg.isExtended() }); | 3986 | return self.fail(src, "stack offset too large", .{}); |
| 3666 | const reg_id: u8 = @truncate(u3, reg.id()); | 3987 | } |
| 3667 | if (off <= 128) { | 3988 | const ioff = -@intCast(i32, off); |
| | 3989 | const encoder = try X8664Encoder.init(self.code, 3); |
| | 3990 | encoder.rex(.{ |
| | 3991 | .w = abi_size == 8, |
| | 3992 | .r = reg.isExtended(), |
| | 3993 | }); |
| | 3994 | encoder.opcode_1byte(0x8B); |
| | 3995 | if (std.math.minInt(i8) <= ioff and ioff <= std.math.maxInt(i8)) { |
| 3668 | // Example: 48 8b 4d 7f mov rcx,QWORD PTR [rbp+0x7f] | 3996 | // Example: 48 8b 4d 7f mov rcx,QWORD PTR [rbp+0x7f] |
| 3669 | const RM = @as(u8, 0b01_000_101) | (reg_id << 3); | 3997 | encoder.modRm_indirectDisp8(reg.low_id(), Register.ebp.low_id()); |
| 3670 | const negative_offset = @intCast(i8, -@intCast(i32, off)); | 3998 | encoder.disp8(@intCast(i8, ioff)); |
| 3671 | const twos_comp = @bitCast(u8, negative_offset); | | |
| 3672 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8b, RM, twos_comp }); | | |
| 3673 | } else if (off <= 2147483648) { | | |
| 3674 | // Example: 48 8b 8d 80 00 00 00 mov rcx,QWORD PTR [rbp+0x80] | | |
| 3675 | const RM = @as(u8, 0b10_000_101) | (reg_id << 3); | | |
| 3676 | const negative_offset = @intCast(i32, -@intCast(i33, off)); | | |
| 3677 | const twos_comp = @bitCast(u32, negative_offset); | | |
| 3678 | self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8b, RM }); | | |
| 3679 | mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), twos_comp); | | |
| 3680 | } else { | 3999 | } else { |
| 3681 | return self.fail(src, "stack offset too large", .{}); | 4000 | // Example: 48 8b 8d 80 00 00 00 mov rcx,QWORD PTR [rbp+0x80] |
| | 4001 | encoder.modRm_indirectDisp32(reg.low_id(), Register.ebp.low_id()); |
| | 4002 | encoder.disp32(ioff); |
| 3682 | } | 4003 | } |
| 3683 | }, | 4004 | }, |
| 3684 | }, | 4005 | }, |