authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-29 20:55:05+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-29 22:06:38+01:00
logb7e223597395358dc63cf88c92ace5eaa455cf89
tree51108f8b375f49eac43b24bbc839dfefdada4f3e
parent08ea1a2eab9472389d57ddbd97e307fcb096a992

stage2: lower 1-byte and 2-byte values saved to stack

* 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 codegen

3 files changed, 103 insertions(+), 33 deletions(-)

src/arch/x86_64/CodeGen.zig+16-15
...@@ -1642,11 +1642,11 @@ fn genBinMathOpMir(...@@ -1642,11 +1642,11 @@ fn genBinMathOpMir(
1642 });1642 });
1643 },1643 },
1644 .immediate => |imm| {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 _ = try self.addInst(.{1646 _ = try self.addInst(.{
1647 .tag = mir_tag,1647 .tag = mir_tag,
1648 .ops = (Mir.Ops{1648 .ops = (Mir.Ops{
1649 .reg1 = dst_reg.to32(),1649 .reg1 = registerAlias(dst_reg, @intCast(u32, abi_size)),
1650 }).encode(),1650 }).encode(),
1651 .data = .{ .imm = @intCast(i32, imm) },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,13 +1751,14 @@ fn genIMulOpMir(self: *Self, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !
1751 });1751 });
1752 },1752 },
1753 .immediate => |imm| {1753 .immediate => |imm| {
1754 // TODO take into account the type's ABI size when selecting the register alias
1754 // register, immediate1755 // register, immediate
1755 if (imm <= math.maxInt(i32)) {1756 if (imm <= math.maxInt(i32)) {
1756 _ = try self.addInst(.{1757 _ = try self.addInst(.{
1757 .tag = .imul_complex,1758 .tag = .imul_complex,
1758 .ops = (Mir.Ops{1759 .ops = (Mir.Ops{
1759 .reg1 = dst_reg,1760 .reg1 = dst_reg.to32(),
1760 .reg2 = dst_reg,1761 .reg2 = dst_reg.to32(),
1761 .flags = 0b10,1762 .flags = 0b10,
1762 }).encode(),1763 }).encode(),
1763 .data = .{ .imm = @intCast(i32, imm) },1764 .data = .{ .imm = @intCast(i32, imm) },
...@@ -2147,7 +2148,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {...@@ -2147,7 +2148,7 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
2147 // This instruction supports only signed 32-bit immediates at most.2148 // This instruction supports only signed 32-bit immediates at most.
2148 const src_mcv = try self.limitImmediateType(bin_op.rhs, i32);2149 const src_mcv = try self.limitImmediateType(bin_op.rhs, i32);
21492150
2150 try self.genBinMathOpMir(.cmp, Type.initTag(.bool), dst_mcv, src_mcv);2151 try self.genBinMathOpMir(.cmp, ty, dst_mcv, src_mcv);
2151 break :result switch (ty.isSignedInt()) {2152 break :result switch (ty.isSignedInt()) {
2152 true => MCValue{ .compare_flags_signed = op },2153 true => MCValue{ .compare_flags_signed = op },
2153 false => MCValue{ .compare_flags_unsigned = op },2154 false => MCValue{ .compare_flags_unsigned = op },
...@@ -2792,16 +2793,10 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro...@@ -2792,16 +2793,10 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
2792 return self.fail("TODO implement set stack variable with large stack offset", .{});2793 return self.fail("TODO implement set stack variable with large stack offset", .{});
2793 }2794 }
2794 switch (abi_size) {2795 switch (abi_size) {
2795 1 => {2796 1, 2, 4 => {
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 => {
2802 // We have a positive stack offset value but we want a twos complement negative2797 // We have a positive stack offset value but we want a twos complement negative
2803 // offset from rbp, which is at the top of the stack frame.2798 // offset from rbp, which is at the top of the stack frame.
2804 // mov DWORD PTR [rbp+offset], immediate2799 // mov [rbp+offset], immediate
2805 const payload = try self.addExtra(Mir.ImmPair{2800 const payload = try self.addExtra(Mir.ImmPair{
2806 .dest_off = -@intCast(i32, adj_off),2801 .dest_off = -@intCast(i32, adj_off),
2807 .operand = @bitCast(i32, @intCast(u32, x_big)),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,7 +2805,12 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
2810 .tag = .mov_mem_imm,2805 .tag = .mov_mem_imm,
2811 .ops = (Mir.Ops{2806 .ops = (Mir.Ops{
2812 .reg1 = .rbp,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 }).encode(),2814 }).encode(),
2815 .data = .{ .payload = payload },2815 .data = .{ .payload = payload },
2816 });2816 });
...@@ -2954,11 +2954,12 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -2954,11 +2954,12 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
2954 return;2954 return;
2955 }2955 }
2956 if (x <= math.maxInt(i32)) {2956 if (x <= math.maxInt(i32)) {
2957 const abi_size = ty.abiSize(self.target.*);
2957 // Next best case: if we set the lower four bytes, the upper four will be zeroed.2958 // Next best case: if we set the lower four bytes, the upper four will be zeroed.
2958 _ = try self.addInst(.{2959 _ = try self.addInst(.{
2959 .tag = .mov,2960 .tag = .mov,
2960 .ops = (Mir.Ops{2961 .ops = (Mir.Ops{
2961 .reg1 = reg.to32(),2962 .reg1 = registerAlias(reg, @intCast(u32, abi_size)),
2962 }).encode(),2963 }).encode(),
2963 .data = .{ .imm = @intCast(i32, x) },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,7 +468,15 @@ fn mirArithMemImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
468 ) catch |err| emit.failWithLoweringError(err);468 ) catch |err| emit.failWithLoweringError(err);
469}469}
470470
471fn immOpSize(imm: i64) u8 {471inline 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
479inline fn immOpSize(imm: i64) u8 {
472 blk: {480 blk: {
473 _ = math.cast(i8, imm) catch break :blk;481 _ = math.cast(i8, imm) catch break :blk;
474 return 8;482 return 8;
...@@ -1370,7 +1378,10 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))...@@ -1370,7 +1378,10 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))
1370 encoder.opcode_1byte(0x66);1378 encoder.opcode_1byte(0x66);
1371 }1379 }
1372 encoder.rex(.{1380 encoder.rex(.{
1373 .w = tag.isSetCC(),1381 .w = switch (reg) {
1382 .ah, .bh, .ch, .dh => true,
1383 else => false,
1384 },
1374 .b = reg.isExtended(),1385 .b = reg.isExtended(),
1375 });1386 });
1376 opc.encode(encoder);1387 opc.encode(encoder);
...@@ -1389,7 +1400,7 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))...@@ -1389,7 +1400,7 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))
1389 return error.OperandSizeMismatch;1400 return error.OperandSizeMismatch;
1390 }1401 }
1391 encoder.rex(.{1402 encoder.rex(.{
1392 .w = tag.isSetCC(),1403 .w = false,
1393 .b = reg.isExtended(),1404 .b = reg.isExtended(),
1394 });1405 });
1395 opc.encode(encoder);1406 opc.encode(encoder);
...@@ -1455,7 +1466,7 @@ fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8),...@@ -1455,7 +1466,7 @@ fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: i64, code: *std.ArrayList(u8),
1455 encoder.opcode_1byte(0x66);1466 encoder.opcode_1byte(0x66);
1456 }1467 }
1457 encoder.rex(.{1468 encoder.rex(.{
1458 .w = reg.size() == 64,1469 .w = setRexWRegister(reg),
1459 });1470 });
1460 opc.encode(encoder);1471 opc.encode(encoder);
1461 switch (reg.size()) {1472 switch (reg.size()) {
...@@ -1488,7 +1499,7 @@ fn lowerToOiEnc(tag: Tag, reg: Register, imm: i64, code: *std.ArrayList(u8)) Low...@@ -1488,7 +1499,7 @@ fn lowerToOiEnc(tag: Tag, reg: Register, imm: i64, code: *std.ArrayList(u8)) Low
1488 encoder.opcode_1byte(0x66);1499 encoder.opcode_1byte(0x66);
1489 }1500 }
1490 encoder.rex(.{1501 encoder.rex(.{
1491 .w = reg.size() == 64,1502 .w = setRexWRegister(reg),
1492 .b = reg.isExtended(),1503 .b = reg.isExtended(),
1493 });1504 });
1494 opc.encodeWithReg(encoder, reg);1505 opc.encodeWithReg(encoder, reg);
...@@ -1525,7 +1536,7 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.Arr...@@ -1525,7 +1536,7 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.Arr
1525 encoder.opcode_1byte(0x66);1536 encoder.opcode_1byte(0x66);
1526 }1537 }
1527 encoder.rex(.{1538 encoder.rex(.{
1528 .w = dst_reg.size() == 64,1539 .w = setRexWRegister(dst_reg),
1529 .b = dst_reg.isExtended(),1540 .b = dst_reg.isExtended(),
1530 });1541 });
1531 opc.encode(encoder);1542 opc.encode(encoder);
...@@ -1623,7 +1634,7 @@ fn lowerToRmEnc(...@@ -1623,7 +1634,7 @@ fn lowerToRmEnc(
1623 }1634 }
1624 const encoder = try Encoder.init(code, 3);1635 const encoder = try Encoder.init(code, 3);
1625 encoder.rex(.{1636 encoder.rex(.{
1626 .w = reg.size() == 64,1637 .w = setRexWRegister(reg) or setRexWRegister(src_reg),
1627 .r = reg.isExtended(),1638 .r = reg.isExtended(),
1628 .b = src_reg.isExtended(),1639 .b = src_reg.isExtended(),
1629 });1640 });
...@@ -1645,7 +1656,7 @@ fn lowerToRmEnc(...@@ -1645,7 +1656,7 @@ fn lowerToRmEnc(
1645 return error.OperandSizeMismatch;1656 return error.OperandSizeMismatch;
1646 }1657 }
1647 encoder.rex(.{1658 encoder.rex(.{
1648 .w = reg.size() == 64,1659 .w = setRexWRegister(reg),
1649 .r = reg.isExtended(),1660 .r = reg.isExtended(),
1650 .b = src_reg.isExtended(),1661 .b = src_reg.isExtended(),
1651 });1662 });
...@@ -1676,7 +1687,7 @@ fn lowerToRmEnc(...@@ -1676,7 +1687,7 @@ fn lowerToRmEnc(
1676 }1687 }
1677 } else {1688 } else {
1678 encoder.rex(.{1689 encoder.rex(.{
1679 .w = reg.size() == 64,1690 .w = setRexWRegister(reg),
1680 .r = reg.isExtended(),1691 .r = reg.isExtended(),
1681 });1692 });
1682 opc.encode(encoder);1693 opc.encode(encoder);
...@@ -1706,7 +1717,7 @@ fn lowerToMrEnc(...@@ -1706,7 +1717,7 @@ fn lowerToMrEnc(
1706 }1717 }
1707 const encoder = try Encoder.init(code, 3);1718 const encoder = try Encoder.init(code, 3);
1708 encoder.rex(.{1719 encoder.rex(.{
1709 .w = dst_reg.size() == 64,1720 .w = setRexWRegister(dst_reg) or setRexWRegister(reg),
1710 .r = reg.isExtended(),1721 .r = reg.isExtended(),
1711 .b = dst_reg.isExtended(),1722 .b = dst_reg.isExtended(),
1712 });1723 });
...@@ -1726,7 +1737,7 @@ fn lowerToMrEnc(...@@ -1726,7 +1737,7 @@ fn lowerToMrEnc(
1726 return error.OperandSizeMismatch;1737 return error.OperandSizeMismatch;
1727 }1738 }
1728 encoder.rex(.{1739 encoder.rex(.{
1729 .w = dst_mem.ptr_size == .qword_ptr,1740 .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg),
1730 .r = reg.isExtended(),1741 .r = reg.isExtended(),
1731 .b = dst_reg.isExtended(),1742 .b = dst_reg.isExtended(),
1732 });1743 });
...@@ -1757,7 +1768,7 @@ fn lowerToMrEnc(...@@ -1757,7 +1768,7 @@ fn lowerToMrEnc(
1757 }1768 }
1758 } else {1769 } else {
1759 encoder.rex(.{1770 encoder.rex(.{
1760 .w = dst_mem.ptr_size == .qword_ptr,1771 .w = dst_mem.ptr_size == .qword_ptr or setRexWRegister(reg),
1761 .r = reg.isExtended(),1772 .r = reg.isExtended(),
1762 });1773 });
1763 opc.encode(encoder);1774 opc.encode(encoder);
...@@ -1794,7 +1805,7 @@ fn lowerToRmiEnc(...@@ -1794,7 +1805,7 @@ fn lowerToRmiEnc(
1794 return error.OperandSizeMismatch;1805 return error.OperandSizeMismatch;
1795 }1806 }
1796 encoder.rex(.{1807 encoder.rex(.{
1797 .w = reg.size() == 64,1808 .w = setRexWRegister(reg) or setRexWRegister(src_reg),
1798 .r = reg.isExtended(),1809 .r = reg.isExtended(),
1799 .b = src_reg.isExtended(),1810 .b = src_reg.isExtended(),
1800 });1811 });
...@@ -1812,7 +1823,7 @@ fn lowerToRmiEnc(...@@ -1812,7 +1823,7 @@ fn lowerToRmiEnc(
1812 return error.OperandSizeMismatch;1823 return error.OperandSizeMismatch;
1813 }1824 }
1814 encoder.rex(.{1825 encoder.rex(.{
1815 .w = reg.size() == 64,1826 .w = setRexWRegister(reg),
1816 .r = reg.isExtended(),1827 .r = reg.isExtended(),
1817 .b = src_reg.isExtended(),1828 .b = src_reg.isExtended(),
1818 });1829 });
...@@ -1843,7 +1854,7 @@ fn lowerToRmiEnc(...@@ -1843,7 +1854,7 @@ fn lowerToRmiEnc(
1843 }1854 }
1844 } else {1855 } else {
1845 encoder.rex(.{1856 encoder.rex(.{
1846 .w = reg.size() == 64,1857 .w = setRexWRegister(reg),
1847 .r = reg.isExtended(),1858 .r = reg.isExtended(),
1848 });1859 });
1849 opc.encode(encoder);1860 opc.encode(encoder);
...@@ -2089,7 +2100,7 @@ test "lower M encoding" {...@@ -2089,7 +2100,7 @@ test "lower M encoding" {
2089 try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(null, 0x10, .qword_ptr), code.buffer());2100 try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(null, 0x10, .qword_ptr), code.buffer());
2090 try expectEqualHexStrings("\xFF\x24\x25\x10\x00\x00\x00", code.emitted(), "jmp qword ptr [ds:0x10]");2101 try expectEqualHexStrings("\xFF\x24\x25\x10\x00\x00\x00", code.emitted(), "jmp qword ptr [ds:0x10]");
2091 try lowerToMEnc(.seta, RegisterOrMemory.reg(.r11b), code.buffer());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}
20942105
2095test "lower O encoding" {2106test "lower O encoding" {
...@@ -2111,9 +2122,9 @@ test "lower RMI encoding" {...@@ -2111,9 +2122,9 @@ test "lower RMI encoding" {
2111 "imul rax, qword ptr [rbp - 8], 0x10",2122 "imul rax, qword ptr [rbp - 8], 0x10",
2112 );2123 );
2113 try lowerToRmiEnc(.imul, .eax, RegisterOrMemory.mem(.rbp, -4, .dword_ptr), 0x10, code.buffer());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 try lowerToRmiEnc(.imul, .ax, RegisterOrMemory.mem(.rbp, -2, .word_ptr), 0x10, code.buffer());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 try lowerToRmiEnc(.imul, .r12, RegisterOrMemory.reg(.r12), 0x10, code.buffer());2128 try lowerToRmiEnc(.imul, .r12, RegisterOrMemory.reg(.r12), 0x10, code.buffer());
2118 try expectEqualHexStrings("\x4D\x69\xE4\x10\x00\x00\x00", code.emitted(), "imul r12, r12, 0x10");2129 try expectEqualHexStrings("\x4D\x69\xE4\x10\x00\x00\x00", code.emitted(), "imul r12, r12, 0x10");
2119 try lowerToRmiEnc(.imul, .r12w, RegisterOrMemory.reg(.r12w), 0x10, code.buffer());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,6 +1604,64 @@ pub fn addCases(ctx: *TestContext) !void {
1604 ":2:28: error: cannot set address space of local variable 'foo'",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}
16091667