Move Intel recipe_* bodies into intel/recipes.py.

Use a PUT_OP macro in the TailRecipe Python class to replace the code
snippet that emits the prefixes + opcode part of the instruction encoding.

Prepare for the addition of REX prefixes by giving the PUT_OP functions
a third argument representing the REX prefix. For the non-REX encodings,
verify that no REX bits wold be needed.
This commit is contained in:
Jakob Stoklund Olesen
2017-07-10 14:06:08 -07:00
parent f837dcf4b7
commit fb227cb389
2 changed files with 176 additions and 319 deletions

View File

@@ -1,8 +1,7 @@
//! Emitting binary Intel machine code.
use binemit::{CodeSink, Reloc, bad_encoding};
use ir::{self, Function, Inst, InstructionData, MemFlags};
use ir::immediates::{Imm64, Offset32};
use ir::{Function, Inst, InstructionData};
use isa::RegUnit;
include!(concat!(env!("OUT_DIR"), "/binemit-intel.rs"));
@@ -21,27 +20,51 @@ impl Into<Reloc> for RelocKind {
}
}
// Emit single-byte opcode.
fn put_op1<CS: CodeSink + ?Sized>(bits: u16, sink: &mut CS) {
debug_assert_eq!(bits & 0x0f00, 0, "Invalid encoding bits for Op1*");
// Mandatory prefix bytes for Mp* opcodes.
const PREFIX: [u8; 3] = [0x66, 0xf3, 0xf2];
// A REX prefix with no bits set: 0b0100WRXB.
const BASE_REX: u8 = 0b0100_0000;
// Create a single-register REX prefix, setting the B bit to bit 3 of the register.
// This is used for instructions that encode a register in the low 3 bits of the opcode and for
// instructions that use the ModR/M `reg` field for something else.
fn rex1(reg_b: RegUnit) -> u8 {
let b = ((reg_b >> 3) & 1) as u8;
BASE_REX | b
}
// Create a dual-register REX prefix, setting:
//
// REX.B = bit 3 of r/m register.
// REX.R = bit 3 of reg register.
fn rex2(rm: RegUnit, reg: RegUnit) -> u8 {
let b = ((rm >> 3) & 1) as u8;
let r = ((reg >> 3) & 1) as u8;
BASE_REX | b | (r << 2)
}
// Emit a single-byte opcode with no REX prefix.
fn put_op1<CS: CodeSink + ?Sized>(bits: u16, rex: u8, sink: &mut CS) {
debug_assert_eq!(bits & 0x8f00, 0, "Invalid encoding bits for Op1*");
debug_assert_eq!(rex, BASE_REX, "Invalid registers for REX-less encoding");
sink.put1(bits as u8);
}
// Emit two-byte opcode: 0F XX
fn put_op2<CS: CodeSink + ?Sized>(bits: u16, sink: &mut CS) {
debug_assert_eq!(bits & 0x0f00, 0x0400, "Invalid encoding bits for Op2*");
fn put_op2<CS: CodeSink + ?Sized>(bits: u16, rex: u8, sink: &mut CS) {
debug_assert_eq!(bits & 0x8f00, 0x0400, "Invalid encoding bits for Op2*");
debug_assert_eq!(rex, BASE_REX, "Invalid registers for REX-less encoding");
sink.put1(0x0f);
sink.put1(bits as u8);
}
// Mandatory prefix bytes for Mp* opcodes.
const PREFIX: [u8; 3] = [0x66, 0xf3, 0xf2];
// Emit single-byte opcode with mandatory prefix.
fn put_mp1<CS: CodeSink + ?Sized>(bits: u16, sink: &mut CS) {
fn put_mp1<CS: CodeSink + ?Sized>(bits: u16, rex: u8, sink: &mut CS) {
debug_assert_eq!(bits & 0x0c00, 0, "Invalid encoding bits for Mp1*");
let pp = (bits >> 8) & 3;
sink.put1(PREFIX[(pp - 1) as usize]);
debug_assert_eq!(rex, BASE_REX, "Invalid registers for REX-less encoding");
sink.put1(bits as u8);
}
@@ -100,290 +123,3 @@ fn modrm_disp32<CS: CodeSink + ?Sized>(rm: RegUnit, reg: RegUnit, sink: &mut CS)
b |= rm;
sink.put1(b);
}
fn recipe_op1rr<CS: CodeSink + ?Sized>(_func: &Function,
_inst: Inst,
sink: &mut CS,
bits: u16,
in_reg0: RegUnit,
in_reg1: RegUnit) {
put_op1(bits, sink);
modrm_rr(in_reg0, in_reg1, sink);
}
fn recipe_op1ur<CS: CodeSink + ?Sized>(_func: &Function,
_inst: Inst,
sink: &mut CS,
bits: u16,
in_reg0: RegUnit,
out_reg0: RegUnit) {
put_op1(bits, sink);
modrm_rr(out_reg0, in_reg0, sink);
}
fn recipe_op1rc<CS: CodeSink + ?Sized>(_func: &Function,
_inst: Inst,
sink: &mut CS,
bits: u16,
in_reg0: RegUnit) {
put_op1(bits, sink);
modrm_r_bits(in_reg0, bits, sink);
}
fn recipe_op1rib<CS: CodeSink + ?Sized>(_func: &Function,
_inst: Inst,
sink: &mut CS,
bits: u16,
in_reg0: RegUnit,
imm: Imm64) {
put_op1(bits, sink);
modrm_r_bits(in_reg0, bits, sink);
let imm: i64 = imm.into();
sink.put1(imm as u8);
}
fn recipe_op1rid<CS: CodeSink + ?Sized>(_func: &Function,
_inst: Inst,
sink: &mut CS,
bits: u16,
in_reg0: RegUnit,
imm: Imm64) {
put_op1(bits, sink);
modrm_r_bits(in_reg0, bits, sink);
let imm: i64 = imm.into();
sink.put4(imm as u32);
}
fn recipe_op1uid<CS: CodeSink + ?Sized>(_func: &Function,
_inst: Inst,
sink: &mut CS,
bits: u16,
imm: Imm64,
out_reg0: RegUnit) {
// The destination register is encoded in the low bits of the opcode. No ModR/M
put_op1(bits | (out_reg0 & 7), sink);
let imm: i64 = imm.into();
sink.put4(imm as u32);
}
// Store recipes.
fn recipe_op1st<CS: CodeSink + ?Sized>(_func: &Function,
_inst: Inst,
sink: &mut CS,
bits: u16,
in_reg0: RegUnit,
in_reg1: RegUnit,
_flags: MemFlags,
_offset: Offset32) {
put_op1(bits, sink);
modrm_rm(in_reg1, in_reg0, sink);
}
// This is just a tighter register class constraint.
fn recipe_op1st_abcd<CS: CodeSink + ?Sized>(func: &Function,
inst: Inst,
sink: &mut CS,
bits: u16,
in_reg0: RegUnit,
in_reg1: RegUnit,
flags: MemFlags,
offset: Offset32) {
recipe_op1st(func, inst, sink, bits, in_reg0, in_reg1, flags, offset)
}
fn recipe_mp1st<CS: CodeSink + ?Sized>(_func: &Function,
_inst: Inst,
sink: &mut CS,
bits: u16,
in_reg0: RegUnit,
in_reg1: RegUnit,
_flags: MemFlags,
_offset: Offset32) {
put_mp1(bits, sink);
modrm_rm(in_reg1, in_reg0, sink);
}
fn recipe_op1stdisp8<CS: CodeSink + ?Sized>(_func: &Function,
_inst: Inst,
sink: &mut CS,
bits: u16,
in_reg0: RegUnit,
in_reg1: RegUnit,
_flags: MemFlags,
offset: Offset32) {
put_op1(bits, sink);
modrm_disp8(in_reg1, in_reg0, sink);
let offset: i32 = offset.into();
sink.put1(offset as u8);
}
fn recipe_op1stdisp8_abcd<CS: CodeSink + ?Sized>(func: &Function,
inst: Inst,
sink: &mut CS,
bits: u16,
in_reg0: RegUnit,
in_reg1: RegUnit,
flags: MemFlags,
offset: Offset32) {
recipe_op1stdisp8(func, inst, sink, bits, in_reg0, in_reg1, flags, offset)
}
fn recipe_mp1stdisp8<CS: CodeSink + ?Sized>(_func: &Function,
_inst: Inst,
sink: &mut CS,
bits: u16,
in_reg0: RegUnit,
in_reg1: RegUnit,
_flags: MemFlags,
offset: Offset32) {
put_mp1(bits, sink);
modrm_disp8(in_reg1, in_reg0, sink);
let offset: i32 = offset.into();
sink.put1(offset as u8);
}
fn recipe_op1stdisp32<CS: CodeSink + ?Sized>(_func: &Function,
_inst: Inst,
sink: &mut CS,
bits: u16,
in_reg0: RegUnit,
in_reg1: RegUnit,
_flags: MemFlags,
offset: Offset32) {
put_op1(bits, sink);
modrm_disp32(in_reg1, in_reg0, sink);
let offset: i32 = offset.into();
sink.put4(offset as u32);
}
fn recipe_op1stdisp32_abcd<CS: CodeSink + ?Sized>(func: &Function,
inst: Inst,
sink: &mut CS,
bits: u16,
in_reg0: RegUnit,
in_reg1: RegUnit,
flags: MemFlags,
offset: Offset32) {
recipe_op1stdisp32(func, inst, sink, bits, in_reg0, in_reg1, flags, offset)
}
fn recipe_mp1stdisp32<CS: CodeSink + ?Sized>(_func: &Function,
_inst: Inst,
sink: &mut CS,
bits: u16,
in_reg0: RegUnit,
in_reg1: RegUnit,
_flags: MemFlags,
offset: Offset32) {
put_mp1(bits, sink);
modrm_disp32(in_reg1, in_reg0, sink);
let offset: i32 = offset.into();
sink.put4(offset as u32);
}
// Load recipes
fn recipe_op1ld<CS: CodeSink + ?Sized>(_func: &Function,
_inst: Inst,
sink: &mut CS,
bits: u16,
in_reg0: RegUnit,
_flags: MemFlags,
_offset: Offset32,
out_reg0: RegUnit) {
put_op1(bits, sink);
modrm_rm(in_reg0, out_reg0, sink);
}
fn recipe_op1lddisp8<CS: CodeSink + ?Sized>(_func: &Function,
_inst: Inst,
sink: &mut CS,
bits: u16,
in_reg0: RegUnit,
_flags: MemFlags,
offset: Offset32,
out_reg0: RegUnit) {
put_op1(bits, sink);
modrm_disp8(in_reg0, out_reg0, sink);
let offset: i32 = offset.into();
sink.put1(offset as u8);
}
fn recipe_op1lddisp32<CS: CodeSink + ?Sized>(_func: &Function,
_inst: Inst,
sink: &mut CS,
bits: u16,
in_reg0: RegUnit,
_flags: MemFlags,
offset: Offset32,
out_reg0: RegUnit) {
put_op1(bits, sink);
modrm_disp32(in_reg0, out_reg0, sink);
let offset: i32 = offset.into();
sink.put4(offset as u32);
}
fn recipe_op2ld<CS: CodeSink + ?Sized>(_func: &Function,
_inst: Inst,
sink: &mut CS,
bits: u16,
in_reg0: RegUnit,
_flags: MemFlags,
_offset: Offset32,
out_reg0: RegUnit) {
put_op2(bits, sink);
modrm_rm(in_reg0, out_reg0, sink);
}
fn recipe_op2lddisp8<CS: CodeSink + ?Sized>(_func: &Function,
_inst: Inst,
sink: &mut CS,
bits: u16,
in_reg0: RegUnit,
_flags: MemFlags,
offset: Offset32,
out_reg0: RegUnit) {
put_op2(bits, sink);
modrm_disp8(in_reg0, out_reg0, sink);
let offset: i32 = offset.into();
sink.put1(offset as u8);
}
fn recipe_op2lddisp32<CS: CodeSink + ?Sized>(_func: &Function,
_inst: Inst,
sink: &mut CS,
bits: u16,
in_reg0: RegUnit,
_flags: MemFlags,
offset: Offset32,
out_reg0: RegUnit) {
put_op2(bits, sink);
modrm_disp32(in_reg0, out_reg0, sink);
let offset: i32 = offset.into();
sink.put4(offset as u32);
}
fn recipe_op1call_id<CS: CodeSink + ?Sized>(_func: &Function,
_inst: Inst,
sink: &mut CS,
bits: u16,
func_ref: ir::FuncRef) {
put_op1(bits, sink);
sink.reloc_func(RelocKind::PCRel4.into(), func_ref);
sink.put4(0);
}
fn recipe_op1call_r<CS: CodeSink + ?Sized>(_func: &Function,
_inst: Inst,
sink: &mut CS,
bits: u16,
in_reg0: RegUnit,
_sig_ref: ir::SigRef) {
put_op1(bits, sink);
modrm_r_bits(in_reg0, bits, sink);
}
fn recipe_op1ret<CS: CodeSink + ?Sized>(_func: &Function, _inst: Inst, sink: &mut CS, bits: u16) {
put_op1(bits, sink);
}