authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-23 01:22:07+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-23 01:22:07+01:00
log9078cb0197e09de701fbc9d5941358b7910f38b2
tree2ed343f167bdf6fef6a7d7a58e97ca5b431b5989
parent1167e248ef8ab94aac3c4218574a09ba9346d34f

stage2: add lowering of M encoding

Examples include jmp / call near with memory or register operand like `jmp [rax]`, or even RIP-relative `call [rip + 0x10]`.

1 files changed, 138 insertions(+), 40 deletions(-)

src/arch/x86_64/Emit.zig+138-40
...@@ -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 reg312 // 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}
327315
328const CondType = enum {316const 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}
630618
631inline fn getModRmExt(tag: Tag) u3 {619inline 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}
646635
...@@ -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}
694683
684fn 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
695fn lowerToTdEnc(tag: Tag, moffs: i64, reg: Register, code: *std.ArrayList(u8)) InnerError!void {745fn 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
772822
773fn lowerToMiEnc(tag: Tag, reg_or_mem: RegisterOrMemory, imm: i32, code: *std.ArrayList(u8)) InnerError!void {823fn 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], imm321177 // 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
1722test "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}