Skip to main content

asmkit/core/
arch_traits.rs

1#[cfg(feature = "aarch64")]
2use crate::aarch64::arch_traits::AARCH64_ARCH_TRAITS;
3#[cfg(feature = "riscv")]
4use crate::riscv::arch_traits::{RISCV32_ARCH_TRAITS, RISCV64_ARCH_TRAITS};
5#[cfg(feature = "x86")]
6use crate::x86::arch_traits::{X64_ARCH_TRAITS, X86_ARCH_TRAITS};
7
8#[cfg(not(feature = "aarch64"))]
9const AARCH64_ARCH_TRAITS: ArchTraits = NO_ARCH_TRAITS;
10#[cfg(not(feature = "riscv"))]
11const RISCV32_ARCH_TRAITS: ArchTraits = NO_ARCH_TRAITS;
12#[cfg(not(feature = "riscv"))]
13const RISCV64_ARCH_TRAITS: ArchTraits = NO_ARCH_TRAITS;
14#[cfg(not(feature = "x86"))]
15const X86_ARCH_TRAITS: ArchTraits = NO_ARCH_TRAITS;
16#[cfg(not(feature = "x86"))]
17const X64_ARCH_TRAITS: ArchTraits = NO_ARCH_TRAITS;
18
19use super::operand::{OperandSignature, RegGroup, RegType};
20use super::types::TypeId;
21
22pub struct ArchTraits {
23    pub sp_reg_id: u8,
24    pub fp_reg_id: u8,
25    pub link_reg_id: u8,
26    pub ip_reg_id: u8,
27    pub hw_stack_alignment: u8,
28    pub min_stack_offset: u32,
29    pub max_stack_offset: u32,
30    pub regs_signature: [OperandSignature; RegType::MaxValue as usize + 1],
31    pub reg_type_to_type_id: [TypeId; RegType::MaxValue as usize + 1],
32    pub type_id_to_reg_type: [RegType; 32],
33}
34
35pub const ARCH_X86: usize = if cfg!(any(target_arch = "x86", target_arch = "x86_64")) {
36    usize::BITS as usize
37} else {
38    0
39};
40
41pub const ARCH_RISCV: usize = if cfg!(any(target_arch = "riscv64", target_arch = "riscv32")) {
42    usize::BITS as usize
43} else {
44    0
45};
46
47/// Instruction set architecture (ISA).
48#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
49pub enum Arch {
50    /// Unknown or uninitialized ISA.
51    Unknown = 0,
52
53    /// 32-bit X86 ISA.
54    X86 = 1,
55    /// 64-bit X86 ISA alsonown as X64, X86_64, and AMD64.
56    X64 = 2,
57
58    /// 32-bit RISC-V ISA.
59    RISCV32 = 3,
60    /// 64-bit RISC-V ISA.
61    RISCV64 = 4,
62
63    // 5 is unused so the even/odd width convention remains intact.
64    /// 64-bit AArch64 ISA.
65    AArch64 = 6,
66
67    Max,
68}
69
70impl Arch {
71    pub const HOST: Arch = {
72        if ARCH_X86 == 32 {
73            Arch::X86
74        } else if ARCH_X86 == 64 {
75            Arch::X64
76        } else if ARCH_RISCV == 32 {
77            Arch::RISCV32
78        } else if ARCH_RISCV == 64 {
79            Arch::RISCV64
80        } else if cfg!(all(target_arch = "aarch64", target_endian = "little")) {
81            Arch::AArch64
82        } else {
83            Arch::Unknown
84        }
85    };
86}
87
88impl Arch {
89    /// Tests whether this is a 32-bit architecture.
90    ///
91    /// Relies on the enum layout convention that 32-bit architectures have odd
92    /// values and 64-bit architectures even values.
93    pub const fn is_32bit(self) -> bool {
94        (self as u32) < Arch::Max as u32 && (self as u32) & 1 != 0
95    }
96
97    /// Tests whether this is a 64-bit architecture.
98    pub const fn is_64bit(self) -> bool {
99        (self as u32) < Arch::Max as u32 && self as u32 != 0 && (self as u32) & 1 == 0
100    }
101}
102
103impl Default for Arch {
104    fn default() -> Self {
105        Self::HOST
106    }
107}
108
109const NO_ARCH_TRAITS: ArchTraits = ArchTraits {
110    fp_reg_id: 0xff,
111    sp_reg_id: 0xff,
112    link_reg_id: 0xff,
113    ip_reg_id: 0xff,
114    hw_stack_alignment: 0,
115    min_stack_offset: 0,
116    max_stack_offset: 0,
117    regs_signature: [OperandSignature::new(0); 32],
118    reg_type_to_type_id: [TypeId::Void; 32],
119    type_id_to_reg_type: [RegType::None; 32],
120};
121
122#[rustfmt::skip]
123const ARCH_TRAITS: [ArchTraits; Arch::Max as usize] =[
124    NO_ARCH_TRAITS,
125    X86_ARCH_TRAITS,
126    X64_ARCH_TRAITS,
127    RISCV32_ARCH_TRAITS,
128    RISCV64_ARCH_TRAITS,
129    NO_ARCH_TRAITS,
130    AARCH64_ARCH_TRAITS,
131];
132
133impl ArchTraits {
134    /// Returns stack pointer register id.
135    pub const fn sp_reg_id(&self) -> u32 {
136        self.sp_reg_id as _
137    }
138
139    /// Returns stack frame register id.
140    pub const fn fp_reg_id(&self) -> u32 {
141        self.fp_reg_id as _
142    }
143
144    /// Returns link register id, if the architecture provides it.
145    pub const fn link_reg_id(&self) -> u32 {
146        self.link_reg_id as _
147    }
148
149    /// Returns instruction pointer register id, if the architecture provides it.
150    pub const fn ip_reg_id(&self) -> u32 {
151        self.ip_reg_id as _
152    }
153    /// Returns a hardware stack alignment requirement.
154    ///
155    /// ## Note
156    /// This is a hardware constraint. Architectures that don't constrain it would return the lowest alignment
157    /// (1), however, some architectures may constrain the alignment, for example AArch64 requires 16-byte alignment.
158    pub const fn hw_stack_alignment(&self) -> u32 {
159        self.hw_stack_alignment as _
160    }
161
162    /// Tests whether the architecture provides link register, which is used across function calls. If the link
163    /// register is not provided then a function call pushes the return address on stack (X86/X64).
164    pub const fn has_link_reg(&self) -> bool {
165        self.link_reg_id != 0xff
166    }
167
168    /// Returns minimum addressable offset on stack guaranteed for all instructions.
169    pub const fn min_stack_offset(&self) -> u32 {
170        self.min_stack_offset
171    }
172
173    /// Returns maximum addressable offset on stack depending on specific instruction.
174    pub const fn max_stack_offset(&self) -> u32 {
175        self.max_stack_offset
176    }
177
178    pub const fn has_reg_type(&self, typ: RegType) -> bool {
179        (typ as u32) <= RegType::MaxValue as u32 && self.regs_signature[typ as usize].is_valid()
180    }
181
182    pub const fn reg_type_to_signature(&self, typ: RegType) -> OperandSignature {
183        self.regs_signature[typ as usize]
184    }
185
186    pub fn reg_type_to_group(&self, typ: RegType) -> RegGroup {
187        self.reg_type_to_signature(typ).reg_group()
188    }
189
190    pub fn reg_type_to_size(&self, typ: RegType) -> u32 {
191        self.reg_type_to_signature(typ).size()
192    }
193
194    pub const fn reg_type_to_type_id(&self, typ: RegType) -> TypeId {
195        self.reg_type_to_type_id[typ as usize]
196    }
197
198    pub const fn by_arch(arch: Arch) -> &'static Self {
199        &ARCH_TRAITS[arch as usize]
200    }
201}