Commit 35fe088e0e

Jakub Konka <kubkon@jakubkonka.com>
2021-12-23 21:14:14
stage2: add lowering of RMI encoding
Example includes imul with 3 operands such as imul r64, r/m64, imm32.
1 parent dba5df6
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");
+}