use crate::core::value::Width;
use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct Opcode(pub u16);
impl Opcode {
pub const MOV: Opcode = Opcode(0x01);
pub const EXT_S: Opcode = Opcode(0x02);
pub const EXT_Z: Opcode = Opcode(0x03);
pub const TRUNC: Opcode = Opcode(0x04);
pub const BSWAP: Opcode = Opcode(0x05);
pub const DEPOSIT: Opcode = Opcode(0x06);
pub const EXTRACT: Opcode = Opcode(0x07);
pub const GET_SLOT: Opcode = Opcode(0x08);
pub const ADD: Opcode = Opcode(0x10);
pub const SUB: Opcode = Opcode(0x11);
pub const MUL: Opcode = Opcode(0x12);
pub const DIV_S: Opcode = Opcode(0x13);
pub const DIV_U: Opcode = Opcode(0x14);
pub const REM_S: Opcode = Opcode(0x15);
pub const REM_U: Opcode = Opcode(0x16);
pub const NEG: Opcode = Opcode(0x17);
pub const ADDC: Opcode = Opcode(0x18);
pub const SUBB: Opcode = Opcode(0x19);
pub const MULU2: Opcode = Opcode(0x1a);
pub const MULS2: Opcode = Opcode(0x1b);
pub const MULHSU: Opcode = Opcode(0x1c);
pub const AND: Opcode = Opcode(0x20);
pub const OR: Opcode = Opcode(0x21);
pub const XOR: Opcode = Opcode(0x22);
pub const NOT: Opcode = Opcode(0x23);
pub const ANDC: Opcode = Opcode(0x24);
pub const SHL: Opcode = Opcode(0x25);
pub const SHR: Opcode = Opcode(0x26);
pub const SAR: Opcode = Opcode(0x27);
pub const ROTL: Opcode = Opcode(0x28);
pub const ROTR: Opcode = Opcode(0x29);
pub const ROTLC: Opcode = Opcode(0x2a);
pub const ROTRC: Opcode = Opcode(0x2b);
pub const CLZ: Opcode = Opcode(0x30);
pub const CTZ: Opcode = Opcode(0x31);
pub const POPCOUNT: Opcode = Opcode(0x32);
pub const SETCOND: Opcode = Opcode(0x38);
pub const MOVCOND: Opcode = Opcode(0x39);
pub const BRCOND: Opcode = Opcode(0x3a);
pub const LD: Opcode = Opcode(0x40);
pub const ST: Opcode = Opcode(0x41);
pub const CMPXCHG: Opcode = Opcode(0x48);
pub const XCHG: Opcode = Opcode(0x49);
pub const FETCH_ADD: Opcode = Opcode(0x4a);
pub const FETCH_AND: Opcode = Opcode(0x4b);
pub const FETCH_OR: Opcode = Opcode(0x4c);
pub const FETCH_XOR: Opcode = Opcode(0x4d);
pub const FETCH_SMIN: Opcode = Opcode(0x4e);
pub const FETCH_SMAX: Opcode = Opcode(0x4f);
pub const FETCH_UMIN: Opcode = Opcode(0x50);
pub const FETCH_UMAX: Opcode = Opcode(0x51);
pub const FENCE: Opcode = Opcode(0x52);
pub const LD_EXCL: Opcode = Opcode(0x53);
pub const ST_EXCL: Opcode = Opcode(0x54);
pub const GOTO_TB: Opcode = Opcode(0x60);
pub const EXIT_TB: Opcode = Opcode(0x61);
pub const LOOKUP_AND_GOTO: Opcode = Opcode(0x62);
pub const CALL_HELPER: Opcode = Opcode(0x63);
pub const CHARGE: Opcode = Opcode(0x64);
pub const INSN_START: Opcode = Opcode(0x65);
pub const PHI: Opcode = Opcode(0x70);
#[must_use]
pub fn name(self) -> &'static str {
match self {
Opcode::MOV => "mov",
Opcode::EXT_S => "ext_s",
Opcode::EXT_Z => "ext_z",
Opcode::TRUNC => "trunc",
Opcode::BSWAP => "bswap",
Opcode::DEPOSIT => "deposit",
Opcode::EXTRACT => "extract",
Opcode::GET_SLOT => "get_slot",
Opcode::ADD => "add",
Opcode::SUB => "sub",
Opcode::MUL => "mul",
Opcode::DIV_S => "div_s",
Opcode::DIV_U => "div_u",
Opcode::REM_S => "rem_s",
Opcode::REM_U => "rem_u",
Opcode::NEG => "neg",
Opcode::ADDC => "addc",
Opcode::SUBB => "subb",
Opcode::MULU2 => "mulu2",
Opcode::MULS2 => "muls2",
Opcode::MULHSU => "mulhsu",
Opcode::AND => "and",
Opcode::OR => "or",
Opcode::XOR => "xor",
Opcode::NOT => "not",
Opcode::ANDC => "andc",
Opcode::SHL => "shl",
Opcode::SHR => "shr",
Opcode::SAR => "sar",
Opcode::ROTL => "rotl",
Opcode::ROTR => "rotr",
Opcode::ROTLC => "rotlc",
Opcode::ROTRC => "rotrc",
Opcode::CLZ => "clz",
Opcode::CTZ => "ctz",
Opcode::POPCOUNT => "popcount",
Opcode::SETCOND => "setcond",
Opcode::MOVCOND => "movcond",
Opcode::BRCOND => "brcond",
Opcode::LD => "ld",
Opcode::ST => "st",
Opcode::CMPXCHG => "cmpxchg",
Opcode::XCHG => "xchg",
Opcode::FETCH_ADD => "fetch_add",
Opcode::FETCH_AND => "fetch_and",
Opcode::FETCH_OR => "fetch_or",
Opcode::FETCH_XOR => "fetch_xor",
Opcode::FETCH_SMIN => "fetch_smin",
Opcode::FETCH_SMAX => "fetch_smax",
Opcode::FETCH_UMIN => "fetch_umin",
Opcode::FETCH_UMAX => "fetch_umax",
Opcode::FENCE => "fence",
Opcode::LD_EXCL => "ld_excl",
Opcode::ST_EXCL => "st_excl",
Opcode::GOTO_TB => "goto_tb",
Opcode::EXIT_TB => "exit_tb",
Opcode::LOOKUP_AND_GOTO => "lookup_and_goto",
Opcode::CALL_HELPER => "call_helper",
Opcode::CHARGE => "charge",
Opcode::INSN_START => "insn_start",
Opcode::PHI => "phi",
_ => "unknown",
}
}
#[inline]
#[must_use]
pub fn is_terminator(self) -> bool {
matches!(
self,
Opcode::GOTO_TB | Opcode::EXIT_TB | Opcode::LOOKUP_AND_GOTO
)
}
#[inline]
#[must_use]
pub fn has_side_effect(self) -> bool {
matches!(
self,
Opcode::ST
| Opcode::CMPXCHG
| Opcode::XCHG
| Opcode::FENCE
| Opcode::ST_EXCL
| Opcode::LD_EXCL
| Opcode::CALL_HELPER
| Opcode::CHARGE
| Opcode::INSN_START
) || (self.0 >= Opcode::FETCH_ADD.0 && self.0 <= Opcode::FETCH_UMAX.0)
}
}
impl fmt::Display for Opcode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[inline]
#[must_use]
pub const fn bitfield_aux(pos: u32, len: u32) -> u32 {
(pos & 0xffff) | ((len & 0xffff) << 16)
}
#[inline]
#[must_use]
pub const fn bitfield_parts(aux: u32) -> (u32, u32) {
(aux & 0xffff, (aux >> 16) & 0xffff)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Cond {
Eq,
Ne,
LtS,
LeS,
GtS,
GeS,
LtU,
LeU,
GtU,
GeU,
}
impl Cond {
#[inline]
#[must_use]
pub const fn invert(self) -> Cond {
match self {
Cond::Eq => Cond::Ne,
Cond::Ne => Cond::Eq,
Cond::LtS => Cond::GeS,
Cond::GeS => Cond::LtS,
Cond::LeS => Cond::GtS,
Cond::GtS => Cond::LeS,
Cond::LtU => Cond::GeU,
Cond::GeU => Cond::LtU,
Cond::LeU => Cond::GtU,
Cond::GtU => Cond::LeU,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Sign {
Unsigned,
Signed,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct MemSpace(pub u8);
impl MemSpace {
pub const MEM: MemSpace = MemSpace(0);
pub const IO: MemSpace = MemSpace(1);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct SegId(pub u8);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Endian {
Little,
Big,
AsRegion,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Align {
None,
Fault,
Split,
Rotate,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum AccessKind {
Fetch,
Load,
Store,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct MemOp {
pub size: Width,
pub sign: Sign,
pub space: MemSpace,
pub seg: Option<SegId>,
pub endian: Endian,
pub align: Align,
pub kind: AccessKind,
pub volatile: bool,
}
impl MemOp {
#[must_use]
pub const fn load(size: Width) -> MemOp {
MemOp {
size,
sign: Sign::Unsigned,
space: MemSpace::MEM,
seg: None,
endian: Endian::Little,
align: Align::None,
kind: AccessKind::Load,
volatile: false,
}
}
#[must_use]
pub const fn store(size: Width) -> MemOp {
MemOp {
kind: AccessKind::Store,
..MemOp::load(size)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn opcodes_name_themselves() {
assert_eq!(Opcode::ADDC.name(), "addc");
assert_eq!(Opcode::LOOKUP_AND_GOTO.name(), "lookup_and_goto");
assert_eq!(Opcode(0xfff).name(), "unknown");
}
#[test]
fn terminators_and_effects_are_disjoint_from_pure_arithmetic() {
assert!(Opcode::EXIT_TB.is_terminator());
assert!(!Opcode::ADD.is_terminator());
assert!(!Opcode::ADD.has_side_effect());
assert!(Opcode::ST.has_side_effect());
assert!(Opcode::CHARGE.has_side_effect());
assert!(Opcode::INSN_START.has_side_effect());
for op in [
Opcode::FETCH_ADD,
Opcode::FETCH_XOR,
Opcode::FETCH_SMIN,
Opcode::FETCH_UMAX,
] {
assert!(op.has_side_effect(), "{op} must not be eliminable");
}
assert!(!Opcode::LD.has_side_effect());
}
#[test]
fn every_condition_has_its_negation_in_the_set() {
for cond in [
Cond::Eq,
Cond::Ne,
Cond::LtS,
Cond::LeS,
Cond::GtS,
Cond::GeS,
Cond::LtU,
Cond::LeU,
Cond::GtU,
Cond::GeU,
] {
assert_ne!(cond.invert(), cond);
assert_eq!(cond.invert().invert(), cond);
}
assert_eq!(Cond::LtS.invert(), Cond::GeS);
assert_eq!(Cond::GtU.invert(), Cond::LeU);
}
#[test]
fn a_store_descriptor_differs_from_a_load_only_in_kind() {
let ld = MemOp::load(Width::U16);
let st = MemOp::store(Width::U16);
assert_eq!(ld.kind, AccessKind::Load);
assert_eq!(st.kind, AccessKind::Store);
assert_eq!(ld.size, st.size);
assert!(!ld.volatile);
}
}