Skip to main content

padlock_core/
arch.rs

1#[derive(Clone, Copy, Debug, PartialEq)]
2pub enum Endianness {
3    Little,
4    Big,
5}
6
7#[derive(Clone, Copy, Debug, PartialEq)]
8pub struct ArchConfig {
9    pub name: &'static str,
10    pub pointer_size: usize,
11    pub cache_line_size: usize,
12    pub max_align: usize,
13    pub endianness: Endianness,
14}
15
16pub const X86_64_SYSV: ArchConfig = ArchConfig {
17    name: "x86_64",
18    pointer_size: 8,
19    cache_line_size: 64,
20    max_align: 16,
21    endianness: Endianness::Little,
22};
23
24pub const AARCH64: ArchConfig = ArchConfig {
25    name: "aarch64",
26    pointer_size: 8,
27    cache_line_size: 64,
28    max_align: 16,
29    endianness: Endianness::Little,
30};
31
32pub const AARCH64_APPLE: ArchConfig = ArchConfig {
33    name: "aarch64-apple",
34    pointer_size: 8,
35    cache_line_size: 128,
36    max_align: 16,
37    endianness: Endianness::Little,
38};
39
40pub const WASM32: ArchConfig = ArchConfig {
41    name: "wasm32",
42    pointer_size: 4,
43    cache_line_size: 64,
44    max_align: 8,
45    endianness: Endianness::Little,
46};
47
48pub const RISCV64: ArchConfig = ArchConfig {
49    name: "riscv64",
50    pointer_size: 8,
51    cache_line_size: 64,
52    max_align: 16,
53    endianness: Endianness::Little,
54};
55
56/// ARM Cortex-M0 / M0+ / M3 — no cache.
57///
58/// cache_line_size = 0 means "no cache": false-sharing analysis is suppressed
59/// entirely for this architecture (division by zero is guarded in the analysis
60/// passes). Padding waste and reorder findings still apply.
61pub const CORTEX_M: ArchConfig = ArchConfig {
62    name: "cortex_m",
63    pointer_size: 4,
64    cache_line_size: 0,
65    max_align: 8,
66    endianness: Endianness::Little,
67};
68
69/// ARM Cortex-M4 / M4F / M7 / M33 — 32-byte cache lines.
70///
71/// The M4 and M7 have optional L1 data caches (32-byte lines). M33 is similar.
72/// False-sharing analysis is active for these targets.
73pub const CORTEX_M4: ArchConfig = ArchConfig {
74    name: "cortex_m4",
75    pointer_size: 4,
76    cache_line_size: 32,
77    max_align: 8,
78    endianness: Endianness::Little,
79};
80
81/// AVR 8-bit (ATmega328P, ATmega2560, etc.) — no cache, 2-byte pointers.
82///
83/// Every type is 1-byte aligned on AVR so padding waste findings will be rare.
84/// False-sharing analysis is suppressed (cache_line_size = 0).
85pub const AVR: ArchConfig = ArchConfig {
86    name: "avr",
87    pointer_size: 2,
88    cache_line_size: 0,
89    max_align: 1,
90    endianness: Endianness::Little,
91};
92
93/// Create a custom `ArchConfig` by overriding specific fields of a base arch.
94///
95/// Useful for `--cache-line-size` and `--word-size` CLI overrides.
96/// The returned reference is intentionally leaked — CLI binaries are short-lived.
97pub fn with_overrides(
98    base: &ArchConfig,
99    cache_line_size: Option<usize>,
100    word_size: Option<usize>,
101) -> &'static ArchConfig {
102    let ptr = word_size.unwrap_or(base.pointer_size);
103    let max_align = if word_size.is_some() {
104        // 32-bit targets typically cap natural alignment at 8; 64-bit at 16
105        if ptr <= 4 { 8 } else { base.max_align }
106    } else {
107        base.max_align
108    };
109    Box::leak(Box::new(ArchConfig {
110        name: "custom",
111        pointer_size: ptr,
112        cache_line_size: cache_line_size.unwrap_or(base.cache_line_size),
113        max_align,
114        endianness: base.endianness,
115    }))
116}
117
118/// Resolve an architecture name string or Rust target triple to a static `ArchConfig`.
119///
120/// Short names: `x86_64`, `aarch64`, `aarch64_apple`, `wasm32`, `riscv64`.
121///
122/// Common Rust target triples are also accepted, for example:
123/// - `x86_64-unknown-linux-gnu`, `x86_64-pc-windows-msvc`
124/// - `aarch64-unknown-linux-gnu`, `aarch64-linux-android`
125/// - `aarch64-apple-darwin`, `aarch64-apple-ios`
126/// - `wasm32-unknown-unknown`, `wasm32-wasi`
127/// - `riscv64gc-unknown-linux-gnu`
128pub fn arch_by_name(name: &str) -> Option<&'static ArchConfig> {
129    match name {
130        // Short names (used in config files and existing code).
131        "x86_64" => Some(&X86_64_SYSV),
132        "aarch64" => Some(&AARCH64),
133        "aarch64_apple" => Some(&AARCH64_APPLE),
134        "wasm32" => Some(&WASM32),
135        "riscv64" => Some(&RISCV64),
136        "cortex_m" => Some(&CORTEX_M),
137        "cortex_m4" => Some(&CORTEX_M4),
138        "avr" => Some(&AVR),
139        // Rust target triples — matched by prefix for flexibility.
140        _ => arch_by_triple(name),
141    }
142}
143
144/// Map a Rust target triple to an `ArchConfig`.
145pub fn arch_by_triple(triple: &str) -> Option<&'static ArchConfig> {
146    if triple.starts_with("x86_64-") {
147        Some(&X86_64_SYSV)
148    } else if triple.starts_with("aarch64-apple-") {
149        // Apple Silicon has a 128-byte cache line.
150        Some(&AARCH64_APPLE)
151    } else if triple.starts_with("aarch64-") {
152        Some(&AARCH64)
153    } else if triple.starts_with("wasm32-") {
154        Some(&WASM32)
155    } else if triple.starts_with("riscv64") {
156        Some(&RISCV64)
157    // ARM Cortex-M — no cache variants (M0, M0+, M3, M23)
158    } else if triple.starts_with("thumbv6m-")
159        || triple.starts_with("thumbv7m-")
160        || triple.starts_with("thumbv8m.base-")
161    {
162        Some(&CORTEX_M)
163    // ARM Cortex-M — cached variants (M4, M7, M33)
164    } else if triple.starts_with("thumbv7em-") || triple.starts_with("thumbv8m.main-") {
165        Some(&CORTEX_M4)
166    // AVR
167    } else if triple.starts_with("avr-") {
168        Some(&AVR)
169    } else {
170        None
171    }
172}
173
174// ── tests ─────────────────────────────────────────────────────────────────────
175
176#[cfg(test)]
177mod tests {
178    use super::*;
179
180    #[test]
181    fn short_names_resolve() {
182        assert_eq!(arch_by_name("x86_64"), Some(&X86_64_SYSV));
183        assert_eq!(arch_by_name("aarch64"), Some(&AARCH64));
184        assert_eq!(arch_by_name("aarch64_apple"), Some(&AARCH64_APPLE));
185        assert_eq!(arch_by_name("wasm32"), Some(&WASM32));
186        assert_eq!(arch_by_name("riscv64"), Some(&RISCV64));
187    }
188
189    #[test]
190    fn target_triples_resolve() {
191        assert_eq!(arch_by_name("x86_64-unknown-linux-gnu"), Some(&X86_64_SYSV));
192        assert_eq!(arch_by_name("x86_64-pc-windows-msvc"), Some(&X86_64_SYSV));
193        assert_eq!(arch_by_name("aarch64-unknown-linux-gnu"), Some(&AARCH64));
194        assert_eq!(arch_by_name("aarch64-linux-android"), Some(&AARCH64));
195        // Apple targets get the 128-byte cache line config.
196        assert_eq!(arch_by_name("aarch64-apple-darwin"), Some(&AARCH64_APPLE));
197        assert_eq!(arch_by_name("aarch64-apple-ios"), Some(&AARCH64_APPLE));
198        assert_eq!(arch_by_name("wasm32-unknown-unknown"), Some(&WASM32));
199        assert_eq!(arch_by_name("riscv64gc-unknown-linux-gnu"), Some(&RISCV64));
200    }
201
202    #[test]
203    fn unknown_triple_returns_none() {
204        assert_eq!(arch_by_name("mips-unknown-linux-gnu"), None);
205        assert_eq!(arch_by_name("totally-bogus"), None);
206    }
207
208    #[test]
209    fn apple_aarch64_has_128_byte_cache_line() {
210        let cfg = arch_by_name("aarch64-apple-darwin").unwrap();
211        assert_eq!(cfg.cache_line_size, 128);
212    }
213
214    #[test]
215    fn cortex_m_short_names() {
216        assert_eq!(arch_by_name("cortex_m"), Some(&CORTEX_M));
217        assert_eq!(arch_by_name("cortex_m4"), Some(&CORTEX_M4));
218        assert_eq!(arch_by_name("avr"), Some(&AVR));
219    }
220
221    #[test]
222    fn cortex_m_no_cache_variants() {
223        // M0/M0+ → no cache
224        assert_eq!(arch_by_name("thumbv6m-none-eabi"), Some(&CORTEX_M));
225        assert_eq!(
226            arch_by_name("thumbv6m-none-eabi").unwrap().cache_line_size,
227            0
228        );
229        // M3 → no cache
230        assert_eq!(arch_by_name("thumbv7m-none-eabi"), Some(&CORTEX_M));
231        // M23 → no cache
232        assert_eq!(arch_by_name("thumbv8m.base-none-eabi"), Some(&CORTEX_M));
233    }
234
235    #[test]
236    fn cortex_m4_cached_variants() {
237        // M4/M4F → 32-byte cache lines
238        assert_eq!(arch_by_name("thumbv7em-none-eabi"), Some(&CORTEX_M4));
239        assert_eq!(arch_by_name("thumbv7em-none-eabihf"), Some(&CORTEX_M4));
240        assert_eq!(
241            arch_by_name("thumbv7em-none-eabi").unwrap().cache_line_size,
242            32
243        );
244        // M33 → 32-byte cache lines
245        assert_eq!(arch_by_name("thumbv8m.main-none-eabi"), Some(&CORTEX_M4));
246        assert_eq!(arch_by_name("thumbv8m.main-none-eabihf"), Some(&CORTEX_M4));
247    }
248
249    #[test]
250    fn avr_triple() {
251        assert_eq!(arch_by_name("avr-unknown-gnu-atmega328p"), Some(&AVR));
252        assert_eq!(arch_by_name("avr").unwrap().pointer_size, 2);
253        assert_eq!(arch_by_name("avr").unwrap().cache_line_size, 0);
254    }
255
256    #[test]
257    fn cortex_m_has_4_byte_pointers() {
258        assert_eq!(CORTEX_M.pointer_size, 4);
259        assert_eq!(CORTEX_M4.pointer_size, 4);
260    }
261}