use crate::aarch64::{FPR, GPR, v, x};
use crate::machine::Address as Amode;
use crate::operand::{Constraint, OperandDesc};
use Form::{
Acquire, Address, Alu, AluI, ArgVal, ArgValFp, Barrier, BrCond, Call, Cmp, CmpI, CmpSet,
CmpSetI, CompareSwap, Convert, Csel, FAlu, FCmp, FCmpSet, FConvert, FMove, FUnary, FetchOp,
FpToInt, Insert, IntToFp, Jcc, Jump, JumpAway, JumpReg, Lea, Load, LoadFp, LoadImm, Move,
MulAdd, Nop, Pop, PopPair, Probe, Push, PushPair, Release, Ret, RetVal, RetVal2, RetVal2Fp,
RetVal3Fp, RetVal4Fp, RetValFp, Select, Set, Store, StoreFp, Swap, Template, Test, Trap, Unary,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Form {
LoadImm,
Insert,
Alu,
AluI,
MulAdd,
Unary,
Convert,
Move,
Cmp,
CmpI,
Test,
CmpSet,
CmpSetI,
Select,
Csel,
Set,
Lea,
Address,
Load,
Store,
LoadFp,
StoreFp,
FAlu,
FUnary,
FMove,
FConvert,
IntToFp,
FpToInt,
FCmp,
FCmpSet,
RetVal,
RetVal2,
RetValFp,
RetVal2Fp,
RetVal3Fp,
RetVal4Fp,
ArgVal,
ArgValFp,
BrCond,
Jump,
Jcc,
JumpAway,
JumpReg,
Call,
Ret,
Push,
Pop,
PushPair,
PopPair,
Probe,
Nop,
Trap,
Barrier,
Acquire,
Release,
Swap,
FetchOp,
CompareSwap,
Template,
}
static ONE_WRITTEN: [OperandDesc; 1] = [OperandDesc::write(GPR)];
static TWO_WRITTEN: [OperandDesc; 2] = [OperandDesc::write(GPR), OperandDesc::write(GPR)];
static INSERT: [OperandDesc; 2] =
[OperandDesc::write(GPR).with(Constraint::Reuse(1)), OperandDesc::read(GPR)];
static ONE_TO_ONE: [OperandDesc; 2] = [OperandDesc::write(GPR), OperandDesc::read(GPR)];
static TWO_TO_ONE: [OperandDesc; 3] =
[OperandDesc::write(GPR), OperandDesc::read(GPR), OperandDesc::read(GPR)];
static THREE_TO_ONE: [OperandDesc; 4] = [
OperandDesc::write(GPR),
OperandDesc::read(GPR),
OperandDesc::read(GPR),
OperandDesc::read(GPR),
];
static TWO_READ: [OperandDesc; 2] = [OperandDesc::read(GPR), OperandDesc::read(GPR)];
static ONE_READ: [OperandDesc; 1] = [OperandDesc::read(GPR)];
static ONE_WRITTEN_FP: [OperandDesc; 1] = [OperandDesc::write(FPR)];
static ONE_READ_FP: [OperandDesc; 1] = [OperandDesc::read(FPR)];
static FP_TO_FP: [OperandDesc; 2] = [OperandDesc::write(FPR), OperandDesc::read(FPR)];
static TWO_FP_TO_FP: [OperandDesc; 3] =
[OperandDesc::write(FPR), OperandDesc::read(FPR), OperandDesc::read(FPR)];
static GPR_TO_FP: [OperandDesc; 2] = [OperandDesc::write(FPR), OperandDesc::read(GPR)];
static FP_TO_GPR: [OperandDesc; 2] = [OperandDesc::write(GPR), OperandDesc::read(FPR)];
static TWO_FP_READ: [OperandDesc; 2] = [OperandDesc::read(FPR), OperandDesc::read(FPR)];
static TWO_FP_TO_ONE: [OperandDesc; 3] =
[OperandDesc::write(GPR), OperandDesc::read(FPR), OperandDesc::read(FPR)];
static RET_VAL: [OperandDesc; 1] = [OperandDesc::read(GPR).with(Constraint::Fixed(x(0)))];
static RET_VAL_2: [OperandDesc; 1] = [OperandDesc::read(GPR).with(Constraint::Fixed(x(1)))];
static RET_VAL_FP: [OperandDesc; 1] = [OperandDesc::read(FPR).with(Constraint::Fixed(v(0)))];
static RET_VAL_2_FP: [OperandDesc; 1] = [OperandDesc::read(FPR).with(Constraint::Fixed(v(1)))];
static RET_VAL_3_FP: [OperandDesc; 1] = [OperandDesc::read(FPR).with(Constraint::Fixed(v(2)))];
static RET_VAL_4_FP: [OperandDesc; 1] = [OperandDesc::read(FPR).with(Constraint::Fixed(v(3)))];
static ACQUIRE: [OperandDesc; 2] = [OperandDesc::write(GPR), OperandDesc::read(GPR)];
static SWAP: [OperandDesc; 4] = [
OperandDesc::write_early(GPR),
OperandDesc::write_early(GPR),
OperandDesc::read(GPR),
OperandDesc::read(GPR),
];
static FETCH_OP: [OperandDesc; 5] = [
OperandDesc::write_early(GPR),
OperandDesc::write_early(GPR),
OperandDesc::write_early(GPR),
OperandDesc::read(GPR),
OperandDesc::read(GPR),
];
static COMPARE_SWAP: [OperandDesc; 5] = [
OperandDesc::write_early(GPR),
OperandDesc::write_early(GPR),
OperandDesc::read(GPR),
OperandDesc::read(GPR),
OperandDesc::read(GPR),
];
static NONE: [OperandDesc; 0] = [];
impl Form {
#[must_use]
pub fn operands(self) -> &'static [OperandDesc] {
match self {
LoadImm | Lea | Address | Load | ArgVal | Set => &ONE_WRITTEN,
Insert => &INSERT,
AluI | Unary | Convert | Move | CmpSetI => &ONE_TO_ONE,
Alu | CmpSet | Csel => &TWO_TO_ONE,
MulAdd | Select => &THREE_TO_ONE,
Cmp | PushPair | Release => &TWO_READ,
Acquire => &ACQUIRE,
Swap => &SWAP,
FetchOp => &FETCH_OP,
CompareSwap => &COMPARE_SWAP,
CmpI | Test | Store | BrCond | JumpReg | Push => &ONE_READ,
Pop => &ONE_WRITTEN,
PopPair => &TWO_WRITTEN,
LoadFp | ArgValFp => &ONE_WRITTEN_FP,
StoreFp => &ONE_READ_FP,
FUnary | FMove | FConvert => &FP_TO_FP,
FAlu => &TWO_FP_TO_FP,
IntToFp => &GPR_TO_FP,
FpToInt => &FP_TO_GPR,
FCmp => &TWO_FP_READ,
FCmpSet => &TWO_FP_TO_ONE,
RetVal => &RET_VAL,
RetVal2 => &RET_VAL_2,
RetValFp => &RET_VAL_FP,
RetVal2Fp => &RET_VAL_2_FP,
RetVal3Fp => &RET_VAL_3_FP,
RetVal4Fp => &RET_VAL_4_FP,
Jump | Jcc | JumpAway | Call | Ret | Nop | Trap | Barrier | Probe | Template => &NONE,
}
}
#[must_use]
pub fn takes_imm(self) -> bool {
matches!(self, LoadImm | Insert | AluI | CmpI | CmpSetI | Probe)
}
#[must_use]
pub fn takes_mem(self) -> bool {
matches!(self, Lea | Load | Store | LoadFp | StoreFp | Probe)
}
#[must_use]
pub fn touches_mem(self) -> bool {
matches!(
self,
Load | Store
| LoadFp
| StoreFp
| Push
| Pop
| PushPair
| PopPair
| Probe
| Call
| Ret
| Barrier
| Acquire
| Release
| Swap
| FetchOp
| CompareSwap
| Template
)
}
}
pub static INSTS: &[(&str, Form)] = &[
("mov_ri_32", LoadImm),
("mov_ri_64", LoadImm),
("movk_ri_16_32", Insert),
("movk_ri_16_64", Insert),
("movk_ri_32_64", Insert),
("movk_ri_48_64", Insert),
("mov_rr_32", Move),
("mov_rr_64", Move),
("add_rr_32", Alu),
("add_rr_64", Alu),
("sub_rr_32", Alu),
("sub_rr_64", Alu),
("and_rr_32", Alu),
("and_rr_64", Alu),
("orr_rr_32", Alu),
("orr_rr_64", Alu),
("eor_rr_32", Alu),
("eor_rr_64", Alu),
("bic_rr_32", Alu),
("bic_rr_64", Alu),
("orn_rr_32", Alu),
("orn_rr_64", Alu),
("mul_rr_32", Alu),
("mul_rr_64", Alu),
("sdiv_rr_32", Alu),
("sdiv_rr_64", Alu),
("udiv_rr_32", Alu),
("udiv_rr_64", Alu),
("lsl_rr_32", Alu),
("lsl_rr_64", Alu),
("lsr_rr_32", Alu),
("lsr_rr_64", Alu),
("asr_rr_32", Alu),
("asr_rr_64", Alu),
("ror_rr_32", Alu),
("ror_rr_64", Alu),
("smulh_rr_64", Alu),
("umulh_rr_64", Alu),
("add_ri_32", AluI),
("add_ri_64", AluI),
("sub_ri_32", AluI),
("sub_ri_64", AluI),
("and_ri_32", AluI),
("and_ri_64", AluI),
("orr_ri_32", AluI),
("orr_ri_64", AluI),
("eor_ri_32", AluI),
("eor_ri_64", AluI),
("lsl_ri_32", AluI),
("lsl_ri_64", AluI),
("lsr_ri_32", AluI),
("lsr_ri_64", AluI),
("asr_ri_32", AluI),
("asr_ri_64", AluI),
("ror_ri_32", AluI),
("ror_ri_64", AluI),
("madd_rrr_32", MulAdd),
("madd_rrr_64", MulAdd),
("msub_rrr_32", MulAdd),
("msub_rrr_64", MulAdd),
("neg_r_32", Unary),
("neg_r_64", Unary),
("mvn_r_32", Unary),
("mvn_r_64", Unary),
("clz_r_32", Unary),
("clz_r_64", Unary),
("rbit_r_32", Unary),
("rbit_r_64", Unary),
("rev_r_32", Unary),
("rev_r_64", Unary),
("rev16_r_32", Unary),
("sxtb_16", Convert),
("sxtb_32", Convert),
("sxtb_64", Convert),
("sxth_32", Convert),
("sxth_64", Convert),
("uxtb_16", Convert),
("uxtb_32", Convert),
("uxtb_64", Convert),
("uxth_32", Convert),
("uxth_64", Convert),
("sxtw_64", Convert),
("uxtw_64", Convert),
("bit_to_8", Unary),
("bit_to_16", Unary),
("bit_to_32", Unary),
("bit_to_64", Unary),
("low_8", Convert),
("low_16", Convert),
("low_32", Convert),
("bit_of_32", AluI),
("bit_of_64", AluI),
("cmp_rr_32", Cmp),
("cmp_ri_32", CmpI),
("test_32", Test),
("cmp_rr_64", Cmp),
("cmp_ri_64", CmpI),
("test_64", Test),
("cmp_set_eq_32", CmpSet),
("cmp_set_eq_64", CmpSet),
("cmp_set_ne_32", CmpSet),
("cmp_set_ne_64", CmpSet),
("cmp_set_lt_32", CmpSet),
("cmp_set_lt_64", CmpSet),
("cmp_set_le_32", CmpSet),
("cmp_set_le_64", CmpSet),
("cmp_set_gt_32", CmpSet),
("cmp_set_gt_64", CmpSet),
("cmp_set_ge_32", CmpSet),
("cmp_set_ge_64", CmpSet),
("cmp_set_lo_32", CmpSet),
("cmp_set_lo_64", CmpSet),
("cmp_set_ls_32", CmpSet),
("cmp_set_ls_64", CmpSet),
("cmp_set_hi_32", CmpSet),
("cmp_set_hi_64", CmpSet),
("cmp_set_hs_32", CmpSet),
("cmp_set_hs_64", CmpSet),
("cmp_set_eq_ri_32", CmpSetI),
("cmp_set_eq_ri_64", CmpSetI),
("cmp_set_ne_ri_32", CmpSetI),
("cmp_set_ne_ri_64", CmpSetI),
("cmp_set_lt_ri_32", CmpSetI),
("cmp_set_lt_ri_64", CmpSetI),
("cmp_set_le_ri_32", CmpSetI),
("cmp_set_le_ri_64", CmpSetI),
("cmp_set_gt_ri_32", CmpSetI),
("cmp_set_gt_ri_64", CmpSetI),
("cmp_set_ge_ri_32", CmpSetI),
("cmp_set_ge_ri_64", CmpSetI),
("cmp_set_lo_ri_32", CmpSetI),
("cmp_set_lo_ri_64", CmpSetI),
("cmp_set_ls_ri_32", CmpSetI),
("cmp_set_ls_ri_64", CmpSetI),
("cmp_set_hi_ri_32", CmpSetI),
("cmp_set_hi_ri_64", CmpSetI),
("cmp_set_hs_ri_32", CmpSetI),
("cmp_set_hs_ri_64", CmpSetI),
("sel_32", Select),
("sel_64", Select),
("csel_eq_32", Csel),
("csel_eq_64", Csel),
("csel_ne_32", Csel),
("csel_ne_64", Csel),
("csel_lt_32", Csel),
("csel_lt_64", Csel),
("csel_le_32", Csel),
("csel_le_64", Csel),
("csel_gt_32", Csel),
("csel_gt_64", Csel),
("csel_ge_32", Csel),
("csel_ge_64", Csel),
("csel_lo_32", Csel),
("csel_lo_64", Csel),
("csel_ls_32", Csel),
("csel_ls_64", Csel),
("csel_hi_32", Csel),
("csel_hi_64", Csel),
("csel_hs_32", Csel),
("csel_hs_64", Csel),
("cset_eq", Set),
("cset_ne", Set),
("cset_lt", Set),
("cset_le", Set),
("cset_gt", Set),
("cset_ge", Set),
("cset_lo", Set),
("cset_ls", Set),
("cset_hi", Set),
("cset_hs", Set),
("cset_mi", Set),
("lea_64", Lea),
("addr_64", Address),
("adr_64", Lea),
("got_64", Address),
("gottprel_64", Address),
("thread_64", Address),
("tls_64", Address),
("ldr_8", Load),
("ldr_16", Load),
("ldr_32", Load),
("ldr_64", Load),
("ldrs_8_32", Load),
("ldrs_8_64", Load),
("ldrs_16_32", Load),
("ldrs_16_64", Load),
("ldrs_32_64", Load),
("str_8", Store),
("str_16", Store),
("str_32", Store),
("str_64", Store),
("ldr_f32", LoadFp),
("ldr_f64", LoadFp),
("ldr_f128", LoadFp),
("str_f32", StoreFp),
("str_f64", StoreFp),
("str_f128", StoreFp),
("fadd_f32", FAlu),
("fadd_f64", FAlu),
("fsub_f32", FAlu),
("fsub_f64", FAlu),
("fmul_f32", FAlu),
("fmul_f64", FAlu),
("fdiv_f32", FAlu),
("fdiv_f64", FAlu),
("fmin_f32", FAlu),
("fmin_f64", FAlu),
("fmax_f32", FAlu),
("fmax_f64", FAlu),
("fminnm_f32", FAlu),
("fminnm_f64", FAlu),
("fmaxnm_f32", FAlu),
("fmaxnm_f64", FAlu),
("fneg_f32", FUnary),
("fneg_f64", FUnary),
("fabs_f32", FUnary),
("fabs_f64", FUnary),
("fsqrt_f32", FUnary),
("fsqrt_f64", FUnary),
("frintz_f32", FUnary),
("frintz_f64", FUnary),
("frintm_f32", FUnary),
("frintm_f64", FUnary),
("frintp_f32", FUnary),
("frintp_f64", FUnary),
("frinta_f32", FUnary),
("frinta_f64", FUnary),
("frintn_f32", FUnary),
("frintn_f64", FUnary),
("frintx_f32", FUnary),
("frintx_f64", FUnary),
("frinti_f32", FUnary),
("frinti_f64", FUnary),
("fmov_rr_f32", FMove),
("fmov_rr_f64", FMove),
("mov_rr_f128", FMove),
("fcvt_f64_f32", FConvert),
("fcvt_f32_f64", FConvert),
("scvtf_32_f32", IntToFp),
("scvtf_32_f64", IntToFp),
("scvtf_64_f32", IntToFp),
("scvtf_64_f64", IntToFp),
("ucvtf_32_f32", IntToFp),
("ucvtf_32_f64", IntToFp),
("ucvtf_64_f32", IntToFp),
("ucvtf_64_f64", IntToFp),
("fcvtzs_f32_32", FpToInt),
("fcvtzs_f32_64", FpToInt),
("fcvtzs_f64_32", FpToInt),
("fcvtzs_f64_64", FpToInt),
("fcvtzu_f32_32", FpToInt),
("fcvtzu_f32_64", FpToInt),
("fcvtzu_f64_32", FpToInt),
("fcvtzu_f64_64", FpToInt),
("fmov_to_f32", IntToFp),
("fmov_to_f64", IntToFp),
("fmov_from_f32", FpToInt),
("fmov_from_f64", FpToInt),
("fcmp_f32", FCmp),
("fcmp_f64", FCmp),
("fcmp_set_eq_f32", FCmpSet),
("fcmp_set_eq_f64", FCmpSet),
("fcmp_set_ne_f32", FCmpSet),
("fcmp_set_ne_f64", FCmpSet),
("fcmp_set_lt_f32", FCmpSet),
("fcmp_set_lt_f64", FCmpSet),
("fcmp_set_le_f32", FCmpSet),
("fcmp_set_le_f64", FCmpSet),
("fcmp_set_gt_f32", FCmpSet),
("fcmp_set_gt_f64", FCmpSet),
("fcmp_set_ge_f32", FCmpSet),
("fcmp_set_ge_f64", FCmpSet),
("fcmp_set_ult_f32", FCmpSet),
("fcmp_set_ult_f64", FCmpSet),
("fcmp_set_ule_f32", FCmpSet),
("fcmp_set_ule_f64", FCmpSet),
("fcmp_set_ugt_f32", FCmpSet),
("fcmp_set_ugt_f64", FCmpSet),
("fcmp_set_uge_f32", FCmpSet),
("fcmp_set_uge_f64", FCmpSet),
("fcmp_set_uno_f32", FCmpSet),
("fcmp_set_uno_f64", FCmpSet),
("fcmp_set_ord_f32", FCmpSet),
("fcmp_set_ord_f64", FCmpSet),
("fcmp_set_one_f32", FCmpSet),
("fcmp_set_one_f64", FCmpSet),
("fcmp_set_ueq_f32", FCmpSet),
("fcmp_set_ueq_f64", FCmpSet),
("ret_val_32", RetVal),
("ret_val_64", RetVal),
("ret_val_f32", RetValFp),
("ret_val_f64", RetValFp),
("ret_val_f128", RetValFp),
("ret_val2_32", RetVal2),
("ret_val2_64", RetVal2),
("ret_val2_f32", RetVal2Fp),
("ret_val2_f64", RetVal2Fp),
("ret_val2_f128", RetVal2Fp),
("ret_val3_f32", RetVal3Fp),
("ret_val3_f64", RetVal3Fp),
("ret_val3_f128", RetVal3Fp),
("ret_val4_f32", RetVal4Fp),
("ret_val4_f64", RetVal4Fp),
("ret_val4_f128", RetVal4Fp),
("arg_val_32", ArgVal),
("arg_val_64", ArgVal),
("arg_val_f32", ArgValFp),
("arg_val_f64", ArgValFp),
("arg_val_f128", ArgValFp),
("br_cond_32", BrCond),
("b", Jump),
("b_eq", Jcc),
("b_ne", Jcc),
("b_lt", Jcc),
("b_le", Jcc),
("b_gt", Jcc),
("b_ge", Jcc),
("b_lo", Jcc),
("b_ls", Jcc),
("b_hi", Jcc),
("b_hs", Jcc),
("b_mi", Jcc),
("b_pl", Jcc),
("b_vs", Jcc),
("b_vc", Jcc),
("b_away", JumpAway),
("br", JumpReg),
("bl", Call),
("blr", Call),
("ret", Ret),
("push_64", Push),
("pop_64", Pop),
("push_pair_64", PushPair),
("pop_pair_64", PopPair),
("align_sp_64", AluI),
("probe_64", Probe),
("nop", Nop),
("trap", Trap),
("fence", Barrier),
("fence_acquire", Barrier),
("ldar_8", Acquire),
("ldar_16", Acquire),
("ldar_32", Acquire),
("ldar_64", Acquire),
("stlr_8", Release),
("stlr_16", Release),
("stlr_32", Release),
("stlr_64", Release),
("xchg_8", Swap),
("xchg_16", Swap),
("xchg_32", Swap),
("xchg_64", Swap),
("xadd_8", FetchOp),
("xadd_16", FetchOp),
("xadd_32", FetchOp),
("xadd_64", FetchOp),
("xsub_8", FetchOp),
("xsub_16", FetchOp),
("xsub_32", FetchOp),
("xsub_64", FetchOp),
("cmpxchg_8", CompareSwap),
("cmpxchg_16", CompareSwap),
("cmpxchg_32", CompareSwap),
("cmpxchg_64", CompareSwap),
("template", Template),
];
pub const TEMPLATE: &str = "template";
#[must_use]
pub fn form(name: &str) -> Option<Form> {
INSTS.iter().find(|(known, _)| *known == name).map(|&(_, form)| form)
}
pub static ADDRESSES: &[(&str, Amode)] =
&[("amode_base", Amode::Base), ("amode_base_offset", Amode::BaseOffset)];
#[must_use]
pub fn address(name: &str) -> Option<Amode> {
ADDRESSES.iter().find(|(known, _)| *known == name).map(|&(_, kind)| kind)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn no_opcode_is_described_twice() {
for (at, (name, _)) in INSTS.iter().enumerate() {
assert!(
INSTS[at + 1..].iter().all(|(other, _)| other != name),
"{name} is in the table twice"
);
}
}
#[test]
fn a_form_that_carries_a_constant_or_an_address_has_a_register_to_go_with_it() {
for &(name, form) in INSTS {
if (form.takes_imm() || form.takes_mem()) && form != Probe {
assert!(!form.operands().is_empty(), "{name} has nothing to put its answer in");
}
}
assert_eq!(form("lea_64"), Some(Lea));
assert!(!Lea.touches_mem());
assert!(Load.touches_mem() && Push.touches_mem());
assert_eq!(form("x64.add_rr_64"), None);
}
#[test]
fn the_only_fixed_registers_are_the_ones_the_convention_names() {
for &(name, form) in INSTS {
let fixed =
form.operands().iter().any(|op| matches!(op.constraint, Constraint::Fixed(_)));
assert_eq!(fixed, name.starts_with("ret_val"), "{name}");
}
}
}