stage2: add lowering of RMI encoding

Example includes imul with 3 operands such as imul r64, r/m64, imm32.
This commit is contained in:
Jakub Konka
2021-12-23 21:14:14 +01:00
parent dba5df64ea
commit 35fe088e0e

View File

@@ -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");
}