Encodings for load/store instructions.

We don't support the full set of Intel addressing modes yet. So far we
have:

- Register indirect, no displacement.
- Register indirect, 8-bit signed displacement.
- Register indirect, 32-bit signed displacement.

The SIB addressing modes will need new Cretonne instruction formats to
represent.
This commit is contained in:
Jakob Stoklund Olesen
2017-05-12 14:26:44 -07:00
parent c998df6274
commit 9629867d0c
5 changed files with 470 additions and 27 deletions

View File

@@ -8,11 +8,30 @@ include!(concat!(env!("OUT_DIR"), "/binemit-intel.rs"));
pub static RELOC_NAMES: [&'static str; 1] = ["Call"];
// Emit single-byte opcode.
fn put_op1<CS: CodeSink + ?Sized>(bits: u16, sink: &mut CS) {
debug_assert!(bits & 0x0f00 == 0, "Invalid encoding bits for Op1*");
sink.put1(bits as u8);
}
// Emit two-byte opcode: 0F XX
fn put_op2<CS: CodeSink + ?Sized>(bits: u16, sink: &mut CS) {
debug_assert!(bits & 0x0f00 == 0x0400, "Invalid encoding bits for Op2*");
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) {
debug_assert!(bits & 0x0c00 == 0, "Invalid encoding bits for Mp1*");
let pp = (bits >> 8) & 3;
sink.put1(PREFIX[(pp - 1) as usize]);
sink.put1(bits as u8);
}
/// Emit a ModR/M byte for reg-reg operands.
fn modrm_rr<CS: CodeSink + ?Sized>(rm: RegUnit, reg: RegUnit, sink: &mut CS) {
let reg = reg as u8 & 7;
@@ -33,6 +52,42 @@ fn modrm_r_bits<CS: CodeSink + ?Sized>(rm: RegUnit, bits: u16, sink: &mut CS) {
sink.put1(b);
}
/// Emit a mode 00 ModR/M byte. This is a register-indirect addressing mode with no offset.
/// Registers %rsp and %rbp are invalid for `rm`, %rsp indicates a SIB byte, and %rbp indicates an
/// absolute immediate 32-bit address.
fn modrm_rm<CS: CodeSink + ?Sized>(rm: RegUnit, reg: RegUnit, sink: &mut CS) {
let reg = reg as u8 & 7;
let rm = rm as u8 & 7;
let mut b = 0b00000000;
b |= reg << 3;
b |= rm;
sink.put1(b);
}
/// Emit a mode 01 ModR/M byte. This is a register-indirect addressing mode with 8-bit
/// displacement.
/// Register %rsp is invalid for `rm`. It indicates the presence of a SIB byte.
fn modrm_disp8<CS: CodeSink + ?Sized>(rm: RegUnit, reg: RegUnit, sink: &mut CS) {
let reg = reg as u8 & 7;
let rm = rm as u8 & 7;
let mut b = 0b01000000;
b |= reg << 3;
b |= rm;
sink.put1(b);
}
/// Emit a mode 10 ModR/M byte. This is a register-indirect addressing mode with 32-bit
/// displacement.
/// Register %rsp is invalid for `rm`. It indicates the presence of a SIB byte.
fn modrm_disp32<CS: CodeSink + ?Sized>(rm: RegUnit, reg: RegUnit, sink: &mut CS) {
let reg = reg as u8 & 7;
let rm = rm as u8 & 7;
let mut b = 0b10000000;
b |= reg << 3;
b |= rm;
sink.put1(b);
}
fn recipe_op1rr<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
if let InstructionData::Binary { args, .. } = func.dfg[inst] {
put_op1(func.encodings[inst].bits(), sink);
@@ -77,3 +132,168 @@ fn recipe_op1rid<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut
panic!("Expected BinaryImm format: {:?}", func.dfg[inst]);
}
}
// Store recipes.
fn recipe_op1st<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
if let InstructionData::Store { args, .. } = func.dfg[inst] {
put_op1(func.encodings[inst].bits(), sink);
modrm_rm(func.locations[args[1]].unwrap_reg(),
func.locations[args[0]].unwrap_reg(),
sink);
} else {
panic!("Expected Store format: {:?}", func.dfg[inst]);
}
}
// This is just a tighter register class constraint.
fn recipe_op1st_abcd<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
recipe_op1st(func, inst, sink)
}
fn recipe_mp1st<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
if let InstructionData::Store { args, .. } = func.dfg[inst] {
put_mp1(func.encodings[inst].bits(), sink);
modrm_rm(func.locations[args[1]].unwrap_reg(),
func.locations[args[0]].unwrap_reg(),
sink);
} else {
panic!("Expected Store format: {:?}", func.dfg[inst]);
}
}
fn recipe_op1stdisp8<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
if let InstructionData::Store { args, offset, .. } = func.dfg[inst] {
put_op1(func.encodings[inst].bits(), sink);
modrm_disp8(func.locations[args[1]].unwrap_reg(),
func.locations[args[0]].unwrap_reg(),
sink);
let offset: i32 = offset.into();
sink.put1(offset as u8);
} else {
panic!("Expected Store format: {:?}", func.dfg[inst]);
}
}
fn recipe_op1stdisp8_abcd<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
recipe_op1stdisp8(func, inst, sink)
}
fn recipe_mp1stdisp8<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
if let InstructionData::Store { args, offset, .. } = func.dfg[inst] {
put_mp1(func.encodings[inst].bits(), sink);
modrm_disp8(func.locations[args[1]].unwrap_reg(),
func.locations[args[0]].unwrap_reg(),
sink);
let offset: i32 = offset.into();
sink.put1(offset as u8);
} else {
panic!("Expected Store format: {:?}", func.dfg[inst]);
}
}
fn recipe_op1stdisp32<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
if let InstructionData::Store { args, offset, .. } = func.dfg[inst] {
put_op1(func.encodings[inst].bits(), sink);
modrm_disp32(func.locations[args[1]].unwrap_reg(),
func.locations[args[0]].unwrap_reg(),
sink);
let offset: i32 = offset.into();
sink.put4(offset as u32);
} else {
panic!("Expected Store format: {:?}", func.dfg[inst]);
}
}
fn recipe_op1stdisp32_abcd<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
recipe_op1stdisp32(func, inst, sink)
}
fn recipe_mp1stdisp32<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
if let InstructionData::Store { args, offset, .. } = func.dfg[inst] {
put_mp1(func.encodings[inst].bits(), sink);
modrm_disp32(func.locations[args[1]].unwrap_reg(),
func.locations[args[0]].unwrap_reg(),
sink);
let offset: i32 = offset.into();
sink.put4(offset as u32);
} else {
panic!("Expected Store format: {:?}", func.dfg[inst]);
}
}
// Load recipes
fn recipe_op1ld<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
if let InstructionData::Load { arg, .. } = func.dfg[inst] {
put_op1(func.encodings[inst].bits(), sink);
modrm_rm(func.locations[arg].unwrap_reg(),
func.locations[func.dfg.first_result(inst)].unwrap_reg(),
sink);
} else {
panic!("Expected Load format: {:?}", func.dfg[inst]);
}
}
fn recipe_op1lddisp8<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
if let InstructionData::Load { arg, offset, .. } = func.dfg[inst] {
put_op1(func.encodings[inst].bits(), sink);
modrm_disp8(func.locations[arg].unwrap_reg(),
func.locations[func.dfg.first_result(inst)].unwrap_reg(),
sink);
let offset: i32 = offset.into();
sink.put1(offset as u8);
} else {
panic!("Expected Load format: {:?}", func.dfg[inst]);
}
}
fn recipe_op1lddisp32<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
if let InstructionData::Load { arg, offset, .. } = func.dfg[inst] {
put_op1(func.encodings[inst].bits(), sink);
modrm_disp32(func.locations[arg].unwrap_reg(),
func.locations[func.dfg.first_result(inst)].unwrap_reg(),
sink);
let offset: i32 = offset.into();
sink.put4(offset as u32);
} else {
panic!("Expected Load format: {:?}", func.dfg[inst]);
}
}
fn recipe_op2ld<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
if let InstructionData::Load { arg, .. } = func.dfg[inst] {
put_op2(func.encodings[inst].bits(), sink);
modrm_rm(func.locations[arg].unwrap_reg(),
func.locations[func.dfg.first_result(inst)].unwrap_reg(),
sink);
} else {
panic!("Expected Load format: {:?}", func.dfg[inst]);
}
}
fn recipe_op2lddisp8<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
if let InstructionData::Load { arg, offset, .. } = func.dfg[inst] {
put_op2(func.encodings[inst].bits(), sink);
modrm_disp8(func.locations[arg].unwrap_reg(),
func.locations[func.dfg.first_result(inst)].unwrap_reg(),
sink);
let offset: i32 = offset.into();
sink.put1(offset as u8);
} else {
panic!("Expected Load format: {:?}", func.dfg[inst]);
}
}
fn recipe_op2lddisp32<CS: CodeSink + ?Sized>(func: &Function, inst: Inst, sink: &mut CS) {
if let InstructionData::Load { arg, offset, .. } = func.dfg[inst] {
put_op2(func.encodings[inst].bits(), sink);
modrm_disp32(func.locations[arg].unwrap_reg(),
func.locations[func.dfg.first_result(inst)].unwrap_reg(),
sink);
let offset: i32 = offset.into();
sink.put4(offset as u32);
} else {
panic!("Expected Load format: {:?}", func.dfg[inst]);
}
}

View File

@@ -11,8 +11,8 @@
/// Check that `x` is the same as `y`.
#[allow(dead_code)]
pub fn is_equal<T: Eq + Copy>(x: T, y: T) -> bool {
x == y
pub fn is_equal<T: Eq + Copy, O: Into<T> + Copy>(x: T, y: O) -> bool {
x == y.into()
}
/// Check that `x` can be represented as a `wd`-bit signed integer with `sc` low zero bits.