1use std::{fmt, str::FromStr};
7
8use crate::arch_table::ArchSpec;
9use crate::cpu_level::X86CpuLevel;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub struct RiscvIsa {
14 pub xlen: u32,
15 pub m: bool,
17 pub a: bool,
19 pub f: bool,
21 pub d: bool,
23 pub c: bool,
25 pub v: bool,
27}
28
29impl RiscvIsa {
30 pub const fn base(xlen: u32) -> Self {
32 Self {
33 xlen,
34 m: false,
35 a: false,
36 f: false,
37 d: false,
38 c: false,
39 v: false,
40 }
41 }
42
43 pub const fn general(xlen: u32) -> Self {
45 Self {
46 xlen,
47 m: true,
48 a: true,
49 f: true,
50 d: true,
51 c: false,
52 v: false,
53 }
54 }
55
56 pub const fn has_float(self) -> bool {
57 self.f || self.d
58 }
59
60 pub fn name(self) -> String {
62 let mut s = format!("rv{}i", self.xlen);
63 for (present, letter) in [
64 (self.m, 'm'),
65 (self.a, 'a'),
66 (self.f, 'f'),
67 (self.d, 'd'),
68 (self.c, 'c'),
69 (self.v, 'v'),
70 ] {
71 if present {
72 s.push(letter);
73 }
74 }
75 s
76 }
77
78 pub fn march(self) -> String {
80 let mut s = format!("rv{}", self.xlen);
81 if self.m && self.a && self.f && self.d {
82 s.push('g');
83 } else {
84 s.push('i');
85 for (present, letter) in [(self.m, 'm'), (self.a, 'a'), (self.f, 'f'), (self.d, 'd')] {
86 if present {
87 s.push(letter);
88 }
89 }
90 }
91 if self.c {
92 s.push('c');
93 }
94 if self.v {
95 s.push('v');
96 }
97 s
98 }
99}
100
101impl FromStr for RiscvIsa {
102 type Err = &'static str;
103
104 fn from_str(s: &str) -> Result<Self, Self::Err> {
106 let rest = s.strip_prefix("rv").ok_or("expected an rv… ISA string")?;
107 let split = rest
108 .find(|c: char| !c.is_ascii_digit())
109 .ok_or("ISA string has no base after the width")?;
110 let (width, letters) = rest.split_at(split);
111 let xlen = match width {
112 "32" => 32,
113 "64" => 64,
114 "128" => 128,
115 _ => return Err("RISC-V width must be 32, 64 or 128"),
116 };
117 let mut isa = Self::base(xlen);
118 let mut seen_base = false;
119 for letter in letters.chars() {
120 match letter {
121 'i' => seen_base = true,
122 'g' => {
123 seen_base = true;
124 isa.m = true;
125 isa.a = true;
126 isa.f = true;
127 isa.d = true;
128 }
129 'm' => isa.m = true,
130 'a' => isa.a = true,
131 'f' => isa.f = true,
132 'd' => {
133 isa.f = true;
134 isa.d = true;
135 }
136 'c' => isa.c = true,
137 'v' => isa.v = true,
138 _ => return Err("unknown RISC-V extension letter"),
139 }
140 }
141 if !seen_base {
142 return Err("RISC-V ISA string needs a base, 'i' or 'g'");
143 }
144 Ok(isa)
145 }
146}
147
148#[derive(Debug, Clone, Copy, PartialEq, Eq)]
151pub enum Armv7Profile {
152 A,
153 R,
154 M,
155}
156
157impl Armv7Profile {
158 pub const fn arch_name(self) -> &'static str {
160 match self {
161 Self::A => "armv7-a",
162 Self::R => "armv7-r",
163 Self::M => "armv7-m",
164 }
165 }
166}
167
168#[derive(Debug, Clone, Copy, PartialEq, Eq)]
170pub struct Armv7Variant {
171 pub profile: Armv7Profile,
172 pub fpu: Option<&'static str>,
176 pub neon: bool,
177 pub hardware_divide: bool,
179}
180
181impl Armv7Variant {
182 pub const fn cortex_m3() -> Self {
185 Self {
186 profile: Armv7Profile::M,
187 fpu: None,
188 neon: false,
189 hardware_divide: true,
190 }
191 }
192
193 pub const fn cortex_a7() -> Self {
195 Self {
196 profile: Armv7Profile::A,
197 fpu: Some("vfpv3-d16"),
198 neon: false,
199 hardware_divide: true,
200 }
201 }
202}
203
204impl Default for Armv7Variant {
205 fn default() -> Self {
206 Self::cortex_a7()
207 }
208}
209
210#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
212pub enum Aarch64Level {
213 #[default]
214 V8_0,
215 V8_1,
216 V8_2,
217}
218
219impl Aarch64Level {
220 pub const fn march(self) -> &'static str {
222 match self {
223 Self::V8_0 => "armv8-a",
224 Self::V8_1 => "armv8.1-a",
225 Self::V8_2 => "armv8.2-a",
226 }
227 }
228
229 pub const fn has_lse_atomics(self) -> bool {
232 match self {
233 Self::V8_0 => false,
234 Self::V8_1 | Self::V8_2 => true,
235 }
236 }
237}
238
239#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
241pub enum PowerLevel {
242 #[default]
243 Power8,
244 Power9,
245 Power10,
246}
247
248impl PowerLevel {
249 pub const fn name(self) -> &'static str {
250 match self {
251 Self::Power8 => "power8",
252 Self::Power9 => "power9",
253 Self::Power10 => "power10",
254 }
255 }
256
257 pub const fn has_modulo_instructions(self) -> bool {
259 match self {
260 Self::Power8 => false,
261 Self::Power9 | Self::Power10 => true,
262 }
263 }
264}
265
266#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
268pub enum ArchVariant {
269 #[default]
271 None,
272 Riscv(RiscvIsa),
273 Armv7(Armv7Variant),
274 Aarch64(Aarch64Level),
275 PowerPC64(PowerLevel),
276 X86(X86CpuLevel),
277}
278
279impl ArchVariant {
280 pub fn riscv(self) -> Option<RiscvIsa> {
281 match self {
282 Self::Riscv(isa) => Some(isa),
283 Self::None | Self::Armv7(_) | Self::Aarch64(_) | Self::PowerPC64(_) | Self::X86(_) => {
284 None
285 }
286 }
287 }
288
289 pub fn armv7(self) -> Option<Armv7Variant> {
290 match self {
291 Self::Armv7(v) => Some(v),
292 Self::None | Self::Riscv(_) | Self::Aarch64(_) | Self::PowerPC64(_) | Self::X86(_) => {
293 None
294 }
295 }
296 }
297
298 pub fn aarch64(self) -> Option<Aarch64Level> {
299 match self {
300 Self::Aarch64(level) => Some(level),
301 Self::None | Self::Riscv(_) | Self::Armv7(_) | Self::PowerPC64(_) | Self::X86(_) => {
302 None
303 }
304 }
305 }
306
307 pub fn x86(self) -> Option<X86CpuLevel> {
308 match self {
309 Self::X86(level) => Some(level),
310 Self::None
311 | Self::Riscv(_)
312 | Self::Armv7(_)
313 | Self::Aarch64(_)
314 | Self::PowerPC64(_) => None,
315 }
316 }
317
318 pub fn refine(self, mut spec: ArchSpec) -> ArchSpec {
324 match self {
325 Self::Riscv(isa) => {
326 if !isa.d {
327 spec.interpreters.gnu = spec.interpreters.gnu_soft_float;
328 }
329 spec
330 }
331 Self::None | Self::Armv7(_) | Self::Aarch64(_) | Self::PowerPC64(_) | Self::X86(_) => {
332 spec
333 }
334 }
335 }
336
337 pub fn powerpc64(self) -> Option<PowerLevel> {
338 match self {
339 Self::PowerPC64(level) => Some(level),
340 Self::None | Self::Riscv(_) | Self::Armv7(_) | Self::Aarch64(_) | Self::X86(_) => None,
341 }
342 }
343}
344
345impl fmt::Display for ArchVariant {
346 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
347 match self {
348 Self::None => write!(f, "default"),
349 Self::Riscv(isa) => write!(f, "{}", isa.name()),
350 Self::Armv7(v) => write!(
351 f,
352 "{}{}",
353 v.profile.arch_name(),
354 if v.neon { "+neon" } else { "" }
355 ),
356 Self::Aarch64(level) => write!(f, "{}", level.march()),
357 Self::PowerPC64(level) => write!(f, "{}", level.name()),
358 Self::X86(level) => write!(f, "{level}"),
359 }
360 }
361}
362
363#[cfg(test)]
364mod tests {
365 use super::*;
366
367 #[test]
368 fn isa_strings_round_trip() {
369 let gc: RiscvIsa = "rv64gc".parse().expect("rv64gc");
370 assert_eq!(gc.name(), "rv64imafdc");
371 assert_eq!(gc.march(), "rv64gc");
372 let imac: RiscvIsa = "rv32imac".parse().expect("rv32imac");
373 assert!(imac.m && imac.a && imac.c);
374 assert!(!imac.has_float());
375 assert_eq!(imac.march(), "rv32imac");
376 assert_eq!(RiscvIsa::general(64).march(), "rv64g");
377 assert!("rv16gc".parse::<RiscvIsa>().is_err());
378 assert!("rv64gz".parse::<RiscvIsa>().is_err());
379 assert!("rv64".parse::<RiscvIsa>().is_err());
380 assert!("rv64mac".parse::<RiscvIsa>().is_err());
381 let gcv: RiscvIsa = "rv64gcv".parse().expect("rv64gcv");
382 assert!(gcv.v);
383 assert_eq!(gcv.name(), "rv64imafdcv");
384 assert_eq!(gcv.march(), "rv64gcv");
385 assert!(!gc.v);
386 }
387
388 #[test]
389 fn bare_riscv_keeps_meaning_imafd() {
390 let g = RiscvIsa::general(64);
391 assert!(g.m && g.a && g.f && g.d && !g.c && !g.v);
392 assert_eq!(g.name(), "rv64imafd");
393 }
394
395 #[test]
396 fn armv7_default_is_what_the_backend_hardcodes() {
397 let v = Armv7Variant::default();
398 assert_eq!(v.profile.arch_name(), "armv7-a");
399 assert_eq!(v.fpu, Some("vfpv3-d16"));
400 assert!(v.hardware_divide && !v.neon);
401 }
402
403 #[test]
404 fn levels_gate_their_instructions() {
405 assert!(!Aarch64Level::default().has_lse_atomics());
406 assert!(Aarch64Level::V8_1.has_lse_atomics());
407 assert_eq!(Aarch64Level::default().march(), "armv8-a");
408 assert!(!PowerLevel::default().has_modulo_instructions());
409 assert!(PowerLevel::Power9.has_modulo_instructions());
410 }
411
412 #[test]
413 fn variant_accessors_pick_their_family() {
414 let v = ArchVariant::Riscv(RiscvIsa::general(32));
415 assert_eq!(v.riscv().map(|i| i.xlen), Some(32));
416 assert!(v.armv7().is_none());
417 assert!(ArchVariant::None.aarch64().is_none());
418 assert_eq!(
419 ArchVariant::PowerPC64(PowerLevel::Power10).to_string(),
420 "power10"
421 );
422 }
423}
424
425#[derive(Debug, Clone, Copy, PartialEq, Eq)]
432pub enum VariantAxis {
433 None,
435 Riscv(RiscvIsa),
436 Armv7(Armv7Variant),
437 Aarch64(Aarch64Level),
438 Power(PowerLevel),
439 X86(X86CpuLevel),
440}
441
442impl VariantAxis {
443 pub const fn default_variant(self) -> ArchVariant {
446 match self {
447 Self::None => ArchVariant::None,
448 Self::Riscv(isa) => ArchVariant::Riscv(isa),
449 Self::Armv7(variant) => ArchVariant::Armv7(variant),
450 Self::Aarch64(level) => ArchVariant::Aarch64(level),
451 Self::Power(level) => ArchVariant::PowerPC64(level),
452 Self::X86(level) => ArchVariant::X86(level),
453 }
454 }
455
456 pub const fn accept(self, chosen: ArchVariant) -> ArchVariant {
461 let belongs = match (self, chosen) {
462 (Self::Riscv(_), ArchVariant::Riscv(_))
463 | (Self::Armv7(_), ArchVariant::Armv7(_))
464 | (Self::Aarch64(_), ArchVariant::Aarch64(_))
465 | (Self::Power(_), ArchVariant::PowerPC64(_))
466 | (Self::X86(_), ArchVariant::X86(_))
467 | (Self::None, ArchVariant::None) => true,
468 (Self::None, _)
469 | (Self::Riscv(_), _)
470 | (Self::Armv7(_), _)
471 | (Self::Aarch64(_), _)
472 | (Self::Power(_), _)
473 | (Self::X86(_), _) => false,
474 };
475 if belongs {
476 chosen
477 } else {
478 self.default_variant()
479 }
480 }
481}