use core::fmt;
macro_rules! extensions {
($($(#[$doc:meta])* $variant:ident = $name:literal,)*) => {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[non_exhaustive]
pub enum Extension {
$($(#[$doc])* $variant,)*
}
impl Extension {
const ALL: &[Extension] = &[$(Extension::$variant),*];
pub const fn name(self) -> &'static str {
match self {
$(Extension::$variant => $name,)*
}
}
}
};
}
extensions! {
E = "e",
M = "m",
A = "a",
F = "f",
D = "d",
Q = "q",
C = "c",
Zca = "zca",
Zcf = "zcf",
Zcd = "zcd",
Zcmp = "zcmp",
Zcmt = "zcmt",
Zilsd = "zilsd",
Zclsd = "zclsd",
V = "v",
H = "h",
Zicsr = "zicsr",
Zifencei = "zifencei",
Zicond = "zicond",
Zalasr = "zalasr",
Zacas = "zacas",
Zabha = "zabha",
Zawrs = "zawrs",
Zicbom = "zicbom",
Zicboz = "zicboz",
Zicbop = "zicbop",
Zihintntl = "zihintntl",
Zihintpause = "zihintpause",
Zimop = "zimop",
Zcmop = "zcmop",
Zicfiss = "zicfiss",
Zicfilp = "zicfilp",
Zba = "zba",
Zbb = "zbb",
Zbc = "zbc",
Zbs = "zbs",
Zbkb = "zbkb",
Zbkc = "zbkc",
Zbkx = "zbkx",
Zknd = "zknd",
Zkne = "zkne",
Zknh = "zknh",
Zksed = "zksed",
Zksh = "zksh",
Zcb = "zcb",
Zfh = "zfh",
Zfhmin = "zfhmin",
Zfa = "zfa",
Zfbfmin = "zfbfmin",
Zfinx = "zfinx",
Zdinx = "zdinx",
Zhinx = "zhinx",
Zhinxmin = "zhinxmin",
Zvfbfmin = "zvfbfmin",
Zvfbfwma = "zvfbfwma",
Zvbb = "zvbb",
Zvbc = "zvbc",
Zvkg = "zvkg",
Zvkned = "zvkned",
Zvknha = "zvknha",
Zvknhb = "zvknhb",
Zvksed = "zvksed",
Zvksh = "zvksh",
Svinval = "svinval",
Sdext = "sdext",
Smrnmi = "smrnmi",
Ssctr = "ssctr",
}
const _: () = assert!(Extension::ALL.len() <= 128);
impl Extension {
pub fn from_name(name: &str) -> Option<Extension> {
Extension::ALL
.iter()
.copied()
.find(|e| e.name().eq_ignore_ascii_case(name))
}
const fn implies(self) -> &'static [Extension] {
use Extension::*;
match self {
D | Zfhmin | Zfa | Zfbfmin => &[F],
C | Zcb | Zcmop | Zcmp => &[Zca],
Zcf => &[Zca, F],
Zcd => &[Zca, D],
Zcmt => &[Zca, Zicsr],
Zclsd => &[Zilsd, Zca],
Q => &[D],
Zfh => &[Zfhmin],
Zdinx | Zhinxmin => &[Zfinx],
Zhinx => &[Zhinxmin],
Zacas | Zabha => &[A],
Zicfiss => &[Zimop],
Zvbb | Zvbc | Zvkg | Zvkned | Zvknha | Zvksed | Zvksh | Zvfbfmin => &[V],
Zvfbfwma => &[Zvfbfmin],
Zvknhb => &[Zvknha],
_ => &[],
}
}
pub(crate) const fn in_x_registers(self) -> Extension {
use Extension::*;
match self {
F => Zfinx,
D => Zdinx,
Zfh => Zhinx,
Zfhmin => Zhinxmin,
ext => ext,
}
}
const fn bit(self) -> u128 {
1 << self as u32
}
}
impl fmt::Display for Extension {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name())
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Extensions(u128);
impl Extensions {
pub const EMPTY: Extensions = Extensions(0);
pub const DEFAULT: Extensions = Extensions::of(Extension::ALL)
.without(Extension::E)
.without(Extension::Zcmp)
.without(Extension::Zcmt)
.without(Extension::Zclsd)
.without(Extension::Zfinx);
pub const GC: Extensions = {
use Extension::*;
Extensions::of(&[M, A, F, D, C, Zicsr, Zifencei])
};
pub const RVA23U64: Extensions = {
use Extension::*;
Extensions::of(&[
M,
A,
F,
D,
C,
V,
Zicsr,
Zicond,
Zawrs,
Zicbom,
Zicboz,
Zicbop,
Zihintntl,
Zihintpause,
Zimop,
Zcmop,
Zba,
Zbb,
Zbs,
Zcb,
Zfhmin,
Zfa,
Zvbb,
])
};
pub const RVA23S64: Extensions = {
use Extension::*;
Extensions::RVA23U64.union(Extensions::of(&[Zifencei, Svinval, H]))
};
pub const fn of(exts: &[Extension]) -> Extensions {
let mut set = Extensions::EMPTY;
let mut i = 0;
while i < exts.len() {
set = set.with(exts[i]);
i += 1;
}
set
}
pub const fn contains(self, ext: Extension) -> bool {
self.0 & ext.bit() != 0
}
pub const fn with(self, ext: Extension) -> Extensions {
if self.contains(ext) {
return self;
}
let mut set = Extensions(self.0 | ext.bit());
let implied = ext.implies();
let mut i = 0;
while i < implied.len() {
set = set.with(implied[i]);
i += 1;
}
if set.contains(Extension::C) && set.contains(Extension::F) {
set = set.with(Extension::Zcf);
}
if set.contains(Extension::C) && set.contains(Extension::D) {
set = set.with(Extension::Zcd);
}
set
}
pub const fn without(self, ext: Extension) -> Extensions {
let mut set = self.remove(ext);
match ext {
Extension::C => set = set.remove(Extension::Zca),
Extension::Zcf | Extension::Zcd => set.0 &= !Extension::C.bit(),
_ => {}
}
set
}
const fn remove(self, ext: Extension) -> Extensions {
let mut set = Extensions(self.0 & !ext.bit());
let mut i = 0;
while i < Extension::ALL.len() {
let other = Extension::ALL[i];
let implied = other.implies();
let mut j = 0;
while j < implied.len() {
if implied[j] as u32 == ext as u32 && set.contains(other) {
set = set.remove(other);
}
j += 1;
}
i += 1;
}
set
}
pub const fn union(self, other: Extensions) -> Extensions {
let mut set = self;
let mut i = 0;
while i < Extension::ALL.len() {
if other.contains(Extension::ALL[i]) {
set = set.with(Extension::ALL[i]);
}
i += 1;
}
set
}
pub fn iter(self) -> impl Iterator<Item = Extension> {
Extension::ALL
.iter()
.copied()
.filter(move |&e| self.contains(e))
}
}
impl Default for Extensions {
fn default() -> Self {
Extensions::DEFAULT
}
}
impl fmt::Debug for Extensions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_set().entries(self.iter()).finish()
}
}
impl From<Extension> for Extensions {
fn from(ext: Extension) -> Self {
Extensions::EMPTY.with(ext)
}
}
impl<const N: usize> From<[Extension; N]> for Extensions {
fn from(exts: [Extension; N]) -> Self {
Extensions::of(&exts)
}
}
impl From<&[Extension]> for Extensions {
fn from(exts: &[Extension]) -> Self {
Extensions::of(exts)
}
}
impl FromIterator<Extension> for Extensions {
fn from_iter<I: IntoIterator<Item = Extension>>(iter: I) -> Self {
iter.into_iter().fold(Extensions::EMPTY, Extensions::with)
}
}
impl core::ops::BitOr for Extensions {
type Output = Extensions;
fn bitor(self, other: Extensions) -> Extensions {
self.union(other)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn names_round_trip() {
for &ext in Extension::ALL {
assert_eq!(Extension::from_name(ext.name()), Some(ext));
}
assert_eq!(Extension::from_name("ZBA"), Some(Extension::Zba));
assert_eq!(Extension::from_name("xtheadba"), None);
}
#[test]
fn all_lists_every_extension_once() {
let mut bits = 0u128;
for (i, &ext) in Extension::ALL.iter().enumerate() {
assert_eq!(ext as usize, i);
bits |= ext.bit();
}
assert_eq!(bits.count_ones() as usize, Extension::ALL.len());
}
#[test]
fn implications() {
use Extension::*;
let set = Extensions::EMPTY.with(Q);
assert_eq!(set, Extensions::of(&[F, D, Q]));
assert_eq!(set.without(F), Extensions::EMPTY);
assert_eq!(set.without(Q), Extensions::of(&[F, D]));
assert!(Extensions::from(Zvknhb).contains(V));
}
}