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}