| ... | ... | @@ -133,6 +133,11 @@ pub fn lowerMir(emit: *Emit) InnerError!void { |
| 133 | 133 | .lea => try emit.mirLea(inst), |
| 134 | 134 | .lea_pie => try emit.mirLeaPie(inst), |
| 135 | 135 | |
| 136 | .shl => try emit.mirShift(.shl, inst), |
| 137 | .sal => try emit.mirShift(.sal, inst), |
| 138 | .shr => try emit.mirShift(.shr, inst), |
| 139 | .sar => try emit.mirShift(.sar, inst), |
| 140 | |
| 136 | 141 | .imul_complex => try emit.mirIMulComplex(inst), |
| 137 | 142 | |
| 138 | 143 | .push => try emit.mirPushPop(.push, inst), |
| ... | ... | @@ -653,6 +658,31 @@ fn mirMovabs(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 653 | 658 | return lowerToFdEnc(.mov, ops.reg1, imm, emit.code); |
| 654 | 659 | } |
| 655 | 660 | |
| 661 | fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 662 | const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); |
| 663 | switch (ops.flags) { |
| 664 | 0b00 => { |
| 665 | // sal reg1, 1 |
| 666 | // M1 |
| 667 | return lowerToM1Enc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); |
| 668 | }, |
| 669 | 0b01 => { |
| 670 | // sal reg1, .cl |
| 671 | // MC |
| 672 | return lowerToMcEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); |
| 673 | }, |
| 674 | 0b10 => { |
| 675 | // sal reg1, imm8 |
| 676 | // MI |
| 677 | const imm = @truncate(u8, emit.mir.instructions.items(.data)[inst].imm); |
| 678 | return lowerToMiImm8Enc(tag, RegisterOrMemory.reg(ops.reg1), imm, emit.code); |
| 679 | }, |
| 680 | 0b11 => { |
| 681 | return emit.fail("TODO unused variant: SHIFT reg1, 0b11", .{}); |
| 682 | }, |
| 683 | } |
| 684 | } |
| 685 | |
| 656 | 686 | fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 657 | 687 | const tag = emit.mir.instructions.items(.tag)[inst]; |
| 658 | 688 | assert(tag == .imul_complex); |
| ... | ... | @@ -743,13 +773,13 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 743 | 773 | emit.code, |
| 744 | 774 | ); |
| 745 | 775 | const end_offset = emit.code.items.len; |
| 746 | | const reloc_type = switch (ops.flags) { |
| 747 | | 0b00 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT), |
| 748 | | 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), |
| 749 | | else => return emit.fail("TODO unused LEA PIE variants 0b10 and 0b11", .{}), |
| 750 | | }; |
| 751 | 776 | const sym_index = emit.mir.instructions.items(.data)[inst].linker_sym_index; |
| 752 | 777 | if (emit.bin_file.cast(link.File.MachO)) |macho_file| { |
| 778 | const reloc_type = switch (ops.flags) { |
| 779 | 0b00 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_GOT), |
| 780 | 0b01 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_SIGNED), |
| 781 | else => return emit.fail("TODO unused LEA PIE variants 0b10 and 0b11", .{}), |
| 782 | }; |
| 753 | 783 | const decl = macho_file.active_decl.?; |
| 754 | 784 | try decl.link.macho.relocs.append(emit.bin_file.allocator, .{ |
| 755 | 785 | .offset = @intCast(u32, end_offset - 4), |
| ... | ... | @@ -1064,6 +1094,10 @@ const Tag = enum { |
| 1064 | 1094 | setng, |
| 1065 | 1095 | setnle, |
| 1066 | 1096 | setg, |
| 1097 | shl, |
| 1098 | sal, |
| 1099 | shr, |
| 1100 | sar, |
| 1067 | 1101 | |
| 1068 | 1102 | fn isSetCC(tag: Tag) bool { |
| 1069 | 1103 | return switch (tag) { |
| ... | ... | @@ -1119,9 +1153,18 @@ const Encoding = enum { |
| 1119 | 1153 | /// OP imm32 |
| 1120 | 1154 | i, |
| 1121 | 1155 | |
| 1156 | /// OP r/m64, 1 |
| 1157 | m1, |
| 1158 | |
| 1159 | /// OP r/m64, .cl |
| 1160 | mc, |
| 1161 | |
| 1122 | 1162 | /// OP r/m64, imm32 |
| 1123 | 1163 | mi, |
| 1124 | 1164 | |
| 1165 | /// OP r/m64, imm8 |
| 1166 | mi8, |
| 1167 | |
| 1125 | 1168 | /// OP r/m64, r64 |
| 1126 | 1169 | mr, |
| 1127 | 1170 | |
| ... | ... | @@ -1230,12 +1273,25 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { |
| 1230 | 1273 | .ret_far => OpCode.oneByte(0xca), |
| 1231 | 1274 | else => null, |
| 1232 | 1275 | }, |
| 1276 | .m1 => return switch (tag) { |
| 1277 | .shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xd0 else 0xd1), |
| 1278 | else => null, |
| 1279 | }, |
| 1280 | .mc => return switch (tag) { |
| 1281 | .shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xd2 else 0xd3), |
| 1282 | else => null, |
| 1283 | }, |
| 1233 | 1284 | .mi => return switch (tag) { |
| 1234 | 1285 | .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(if (is_one_byte) 0x80 else 0x81), |
| 1235 | 1286 | .mov => OpCode.oneByte(if (is_one_byte) 0xc6 else 0xc7), |
| 1236 | 1287 | .@"test" => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7), |
| 1237 | 1288 | else => null, |
| 1238 | 1289 | }, |
| 1290 | .mi8 => return switch (tag) { |
| 1291 | .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(0x83), |
| 1292 | .shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xc0 else 0xc1), |
| 1293 | else => null, |
| 1294 | }, |
| 1239 | 1295 | .mr => return switch (tag) { |
| 1240 | 1296 | .adc => OpCode.oneByte(if (is_one_byte) 0x10 else 0x11), |
| 1241 | 1297 | .add => OpCode.oneByte(if (is_one_byte) 0x00 else 0x01), |
| ... | ... | @@ -1331,6 +1387,11 @@ inline fn getModRmExt(tag: Tag) ?u3 { |
| 1331 | 1387 | .setnle, |
| 1332 | 1388 | .setg, |
| 1333 | 1389 | => 0x0, |
| 1390 | .shl, |
| 1391 | .sal, |
| 1392 | => 0x4, |
| 1393 | .shr => 0x5, |
| 1394 | .sar => 0x7, |
| 1334 | 1395 | else => null, |
| 1335 | 1396 | }; |
| 1336 | 1397 | } |
| ... | ... | @@ -1528,8 +1589,8 @@ fn lowerToDEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void { |
| 1528 | 1589 | encoder.imm32(@bitCast(i32, imm)); |
| 1529 | 1590 | } |
| 1530 | 1591 | |
| 1531 | | fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void { |
| 1532 | | const opc = getOpCode(tag, .m, false).?; |
| 1592 | fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *std.ArrayList(u8)) InnerError!void { |
| 1593 | const opc = getOpCode(tag, enc, reg_or_mem.size() == 8).?; |
| 1533 | 1594 | const modrm_ext = getModRmExt(tag).?; |
| 1534 | 1595 | switch (reg_or_mem) { |
| 1535 | 1596 | .register => |reg| { |
| ... | ... | @@ -1537,11 +1598,9 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) |
| 1537 | 1598 | if (reg.size() == 16) { |
| 1538 | 1599 | encoder.prefix16BitMode(); |
| 1539 | 1600 | } |
| 1601 | const wide = if (tag == .jmp_near) false else setRexWRegister(reg); |
| 1540 | 1602 | encoder.rex(.{ |
| 1541 | | .w = switch (reg) { |
| 1542 | | .ah, .bh, .ch, .dh => true, |
| 1543 | | else => false, |
| 1544 | | }, |
| 1603 | .w = wide, |
| 1545 | 1604 | .b = reg.isExtended(), |
| 1546 | 1605 | }); |
| 1547 | 1606 | opc.encode(encoder); |
| ... | ... | @@ -1553,8 +1612,9 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) |
| 1553 | 1612 | encoder.prefix16BitMode(); |
| 1554 | 1613 | } |
| 1555 | 1614 | if (mem_op.base) |base| { |
| 1615 | const wide = if (tag == .jmp_near) false else mem_op.ptr_size == .qword_ptr; |
| 1556 | 1616 | encoder.rex(.{ |
| 1557 | | .w = false, |
| 1617 | .w = wide, |
| 1558 | 1618 | .b = base.isExtended(), |
| 1559 | 1619 | }); |
| 1560 | 1620 | } |
| ... | ... | @@ -1564,6 +1624,18 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) |
| 1564 | 1624 | } |
| 1565 | 1625 | } |
| 1566 | 1626 | |
| 1627 | fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void { |
| 1628 | return lowerToMxEnc(tag, reg_or_mem, .m, code); |
| 1629 | } |
| 1630 | |
| 1631 | fn lowerToM1Enc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void { |
| 1632 | return lowerToMxEnc(tag, reg_or_mem, .m1, code); |
| 1633 | } |
| 1634 | |
| 1635 | fn lowerToMcEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void { |
| 1636 | return lowerToMxEnc(tag, reg_or_mem, .mc, code); |
| 1637 | } |
| 1638 | |
| 1567 | 1639 | fn lowerToTdEnc(tag: Tag, moffs: u64, reg: Register, code: *std.ArrayList(u8)) InnerError!void { |
| 1568 | 1640 | return lowerToTdFdEnc(tag, reg, moffs, code, true); |
| 1569 | 1641 | } |
| ... | ... | @@ -1614,9 +1686,15 @@ fn lowerToOiEnc(tag: Tag, reg: Register, imm: u64, code: *std.ArrayList(u8)) Inn |
| 1614 | 1686 | } |
| 1615 | 1687 | } |
| 1616 | 1688 | |
| 1617 | | fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.ArrayList(u8)) InnerError!void { |
| 1689 | fn lowerToMiXEnc( |
| 1690 | tag: Tag, |
| 1691 | reg_or_mem: RegisterOrMemory, |
| 1692 | imm: u32, |
| 1693 | enc: Encoding, |
| 1694 | code: *std.ArrayList(u8), |
| 1695 | ) InnerError!void { |
| 1618 | 1696 | const modrm_ext = getModRmExt(tag).?; |
| 1619 | | const opc = getOpCode(tag, .mi, reg_or_mem.size() == 8).?; |
| 1697 | const opc = getOpCode(tag, enc, reg_or_mem.size() == 8).?; |
| 1620 | 1698 | switch (reg_or_mem) { |
| 1621 | 1699 | .register => |dst_reg| { |
| 1622 | 1700 | const encoder = try Encoder.init(code, 7); |
| ... | ... | @@ -1632,7 +1710,7 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.Arr |
| 1632 | 1710 | }); |
| 1633 | 1711 | opc.encode(encoder); |
| 1634 | 1712 | encoder.modRm_direct(modrm_ext, dst_reg.lowId()); |
| 1635 | | encodeImm(encoder, imm, dst_reg.size()); |
| 1713 | encodeImm(encoder, imm, if (enc == .mi8) 8 else dst_reg.size()); |
| 1636 | 1714 | }, |
| 1637 | 1715 | .memory => |dst_mem| { |
| 1638 | 1716 | const encoder = try Encoder.init(code, 12); |
| ... | ... | @@ -1651,11 +1729,19 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.Arr |
| 1651 | 1729 | } |
| 1652 | 1730 | opc.encode(encoder); |
| 1653 | 1731 | dst_mem.encode(encoder, modrm_ext); |
| 1654 | | encodeImm(encoder, imm, dst_mem.ptr_size.size()); |
| 1732 | encodeImm(encoder, imm, if (enc == .mi8) 8 else dst_mem.ptr_size.size()); |
| 1655 | 1733 | }, |
| 1656 | 1734 | } |
| 1657 | 1735 | } |
| 1658 | 1736 | |
| 1737 | fn lowerToMiImm8Enc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u8, code: *std.ArrayList(u8)) InnerError!void { |
| 1738 | return lowerToMiXEnc(tag, reg_or_mem, imm, .mi8, code); |
| 1739 | } |
| 1740 | |
| 1741 | fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: u32, code: *std.ArrayList(u8)) InnerError!void { |
| 1742 | return lowerToMiXEnc(tag, reg_or_mem, imm, .mi, code); |
| 1743 | } |
| 1744 | |
| 1659 | 1745 | fn lowerToRmEnc( |
| 1660 | 1746 | tag: Tag, |
| 1661 | 1747 | reg: Register, |
| ... | ... | @@ -1902,6 +1988,9 @@ test "lower MI encoding" { |
| 1902 | 1988 | emit.lowered(), |
| 1903 | 1989 | "mov qword ptr [rcx*2 + 0x10000000], 0x10", |
| 1904 | 1990 | ); |
| 1991 | |
| 1992 | try lowerToMiImm8Enc(.add, RegisterOrMemory.reg(.rax), 0x10, emit.code()); |
| 1993 | try expectEqualHexStrings("\x48\x83\xC0\x10", emit.lowered(), "add rax, 0x10"); |
| 1905 | 1994 | } |
| 1906 | 1995 | |
| 1907 | 1996 | test "lower RM encoding" { |
| ... | ... | @@ -2100,6 +2189,41 @@ test "lower M encoding" { |
| 2100 | 2189 | try expectEqualHexStrings("\x41\x0F\x97\xC3", emit.lowered(), "seta r11b"); |
| 2101 | 2190 | } |
| 2102 | 2191 | |
| 2192 | test "lower M1 and MC encodings" { |
| 2193 | var emit = TestEmit.init(); |
| 2194 | defer emit.deinit(); |
| 2195 | try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12), emit.code()); |
| 2196 | try expectEqualHexStrings("\x49\xD1\xE4", emit.lowered(), "sal r12, 1"); |
| 2197 | try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12d), emit.code()); |
| 2198 | try expectEqualHexStrings("\x41\xD1\xE4", emit.lowered(), "sal r12d, 1"); |
| 2199 | try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12w), emit.code()); |
| 2200 | try expectEqualHexStrings("\x66\x41\xD1\xE4", emit.lowered(), "sal r12w, 1"); |
| 2201 | try lowerToM1Enc(.sal, RegisterOrMemory.reg(.r12b), emit.code()); |
| 2202 | try expectEqualHexStrings("\x41\xD0\xE4", emit.lowered(), "sal r12b, 1"); |
| 2203 | try lowerToM1Enc(.sal, RegisterOrMemory.reg(.rax), emit.code()); |
| 2204 | try expectEqualHexStrings("\x48\xD1\xE0", emit.lowered(), "sal rax, 1"); |
| 2205 | try lowerToM1Enc(.sal, RegisterOrMemory.reg(.eax), emit.code()); |
| 2206 | try expectEqualHexStrings("\xD1\xE0", emit.lowered(), "sal eax, 1"); |
| 2207 | try lowerToM1Enc(.sal, RegisterOrMemory.mem(.qword_ptr, .{ |
| 2208 | .disp = @bitCast(u32, @as(i32, -0x10)), |
| 2209 | .base = .rbp, |
| 2210 | }), emit.code()); |
| 2211 | try expectEqualHexStrings("\x48\xD1\x65\xF0", emit.lowered(), "sal qword ptr [rbp - 0x10], 1"); |
| 2212 | try lowerToM1Enc(.sal, RegisterOrMemory.mem(.dword_ptr, .{ |
| 2213 | .disp = @bitCast(u32, @as(i32, -0x10)), |
| 2214 | .base = .rbp, |
| 2215 | }), emit.code()); |
| 2216 | try expectEqualHexStrings("\xD1\x65\xF0", emit.lowered(), "sal dword ptr [rbp - 0x10], 1"); |
| 2217 | |
| 2218 | try lowerToMcEnc(.shr, RegisterOrMemory.reg(.r12), emit.code()); |
| 2219 | try expectEqualHexStrings("\x49\xD3\xEC", emit.lowered(), "shr r12, cl"); |
| 2220 | try lowerToMcEnc(.shr, RegisterOrMemory.reg(.rax), emit.code()); |
| 2221 | try expectEqualHexStrings("\x48\xD3\xE8", emit.lowered(), "shr rax, cl"); |
| 2222 | |
| 2223 | try lowerToMcEnc(.sar, RegisterOrMemory.reg(.rsi), emit.code()); |
| 2224 | try expectEqualHexStrings("\x48\xD3\xFE", emit.lowered(), "sar rsi, cl"); |
| 2225 | } |
| 2226 | |
| 2103 | 2227 | test "lower O encoding" { |
| 2104 | 2228 | var emit = TestEmit.init(); |
| 2105 | 2229 | defer emit.deinit(); |