Files
lang/src/arch/x86_64/reg.rs
T
2026-06-12 22:57:01 -04:00

198 lines
5.0 KiB
Rust

#[derive(Clone, Copy, PartialEq)]
pub struct Reg {
val: u8,
high: bool,
width: Width,
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
#[repr(u8)]
pub enum Width {
B8 = 0,
B16 = 1,
B32 = 2,
B64 = 3,
}
def_regs! {
0b0000 : rax eax ax al,
0b0001 : rcx ecx cx cl !_,
0b0010 : rdx edx dx dl,
0b0011 : rbx ebx bx bl,
0b0100 : rsp esp sp spl norex=ah !_,
0b0101 : rbp ebp bp bpl norex=ch,
0b0110 : rsi esi si sil norex=dh !_,
0b0111 : rdi edi di dil norex=bh,
0b1000 : r8 r8d r8w r8b,
0b1001 : r9 r9d r9w r9b !_,
0b1010 : r10 r10d r10w r10b,
0b1011 : r11 r11d r11w r11b,
0b1100 : r12 r12d r12w r12b !_,
0b1101 : r13 r13d r13w r13b,
0b1110 : r14 r14d r14w r14b,
0b1111 : r15 r15d r15w r15b,
}
impl Reg {
pub fn base(&self) -> u8 {
self.val & 0b111
}
/// checks if register is not one of the first 8 (0-7)
pub fn gt8(&self) -> bool {
self.val >= 0b1000
}
pub fn gt4(&self) -> bool {
self.val >= 0b0100
}
pub fn width(&self) -> Width {
self.width
}
pub fn not8(&self) -> u8 {
self.width.not8()
}
pub fn high(&self) -> bool {
self.high
}
/// if self has 64 bit width, changes width to 32 bit
pub fn lower64(&self) -> Self {
let mut new = *self;
new.width = new.width.min(Width::B32);
new
}
pub fn requires_rex(&self) -> bool {
self.gt8()
|| self.width == Width::B64
|| (self.gt4() && self.width == Width::B8 && !self.high)
}
pub fn incompatible(&self, other: &Reg) -> bool {
(self.requires_rex() && other.high) || (self.high && other.requires_rex())
}
const fn new(val: u8, width: Width, high: bool) -> Self {
Self { val, high, width }
}
}
impl Width {
pub const fn max_val(&self) -> u64 {
match self {
Self::B64 => u64::MAX,
Self::B32 => u32::MAX as u64,
Self::B16 => u16::MAX as u64,
Self::B8 { .. } => u8::MAX as u64,
}
}
pub fn min(self, other: Self) -> Self {
if self <= other { self } else { other }
}
pub const fn bytes(&self) -> usize {
match self {
Self::B64 => 8,
Self::B32 => 4,
Self::B16 => 2,
Self::B8 { .. } => 1,
}
}
/// greater than 8 bits
pub const fn not8(&self) -> u8 {
!matches!(self, Self::B8) as u8
}
}
macro_rules! filter {
($($filtered:ident)*; ! $_:tt $($item:ident)*; $($rest:tt)*) => {
filter!($($filtered)* $($item)*; $($rest)*)
};
($($filtered:ident)*; $($item:ident)*; $($rest:tt)*) => {
filter!($($filtered)*; $($rest)*)
};
($($filtered:ident)*;) => {
[$($filtered, )*]
};
}
use filter;
macro_rules! def_regs {
($($val:literal : $B64:ident $B32:ident $B16:ident $B8:ident $(norex=$B8H:ident)? $(!$imp:tt)?,)*) => {
$(
#[allow(non_upper_case_globals)]
pub const $B64: Reg = Reg::new($val, Width::B64, false);
#[allow(non_upper_case_globals)]
pub const $B32: Reg = Reg::new($val, Width::B32, false);
#[allow(non_upper_case_globals)]
pub const $B16: Reg = Reg::new($val, Width::B16, false);
#[allow(non_upper_case_globals)]
pub const $B8 : Reg = Reg::new($val, Width::B8 , false);
$(
#[allow(non_upper_case_globals)]
pub const $B8H: Reg = Reg::new($val, Width::B8, true);
)?
)*
impl Reg {
// #[cfg(test)]
// pub const ALL: &[Reg] = &[
// $( $B64, $B32, $B16, $B8, $($B8H,)? )*
// ];
#[cfg(test)]
pub const IMPORTANT: &[Reg] = &
filter!(; $($(!$imp)? $B64 $B32 $B16 $B8 $($B8H)?; )* )
;
pub fn parse(s: &str) -> Option<Self> {
Some(match s.to_lowercase().as_str() {
$(
stringify!($B64) => $B64,
stringify!($B32) => $B32,
stringify!($B16) => $B16,
stringify!($B8 ) => $B8,
$(
stringify!($B8H) => $B8H,
)?
)*
_ => return None,
})
}
}
impl std::fmt::Display for Reg {
#[allow(non_upper_case_globals)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", match *self {
$(
$B64 => stringify!($B64),
$B32 => stringify!($B32),
$B16 => stringify!($B16),
$B8 => stringify!($B8),
$(
$B8H => stringify!($B8H),
)?
)*
_ => "UNKNOWN",
})
}
}
};
}
use def_regs;
use crate::arch::x86_64::Imm;
impl From<Reg> for Width {
fn from(value: Reg) -> Self {
value.width
}
}