Commit 35fe088e0e
Changed files (1)
src
arch
x86_64
src/arch/x86_64/Emit.zig
@@ -516,6 +516,9 @@ const Encoding = enum {
/// OP moffs, al/ax/eax/rax
td,
+
+ /// OP r64, r/m64, imm32
+ rmi,
};
const OpCode = union(enum) {
@@ -651,6 +654,10 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
.mov => OpCode.oneByte(if (is_one_byte) 0xa2 else 0xa3),
else => null,
},
+ .rmi => return switch (tag) {
+ .imul => OpCode.oneByte(if (is_one_byte) 0x6b else 0x69),
+ else => null,
+ },
}
}
@@ -1180,6 +1187,96 @@ fn lowerToMrEnc(
}
}
+fn lowerToRmiEnc(
+ tag: Tag,
+ reg: Register,
+ reg_or_mem: RegisterOrMemory,
+ imm: i32,
+ code: *std.ArrayList(u8),
+) InnerError!void {
+ const opc = getOpCode(tag, .rmi, reg.size() == 8).?;
+ switch (reg_or_mem) {
+ .register => |src_reg| {
+ if (reg.size() != src_reg.size()) return error.EmitFail;
+ const encoder = try Encoder.init(code, 7);
+ encoder.rex(.{
+ .w = reg.size() == 64,
+ .r = reg.isExtended(),
+ .b = src_reg.isExtended(),
+ });
+ opc.encode(encoder);
+ encoder.modRm_direct(reg.lowId(), src_reg.lowId());
+ switch (reg.size()) {
+ 8 => {
+ const imm8 = try math.cast(i8, imm);
+ encoder.imm8(imm8);
+ },
+ 16 => {
+ const imm16 = try math.cast(i16, imm);
+ encoder.imm16(imm16);
+ },
+ 32, 64 => encoder.imm32(imm),
+ else => unreachable,
+ }
+ },
+ .memory => |src_mem| {
+ const encoder = try Encoder.init(code, 13);
+ if (reg.size() == 16) {
+ encoder.opcode_1byte(0x66);
+ }
+ if (src_mem.reg) |src_reg| {
+ // TODO handle 32-bit base register - requires prefix 0x67
+ // Intel Manual, Vol 1, chapter 3.6 and 3.6.1
+ if (src_reg.size() != 64) return error.EmitFail;
+ encoder.rex(.{
+ .w = reg.size() == 64,
+ .r = reg.isExtended(),
+ .b = src_reg.isExtended(),
+ });
+ opc.encode(encoder);
+ if (src_reg.lowId() == 4) {
+ if (src_mem.disp == 0) {
+ encoder.modRm_SIBDisp0(reg.lowId());
+ encoder.sib_base(src_reg.lowId());
+ } else if (immOpSize(src_mem.disp) == 8) {
+ encoder.modRm_SIBDisp8(reg.lowId());
+ encoder.sib_baseDisp8(src_reg.lowId());
+ encoder.disp8(@intCast(i8, src_mem.disp));
+ } else {
+ encoder.modRm_SIBDisp32(reg.lowId());
+ encoder.sib_baseDisp32(src_reg.lowId());
+ encoder.disp32(src_mem.disp);
+ }
+ } else {
+ if (src_mem.disp == 0) {
+ encoder.modRm_indirectDisp0(reg.lowId(), src_reg.lowId());
+ } else if (immOpSize(src_mem.disp) == 8) {
+ encoder.modRm_indirectDisp8(reg.lowId(), src_reg.lowId());
+ encoder.disp8(@intCast(i8, src_mem.disp));
+ } else {
+ encoder.modRm_indirectDisp32(reg.lowId(), src_reg.lowId());
+ encoder.disp32(src_mem.disp);
+ }
+ }
+ } else {
+ encoder.rex(.{
+ .w = reg.size() == 64,
+ .r = reg.isExtended(),
+ });
+ opc.encode(encoder);
+ if (src_mem.rip) {
+ encoder.modRm_RIPDisp32(reg.lowId());
+ } else {
+ encoder.modRm_SIBDisp0(reg.lowId());
+ encoder.sib_disp32();
+ }
+ encoder.disp32(src_mem.disp);
+ }
+ encoder.imm32(imm);
+ },
+ }
+}
+
fn mirArith(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
switch (ops.flags) {
@@ -1383,22 +1480,7 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
0b00 => return lowerToRmEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code),
0b10 => {
const imm = emit.mir.instructions.items(.data)[inst].imm;
- const opc: u8 = if (imm <= math.maxInt(i8)) 0x6b else 0x69;
- const encoder = try Encoder.init(emit.code, 7);
- encoder.rex(.{
- .w = ops.reg1.size() == 64,
- .r = ops.reg1.isExtended(),
- .b = ops.reg1.isExtended(),
- });
- encoder.opcode_1byte(opc);
- encoder.modRm_direct(ops.reg1.lowId(), ops.reg2.lowId());
- if (imm <= math.maxInt(i8)) {
- encoder.imm8(@intCast(i8, imm));
- } else if (imm <= math.maxInt(i16)) {
- encoder.imm16(@intCast(i16, imm));
- } else {
- encoder.imm32(imm);
- }
+ return lowerToRmiEnc(.imul, ops.reg1, RegisterOrMemory.reg(ops.reg2), imm, emit.code);
},
else => return emit.fail("TODO implement imul", .{}),
}
@@ -1842,3 +1924,12 @@ test "lower O encoding" {
try lowerToOEnc(.push, .r12w, code.buffer());
try expectEqualHexStrings("\x66\x41\x54", code.emitted(), "push r12w");
}
+
+test "lower RMI encoding" {
+ var code = TestEmitCode.init();
+ defer code.deinit();
+ try lowerToRmiEnc(.imul, .rax, RegisterOrMemory.mem(.rbp, -8), 0x10, code.buffer());
+ try expectEqualHexStrings("\x48\x69\x45\xF8\x10\x00\x00\x00", code.emitted(), "imul rax, [rbp - 8], 0x10");
+ try lowerToRmiEnc(.imul, .r12, RegisterOrMemory.reg(.r12), 0x10, code.buffer());
+ try expectEqualHexStrings("\x4D\x69\xE4\x10\x00\x00\x00", code.emitted(), "imul r12, r12, 0x10");
+}