| ... | @@ -304,25 +304,13 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { | ... | @@ -304,25 +304,13 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 304 | }); | 304 | }); |
| 305 | return; | 305 | return; |
| 306 | } | 306 | } |
| 307 | const modrm_ext: u3 = switch (tag) { | | |
| 308 | .jmp_near => 0x4, | | |
| 309 | .call_near => 0x2, | | |
| 310 | else => unreachable, | | |
| 311 | }; | | |
| 312 | if (ops.reg1 == .none) { | 307 | if (ops.reg1 == .none) { |
| 313 | // JMP/CALL [imm] | 308 | // JMP/CALL [imm] |
| 314 | const imm = emit.mir.instructions.items(.data)[inst].imm; | 309 | const imm = emit.mir.instructions.items(.data)[inst].imm; |
| 315 | const encoder = try Encoder.init(emit.code, 7); | 310 | return lowerToMEnc(tag, RegisterOrMemory.mem(null, imm), emit.code); |
| 316 | encoder.opcode_1byte(0xff); | | |
| 317 | encoder.modRm_SIBDisp0(modrm_ext); | | |
| 318 | encoder.sib_disp32(); | | |
| 319 | encoder.imm32(imm); | | |
| 320 | return; | | |
| 321 | } | 311 | } |
| 322 | // JMP/CALL reg | 312 | // JMP/CALL reg |
| 323 | const encoder = try Encoder.init(emit.code, 2); | 313 | return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); |
| 324 | encoder.opcode_1byte(0xff); | | |
| 325 | encoder.modRm_direct(modrm_ext, ops.reg1.lowId()); | | |
| 326 | } | 314 | } |
| 327 | | 315 | |
| 328 | const CondType = enum { | 316 | const CondType = enum { |
| ... | @@ -628,7 +616,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding) ?u8 { | ... | @@ -628,7 +616,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding) ?u8 { |
| 628 | } | 616 | } |
| 629 | } | 617 | } |
| 630 | | 618 | |
| 631 | inline fn getModRmExt(tag: Tag) u3 { | 619 | inline fn getModRmExt(tag: Tag) ?u3 { |
| 632 | return switch (tag) { | 620 | return switch (tag) { |
| 633 | .adc => 0x2, | 621 | .adc => 0x2, |
| 634 | .add => 0x0, | 622 | .add => 0x0, |
| ... | @@ -639,8 +627,9 @@ inline fn getModRmExt(tag: Tag) u3 { | ... | @@ -639,8 +627,9 @@ inline fn getModRmExt(tag: Tag) u3 { |
| 639 | .sbb => 0x3, | 627 | .sbb => 0x3, |
| 640 | .cmp => 0x7, | 628 | .cmp => 0x7, |
| 641 | .mov => 0x0, | 629 | .mov => 0x0, |
| | 630 | .jmp_near => 0x4, |
| 642 | .call_near => 0x2, | 631 | .call_near => 0x2, |
| 643 | else => unreachable, | 632 | else => null, |
| 644 | }; | 633 | }; |
| 645 | } | 634 | } |
| 646 | | 635 | |
| ... | @@ -692,6 +681,67 @@ fn lowerToDEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) InnerError!void { | ... | @@ -692,6 +681,67 @@ fn lowerToDEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) InnerError!void { |
| 692 | encoder.imm32(imm); | 681 | encoder.imm32(imm); |
| 693 | } | 682 | } |
| 694 | | 683 | |
| | 684 | fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) InnerError!void { |
| | 685 | const opc = getOpCode(tag, .m).?; |
| | 686 | const modrm_ext = getModRmExt(tag).?; |
| | 687 | switch (reg_or_mem) { |
| | 688 | .register => |reg| { |
| | 689 | if (reg.size() != 64) return error.EmitFail; |
| | 690 | const encoder = try Encoder.init(code, 3); |
| | 691 | encoder.rex(.{ |
| | 692 | .w = false, |
| | 693 | .b = reg.isExtended(), |
| | 694 | }); |
| | 695 | encoder.opcode_1byte(opc); |
| | 696 | encoder.modRm_direct(modrm_ext, reg.lowId()); |
| | 697 | }, |
| | 698 | .memory => |mem_op| { |
| | 699 | const encoder = try Encoder.init(code, 8); |
| | 700 | if (mem_op.reg) |reg| { |
| | 701 | if (reg.size() != 64) return error.EmitFail; |
| | 702 | encoder.rex(.{ |
| | 703 | .w = false, |
| | 704 | .b = reg.isExtended(), |
| | 705 | }); |
| | 706 | encoder.opcode_1byte(opc); |
| | 707 | if (reg.lowId() == 4) { |
| | 708 | if (mem_op.disp == 0) { |
| | 709 | encoder.modRm_SIBDisp0(modrm_ext); |
| | 710 | encoder.sib_base(reg.lowId()); |
| | 711 | } else if (immOpSize(mem_op.disp) == 8) { |
| | 712 | encoder.modRm_SIBDisp8(modrm_ext); |
| | 713 | encoder.sib_baseDisp8(reg.lowId()); |
| | 714 | encoder.disp8(@intCast(i8, mem_op.disp)); |
| | 715 | } else { |
| | 716 | encoder.modRm_SIBDisp32(modrm_ext); |
| | 717 | encoder.sib_baseDisp32(reg.lowId()); |
| | 718 | encoder.disp32(mem_op.disp); |
| | 719 | } |
| | 720 | } else { |
| | 721 | if (mem_op.disp == 0) { |
| | 722 | encoder.modRm_indirectDisp0(modrm_ext, reg.lowId()); |
| | 723 | } else if (immOpSize(mem_op.disp) == 8) { |
| | 724 | encoder.modRm_indirectDisp8(modrm_ext, reg.lowId()); |
| | 725 | encoder.disp8(@intCast(i8, mem_op.disp)); |
| | 726 | } else { |
| | 727 | encoder.modRm_indirectDisp32(modrm_ext, reg.lowId()); |
| | 728 | encoder.disp32(mem_op.disp); |
| | 729 | } |
| | 730 | } |
| | 731 | } else { |
| | 732 | encoder.opcode_1byte(opc); |
| | 733 | if (mem_op.rip) { |
| | 734 | encoder.modRm_RIPDisp32(modrm_ext); |
| | 735 | } else { |
| | 736 | encoder.modRm_SIBDisp0(modrm_ext); |
| | 737 | encoder.sib_disp32(); |
| | 738 | } |
| | 739 | encoder.disp32(mem_op.disp); |
| | 740 | } |
| | 741 | }, |
| | 742 | } |
| | 743 | } |
| | 744 | |
| 695 | fn lowerToTdEnc(tag: Tag, moffs: i64, reg: Register, code: *std.ArrayList(u8)) InnerError!void { | 745 | fn lowerToTdEnc(tag: Tag, moffs: i64, reg: Register, code: *std.ArrayList(u8)) InnerError!void { |
| 696 | return lowerToTdFdEnc(tag, reg, moffs, code, true); | 746 | return lowerToTdFdEnc(tag, reg, moffs, code, true); |
| 697 | } | 747 | } |
| ... | @@ -772,7 +822,7 @@ fn lowerToOiEnc(tag: Tag, reg: Register, imm: i64, code: *std.ArrayList(u8)) Inn | ... | @@ -772,7 +822,7 @@ fn lowerToOiEnc(tag: Tag, reg: Register, imm: i64, code: *std.ArrayList(u8)) Inn |
| 772 | | 822 | |
| 773 | fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.ArrayList(u8)) InnerError!void { | 823 | fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.ArrayList(u8)) InnerError!void { |
| 774 | var opc = getOpCode(tag, .mi).?; | 824 | var opc = getOpCode(tag, .mi).?; |
| 775 | const modrm_ext = getModRmExt(tag); | 825 | const modrm_ext = getModRmExt(tag).?; |
| 776 | switch (reg_or_mem) { | 826 | switch (reg_or_mem) { |
| 777 | .register => |dst_reg| { | 827 | .register => |dst_reg| { |
| 778 | if (dst_reg.size() == 8) { | 828 | if (dst_reg.size() == 8) { |
| ... | @@ -819,16 +869,25 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.Arr | ... | @@ -819,16 +869,25 @@ fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.Arr |
| 819 | .b = dst_reg.isExtended(), | 869 | .b = dst_reg.isExtended(), |
| 820 | }); | 870 | }); |
| 821 | encoder.opcode_1byte(opc); | 871 | encoder.opcode_1byte(opc); |
| 822 | if (dst_mem.disp == 0) { | 872 | if (dst_reg.lowId() == 4) { |
| 823 | encoder.modRm_indirectDisp0(modrm_ext, dst_reg.lowId()); | 873 | if (dst_mem.disp == 0) { |
| 824 | } else if (immOpSize(dst_mem.disp) == 8) { | 874 | encoder.modRm_SIBDisp0(modrm_ext); |
| 825 | encoder.modRm_indirectDisp8(modrm_ext, dst_reg.lowId()); | 875 | encoder.sib_base(dst_reg.lowId()); |
| 826 | encoder.disp8(@intCast(i8, dst_mem.disp)); | 876 | } else if (immOpSize(dst_mem.disp) == 8) { |
| 827 | } else { | 877 | encoder.modRm_SIBDisp8(modrm_ext); |
| 828 | if (dst_reg.lowId() == 4) { | 878 | encoder.sib_baseDisp8(dst_reg.lowId()); |
| | 879 | encoder.disp8(@intCast(i8, dst_mem.disp)); |
| | 880 | } else { |
| 829 | encoder.modRm_SIBDisp32(modrm_ext); | 881 | encoder.modRm_SIBDisp32(modrm_ext); |
| 830 | encoder.sib_baseDisp32(dst_reg.lowId()); | 882 | encoder.sib_baseDisp32(dst_reg.lowId()); |
| 831 | encoder.disp32(dst_mem.disp); | 883 | encoder.disp32(dst_mem.disp); |
| | 884 | } |
| | 885 | } else { |
| | 886 | if (dst_mem.disp == 0) { |
| | 887 | encoder.modRm_indirectDisp0(modrm_ext, dst_reg.lowId()); |
| | 888 | } else if (immOpSize(dst_mem.disp) == 8) { |
| | 889 | encoder.modRm_indirectDisp8(modrm_ext, dst_reg.lowId()); |
| | 890 | encoder.disp8(@intCast(i8, dst_mem.disp)); |
| 832 | } else { | 891 | } else { |
| 833 | encoder.modRm_indirectDisp32(modrm_ext, dst_reg.lowId()); | 892 | encoder.modRm_indirectDisp32(modrm_ext, dst_reg.lowId()); |
| 834 | encoder.disp32(dst_mem.disp); | 893 | encoder.disp32(dst_mem.disp); |
| ... | @@ -886,16 +945,25 @@ fn lowerToRmEnc( | ... | @@ -886,16 +945,25 @@ fn lowerToRmEnc( |
| 886 | .b = src_reg.isExtended(), | 945 | .b = src_reg.isExtended(), |
| 887 | }); | 946 | }); |
| 888 | encoder.opcode_1byte(opc); | 947 | encoder.opcode_1byte(opc); |
| 889 | if (src_mem.disp == 0) { | 948 | if (src_reg.lowId() == 4) { |
| 890 | encoder.modRm_indirectDisp0(reg.lowId(), src_reg.lowId()); | 949 | if (src_mem.disp == 0) { |
| 891 | } else if (immOpSize(src_mem.disp) == 8) { | 950 | encoder.modRm_SIBDisp0(reg.lowId()); |
| 892 | encoder.modRm_indirectDisp8(reg.lowId(), src_reg.lowId()); | 951 | encoder.sib_base(src_reg.lowId()); |
| 893 | encoder.disp8(@intCast(i8, src_mem.disp)); | 952 | } else if (immOpSize(src_mem.disp) == 8) { |
| 894 | } else { | 953 | encoder.modRm_SIBDisp8(reg.lowId()); |
| 895 | if (src_reg.lowId() == 4) { | 954 | encoder.sib_baseDisp8(src_reg.lowId()); |
| | 955 | encoder.disp8(@intCast(i8, src_mem.disp)); |
| | 956 | } else { |
| 896 | encoder.modRm_SIBDisp32(reg.lowId()); | 957 | encoder.modRm_SIBDisp32(reg.lowId()); |
| 897 | encoder.sib_baseDisp32(src_reg.lowId()); | 958 | encoder.sib_baseDisp32(src_reg.lowId()); |
| 898 | encoder.disp32(src_mem.disp); | 959 | encoder.disp32(src_mem.disp); |
| | 960 | } |
| | 961 | } else { |
| | 962 | if (src_mem.disp == 0) { |
| | 963 | encoder.modRm_indirectDisp0(reg.lowId(), src_reg.lowId()); |
| | 964 | } else if (immOpSize(src_mem.disp) == 8) { |
| | 965 | encoder.modRm_indirectDisp8(reg.lowId(), src_reg.lowId()); |
| | 966 | encoder.disp8(@intCast(i8, src_mem.disp)); |
| 899 | } else { | 967 | } else { |
| 900 | encoder.modRm_indirectDisp32(reg.lowId(), src_reg.lowId()); | 968 | encoder.modRm_indirectDisp32(reg.lowId(), src_reg.lowId()); |
| 901 | encoder.disp32(src_mem.disp); | 969 | encoder.disp32(src_mem.disp); |
| ... | @@ -960,16 +1028,25 @@ fn lowerToMrEnc( | ... | @@ -960,16 +1028,25 @@ fn lowerToMrEnc( |
| 960 | .b = dst_reg.isExtended(), | 1028 | .b = dst_reg.isExtended(), |
| 961 | }); | 1029 | }); |
| 962 | encoder.opcode_1byte(opc); | 1030 | encoder.opcode_1byte(opc); |
| 963 | if (dst_mem.disp == 0) { | 1031 | if (dst_reg.lowId() == 4) { |
| 964 | encoder.modRm_indirectDisp0(reg.lowId(), dst_reg.lowId()); | 1032 | if (dst_mem.disp == 0) { |
| 965 | } else if (immOpSize(dst_mem.disp) == 8) { | 1033 | encoder.modRm_SIBDisp0(reg.lowId()); |
| 966 | encoder.modRm_indirectDisp8(reg.lowId(), dst_reg.lowId()); | 1034 | encoder.sib_base(dst_reg.lowId()); |
| 967 | encoder.disp8(@intCast(i8, dst_mem.disp)); | 1035 | } else if (immOpSize(dst_mem.disp) == 8) { |
| 968 | } else { | 1036 | encoder.modRm_SIBDisp8(reg.lowId()); |
| 969 | if (dst_reg.lowId() == 4) { | 1037 | encoder.sib_baseDisp8(dst_reg.lowId()); |
| | 1038 | encoder.disp8(@intCast(i8, dst_mem.disp)); |
| | 1039 | } else { |
| 970 | encoder.modRm_SIBDisp32(reg.lowId()); | 1040 | encoder.modRm_SIBDisp32(reg.lowId()); |
| 971 | encoder.sib_baseDisp32(dst_reg.lowId()); | 1041 | encoder.sib_baseDisp32(dst_reg.lowId()); |
| 972 | encoder.disp32(dst_mem.disp); | 1042 | encoder.disp32(dst_mem.disp); |
| | 1043 | } |
| | 1044 | } else { |
| | 1045 | if (dst_mem.disp == 0) { |
| | 1046 | encoder.modRm_indirectDisp0(reg.lowId(), dst_reg.lowId()); |
| | 1047 | } else if (immOpSize(dst_mem.disp) == 8) { |
| | 1048 | encoder.modRm_indirectDisp8(reg.lowId(), dst_reg.lowId()); |
| | 1049 | encoder.disp8(@intCast(i8, dst_mem.disp)); |
| 973 | } else { | 1050 | } else { |
| 974 | encoder.modRm_indirectDisp32(reg.lowId(), dst_reg.lowId()); | 1051 | encoder.modRm_indirectDisp32(reg.lowId(), dst_reg.lowId()); |
| 975 | encoder.disp32(dst_mem.disp); | 1052 | encoder.disp32(dst_mem.disp); |
| ... | @@ -1099,7 +1176,7 @@ fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void | ... | @@ -1099,7 +1176,7 @@ fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void |
| 1099 | if (ops.reg2 == .none) { | 1176 | if (ops.reg2 == .none) { |
| 1100 | // OP [reg1 + scale*rax + 0], imm32 | 1177 | // OP [reg1 + scale*rax + 0], imm32 |
| 1101 | var opc = getOpCode(tag, .mi).?; | 1178 | var opc = getOpCode(tag, .mi).?; |
| 1102 | const modrm_ext = getModRmExt(tag); | 1179 | const modrm_ext = getModRmExt(tag).?; |
| 1103 | if (ops.reg1.size() == 8) { | 1180 | if (ops.reg1.size() == 8) { |
| 1104 | opc -= 1; | 1181 | opc -= 1; |
| 1105 | } | 1182 | } |
| ... | @@ -1153,7 +1230,7 @@ fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void | ... | @@ -1153,7 +1230,7 @@ fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void |
| 1153 | if (ops.reg1.size() == 8) { | 1230 | if (ops.reg1.size() == 8) { |
| 1154 | opc -= 1; | 1231 | opc -= 1; |
| 1155 | } | 1232 | } |
| 1156 | const modrm_ext = getModRmExt(tag); | 1233 | const modrm_ext = getModRmExt(tag).?; |
| 1157 | const encoder = try Encoder.init(emit.code, 2); | 1234 | const encoder = try Encoder.init(emit.code, 2); |
| 1158 | encoder.rex(.{ | 1235 | encoder.rex(.{ |
| 1159 | .w = ops.reg1.size() == 64, | 1236 | .w = ops.reg1.size() == 64, |
| ... | @@ -1641,3 +1718,24 @@ test "lower FD/TD encoding" { | ... | @@ -1641,3 +1718,24 @@ test "lower FD/TD encoding" { |
| 1641 | try lowerToFdEnc(.mov, .al, 0x10, code.buffer()); | 1718 | try lowerToFdEnc(.mov, .al, 0x10, code.buffer()); |
| 1642 | try expectEqualHexStrings("\xa0\x10", code.emitted(), "mov al, ds:0x10"); | 1719 | try expectEqualHexStrings("\xa0\x10", code.emitted(), "mov al, ds:0x10"); |
| 1643 | } | 1720 | } |
| | 1721 | |
| | 1722 | test "lower M encoding" { |
| | 1723 | var code = TestEmitCode.init(); |
| | 1724 | defer code.deinit(); |
| | 1725 | try lowerToMEnc(.jmp_near, RegisterOrMemory.reg(.r12), code.buffer()); |
| | 1726 | try expectEqualHexStrings("\x41\xFF\xE4", code.emitted(), "jmp r12"); |
| | 1727 | try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.r12, 0), code.buffer()); |
| | 1728 | try expectEqualHexStrings("\x41\xFF\x24\x24", code.emitted(), "jmp qword ptr [r12]"); |
| | 1729 | try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.r12, 0x10), code.buffer()); |
| | 1730 | try expectEqualHexStrings("\x41\xFF\x64\x24\x10", code.emitted(), "jmp qword ptr [r12 + 0x10]"); |
| | 1731 | try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(.r12, 0x1000), code.buffer()); |
| | 1732 | try expectEqualHexStrings( |
| | 1733 | "\x41\xFF\xA4\x24\x00\x10\x00\x00", |
| | 1734 | code.emitted(), |
| | 1735 | "jmp qword ptr [r12 + 0x1000]", |
| | 1736 | ); |
| | 1737 | try lowerToMEnc(.jmp_near, RegisterOrMemory.rip(0x10), code.buffer()); |
| | 1738 | try expectEqualHexStrings("\xFF\x25\x10\x00\x00\x00", code.emitted(), "jmp qword ptr [rip + 0x10]"); |
| | 1739 | try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(null, 0x10), code.buffer()); |
| | 1740 | try expectEqualHexStrings("\xFF\x24\x25\x10\x00\x00\x00", code.emitted(), "jmp qword ptr [ds:0x10]"); |
| | 1741 | } |