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}