Skip to main content

disc_v/
extension.rs

1// Copyright (c) 2026 Christopher Swenson
2// Licensed under the Apache-2.0 license.
3
4//! Extensions, and sets of them.
5
6use core::fmt;
7
8/// Defines [`Extension`], with each variant's name in an ISA string, and
9/// [`Extension::ALL`], so that every variant is in `ALL`.
10macro_rules! extensions {
11    ($($(#[$doc:meta])* $variant:ident = $name:literal,)*) => {
12        /// A RISC-V extension that defines instructions disc-v can decode.
13        ///
14        /// Extensions that define no instructions, such as Zkt or Sstc, are
15        /// not listed; [`Decoder::from_march`](crate::Decoder::from_march)
16        /// accepts and ignores them.
17        #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
18        #[non_exhaustive]
19        pub enum Extension {
20            $($(#[$doc])* $variant,)*
21        }
22
23        impl Extension {
24            /// Every extension, in declaration order.
25            const ALL: &[Extension] = &[$(Extension::$variant),*];
26
27            /// The extension's name in an ISA string, such as `"zba"` or
28            /// `"m"`.
29            pub const fn name(self) -> &'static str {
30                match self {
31                    $(Extension::$variant => $name,)*
32                }
33            }
34        }
35    };
36}
37
38extensions! {
39    /// The RV32E and RV64E bases: only registers x0-x15 exist, so
40    /// instructions that use x16-x31 are reserved. This restricts the base
41    /// ISA rather than adding instructions, so it is not in
42    /// [`Extensions::DEFAULT`].
43    E = "e",
44    /// Integer multiplication and division.
45    M = "m",
46    /// Atomic instructions.
47    A = "a",
48    /// Single-precision floating point.
49    F = "f",
50    /// Double-precision floating point. Implies F.
51    D = "d",
52    /// Quad-precision floating point. Implies D.
53    Q = "q",
54    /// Compressed instructions: Zca, and Zcf and Zcd when F and D are
55    /// enabled.
56    C = "c",
57    /// Compressed integer instructions.
58    Zca = "zca",
59    /// Compressed single-precision loads and stores (RV32 only). Implies Zca
60    /// and F.
61    Zcf = "zcf",
62    /// Compressed double-precision loads and stores. Implies Zca and D.
63    Zcd = "zcd",
64    /// Compressed push, pop and register moves (`cm.push`, ...). Implies
65    /// Zca, and conflicts with Zcd.
66    Zcmp = "zcmp",
67    /// Compressed table jumps (`cm.jt`, `cm.jalt`). Implies Zca and Zicsr,
68    /// and conflicts with Zcd.
69    Zcmt = "zcmt",
70    /// Load and store register pairs on RV32 (`ld`, `sd`).
71    Zilsd = "zilsd",
72    /// Compressed load and store register pairs on RV32 (`c.ld`, `c.sd`,
73    /// ...). Implies Zilsd and Zca, and conflicts with Zcf.
74    Zclsd = "zclsd",
75    /// Vectors.
76    V = "v",
77    /// The hypervisor extension.
78    H = "h",
79    /// Control and status register instructions.
80    Zicsr = "zicsr",
81    /// `fence.i`.
82    Zifencei = "zifencei",
83    /// Integer conditional operations (`czero.*`).
84    Zicond = "zicond",
85    /// Load-acquire and store-release (`lw.aq`, `sw.rl`, ...).
86    Zalasr = "zalasr",
87    /// Atomic compare-and-swap (`amocas.*`). Implies A.
88    Zacas = "zacas",
89    /// Byte and halfword atomics (`amoadd.b`, ...). Implies A.
90    Zabha = "zabha",
91    /// Wait-on-reservation-set (`wrs.*`).
92    Zawrs = "zawrs",
93    /// Cache-block management (`cbo.clean`, `cbo.flush`, `cbo.inval`).
94    Zicbom = "zicbom",
95    /// Cache-block zero (`cbo.zero`).
96    Zicboz = "zicboz",
97    /// Cache-block prefetch hints (`prefetch.*`).
98    Zicbop = "zicbop",
99    /// Non-temporal locality hints (`ntl.*`).
100    Zihintntl = "zihintntl",
101    /// The `pause` hint.
102    Zihintpause = "zihintpause",
103    /// May-be-operations (`mop.r.N`, `mop.rr.N`).
104    Zimop = "zimop",
105    /// Compressed may-be-operations (`c.mop.N`). Implies C.
106    Zcmop = "zcmop",
107    /// Shadow stacks (`sspush`, `sspopchk`, `ssrdp`, `ssamoswap.*`).
108    /// Implies Zimop.
109    Zicfiss = "zicfiss",
110    /// Landing pads (`lpad`).
111    Zicfilp = "zicfilp",
112    /// Address generation (`sh1add`, ...).
113    Zba = "zba",
114    /// Basic bit manipulation (`andn`, `clz`, ...).
115    Zbb = "zbb",
116    /// Carry-less multiplication (`clmul`, ...).
117    Zbc = "zbc",
118    /// Single-bit instructions (`bset`, ...).
119    Zbs = "zbs",
120    /// Bit manipulation for cryptography (`pack`, `brev8`, ...). It also
121    /// provides several Zbb instructions, such as `rol` and `rev8`.
122    Zbkb = "zbkb",
123    /// Carry-less multiplication for cryptography: Zbc's `clmul` and
124    /// `clmulh`.
125    Zbkc = "zbkc",
126    /// Crossbar permutations (`xperm4`, `xperm8`).
127    Zbkx = "zbkx",
128    /// AES decryption.
129    Zknd = "zknd",
130    /// AES encryption.
131    Zkne = "zkne",
132    /// SHA-256 and SHA-512.
133    Zknh = "zknh",
134    /// SM4.
135    Zksed = "zksed",
136    /// SM3.
137    Zksh = "zksh",
138    /// Simple compressed instructions (`c.lbu`, `c.mul`, ...). Implies C.
139    Zcb = "zcb",
140    /// Half-precision floating point. Implies Zfhmin.
141    Zfh = "zfh",
142    /// Minimal half-precision floating point: loads, stores and
143    /// conversions. Implies F.
144    Zfhmin = "zfhmin",
145    /// Additional floating-point instructions (`fli`, `fround`, ...).
146    /// Implies F.
147    Zfa = "zfa",
148    /// Conversions between BFloat16 and single precision. Implies F.
149    Zfbfmin = "zfbfmin",
150    /// Single-precision floating point in the integer registers: F's
151    /// instructions except its loads, stores and moves, with integer
152    /// register operands. Conflicts with F (and so with the extensions that
153    /// imply it, such as D, Zfa and C's Zcf).
154    Zfinx = "zfinx",
155    /// Double-precision floating point in the integer registers (register
156    /// pairs on RV32). Implies Zfinx.
157    Zdinx = "zdinx",
158    /// Half-precision floating point in the integer registers. Implies
159    /// Zhinxmin.
160    Zhinx = "zhinx",
161    /// Minimal half-precision floating point in the integer registers: the
162    /// conversions. Implies Zfinx.
163    Zhinxmin = "zhinxmin",
164    /// Vector BFloat16 conversions. Implies V.
165    Zvfbfmin = "zvfbfmin",
166    /// Vector BFloat16 widening multiply-add. Implies Zvfbfmin.
167    Zvfbfwma = "zvfbfwma",
168    /// Vector bit manipulation. Implies V.
169    Zvbb = "zvbb",
170    /// Vector carry-less multiplication. Implies V.
171    Zvbc = "zvbc",
172    /// Vector GCM/GMAC. Implies V.
173    Zvkg = "zvkg",
174    /// Vector AES. Implies V.
175    Zvkned = "zvkned",
176    /// Vector SHA-256. Implies V.
177    Zvknha = "zvknha",
178    /// Vector SHA-256 and SHA-512. Implies Zvknha, whose instructions it
179    /// shares.
180    Zvknhb = "zvknhb",
181    /// Vector SM4. Implies V.
182    Zvksed = "zvksed",
183    /// Vector SM3. Implies V.
184    Zvksh = "zvksh",
185    /// Fine-grained address-translation cache invalidation (`sinval.vma`,
186    /// ...).
187    Svinval = "svinval",
188    /// Debug mode (`dret`).
189    Sdext = "sdext",
190    /// Resumable non-maskable interrupts (`mnret`).
191    Smrnmi = "smrnmi",
192    /// Control-transfer records (`sctrclr`).
193    Ssctr = "ssctr",
194}
195
196// Extensions are a bit set in a u128.
197const _: () = assert!(Extension::ALL.len() <= 128);
198
199impl Extension {
200    /// The extension named `name` in an ISA string, ignoring case.
201    pub fn from_name(name: &str) -> Option<Extension> {
202        Extension::ALL
203            .iter()
204            .copied()
205            .find(|e| e.name().eq_ignore_ascii_case(name))
206    }
207
208    /// The extensions this one implies.
209    const fn implies(self) -> &'static [Extension] {
210        use Extension::*;
211        match self {
212            D | Zfhmin | Zfa | Zfbfmin => &[F],
213            C | Zcb | Zcmop | Zcmp => &[Zca],
214            Zcf => &[Zca, F],
215            Zcd => &[Zca, D],
216            Zcmt => &[Zca, Zicsr],
217            Zclsd => &[Zilsd, Zca],
218            Q => &[D],
219            Zfh => &[Zfhmin],
220            Zdinx | Zhinxmin => &[Zfinx],
221            Zhinx => &[Zhinxmin],
222            Zacas | Zabha => &[A],
223            Zicfiss => &[Zimop],
224            Zvbb | Zvbc | Zvkg | Zvkned | Zvknha | Zvksed | Zvksh | Zvfbfmin => &[V],
225            Zvfbfwma => &[Zvfbfmin],
226            Zvknhb => &[Zvknha],
227            _ => &[],
228        }
229    }
230
231    /// The extension that provides this one's instructions with their
232    /// floating-point operands in the integer registers, such as Zfinx for
233    /// F, or the extension itself if there is none.
234    pub(crate) const fn in_x_registers(self) -> Extension {
235        use Extension::*;
236        match self {
237            F => Zfinx,
238            D => Zdinx,
239            Zfh => Zhinx,
240            Zfhmin => Zhinxmin,
241            ext => ext,
242        }
243    }
244
245    const fn bit(self) -> u128 {
246        1 << self as u32
247    }
248}
249
250impl fmt::Display for Extension {
251    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252        f.write_str(self.name())
253    }
254}
255
256/// A set of [`Extension`]s.
257///
258/// Adding an extension also adds the extensions it implies (D implies F),
259/// and removing one also removes the extensions that imply it (removing F
260/// removes D).
261///
262/// ```
263/// use disc_v::{Extension, Extensions};
264///
265/// let exts = Extensions::GC.with(Extension::Zba).without(Extension::D);
266/// assert!(exts.contains(Extension::F));
267/// assert!(!exts.contains(Extension::Q));
268/// ```
269#[derive(Clone, Copy, PartialEq, Eq, Hash)]
270pub struct Extensions(u128);
271
272impl Extensions {
273    /// No extensions: only the base integer ISA.
274    pub const EMPTY: Extensions = Extensions(0);
275
276    /// Every supported extension that does not conflict with another. This
277    /// is what [`decode`](fn@crate::decode) and the other free functions use.
278    pub const DEFAULT: Extensions = Extensions::of(Extension::ALL)
279        .without(Extension::E)
280        .without(Extension::Zcmp)
281        .without(Extension::Zcmt)
282        .without(Extension::Zclsd)
283        .without(Extension::Zfinx);
284
285    /// G and C: IMAFD, Zicsr, Zifencei and C, as in RV32GC and RV64GC.
286    pub const GC: Extensions = {
287        use Extension::*;
288        Extensions::of(&[M, A, F, D, C, Zicsr, Zifencei])
289    };
290
291    /// The extensions of the RVA23U64 profile that define instructions.
292    pub const RVA23U64: Extensions = {
293        use Extension::*;
294        Extensions::of(&[
295            M,
296            A,
297            F,
298            D,
299            C,
300            V,
301            Zicsr,
302            Zicond,
303            Zawrs,
304            Zicbom,
305            Zicboz,
306            Zicbop,
307            Zihintntl,
308            Zihintpause,
309            Zimop,
310            Zcmop,
311            Zba,
312            Zbb,
313            Zbs,
314            Zcb,
315            Zfhmin,
316            Zfa,
317            Zvbb,
318        ])
319    };
320
321    /// The extensions of the RVA23S64 profile that define instructions:
322    /// RVA23U64, Zifencei, Svinval and H.
323    pub const RVA23S64: Extensions = {
324        use Extension::*;
325        Extensions::RVA23U64.union(Extensions::of(&[Zifencei, Svinval, H]))
326    };
327
328    /// The extensions in `exts`, and the extensions they imply.
329    pub const fn of(exts: &[Extension]) -> Extensions {
330        let mut set = Extensions::EMPTY;
331        let mut i = 0;
332        while i < exts.len() {
333            set = set.with(exts[i]);
334            i += 1;
335        }
336        set
337    }
338
339    /// Whether the set contains `ext`.
340    pub const fn contains(self, ext: Extension) -> bool {
341        self.0 & ext.bit() != 0
342    }
343
344    /// The set with `ext` and the extensions it implies added.
345    pub const fn with(self, ext: Extension) -> Extensions {
346        if self.contains(ext) {
347            return self;
348        }
349        let mut set = Extensions(self.0 | ext.bit());
350        let implied = ext.implies();
351        let mut i = 0;
352        while i < implied.len() {
353            set = set.with(implied[i]);
354            i += 1;
355        }
356        // C includes Zcf and Zcd when F and D are enabled.
357        if set.contains(Extension::C) && set.contains(Extension::F) {
358            set = set.with(Extension::Zcf);
359        }
360        if set.contains(Extension::C) && set.contains(Extension::D) {
361            set = set.with(Extension::Zcd);
362        }
363        set
364    }
365
366    /// The set with `ext` and the extensions that imply it removed.
367    ///
368    /// C is a bundle of Zca and (with F and D) Zcf and Zcd: removing C
369    /// removes every compressed instruction, and removing Zcf or Zcd removes
370    /// C but keeps Zca.
371    pub const fn without(self, ext: Extension) -> Extensions {
372        let mut set = self.remove(ext);
373        match ext {
374            Extension::C => set = set.remove(Extension::Zca),
375            Extension::Zcf | Extension::Zcd => set.0 &= !Extension::C.bit(),
376            _ => {}
377        }
378        set
379    }
380
381    /// The set with `ext` and the extensions that imply it removed.
382    const fn remove(self, ext: Extension) -> Extensions {
383        let mut set = Extensions(self.0 & !ext.bit());
384        let mut i = 0;
385        while i < Extension::ALL.len() {
386            let other = Extension::ALL[i];
387            let implied = other.implies();
388            let mut j = 0;
389            while j < implied.len() {
390                if implied[j] as u32 == ext as u32 && set.contains(other) {
391                    set = set.remove(other);
392                }
393                j += 1;
394            }
395            i += 1;
396        }
397        set
398    }
399
400    /// The extensions in either set (and the extensions they imply
401    /// together, such as Zcd for C and D).
402    pub const fn union(self, other: Extensions) -> Extensions {
403        let mut set = self;
404        let mut i = 0;
405        while i < Extension::ALL.len() {
406            if other.contains(Extension::ALL[i]) {
407                set = set.with(Extension::ALL[i]);
408            }
409            i += 1;
410        }
411        set
412    }
413
414    /// The extensions in the set, in declaration order.
415    pub fn iter(self) -> impl Iterator<Item = Extension> {
416        Extension::ALL
417            .iter()
418            .copied()
419            .filter(move |&e| self.contains(e))
420    }
421}
422
423impl Default for Extensions {
424    /// [`Extensions::DEFAULT`].
425    fn default() -> Self {
426        Extensions::DEFAULT
427    }
428}
429
430impl fmt::Debug for Extensions {
431    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
432        f.debug_set().entries(self.iter()).finish()
433    }
434}
435
436impl From<Extension> for Extensions {
437    fn from(ext: Extension) -> Self {
438        Extensions::EMPTY.with(ext)
439    }
440}
441
442impl<const N: usize> From<[Extension; N]> for Extensions {
443    fn from(exts: [Extension; N]) -> Self {
444        Extensions::of(&exts)
445    }
446}
447
448impl From<&[Extension]> for Extensions {
449    fn from(exts: &[Extension]) -> Self {
450        Extensions::of(exts)
451    }
452}
453
454impl FromIterator<Extension> for Extensions {
455    fn from_iter<I: IntoIterator<Item = Extension>>(iter: I) -> Self {
456        iter.into_iter().fold(Extensions::EMPTY, Extensions::with)
457    }
458}
459
460impl core::ops::BitOr for Extensions {
461    type Output = Extensions;
462
463    fn bitor(self, other: Extensions) -> Extensions {
464        self.union(other)
465    }
466}
467
468#[cfg(test)]
469mod tests {
470    use super::*;
471
472    #[test]
473    fn names_round_trip() {
474        for &ext in Extension::ALL {
475            assert_eq!(Extension::from_name(ext.name()), Some(ext));
476        }
477        assert_eq!(Extension::from_name("ZBA"), Some(Extension::Zba));
478        assert_eq!(Extension::from_name("xtheadba"), None);
479    }
480
481    #[test]
482    fn all_lists_every_extension_once() {
483        let mut bits = 0u128;
484        for (i, &ext) in Extension::ALL.iter().enumerate() {
485            assert_eq!(ext as usize, i);
486            bits |= ext.bit();
487        }
488        assert_eq!(bits.count_ones() as usize, Extension::ALL.len());
489    }
490
491    #[test]
492    fn implications() {
493        use Extension::*;
494        let set = Extensions::EMPTY.with(Q);
495        assert_eq!(set, Extensions::of(&[F, D, Q]));
496        assert_eq!(set.without(F), Extensions::EMPTY);
497        assert_eq!(set.without(Q), Extensions::of(&[F, D]));
498        assert!(Extensions::from(Zvknhb).contains(V));
499    }
500}