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        ///
18        /// The instructions of the vector and vendor extensions are only
19        /// decoded with the `vector`, `vendor-thead` and `vendor-corev`
20        /// Cargo features, which are on by default.
21        #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
22        #[non_exhaustive]
23        pub enum Extension {
24            $($(#[$doc])* $variant,)*
25        }
26
27        impl Extension {
28            /// Every extension, in declaration order.
29            const ALL: &[Extension] = &[$(Extension::$variant),*];
30
31            /// The extension's name in an ISA string, such as `"zba"` or
32            /// `"m"`.
33            pub const fn name(self) -> &'static str {
34                match self {
35                    $(Extension::$variant => $name,)*
36                }
37            }
38        }
39    };
40}
41
42extensions! {
43    /// The RV32E and RV64E bases: only registers x0-x15 exist, so
44    /// instructions that use x16-x31 are reserved. This restricts the base
45    /// ISA rather than adding instructions, so it is not in
46    /// [`Extensions::DEFAULT`].
47    E = "e",
48    /// Integer multiplication and division.
49    M = "m",
50    /// Atomic instructions.
51    A = "a",
52    /// Single-precision floating point.
53    F = "f",
54    /// Double-precision floating point. Implies F.
55    D = "d",
56    /// Quad-precision floating point. Implies D.
57    Q = "q",
58    /// Compressed instructions: Zca, and Zcf and Zcd when F and D are
59    /// enabled.
60    C = "c",
61    /// Compressed integer instructions.
62    Zca = "zca",
63    /// Compressed single-precision loads and stores (RV32 only). Implies Zca
64    /// and F.
65    Zcf = "zcf",
66    /// Compressed double-precision loads and stores. Implies Zca and D.
67    Zcd = "zcd",
68    /// Compressed push, pop and register moves (`cm.push`, ...). Implies
69    /// Zca, and conflicts with Zcd.
70    Zcmp = "zcmp",
71    /// Compressed table jumps (`cm.jt`, `cm.jalt`). Implies Zca and Zicsr,
72    /// and conflicts with Zcd.
73    Zcmt = "zcmt",
74    /// Load and store register pairs on RV32 (`ld`, `sd`).
75    Zilsd = "zilsd",
76    /// Compressed load and store register pairs on RV32 (`c.ld`, `c.sd`,
77    /// ...). Implies Zilsd and Zca, and conflicts with Zcf.
78    Zclsd = "zclsd",
79    /// Vectors.
80    V = "v",
81    /// The hypervisor extension.
82    H = "h",
83    /// Control and status register instructions.
84    Zicsr = "zicsr",
85    /// `fence.i`.
86    Zifencei = "zifencei",
87    /// Integer conditional operations (`czero.*`).
88    Zicond = "zicond",
89    /// Load-acquire and store-release (`lw.aq`, `sw.rl`, ...).
90    Zalasr = "zalasr",
91    /// Atomic compare-and-swap (`amocas.*`). Implies A.
92    Zacas = "zacas",
93    /// Byte and halfword atomics (`amoadd.b`, ...). Implies A.
94    Zabha = "zabha",
95    /// Wait-on-reservation-set (`wrs.*`).
96    Zawrs = "zawrs",
97    /// Cache-block management (`cbo.clean`, `cbo.flush`, `cbo.inval`).
98    Zicbom = "zicbom",
99    /// Cache-block zero (`cbo.zero`).
100    Zicboz = "zicboz",
101    /// Cache-block prefetch hints (`prefetch.*`).
102    Zicbop = "zicbop",
103    /// Non-temporal locality hints (`ntl.*`).
104    Zihintntl = "zihintntl",
105    /// The `pause` hint.
106    Zihintpause = "zihintpause",
107    /// May-be-operations (`mop.r.N`, `mop.rr.N`).
108    Zimop = "zimop",
109    /// Compressed may-be-operations (`c.mop.N`). Implies C.
110    Zcmop = "zcmop",
111    /// Shadow stacks (`sspush`, `sspopchk`, `ssrdp`, `ssamoswap.*`).
112    /// Implies Zimop.
113    Zicfiss = "zicfiss",
114    /// Landing pads (`lpad`).
115    Zicfilp = "zicfilp",
116    /// Address generation (`sh1add`, ...).
117    Zba = "zba",
118    /// Basic bit manipulation (`andn`, `clz`, ...).
119    Zbb = "zbb",
120    /// Carry-less multiplication (`clmul`, ...).
121    Zbc = "zbc",
122    /// Single-bit instructions (`bset`, ...).
123    Zbs = "zbs",
124    /// Bit manipulation for cryptography (`pack`, `brev8`, ...). It also
125    /// provides several Zbb instructions, such as `rol` and `rev8`.
126    Zbkb = "zbkb",
127    /// Carry-less multiplication for cryptography: Zbc's `clmul` and
128    /// `clmulh`.
129    Zbkc = "zbkc",
130    /// Crossbar permutations (`xperm4`, `xperm8`).
131    Zbkx = "zbkx",
132    /// AES decryption.
133    Zknd = "zknd",
134    /// AES encryption.
135    Zkne = "zkne",
136    /// SHA-256 and SHA-512.
137    Zknh = "zknh",
138    /// SM4.
139    Zksed = "zksed",
140    /// SM3.
141    Zksh = "zksh",
142    /// Simple compressed instructions (`c.lbu`, `c.mul`, ...). Implies C.
143    Zcb = "zcb",
144    /// Half-precision floating point. Implies Zfhmin.
145    Zfh = "zfh",
146    /// Minimal half-precision floating point: loads, stores and
147    /// conversions. Implies F.
148    Zfhmin = "zfhmin",
149    /// Additional floating-point instructions (`fli`, `fround`, ...).
150    /// Implies F.
151    Zfa = "zfa",
152    /// Conversions between BFloat16 and single precision. Implies F.
153    Zfbfmin = "zfbfmin",
154    /// Single-precision floating point in the integer registers: F's
155    /// instructions except its loads, stores and moves, with integer
156    /// register operands. Conflicts with F (and so with the extensions that
157    /// imply it, such as D, Zfa and C's Zcf).
158    Zfinx = "zfinx",
159    /// Double-precision floating point in the integer registers (register
160    /// pairs on RV32). Implies Zfinx.
161    Zdinx = "zdinx",
162    /// Half-precision floating point in the integer registers. Implies
163    /// Zhinxmin.
164    Zhinx = "zhinx",
165    /// Minimal half-precision floating point in the integer registers: the
166    /// conversions. Implies Zfinx.
167    Zhinxmin = "zhinxmin",
168    /// Vector BFloat16 conversions. Implies V.
169    Zvfbfmin = "zvfbfmin",
170    /// Vector BFloat16 widening multiply-add. Implies Zvfbfmin.
171    Zvfbfwma = "zvfbfwma",
172    /// Vector bit manipulation. Implies V.
173    Zvbb = "zvbb",
174    /// Vector carry-less multiplication. Implies V.
175    Zvbc = "zvbc",
176    /// Vector GCM/GMAC. Implies V.
177    Zvkg = "zvkg",
178    /// Vector AES. Implies V.
179    Zvkned = "zvkned",
180    /// Vector SHA-256. Implies V.
181    Zvknha = "zvknha",
182    /// Vector SHA-256 and SHA-512. Implies Zvknha, whose instructions it
183    /// shares.
184    Zvknhb = "zvknhb",
185    /// Vector SM4. Implies V.
186    Zvksed = "zvksed",
187    /// Vector SM3. Implies V.
188    Zvksh = "zvksh",
189    /// Fine-grained address-translation cache invalidation (`sinval.vma`,
190    /// ...).
191    Svinval = "svinval",
192    /// Debug mode (`dret`).
193    Sdext = "sdext",
194    /// Resumable non-maskable interrupts (`mnret`).
195    Smrnmi = "smrnmi",
196    /// Control-transfer records (`sctrclr`).
197    Ssctr = "ssctr",
198    /// T-Head address calculation (`th.addsl`).
199    XTheadBa = "xtheadba",
200    /// T-Head basic bit manipulation (`th.ext`, `th.ff1`, `th.rev`, ...).
201    XTheadBb = "xtheadbb",
202    /// T-Head single-bit test (`th.tst`).
203    XTheadBs = "xtheadbs",
204    /// T-Head cache management (`th.dcache.*`, `th.icache.*`, `th.l2cache.*`).
205    XTheadCmo = "xtheadcmo",
206    /// T-Head conditional moves (`th.mveqz`, `th.mvnez`).
207    XTheadCondMov = "xtheadcondmov",
208    /// T-Head indexed floating-point loads and stores (`th.flrw`, ...).
209    /// Implies F.
210    XTheadFMemIdx = "xtheadfmemidx",
211    /// T-Head moves of the upper half of a double-precision register on RV32
212    /// (`th.fmv.hw.x`, `th.fmv.x.hw`). Implies D.
213    XTheadFmv = "xtheadfmv",
214    /// T-Head interrupt register saving (`th.ipush`, `th.ipop`).
215    XTheadInt = "xtheadint",
216    /// T-Head multiply-accumulate (`th.mula`, ...).
217    XTheadMac = "xtheadmac",
218    /// T-Head indexed loads and stores, and loads and stores that update the
219    /// base register (`th.lrw`, `th.lbia`, ...).
220    XTheadMemIdx = "xtheadmemidx",
221    /// T-Head load and store pairs (`th.lwd`, `th.sdd`, ...).
222    XTheadMemPair = "xtheadmempair",
223    /// T-Head synchronization (`th.sync`, `th.sfence.vmas`, ...).
224    XTheadSync = "xtheadsync",
225    /// CORE-V ALU operations (`cv.abs`, `cv.clip`, `cv.addn`, ...).
226    XCValu = "xcvalu",
227    /// CORE-V branches with an immediate (`cv.beqimm`, `cv.bneimm`).
228    XCVbi = "xcvbi",
229    /// CORE-V bit manipulation (`cv.extract`, `cv.bset`, `cv.cnt`, ...).
230    XCVbitmanip = "xcvbitmanip",
231    /// CORE-V event load (`cv.elw`).
232    XCVelw = "xcvelw",
233    /// CORE-V multiply-accumulate (`cv.mac`, `cv.mulsn`, ...).
234    XCVmac = "xcvmac",
235    /// CORE-V post-increment and register-offset loads and stores (`cv.lw`,
236    /// `cv.sw`, ...).
237    XCVmem = "xcvmem",
238    /// CORE-V packed SIMD (`cv.add.h`, `cv.dotsp.b`, ...).
239    XCVsimd = "xcvsimd",
240}
241
242// Extensions are a bit set in a u128.
243const _: () = assert!(Extension::ALL.len() <= 128);
244
245impl Extension {
246    /// The extension named `name` in an ISA string, ignoring case.
247    pub fn from_name(name: &str) -> Option<Extension> {
248        Extension::ALL
249            .iter()
250            .copied()
251            .find(|e| e.name().eq_ignore_ascii_case(name))
252    }
253
254    /// The extensions this one implies.
255    const fn implies(self) -> &'static [Extension] {
256        use Extension::*;
257        match self {
258            D | Zfhmin | Zfa | Zfbfmin => &[F],
259            C | Zcb | Zcmop | Zcmp => &[Zca],
260            Zcf => &[Zca, F],
261            Zcd => &[Zca, D],
262            Zcmt => &[Zca, Zicsr],
263            Zclsd => &[Zilsd, Zca],
264            Q => &[D],
265            Zfh => &[Zfhmin],
266            Zdinx | Zhinxmin => &[Zfinx],
267            Zhinx => &[Zhinxmin],
268            Zacas | Zabha => &[A],
269            Zicfiss => &[Zimop],
270            Zvbb | Zvbc | Zvkg | Zvkned | Zvknha | Zvksed | Zvksh | Zvfbfmin => &[V],
271            Zvfbfwma => &[Zvfbfmin],
272            Zvknhb => &[Zvknha],
273            XTheadFMemIdx => &[F],
274            XTheadFmv => &[D],
275            _ => &[],
276        }
277    }
278
279    /// The extension that provides this one's instructions with their
280    /// floating-point operands in the integer registers, such as Zfinx for
281    /// F, or the extension itself if there is none.
282    pub(crate) const fn in_x_registers(self) -> Extension {
283        use Extension::*;
284        match self {
285            F => Zfinx,
286            D => Zdinx,
287            Zfh => Zhinx,
288            Zfhmin => Zhinxmin,
289            ext => ext,
290        }
291    }
292
293    /// Whether this is a vendor extension, such as XTheadBa.
294    pub(crate) const fn is_vendor(self) -> bool {
295        matches!(self.name().as_bytes(), [b'x', ..])
296    }
297
298    const fn bit(self) -> u128 {
299        1 << self as u32
300    }
301}
302
303impl fmt::Display for Extension {
304    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305        f.write_str(self.name())
306    }
307}
308
309/// A set of [`Extension`]s.
310///
311/// Adding an extension also adds the extensions it implies (D implies F),
312/// and removing one also removes the extensions that imply it (removing F
313/// removes D).
314///
315/// ```
316/// use disc_v::{Extension, Extensions};
317///
318/// let exts = Extensions::GC.with(Extension::Zba).without(Extension::D);
319/// assert!(exts.contains(Extension::F));
320/// assert!(!exts.contains(Extension::Q));
321/// ```
322#[derive(Clone, Copy, PartialEq, Eq, Hash)]
323pub struct Extensions(u128);
324
325impl Extensions {
326    /// No extensions: only the base integer ISA.
327    pub const EMPTY: Extensions = Extensions(0);
328
329    /// Every supported standard extension that does not conflict with
330    /// another. Vendor extensions are left out: they use the custom opcode
331    /// spaces, which each vendor's cores use differently. This is what
332    /// [`decode`](fn@crate::decode) and the other free functions use.
333    pub const DEFAULT: Extensions = {
334        let mut set = Extensions::of(Extension::ALL)
335            .without(Extension::E)
336            .without(Extension::Zcmp)
337            .without(Extension::Zcmt)
338            .without(Extension::Zclsd)
339            .without(Extension::Zfinx);
340        let mut i = 0;
341        while i < Extension::ALL.len() {
342            if Extension::ALL[i].is_vendor() {
343                set = set.without(Extension::ALL[i]);
344            }
345            i += 1;
346        }
347        set
348    };
349
350    /// G and C: IMAFD, Zicsr, Zifencei and C, as in RV32GC and RV64GC.
351    pub const GC: Extensions = {
352        use Extension::*;
353        Extensions::of(&[M, A, F, D, C, Zicsr, Zifencei])
354    };
355
356    /// The extensions of the RVA23U64 profile that define instructions.
357    pub const RVA23U64: Extensions = {
358        use Extension::*;
359        Extensions::of(&[
360            M,
361            A,
362            F,
363            D,
364            C,
365            V,
366            Zicsr,
367            Zicond,
368            Zawrs,
369            Zicbom,
370            Zicboz,
371            Zicbop,
372            Zihintntl,
373            Zihintpause,
374            Zimop,
375            Zcmop,
376            Zba,
377            Zbb,
378            Zbs,
379            Zcb,
380            Zfhmin,
381            Zfa,
382            Zvbb,
383        ])
384    };
385
386    /// The extensions of the RVA23S64 profile that define instructions:
387    /// RVA23U64, Zifencei, Svinval and H.
388    pub const RVA23S64: Extensions = {
389        use Extension::*;
390        Extensions::RVA23U64.union(Extensions::of(&[Zifencei, Svinval, H]))
391    };
392
393    /// Every T-Head vendor extension (XThead*), as in T-Head's C9xx cores.
394    pub const XTHEAD: Extensions = {
395        use Extension::*;
396        Extensions::of(&[
397            XTheadBa,
398            XTheadBb,
399            XTheadBs,
400            XTheadCmo,
401            XTheadCondMov,
402            XTheadFMemIdx,
403            XTheadFmv,
404            XTheadInt,
405            XTheadMac,
406            XTheadMemIdx,
407            XTheadMemPair,
408            XTheadSync,
409        ])
410    };
411
412    /// Every CORE-V vendor extension (XCV*), as in the OpenHW Group's
413    /// CV32E40P core.
414    pub const XCV: Extensions = {
415        use Extension::*;
416        Extensions::of(&[XCValu, XCVbi, XCVbitmanip, XCVelw, XCVmac, XCVmem, XCVsimd])
417    };
418
419    /// The extensions in `exts`, and the extensions they imply.
420    pub const fn of(exts: &[Extension]) -> Extensions {
421        let mut set = Extensions::EMPTY;
422        let mut i = 0;
423        while i < exts.len() {
424            set = set.with(exts[i]);
425            i += 1;
426        }
427        set
428    }
429
430    /// Whether the set contains `ext`.
431    pub const fn contains(self, ext: Extension) -> bool {
432        self.0 & ext.bit() != 0
433    }
434
435    /// The set with `ext` and the extensions it implies added.
436    pub const fn with(self, ext: Extension) -> Extensions {
437        if self.contains(ext) {
438            return self;
439        }
440        let mut set = Extensions(self.0 | ext.bit());
441        let implied = ext.implies();
442        let mut i = 0;
443        while i < implied.len() {
444            set = set.with(implied[i]);
445            i += 1;
446        }
447        // C includes Zcf and Zcd when F and D are enabled.
448        if set.contains(Extension::C) && set.contains(Extension::F) {
449            set = set.with(Extension::Zcf);
450        }
451        if set.contains(Extension::C) && set.contains(Extension::D) {
452            set = set.with(Extension::Zcd);
453        }
454        set
455    }
456
457    /// The set with `ext` and the extensions that imply it removed.
458    ///
459    /// C is a bundle of Zca and (with F and D) Zcf and Zcd: removing C
460    /// removes every compressed instruction, and removing Zcf or Zcd removes
461    /// C but keeps Zca.
462    pub const fn without(self, ext: Extension) -> Extensions {
463        let mut set = self.remove(ext);
464        match ext {
465            Extension::C => set = set.remove(Extension::Zca),
466            Extension::Zcf | Extension::Zcd => set.0 &= !Extension::C.bit(),
467            _ => {}
468        }
469        set
470    }
471
472    /// The set with `ext` and the extensions that imply it removed.
473    const fn remove(self, ext: Extension) -> Extensions {
474        let mut set = Extensions(self.0 & !ext.bit());
475        let mut i = 0;
476        while i < Extension::ALL.len() {
477            let other = Extension::ALL[i];
478            let implied = other.implies();
479            let mut j = 0;
480            while j < implied.len() {
481                if implied[j] as u32 == ext as u32 && set.contains(other) {
482                    set = set.remove(other);
483                }
484                j += 1;
485            }
486            i += 1;
487        }
488        set
489    }
490
491    /// The extensions in either set (and the extensions they imply
492    /// together, such as Zcd for C and D).
493    pub const fn union(self, other: Extensions) -> Extensions {
494        let mut set = self;
495        let mut i = 0;
496        while i < Extension::ALL.len() {
497            if other.contains(Extension::ALL[i]) {
498                set = set.with(Extension::ALL[i]);
499            }
500            i += 1;
501        }
502        set
503    }
504
505    /// The extensions in the set, in declaration order.
506    pub fn iter(self) -> impl Iterator<Item = Extension> {
507        Extension::ALL
508            .iter()
509            .copied()
510            .filter(move |&e| self.contains(e))
511    }
512}
513
514impl Default for Extensions {
515    /// [`Extensions::DEFAULT`].
516    fn default() -> Self {
517        Extensions::DEFAULT
518    }
519}
520
521impl fmt::Debug for Extensions {
522    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
523        f.debug_set().entries(self.iter()).finish()
524    }
525}
526
527impl From<Extension> for Extensions {
528    fn from(ext: Extension) -> Self {
529        Extensions::EMPTY.with(ext)
530    }
531}
532
533impl<const N: usize> From<[Extension; N]> for Extensions {
534    fn from(exts: [Extension; N]) -> Self {
535        Extensions::of(&exts)
536    }
537}
538
539impl From<&[Extension]> for Extensions {
540    fn from(exts: &[Extension]) -> Self {
541        Extensions::of(exts)
542    }
543}
544
545impl FromIterator<Extension> for Extensions {
546    fn from_iter<I: IntoIterator<Item = Extension>>(iter: I) -> Self {
547        iter.into_iter().fold(Extensions::EMPTY, Extensions::with)
548    }
549}
550
551impl core::ops::BitOr for Extensions {
552    type Output = Extensions;
553
554    fn bitor(self, other: Extensions) -> Extensions {
555        self.union(other)
556    }
557}
558
559#[cfg(test)]
560mod tests {
561    use super::*;
562
563    #[test]
564    fn names_round_trip() {
565        for &ext in Extension::ALL {
566            assert_eq!(Extension::from_name(ext.name()), Some(ext));
567        }
568        assert_eq!(Extension::from_name("ZBA"), Some(Extension::Zba));
569        assert_eq!(Extension::from_name("XTheadBa"), Some(Extension::XTheadBa));
570        assert_eq!(Extension::from_name("xventanacondops"), None);
571    }
572
573    #[test]
574    fn all_lists_every_extension_once() {
575        let mut bits = 0u128;
576        for (i, &ext) in Extension::ALL.iter().enumerate() {
577            assert_eq!(ext as usize, i);
578            bits |= ext.bit();
579        }
580        assert_eq!(bits.count_ones() as usize, Extension::ALL.len());
581    }
582
583    #[test]
584    fn implications() {
585        use Extension::*;
586        let set = Extensions::EMPTY.with(Q);
587        assert_eq!(set, Extensions::of(&[F, D, Q]));
588        assert_eq!(set.without(F), Extensions::EMPTY);
589        assert_eq!(set.without(Q), Extensions::of(&[F, D]));
590        assert!(Extensions::from(Zvknhb).contains(V));
591    }
592}