use crate::regs::PhysReg;
use Arg::{Imm, Label, Mem, Named, Reg, Symbol};
use Width::{Byte, Long, Quad, Word};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Width {
Byte,
Word,
Long,
Quad,
}
impl Width {
#[must_use]
pub fn index(self) -> usize {
match self {
Byte => 0,
Word => 1,
Long => 2,
Quad => 3,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Arg {
Reg(u8, Width),
Named(&'static str),
Imm,
Mem,
Symbol,
Label,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Written {
pub mnemonic: &'static str,
pub args: &'static [Arg],
}
const fn spell(mnemonic: &'static str, args: &'static [Arg]) -> Written {
Written { mnemonic, args }
}
static CMP_8: [Arg; 2] = [Reg(2, Byte), Reg(1, Byte)];
static CMP_16: [Arg; 2] = [Reg(2, Word), Reg(1, Word)];
static CMP_32: [Arg; 2] = [Reg(2, Long), Reg(1, Long)];
static CMP_64: [Arg; 2] = [Reg(2, Quad), Reg(1, Quad)];
static SET: [Arg; 1] = [Reg(0, Byte)];
static DIVISOR_8: [Arg; 1] = [Reg(3, Byte)];
static DIVISOR_16: [Arg; 1] = [Reg(3, Word)];
static DIVISOR_32: [Arg; 1] = [Reg(3, Long)];
static DIVISOR_64: [Arg; 1] = [Reg(3, Quad)];
static CLEAR_FOR_QUOTIENT: [Arg; 2] = [Reg(1, Long), Reg(1, Long)];
static CLEAR_FOR_REMAINDER: [Arg; 2] = [Reg(0, Long), Reg(0, Long)];
static WIDEN_FOR_QUOTIENT: [Arg; 2] = [Reg(2, Byte), Reg(0, Long)];
static WIDEN_FOR_REMAINDER: [Arg; 2] = [Reg(2, Byte), Reg(1, Long)];
static HIGH_HALF: [Arg; 2] = [Named("ah"), Reg(0, Byte)];
static TEXT: &[(&str, &[Written])] = &[
("mov_ri_8", &[spell("movb", &[Imm, Reg(0, Byte)])]),
("mov_ri_16", &[spell("movw", &[Imm, Reg(0, Word)])]),
("mov_ri_32", &[spell("movl", &[Imm, Reg(0, Long)])]),
("mov_ri_64", &[spell("movq", &[Imm, Reg(0, Quad)])]),
("add_rr_8", &[spell("addb", &[Reg(2, Byte), Reg(0, Byte)])]),
("add_rr_16", &[spell("addw", &[Reg(2, Word), Reg(0, Word)])]),
("add_rr_32", &[spell("addl", &[Reg(2, Long), Reg(0, Long)])]),
("add_rr_64", &[spell("addq", &[Reg(2, Quad), Reg(0, Quad)])]),
("sub_rr_8", &[spell("subb", &[Reg(2, Byte), Reg(0, Byte)])]),
("sub_rr_16", &[spell("subw", &[Reg(2, Word), Reg(0, Word)])]),
("sub_rr_32", &[spell("subl", &[Reg(2, Long), Reg(0, Long)])]),
("sub_rr_64", &[spell("subq", &[Reg(2, Quad), Reg(0, Quad)])]),
("and_rr_8", &[spell("andb", &[Reg(2, Byte), Reg(0, Byte)])]),
("and_rr_16", &[spell("andw", &[Reg(2, Word), Reg(0, Word)])]),
("and_rr_32", &[spell("andl", &[Reg(2, Long), Reg(0, Long)])]),
("and_rr_64", &[spell("andq", &[Reg(2, Quad), Reg(0, Quad)])]),
("or_rr_8", &[spell("orb", &[Reg(2, Byte), Reg(0, Byte)])]),
("or_rr_16", &[spell("orw", &[Reg(2, Word), Reg(0, Word)])]),
("or_rr_32", &[spell("orl", &[Reg(2, Long), Reg(0, Long)])]),
("or_rr_64", &[spell("orq", &[Reg(2, Quad), Reg(0, Quad)])]),
("xor_rr_8", &[spell("xorb", &[Reg(2, Byte), Reg(0, Byte)])]),
("xor_rr_16", &[spell("xorw", &[Reg(2, Word), Reg(0, Word)])]),
("xor_rr_32", &[spell("xorl", &[Reg(2, Long), Reg(0, Long)])]),
("xor_rr_64", &[spell("xorq", &[Reg(2, Quad), Reg(0, Quad)])]),
("imul_rr_8", &[spell("imull", &[Reg(2, Long), Reg(0, Long)])]),
("imul_rr_16", &[spell("imulw", &[Reg(2, Word), Reg(0, Word)])]),
("imul_rr_32", &[spell("imull", &[Reg(2, Long), Reg(0, Long)])]),
("imul_rr_64", &[spell("imulq", &[Reg(2, Quad), Reg(0, Quad)])]),
("add_ri_8", &[spell("addb", &[Imm, Reg(0, Byte)])]),
("add_ri_16", &[spell("addw", &[Imm, Reg(0, Word)])]),
("add_ri_32", &[spell("addl", &[Imm, Reg(0, Long)])]),
("add_ri_64", &[spell("addq", &[Imm, Reg(0, Quad)])]),
("sub_ri_8", &[spell("subb", &[Imm, Reg(0, Byte)])]),
("sub_ri_16", &[spell("subw", &[Imm, Reg(0, Word)])]),
("sub_ri_32", &[spell("subl", &[Imm, Reg(0, Long)])]),
("sub_ri_64", &[spell("subq", &[Imm, Reg(0, Quad)])]),
("and_ri_8", &[spell("andb", &[Imm, Reg(0, Byte)])]),
("and_ri_16", &[spell("andw", &[Imm, Reg(0, Word)])]),
("and_ri_32", &[spell("andl", &[Imm, Reg(0, Long)])]),
("and_ri_64", &[spell("andq", &[Imm, Reg(0, Quad)])]),
("or_ri_8", &[spell("orb", &[Imm, Reg(0, Byte)])]),
("or_ri_16", &[spell("orw", &[Imm, Reg(0, Word)])]),
("or_ri_32", &[spell("orl", &[Imm, Reg(0, Long)])]),
("or_ri_64", &[spell("orq", &[Imm, Reg(0, Quad)])]),
("xor_ri_8", &[spell("xorb", &[Imm, Reg(0, Byte)])]),
("xor_ri_16", &[spell("xorw", &[Imm, Reg(0, Word)])]),
("xor_ri_32", &[spell("xorl", &[Imm, Reg(0, Long)])]),
("xor_ri_64", &[spell("xorq", &[Imm, Reg(0, Quad)])]),
("imul_ri_8", &[spell("imull", &[Imm, Reg(1, Long), Reg(0, Long)])]),
("imul_ri_16", &[spell("imulw", &[Imm, Reg(1, Word), Reg(0, Word)])]),
("imul_ri_32", &[spell("imull", &[Imm, Reg(1, Long), Reg(0, Long)])]),
("imul_ri_64", &[spell("imulq", &[Imm, Reg(1, Quad), Reg(0, Quad)])]),
("neg_r_8", &[spell("negb", &[Reg(0, Byte)])]),
("neg_r_16", &[spell("negw", &[Reg(0, Word)])]),
("neg_r_32", &[spell("negl", &[Reg(0, Long)])]),
("neg_r_64", &[spell("negq", &[Reg(0, Quad)])]),
("not_r_8", &[spell("notb", &[Reg(0, Byte)])]),
("not_r_16", &[spell("notw", &[Reg(0, Word)])]),
("not_r_32", &[spell("notl", &[Reg(0, Long)])]),
("not_r_64", &[spell("notq", &[Reg(0, Quad)])]),
("idiv_quo_8", &[spell("cbtw", &[]), spell("idivb", &DIVISOR_8)]),
("idiv_quo_16", &[spell("cwtd", &[]), spell("idivw", &DIVISOR_16)]),
("idiv_quo_32", &[spell("cltd", &[]), spell("idivl", &DIVISOR_32)]),
("idiv_quo_64", &[spell("cqto", &[]), spell("idivq", &DIVISOR_64)]),
("idiv_rem_8", &[spell("cbtw", &[]), spell("idivb", &DIVISOR_8), spell("movb", &HIGH_HALF)]),
("idiv_rem_16", &[spell("cwtd", &[]), spell("idivw", &DIVISOR_16)]),
("idiv_rem_32", &[spell("cltd", &[]), spell("idivl", &DIVISOR_32)]),
("idiv_rem_64", &[spell("cqto", &[]), spell("idivq", &DIVISOR_64)]),
("div_quo_8", &[spell("movzbl", &WIDEN_FOR_QUOTIENT), spell("divb", &DIVISOR_8)]),
("div_quo_16", &[spell("xorl", &CLEAR_FOR_QUOTIENT), spell("divw", &DIVISOR_16)]),
("div_quo_32", &[spell("xorl", &CLEAR_FOR_QUOTIENT), spell("divl", &DIVISOR_32)]),
("div_quo_64", &[spell("xorl", &CLEAR_FOR_QUOTIENT), spell("divq", &DIVISOR_64)]),
(
"div_rem_8",
&[
spell("movzbl", &WIDEN_FOR_REMAINDER),
spell("divb", &DIVISOR_8),
spell("movb", &HIGH_HALF),
],
),
("div_rem_16", &[spell("xorl", &CLEAR_FOR_REMAINDER), spell("divw", &DIVISOR_16)]),
("div_rem_32", &[spell("xorl", &CLEAR_FOR_REMAINDER), spell("divl", &DIVISOR_32)]),
("div_rem_64", &[spell("xorl", &CLEAR_FOR_REMAINDER), spell("divq", &DIVISOR_64)]),
("shl_ri_8", &[spell("shlb", &[Imm, Reg(0, Byte)])]),
("shl_ri_16", &[spell("shlw", &[Imm, Reg(0, Word)])]),
("shl_ri_32", &[spell("shll", &[Imm, Reg(0, Long)])]),
("shl_ri_64", &[spell("shlq", &[Imm, Reg(0, Quad)])]),
("shr_ri_8", &[spell("shrb", &[Imm, Reg(0, Byte)])]),
("shr_ri_16", &[spell("shrw", &[Imm, Reg(0, Word)])]),
("shr_ri_32", &[spell("shrl", &[Imm, Reg(0, Long)])]),
("shr_ri_64", &[spell("shrq", &[Imm, Reg(0, Quad)])]),
("sar_ri_8", &[spell("sarb", &[Imm, Reg(0, Byte)])]),
("sar_ri_16", &[spell("sarw", &[Imm, Reg(0, Word)])]),
("sar_ri_32", &[spell("sarl", &[Imm, Reg(0, Long)])]),
("sar_ri_64", &[spell("sarq", &[Imm, Reg(0, Quad)])]),
("shl_rcl_8", &[spell("shlb", &[Reg(2, Byte), Reg(0, Byte)])]),
("shl_rcl_16", &[spell("shlw", &[Reg(2, Byte), Reg(0, Word)])]),
("shl_rcl_32", &[spell("shll", &[Reg(2, Byte), Reg(0, Long)])]),
("shl_rcl_64", &[spell("shlq", &[Reg(2, Byte), Reg(0, Quad)])]),
("shr_rcl_8", &[spell("shrb", &[Reg(2, Byte), Reg(0, Byte)])]),
("shr_rcl_16", &[spell("shrw", &[Reg(2, Byte), Reg(0, Word)])]),
("shr_rcl_32", &[spell("shrl", &[Reg(2, Byte), Reg(0, Long)])]),
("shr_rcl_64", &[spell("shrq", &[Reg(2, Byte), Reg(0, Quad)])]),
("sar_rcl_8", &[spell("sarb", &[Reg(2, Byte), Reg(0, Byte)])]),
("sar_rcl_16", &[spell("sarw", &[Reg(2, Byte), Reg(0, Word)])]),
("sar_rcl_32", &[spell("sarl", &[Reg(2, Byte), Reg(0, Long)])]),
("sar_rcl_64", &[spell("sarq", &[Reg(2, Byte), Reg(0, Quad)])]),
("cmp_set_e_8", &[spell("cmpb", &CMP_8), spell("sete", &SET)]),
("cmp_set_e_16", &[spell("cmpw", &CMP_16), spell("sete", &SET)]),
("cmp_set_e_32", &[spell("cmpl", &CMP_32), spell("sete", &SET)]),
("cmp_set_e_64", &[spell("cmpq", &CMP_64), spell("sete", &SET)]),
("cmp_set_ne_8", &[spell("cmpb", &CMP_8), spell("setne", &SET)]),
("cmp_set_ne_16", &[spell("cmpw", &CMP_16), spell("setne", &SET)]),
("cmp_set_ne_32", &[spell("cmpl", &CMP_32), spell("setne", &SET)]),
("cmp_set_ne_64", &[spell("cmpq", &CMP_64), spell("setne", &SET)]),
("cmp_set_l_8", &[spell("cmpb", &CMP_8), spell("setl", &SET)]),
("cmp_set_l_16", &[spell("cmpw", &CMP_16), spell("setl", &SET)]),
("cmp_set_l_32", &[spell("cmpl", &CMP_32), spell("setl", &SET)]),
("cmp_set_l_64", &[spell("cmpq", &CMP_64), spell("setl", &SET)]),
("cmp_set_le_8", &[spell("cmpb", &CMP_8), spell("setle", &SET)]),
("cmp_set_le_16", &[spell("cmpw", &CMP_16), spell("setle", &SET)]),
("cmp_set_le_32", &[spell("cmpl", &CMP_32), spell("setle", &SET)]),
("cmp_set_le_64", &[spell("cmpq", &CMP_64), spell("setle", &SET)]),
("cmp_set_g_8", &[spell("cmpb", &CMP_8), spell("setg", &SET)]),
("cmp_set_g_16", &[spell("cmpw", &CMP_16), spell("setg", &SET)]),
("cmp_set_g_32", &[spell("cmpl", &CMP_32), spell("setg", &SET)]),
("cmp_set_g_64", &[spell("cmpq", &CMP_64), spell("setg", &SET)]),
("cmp_set_ge_8", &[spell("cmpb", &CMP_8), spell("setge", &SET)]),
("cmp_set_ge_16", &[spell("cmpw", &CMP_16), spell("setge", &SET)]),
("cmp_set_ge_32", &[spell("cmpl", &CMP_32), spell("setge", &SET)]),
("cmp_set_ge_64", &[spell("cmpq", &CMP_64), spell("setge", &SET)]),
("cmp_set_b_8", &[spell("cmpb", &CMP_8), spell("setb", &SET)]),
("cmp_set_b_16", &[spell("cmpw", &CMP_16), spell("setb", &SET)]),
("cmp_set_b_32", &[spell("cmpl", &CMP_32), spell("setb", &SET)]),
("cmp_set_b_64", &[spell("cmpq", &CMP_64), spell("setb", &SET)]),
("cmp_set_be_8", &[spell("cmpb", &CMP_8), spell("setbe", &SET)]),
("cmp_set_be_16", &[spell("cmpw", &CMP_16), spell("setbe", &SET)]),
("cmp_set_be_32", &[spell("cmpl", &CMP_32), spell("setbe", &SET)]),
("cmp_set_be_64", &[spell("cmpq", &CMP_64), spell("setbe", &SET)]),
("cmp_set_a_8", &[spell("cmpb", &CMP_8), spell("seta", &SET)]),
("cmp_set_a_16", &[spell("cmpw", &CMP_16), spell("seta", &SET)]),
("cmp_set_a_32", &[spell("cmpl", &CMP_32), spell("seta", &SET)]),
("cmp_set_a_64", &[spell("cmpq", &CMP_64), spell("seta", &SET)]),
("cmp_set_ae_8", &[spell("cmpb", &CMP_8), spell("setae", &SET)]),
("cmp_set_ae_16", &[spell("cmpw", &CMP_16), spell("setae", &SET)]),
("cmp_set_ae_32", &[spell("cmpl", &CMP_32), spell("setae", &SET)]),
("cmp_set_ae_64", &[spell("cmpq", &CMP_64), spell("setae", &SET)]),
("movzx_8_16", &[spell("movzbw", &[Reg(1, Byte), Reg(0, Word)])]),
("movzx_8_32", &[spell("movzbl", &[Reg(1, Byte), Reg(0, Long)])]),
("movzx_8_64", &[spell("movzbq", &[Reg(1, Byte), Reg(0, Quad)])]),
("movzx_16_32", &[spell("movzwl", &[Reg(1, Word), Reg(0, Long)])]),
("movzx_16_64", &[spell("movzwq", &[Reg(1, Word), Reg(0, Quad)])]),
("mov_32_to_64", &[spell("movl", &[Reg(1, Long), Reg(0, Long)])]),
("movsx_8_16", &[spell("movsbw", &[Reg(1, Byte), Reg(0, Word)])]),
("movsx_8_32", &[spell("movsbl", &[Reg(1, Byte), Reg(0, Long)])]),
("movsx_8_64", &[spell("movsbq", &[Reg(1, Byte), Reg(0, Quad)])]),
("movsx_16_32", &[spell("movswl", &[Reg(1, Word), Reg(0, Long)])]),
("movsx_16_64", &[spell("movswq", &[Reg(1, Word), Reg(0, Quad)])]),
("movsxd_32_64", &[spell("movslq", &[Reg(1, Long), Reg(0, Quad)])]),
("low_8", &[spell("movb", &[Reg(1, Byte), Reg(0, Byte)])]),
("low_16", &[spell("movw", &[Reg(1, Word), Reg(0, Word)])]),
("low_32", &[spell("movl", &[Reg(1, Long), Reg(0, Long)])]),
("lea_64", &[spell("leaq", &[Mem, Reg(0, Quad)])]),
("mov_rm_8", &[spell("movb", &[Mem, Reg(0, Byte)])]),
("mov_rm_16", &[spell("movw", &[Mem, Reg(0, Word)])]),
("mov_rm_32", &[spell("movl", &[Mem, Reg(0, Long)])]),
("mov_rm_64", &[spell("movq", &[Mem, Reg(0, Quad)])]),
("mov_mr_8", &[spell("movb", &[Reg(0, Byte), Mem])]),
("mov_mr_16", &[spell("movw", &[Reg(0, Word), Mem])]),
("mov_mr_32", &[spell("movl", &[Reg(0, Long), Mem])]),
("mov_mr_64", &[spell("movq", &[Reg(0, Quad), Mem])]),
("ret_val_8", &[]),
("ret_val_16", &[]),
("ret_val_32", &[]),
("ret_val_64", &[]),
("arg_val_8", &[]),
("arg_val_16", &[]),
("arg_val_32", &[]),
("arg_val_64", &[]),
("br_cond_8", &[]),
("call", &[spell("call", &[Symbol])]),
("test_rr_8", &[spell("testb", &[Reg(0, Byte), Reg(0, Byte)])]),
("jcc_e", &[spell("je", &[Label])]),
("jcc_ne", &[spell("jne", &[Label])]),
("jmp", &[spell("jmp", &[Label])]),
("mov_rr_64", &[spell("movq", &[Reg(1, Quad), Reg(0, Quad)])]),
("push_64", &[spell("pushq", &[Reg(0, Quad)])]),
("pop_64", &[spell("popq", &[Reg(0, Quad)])]),
("ret", &[spell("ret", &[])]),
("movaps_rr", &[spell("movaps", &[Reg(1, Quad), Reg(0, Quad)])]),
("movaps_rm", &[spell("movaps", &[Mem, Reg(0, Quad)])]),
("movaps_mr", &[spell("movaps", &[Reg(0, Quad), Mem])]),
];
#[must_use]
pub fn written(name: &str) -> Option<&'static [Written]> {
TEXT.iter().find(|(known, _)| *known == name).map(|&(_, insts)| insts)
}
static GPR_TEXT: [[&str; 4]; 16] = [
["al", "ax", "eax", "rax"],
["cl", "cx", "ecx", "rcx"],
["dl", "dx", "edx", "rdx"],
["bl", "bx", "ebx", "rbx"],
["spl", "sp", "esp", "rsp"],
["bpl", "bp", "ebp", "rbp"],
["sil", "si", "esi", "rsi"],
["dil", "di", "edi", "rdi"],
["r8b", "r8w", "r8d", "r8"],
["r9b", "r9w", "r9d", "r9"],
["r10b", "r10w", "r10d", "r10"],
["r11b", "r11w", "r11d", "r11"],
["r12b", "r12w", "r12d", "r12"],
["r13b", "r13w", "r13d", "r13"],
["r14b", "r14w", "r14d", "r14"],
["r15b", "r15w", "r15d", "r15"],
];
#[must_use]
pub fn gpr_name(reg: PhysReg, width: Width) -> Option<&'static str> {
GPR_TEXT.get(usize::from(reg.number())).map(|names| names[width.index()])
}
#[cfg(test)]
mod tests {
use super::*;
use crate::operand::Constraint;
use crate::x86_64::insts::{Form, INSTS, form};
use crate::x86_64::{GPR, REGS};
fn named(insts: &[Written]) -> Vec<u8> {
let mut at: Vec<u8> = insts
.iter()
.flat_map(|inst| inst.args)
.filter_map(|arg| match *arg {
Reg(at, _) => Some(at),
_ => None,
})
.collect();
at.sort_unstable();
at.dedup();
at
}
#[test]
fn every_opcode_is_written_once_and_in_the_order_it_is_described_in() {
let written: Vec<&str> = TEXT.iter().map(|&(name, _)| name).collect();
let described: Vec<&str> = INSTS.iter().map(|&(name, _)| name).collect();
assert_eq!(written, described);
}
#[test]
fn every_argument_names_something_the_instruction_really_has() {
for &(name, insts) in TEXT {
let form = form(name).expect("every written opcode is a described opcode");
let operands = form.operands();
let (mut imm, mut mem, mut symbol, mut label) = (false, false, false, false);
for arg in insts.iter().flat_map(|inst| inst.args) {
match *arg {
Reg(at, _) => assert!(
usize::from(at) < operands.len(),
"{name} names operand {at} and has {} of them",
operands.len()
),
Named(register) => assert!(
REGS.reg_named(register).is_none(),
"{name} names {register}, which is a register something could be in"
),
Imm => imm = true,
Mem => mem = true,
Symbol => symbol = true,
Label => label = true,
}
}
assert_eq!(imm, form.takes_imm(), "{name} and its immediate disagree");
assert_eq!(mem, form.takes_mem(), "{name} and its addressing mode disagree");
assert_eq!(symbol, form == Form::Call, "{name} and its symbol disagree");
assert_eq!(
label,
matches!(form, Form::Jcc | Form::Jmp),
"{name} and where it goes disagree"
);
}
}
#[test]
fn an_operand_no_instruction_names_is_one_that_is_not_written() {
for &(name, insts) in TEXT {
let form = form(name).expect("every written opcode is a described opcode");
if insts.is_empty() || matches!(form, Form::DivQuo | Form::DivRem) {
continue;
}
let named = named(insts);
let operands = form.operands();
for at in 0..operands.len() {
let tied = operands.iter().enumerate().any(|(other, operand)| {
operand.constraint == Constraint::Reuse(at as u8)
&& named.contains(&(other as u8))
});
assert!(
named.contains(&(at as u8)) || tied,
"{name} has an operand {at} that nothing written for it names"
);
}
}
}
#[test]
fn an_opcode_that_is_not_an_instruction_is_written_as_no_instructions() {
for name in ["ret_val_32", "arg_val_64", "br_cond_8"] {
assert_eq!(written(name), Some([].as_slice()), "{name}");
}
for &(name, insts) in TEXT {
let form = form(name).expect("every written opcode is a described opcode");
assert_eq!(
insts.is_empty(),
matches!(form, Form::RetVal | Form::ArgVal | Form::BrCond),
"{name} and whether it is an instruction disagree"
);
}
}
#[test]
fn the_widest_spelling_of_a_register_is_the_one_the_register_file_gives_it() {
for number in 0..16u8 {
let reg = PhysReg::new(number);
assert_eq!(gpr_name(reg, Quad), REGS.name(GPR, reg), "register {number}");
}
assert_eq!(gpr_name(PhysReg::new(16), Quad), None);
}
#[test]
fn a_register_is_spelled_by_how_much_of_it_an_instruction_reads() {
use crate::x86_64::{R8, RAX, RDI};
assert_eq!(gpr_name(RAX, Byte), Some("al"));
assert_eq!(gpr_name(RAX, Long), Some("eax"));
assert_eq!(gpr_name(RDI, Byte), Some("dil"));
assert_eq!(gpr_name(RDI, Word), Some("di"));
assert_eq!(gpr_name(R8, Long), Some("r8d"));
}
#[test]
fn an_opcode_is_written_under_the_name_the_machine_ir_holds() {
let add = written("add_rr_32").expect("an opcode this target has");
assert_eq!(add, [spell("addl", &[Reg(2, Long), Reg(0, Long)])]);
assert_eq!(written("x64.add_rr_32"), None, "the prefix is not part of the opcode");
assert_eq!(written("add_rr_128"), None);
}
#[test]
fn an_opcode_the_machine_has_no_single_instruction_for_is_written_as_the_ones_it_has() {
let compare = written("cmp_set_l_32").expect("an opcode this target has");
assert_eq!(compare.iter().map(|inst| inst.mnemonic).collect::<Vec<_>>(), ["cmpl", "setl"]);
let divide = written("idiv_quo_64").expect("an opcode this target has");
assert_eq!(divide.iter().map(|inst| inst.mnemonic).collect::<Vec<_>>(), ["cqto", "idivq"]);
let remainder = written("div_rem_8").expect("an opcode this target has");
assert_eq!(
remainder.iter().map(|inst| inst.mnemonic).collect::<Vec<_>>(),
["movzbl", "divb", "movb"]
);
assert_eq!(remainder[2].args, [Named("ah"), Reg(0, Byte)]);
}
}