| author | |
| committer | |
| log | b7e223597395358dc63cf88c92ace5eaa455cf89 |
| tree | 51108f8b375f49eac43b24bbc839dfefdada4f3e |
| parent | 08ea1a2eab9472389d57ddbd97e307fcb096a992 |
* fix handling of `ah`, `bh`, `ch`, and `dh` registers (which are
actually used as aliases to `dil`, etc. registers). Currenly, we
treat them as aliases only meaning when we encounter `ah` we make
sure to set the REX.W to promote the instruction to 64bits and use
`dil` register instead - otherwise we might have mismatch between
registers used in different parts of the codegen. In the future,
we can and should use `ah`, etc. as upper 8bit halves of 16bit
registers `ax`, etc.
* fix bug in `airCmp` where `.cmp` MIR instruction shouldn't force
type `Bool` but let the type of the original type propagate downwards
- we need this to make an informed choice of the target register
size and hence choose the right encoding down the line.
* implement lowering of 1-byte and 2-byte values to stack and add
matching stage2 tests for x86_64 codegen3 files changed, 103 insertions(+), 33 deletions(-)
src/arch/x86_64/CodeGen.zig+16-15| ... | ... | @@ -1642,11 +1642,11 @@ fn genBinMathOpMir( |
| 1642 | 1642 | }); |
| 1643 | 1643 | }, |
| 1644 | 1644 | .immediate => |imm| { |
| 1645 | // TODO I am not quite sure why we need to set the size of the register here... | |
| 1645 | const abi_size = dst_ty.abiSize(self.target.*); | |
| 1646 | 1646 | _ = try self.addInst(.{ |
| 1647 | 1647 | .tag = mir_tag, |
| 1648 | 1648 | .ops = (Mir.Ops{ |
| 1649 | .reg1 = dst_reg.to32(), | |
| 1649 | .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)), | |
| 1650 | 1650 | }).encode(), |
| 1651 | 1651 | .data = .{ .imm = @intCast(i32, imm) }, |
| 1652 | 1652 | }); |
| ... | ... | @@ -1751,13 +1751,14 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) ! |
| 1751 | 1751 | }); |
| 1752 | 1752 | }, |
| 1753 | 1753 | .immediate => |imm| { |
| 1754 | // TODO take into account the type's ABI size when selecting the register alias | |
| 1754 | 1755 | // register, immediate |
| 1755 | 1756 | if (imm <= math.maxInt(i32)) { |
| 1756 | 1757 | _ = try self.addInst(.{ |
| 1757 | 1758 | .tag = .imul_complex, |
| 1758 | 1759 | .ops = (Mir.Ops{ |
| 1759 | .reg1 = dst_reg, | |
| 1760 | .reg2 = dst_reg, | |
| 1760 | .reg1 = dst_reg.to32(), | |
| 1761 | .reg2 = dst_reg.to32(), | |
| 1761 | 1762 | .flags = 0b10, |
| 1762 | 1763 | }).encode(), |
| 1763 | 1764 | .data = .{ .imm = @intCast(i32, imm) }, |
| ... | ... | @@ -2147,7 +2148,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 2147 | 2148 | // This instruction supports only signed 32-bit immediates at most. |
| 2148 | 2149 | const src_mcv = try self.limitImmediateType(bin_op.rhs, i32); |
| 2149 | 2150 | |
| 2150 | try self.genBinMathOpMir(.cmp, Type.initTag(.bool), dst_mcv, src_mcv); | |
| 2151 | try self.genBinMathOpMir(.cmp, ty, dst_mcv, src_mcv); | |
| 2151 | 2152 | break :result switch (ty.isSignedInt()) { |
| 2152 | 2153 | true => MCValue{ .compare_flags_signed = op }, |
| 2153 | 2154 | false => MCValue{ .compare_flags_unsigned = op }, |
| ... | ... | @@ -2792,16 +2793,10 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 2792 | 2793 | return self.fail("TODO implement set stack variable with large stack offset", .{}); |
| 2793 | 2794 | } |
| 2794 | 2795 | switch (abi_size) { |
| 2795 | 1 => { | |
| 2796 | return self.fail("TODO implement set abi_size=1 stack variable with immediate", .{}); | |
| 2797 | }, | |
| 2798 | 2 => { | |
| 2799 | return self.fail("TODO implement set abi_size=2 stack variable with immediate", .{}); | |
| 2800 | }, | |
| 2801 | 4 => { | |
| 2796 | 1, 2, 4 => { | |
| 2802 | 2797 | // We have a positive stack offset value but we want a twos complement negative |
| 2803 | 2798 | // offset from rbp, which is at the top of the stack frame. |
| 2804 | // mov DWORD PTR [rbp+offset], immediate | |
| 2799 | // mov [rbp+offset], immediate | |
| 2805 | 2800 | const payload = try self.addExtra(Mir.ImmPair{ |
| 2806 | 2801 | .dest_off = -@intCast(i32, adj_off), |
| 2807 | 2802 | .operand = @bitCast(i32, @intCast(u32, x_big)), |
| ... | ... | @@ -2810,7 +2805,12 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 2810 | 2805 | .tag = .mov_mem_imm, |
| 2811 | 2806 | .ops = (Mir.Ops{ |
| 2812 | 2807 | .reg1 = .rbp, |
| 2813 | .flags = 0b10, | |
| 2808 | .flags = switch (abi_size) { | |
| 2809 | 1 => 0b00, | |
| 2810 | 2 => 0b01, | |
| 2811 | 4 => 0b10, | |
| 2812 | else => unreachable, | |
| 2813 | }, | |
| 2814 | 2814 | }).encode(), |
| 2815 | 2815 | .data = .{ .payload = payload }, |
| 2816 | 2816 | }); |
| ... | ... | @@ -2954,11 +2954,12 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2954 | 2954 | return; |
| 2955 | 2955 | } |
| 2956 | 2956 | if (x <= math.maxInt(i32)) { |
| 2957 | const abi_size = ty.abiSize(self.target.*); | |
| 2957 | 2958 | // Next best case: if we set the lower four bytes, the upper four will be zeroed. |
| 2958 | 2959 | _ = try self.addInst(.{ |
| 2959 | 2960 | .tag = .mov, |
| 2960 | 2961 | .ops = (Mir.Ops{ |
| 2961 | .reg1 = reg.to32(), | |
| 2962 | .reg1 = registerAlias(reg, @intCast(u32, abi_size)), | |
| 2962 | 2963 | }).encode(), |
| 2963 | 2964 | .data = .{ .imm = @intCast(i32, x) }, |
| 2964 | 2965 | }); |
src/arch/x86_64/Emit.zig+29-18| ... | ... | @@ -468,7 +468,15 @@ fn mirArithMemImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 468 | 468 | ) catch |err| emit.failWithLoweringError(err); |
| 469 | 469 | } |
| 470 | 470 | |
| 471 | fn immOpSize(imm: i64) u8 { | |
| 471 | inline fn setRexWRegister(reg: Register) bool { | |
| 472 | if (reg.size() == 64) return true; | |
| 473 | return switch (reg) { | |
| 474 | .ah, .bh, .ch, .dh => true, | |
| 475 | else => false, | |
| 476 | }; | |
| 477 | } | |
| 478 | ||
| 479 | inline fn immOpSize(imm: i64) u8 { | |
| 472 | 480 | blk: { |
| 473 | 481 | _ = math.cast(i8, imm) catch break :blk; |
| 474 | 482 | return 8; |
| ... | ... | @@ -1370,7 +1378,10 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) |
| 1370 | 1378 | encoder.opcode_1byte(0x66); |
| 1371 | 1379 | } |
| 1372 | 1380 | encoder.rex(.{ |
| 1373 | .w = tag.isSetCC(), | |
| 1381 | .w = switch (reg) { | |
| 1382 | .ah, .bh, .ch, .dh => true, | |
| 1383 | else => false, | |
| 1384 | }, | |
| 1374 | 1385 | .b = reg.isExtended(), |
| 1375 | 1386 | }); |
| 1376 | 1387 | opc.encode(encoder); |
| ... | ... | @@ -1389,7 +1400,7 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) |
| 1389 | 1400 | return error.OperandSizeMismatch; |
| 1390 | 1401 | } |
| 1391 | 1402 | encoder.rex(.{ |
| 1392 | .w = tag.isSetCC(), | |
| 1403 | .w = false, | |
| 1393 | 1404 | .b = reg.isExtended(), |
| 1394 | 1405 | }); |
| 1395 | 1406 | opc.encode(encoder); |
| ... | ... | @@ -1455,7 +1466,7 @@ fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8), |
| 1455 | 1466 | encoder.opcode_1byte(0x66); |
| 1456 | 1467 | } |
| 1457 | 1468 | encoder.rex(.{ |
| 1458 | .w = reg.size() == 64, | |
| 1469 | .w = setRexWRegister(reg), | |
| 1459 | 1470 | }); |
| 1460 | 1471 | opc.encode(encoder); |
| 1461 | 1472 | switch (reg.size()) { |
| ... | ... | @@ -1488,7 +1499,7 @@ fn lowerToOiEnc(tag: Tag, reg: Register, imm: i64, code: *std.ArrayList(u8)) Low |
| 1488 | 1499 | encoder.opcode_1byte(0x66); |
| 1489 | 1500 | } |
| 1490 | 1501 | encoder.rex(.{ |
| 1491 | .w = reg.size() == 64, | |
| 1502 | .w = setRexWRegister(reg), | |
| 1492 | 1503 | .b = reg.isExtended(), |
| 1493 | 1504 | }); |
| 1494 | 1505 | opc.encodeWithReg(encoder, reg); |
| ... | ... | @@ -1525,7 +1536,7 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.Arr |
| 1525 | 1536 | encoder.opcode_1byte(0x66); |
| 1526 | 1537 | } |
| 1527 | 1538 | encoder.rex(.{ |
| 1528 | .w = dst_reg.size() == 64, | |
| 1539 | .w = setRexWRegister(dst_reg), | |
| 1529 | 1540 | .b = dst_reg.isExtended(), |
| 1530 | 1541 | }); |
| 1531 | 1542 | opc.encode(encoder); |
| ... | ... | @@ -1623,7 +1634,7 @@ fn lowerToRmEnc( |
| 1623 | 1634 | } |
| 1624 | 1635 | const encoder = try Encoder.init(code, 3); |
| 1625 | 1636 | encoder.rex(.{ |
| 1626 | .w = reg.size() == 64, | |
| 1637 | .w = setRexWRegister(reg) or setRexWRegister(src_reg), | |
| 1627 | 1638 | .r = reg.isExtended(), |
| 1628 | 1639 | .b = src_reg.isExtended(), |
| 1629 | 1640 | }); |
| ... | ... | @@ -1645,7 +1656,7 @@ fn lowerToRmEnc( |
| 1645 | 1656 | return error.OperandSizeMismatch; |
| 1646 | 1657 | } |
| 1647 | 1658 | encoder.rex(.{ |
| 1648 | .w = reg.size() == 64, | |
| 1659 | .w = setRexWRegister(reg), | |
| 1649 | 1660 | .r = reg.isExtended(), |
| 1650 | 1661 | .b = src_reg.isExtended(), |
| 1651 | 1662 | }); |
| ... | ... | @@ -1676,7 +1687,7 @@ fn lowerToRmEnc( |
| 1676 | 1687 | } |
| 1677 | 1688 | } else { |
| 1678 | 1689 | encoder.rex(.{ |
| 1679 | .w = reg.size() == 64, | |
| 1690 | .w = setRexWRegister(reg), | |
| 1680 | 1691 | .r = reg.isExtended(), |
| 1681 | 1692 | }); |
| 1682 | 1693 | opc.encode(encoder); |
| ... | ... | @@ -1706,7 +1717,7 @@ fn lowerToMrEnc( |
| 1706 | 1717 | } |
| 1707 | 1718 | const encoder = try Encoder.init(code, 3); |
| 1708 | 1719 | encoder.rex(.{ |
| 1709 | .w = dst_reg.size() == 64, | |
| 1720 | .w = setRexWRegister(dst_reg) or setRexWRegister(reg), | |
| 1710 | 1721 | .r = reg.isExtended(), |
| 1711 | 1722 | .b = dst_reg.isExtended(), |
| 1712 | 1723 | }); |
| ... | ... | @@ -1726,7 +1737,7 @@ fn lowerToMrEnc( |
| 1726 | 1737 | return error.OperandSizeMismatch; |
| 1727 | 1738 | } |
| 1728 | 1739 | encoder.rex(.{ |
| 1729 | .w = dst_mem.ptr_size == .qword_ptr, | |
| 1740 | .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), | |
| 1730 | 1741 | .r = reg.isExtended(), |
| 1731 | 1742 | .b = dst_reg.isExtended(), |
| 1732 | 1743 | }); |
| ... | ... | @@ -1757,7 +1768,7 @@ fn lowerToMrEnc( |
| 1757 | 1768 | } |
| 1758 | 1769 | } else { |
| 1759 | 1770 | encoder.rex(.{ |
| 1760 | .w = dst_mem.ptr_size == .qword_ptr, | |
| 1771 | .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg), | |
| 1761 | 1772 | .r = reg.isExtended(), |
| 1762 | 1773 | }); |
| 1763 | 1774 | opc.encode(encoder); |
| ... | ... | @@ -1794,7 +1805,7 @@ fn lowerToRmiEnc( |
| 1794 | 1805 | return error.OperandSizeMismatch; |
| 1795 | 1806 | } |
| 1796 | 1807 | encoder.rex(.{ |
| 1797 | .w = reg.size() == 64, | |
| 1808 | .w = setRexWRegister(reg) or setRexWRegister(src_reg), | |
| 1798 | 1809 | .r = reg.isExtended(), |
| 1799 | 1810 | .b = src_reg.isExtended(), |
| 1800 | 1811 | }); |
| ... | ... | @@ -1812,7 +1823,7 @@ fn lowerToRmiEnc( |
| 1812 | 1823 | return error.OperandSizeMismatch; |
| 1813 | 1824 | } |
| 1814 | 1825 | encoder.rex(.{ |
| 1815 | .w = reg.size() == 64, | |
| 1826 | .w = setRexWRegister(reg), | |
| 1816 | 1827 | .r = reg.isExtended(), |
| 1817 | 1828 | .b = src_reg.isExtended(), |
| 1818 | 1829 | }); |
| ... | ... | @@ -1843,7 +1854,7 @@ fn lowerToRmiEnc( |
| 1843 | 1854 | } |
| 1844 | 1855 | } else { |
| 1845 | 1856 | encoder.rex(.{ |
| 1846 | .w = reg.size() == 64, | |
| 1857 | .w = setRexWRegister(reg), | |
| 1847 | 1858 | .r = reg.isExtended(), |
| 1848 | 1859 | }); |
| 1849 | 1860 | opc.encode(encoder); |
| ... | ... | @@ -2089,7 +2100,7 @@ test "lower M encoding" { |
| 2089 | 2100 | try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(null, 0x10, .qword_ptr), code.buffer()); |
| 2090 | 2101 | try expectEqualHexStrings("\xFF\x24\x25\x10\x00\x00\x00", code.emitted(), "jmp qword ptr [ds:0x10]"); |
| 2091 | 2102 | try lowerToMEnc(.seta, RegisterOrMemory.reg(.r11b), code.buffer()); |
| 2092 | try expectEqualHexStrings("\x49\x0F\x97\xC3", code.emitted(), "seta r11b"); | |
| 2103 | try expectEqualHexStrings("\x41\x0F\x97\xC3", code.emitted(), "seta r11b"); | |
| 2093 | 2104 | } |
| 2094 | 2105 | |
| 2095 | 2106 | test "lower O encoding" { |
| ... | ... | @@ -2111,9 +2122,9 @@ test "lower RMI encoding" { |
| 2111 | 2122 | "imul rax, qword ptr [rbp - 8], 0x10", |
| 2112 | 2123 | ); |
| 2113 | 2124 | try lowerToRmiEnc(.imul, .eax, RegisterOrMemory.mem(.rbp, -4, .dword_ptr), 0x10, code.buffer()); |
| 2114 | try expectEqualHexStrings("\x69\x45\xFC\x10\x00\x00\x00", code.emitted(), "imul ax, [rbp - 2], 0x10"); | |
| 2125 | try expectEqualHexStrings("\x69\x45\xFC\x10\x00\x00\x00", code.emitted(), "imul eax, dword ptr [rbp - 4], 0x10"); | |
| 2115 | 2126 | try lowerToRmiEnc(.imul, .ax, RegisterOrMemory.mem(.rbp, -2, .word_ptr), 0x10, code.buffer()); |
| 2116 | try expectEqualHexStrings("\x66\x69\x45\xFE\x10\x00", code.emitted(), "imul eax, [rbp - 4], 0x10"); | |
| 2127 | try expectEqualHexStrings("\x66\x69\x45\xFE\x10\x00", code.emitted(), "imul ax, word ptr [rbp - 2], 0x10"); | |
| 2117 | 2128 | try lowerToRmiEnc(.imul, .r12, RegisterOrMemory.reg(.r12), 0x10, code.buffer()); |
| 2118 | 2129 | try expectEqualHexStrings("\x4D\x69\xE4\x10\x00\x00\x00", code.emitted(), "imul r12, r12, 0x10"); |
| 2119 | 2130 | try lowerToRmiEnc(.imul, .r12w, RegisterOrMemory.reg(.r12w), 0x10, code.buffer()); |
test/stage2/x86_64.zig+58| ... | ... | @@ -1604,6 +1604,64 @@ pub fn addCases(ctx: *TestContext) !void { |
| 1604 | 1604 | ":2:28: error: cannot set address space of local variable 'foo'", |
| 1605 | 1605 | }); |
| 1606 | 1606 | } |
| 1607 | ||
| 1608 | { | |
| 1609 | var case = ctx.exe("saving vars of different ABI size to stack", target); | |
| 1610 | ||
| 1611 | case.addCompareOutput( | |
| 1612 | \\pub fn main() void { | |
| 1613 | \\ assert(callMe(2) == 24); | |
| 1614 | \\} | |
| 1615 | \\ | |
| 1616 | \\fn callMe(a: u8) u8 { | |
| 1617 | \\ var b: u8 = a + 10; | |
| 1618 | \\ const c = 2 * b; | |
| 1619 | \\ return c; | |
| 1620 | \\} | |
| 1621 | \\ | |
| 1622 | \\pub fn assert(ok: bool) void { | |
| 1623 | \\ if (!ok) unreachable; // assertion failure | |
| 1624 | \\} | |
| 1625 | , | |
| 1626 | "", | |
| 1627 | ); | |
| 1628 | ||
| 1629 | case.addCompareOutput( | |
| 1630 | \\pub fn main() void { | |
| 1631 | \\ assert(callMe(2) == 24); | |
| 1632 | \\} | |
| 1633 | \\ | |
| 1634 | \\fn callMe(a: u16) u16 { | |
| 1635 | \\ var b: u16 = a + 10; | |
| 1636 | \\ const c = 2 * b; | |
| 1637 | \\ return c; | |
| 1638 | \\} | |
| 1639 | \\ | |
| 1640 | \\pub fn assert(ok: bool) void { | |
| 1641 | \\ if (!ok) unreachable; // assertion failure | |
| 1642 | \\} | |
| 1643 | , | |
| 1644 | "", | |
| 1645 | ); | |
| 1646 | ||
| 1647 | case.addCompareOutput( | |
| 1648 | \\pub fn main() void { | |
| 1649 | \\ assert(callMe(2) == 24); | |
| 1650 | \\} | |
| 1651 | \\ | |
| 1652 | \\fn callMe(a: u32) u32 { | |
| 1653 | \\ var b: u32 = a + 10; | |
| 1654 | \\ const c = 2 * b; | |
| 1655 | \\ return c; | |
| 1656 | \\} | |
| 1657 | \\ | |
| 1658 | \\pub fn assert(ok: bool) void { | |
| 1659 | \\ if (!ok) unreachable; // assertion failure | |
| 1660 | \\} | |
| 1661 | , | |
| 1662 | "", | |
| 1663 | ); | |
| 1664 | } | |
| 1607 | 1665 | } |
| 1608 | 1666 | } |
| 1609 | 1667 |