1use core::fmt;
7
8use crate::{Disassembler, Extension, Extensions, Instruction, Isa, inst_length};
9
10const CONFLICTS: &[(Extension, Extension)] = &[
13 (Extension::Zcmp, Extension::Zcd),
15 (Extension::Zcmt, Extension::Zcd),
16 (Extension::Zclsd, Extension::Zcf),
18 (Extension::Zfinx, Extension::F),
21 (Extension::XCVmem, Extension::XTheadBa),
23 (Extension::XCVmem, Extension::XTheadBb),
24 (Extension::XCVmem, Extension::XTheadBs),
25 (Extension::XCVmem, Extension::XTheadCmo),
26 (Extension::XCVmem, Extension::XTheadCondMov),
27 (Extension::XCVmem, Extension::XTheadFmv),
28 (Extension::XCVmem, Extension::XTheadInt),
29 (Extension::XCVmem, Extension::XTheadMac),
30 (Extension::XCVmem, Extension::XTheadMemIdx),
31 (Extension::XCVmem, Extension::XTheadMemPair),
32 (Extension::XCVmem, Extension::XTheadSync),
33 (Extension::XCVbi, Extension::XTheadFMemIdx),
34 (Extension::XCVelw, Extension::XTheadBb),
35];
36
37#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
61pub struct Decoder {
62 isa: Isa,
63 extensions: Extensions,
64}
65
66impl Decoder {
67 pub const RV32GC: Decoder = Decoder::unchecked(Isa::Rv32, Extensions::GC);
69
70 pub const RV64GC: Decoder = Decoder::unchecked(Isa::Rv64, Extensions::GC);
72
73 pub const RVA23U64: Decoder = Decoder::unchecked(Isa::Rv64, Extensions::RVA23U64);
75
76 pub const RVA23S64: Decoder = Decoder::unchecked(Isa::Rv64, Extensions::RVA23S64);
79
80 pub const fn new(isa: Isa) -> Decoder {
83 Decoder::unchecked(isa, Extensions::DEFAULT)
84 }
85
86 const fn unchecked(isa: Isa, extensions: Extensions) -> Decoder {
87 Decoder { isa, extensions }
88 }
89
90 pub fn with_only(isa: Isa, extensions: impl Into<Extensions>) -> Result<Decoder, Conflict> {
101 let extensions = extensions.into();
102 match find_conflict(extensions) {
103 Some(conflict) => Err(conflict),
104 None => Ok(Decoder { isa, extensions }),
105 }
106 }
107
108 pub fn with(self, ext: Extension) -> Result<Decoder, Conflict> {
111 Decoder::with_only(self.isa, self.extensions.with(ext))
112 }
113
114 pub fn try_with(self, ext: Extension) -> Decoder {
118 self.with(ext).unwrap_or(self)
119 }
120
121 pub fn without(self, ext: Extension) -> Decoder {
123 Decoder {
124 extensions: self.extensions.without(ext),
125 ..self
126 }
127 }
128
129 pub fn isa(&self) -> Isa {
131 self.isa
132 }
133
134 pub fn extensions(&self) -> Extensions {
136 self.extensions
137 }
138
139 pub fn decode(&self, pc: u64, inst: u64) -> Instruction {
142 crate::decode::decode(self.isa, self.extensions, pc, inst)
143 }
144
145 pub fn decode_bytes(&self, pc: u64, bytes: &[u8]) -> Option<Instruction> {
148 let first = u16::from_le_bytes([*bytes.first()?, *bytes.get(1)?]);
149 let len = inst_length(first).unwrap_or(2);
150 let mut word = [0; 8];
151 word[..len].copy_from_slice(bytes.get(..len)?);
152 Some(self.decode(pc, u64::from_le_bytes(word)))
153 }
154
155 pub fn disassemble<'a>(&self, pc: u64, bytes: &'a [u8]) -> Disassembler<'a> {
158 Disassembler {
159 decoder: *self,
160 pc,
161 bytes,
162 }
163 }
164
165 pub fn from_march(march: &str) -> Result<Decoder, MarchError<'_>> {
189 let (isa, rest) = [
190 ("rv32", Isa::Rv32),
191 ("rv64", Isa::Rv64),
192 ("rv128", Isa::Rv128),
193 ]
194 .into_iter()
195 .find_map(|(prefix, isa)| {
196 let (head, rest) = march.split_at_checked(prefix.len())?;
197 head.eq_ignore_ascii_case(prefix).then_some((isa, rest))
198 })
199 .ok_or(MarchError::InvalidBase)?;
200 let mut extensions = Extensions::EMPTY;
201 for (i, token) in rest.split(['_']).enumerate() {
204 let first = token.chars().next().map(|c| c.to_ascii_lowercase());
205 match first {
206 Some('z' | 's' | 'x') if i > 0 => {
207 extensions = extensions.union(multi_letter(token)?);
208 }
209 _ => {
210 if i == 0 && !matches!(first, Some('i' | 'g' | 'e')) {
211 return Err(MarchError::InvalidBase);
212 }
213 extensions = extensions.union(single_letters(token)?);
214 }
215 }
216 }
217 Decoder::with_only(isa, extensions).map_err(MarchError::Conflict)
218 }
219}
220
221fn single_letters(token: &str) -> Result<Extensions, MarchError<'_>> {
223 use Extension::*;
224 let mut extensions = Extensions::EMPTY;
225 let mut rest = token;
226 while let Some(c) = rest.chars().next() {
227 let Some((name, after)) = rest.split_at_checked(c.len_utf8()) else {
228 break;
229 };
230 rest = strip_version(after);
231 extensions = extensions.union(match c.to_ascii_lowercase() {
232 'i' => Extensions::EMPTY,
233 'e' => E.into(),
235 'g' => Extensions::of(&[M, A, F, D, Zicsr, Zifencei]),
236 'b' => Extensions::of(&[Zba, Zbb, Zbs]),
237 c => match Extension::from_name(name) {
238 Some(ext) if c.is_ascii_alphabetic() => ext.into(),
239 _ => return Err(MarchError::UnsupportedExtension(name)),
240 },
241 });
242 }
243 Ok(extensions)
244}
245
246fn multi_letter(token: &str) -> Result<Extensions, MarchError<'_>> {
248 use Extension::*;
249 let name = token.strip_suffix(trailing_version(token)).unwrap_or(token);
250 if let Some(ext) = Extension::from_name(name) {
251 return Ok(ext.into());
252 }
253 let lower = |s: &str| name.eq_ignore_ascii_case(s);
254 let has_prefix = |p: &str| {
255 name.get(..p.len())
256 .is_some_and(|h| h.eq_ignore_ascii_case(p))
257 };
258 Ok(if lower("zmmul") {
259 M.into()
260 } else if lower("zaamo") || lower("zalrsc") {
261 A.into()
262 } else if lower("zce") {
263 Extensions::of(&[Zca, Zcb, Zcmp, Zcmt])
264 } else if has_prefix("zve") {
265 V.into()
266 } else if lower("zkn") {
267 Extensions::of(&[Zbkb, Zbkc, Zbkx, Zkne, Zknd, Zknh])
268 } else if lower("zks") {
269 Extensions::of(&[Zbkb, Zbkc, Zbkx, Zksed, Zksh])
270 } else if lower("zk") {
271 Extensions::of(&[Zbkb, Zbkc, Zbkx, Zkne, Zknd, Zknh])
272 } else if lower("sha") {
273 H.into()
274 } else if lower("smctr") {
275 Ssctr.into()
276 } else if has_prefix("zvl") || NO_INSTRUCTIONS.iter().any(|n| lower(n)) {
277 Extensions::EMPTY
278 } else if has_prefix("s") {
279 Extensions::EMPTY
282 } else {
283 return Err(MarchError::UnsupportedExtension(name));
284 })
285}
286
287const NO_INSTRUCTIONS: &[&str] = &[
289 "zicntr", "zihpm", "zkt", "zvkt", "zvfh", "zvfhmin", "ztso", "za64rs", "za128rs", "zama16b",
290 "zic64b", "ziccamoa", "ziccif", "zicclsm", "ziccrse", "supm", "zkr",
291];
292
293fn strip_version(s: &str) -> &str {
295 s.strip_prefix(leading_version(s)).unwrap_or(s)
296}
297
298fn leading_version(s: &str) -> &str {
301 fn skip_digits(s: &str) -> &str {
302 s.trim_start_matches(|c: char| c.is_ascii_digit())
303 }
304 let after_major = skip_digits(s);
305 if after_major.len() == s.len() {
306 return "";
307 }
308 let after = match after_major.strip_prefix(['p', 'P']) {
309 Some(minor) if minor.starts_with(|c: char| c.is_ascii_digit()) => skip_digits(minor),
310 _ => after_major,
311 };
312 s.strip_suffix(after).unwrap_or("")
313}
314
315fn trailing_version(s: &str) -> &str {
318 fn skip_digits(s: &str) -> &str {
319 s.trim_end_matches(|c: char| c.is_ascii_digit())
320 }
321 let before_minor = skip_digits(s);
322 if before_minor.len() == s.len() {
323 return "";
324 }
325 let before = match before_minor.strip_suffix(['p', 'P']) {
326 Some(major) if major.ends_with(|c: char| c.is_ascii_digit()) => skip_digits(major),
327 _ => before_minor,
328 };
329 s.strip_prefix(before).unwrap_or("")
330}
331
332fn find_conflict(extensions: Extensions) -> Option<Conflict> {
334 CONFLICTS
335 .iter()
336 .find(|(a, b)| extensions.contains(*a) && extensions.contains(*b))
337 .map(|&(extension, conflicts_with)| Conflict {
338 extension,
339 conflicts_with,
340 })
341}
342
343#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
346pub struct Conflict {
347 extension: Extension,
348 conflicts_with: Extension,
349}
350
351impl Conflict {
352 pub fn extension(&self) -> Extension {
354 self.extension
355 }
356
357 pub fn conflicts_with(&self) -> Extension {
359 self.conflicts_with
360 }
361}
362
363impl fmt::Display for Conflict {
364 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
365 write!(
366 f,
367 "{} conflicts with {}",
368 self.extension, self.conflicts_with
369 )
370 }
371}
372
373impl core::error::Error for Conflict {}
374
375#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
377#[non_exhaustive]
378pub enum MarchError<'a> {
379 InvalidBase,
382 UnsupportedExtension(&'a str),
384 Conflict(Conflict),
386}
387
388impl fmt::Display for MarchError<'_> {
389 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
390 match self {
391 MarchError::InvalidBase => {
392 f.write_str("ISA string does not start with rv32, rv64 or rv128 and i, e or g")
393 }
394 MarchError::UnsupportedExtension(name) => write!(f, "unsupported extension {name}"),
395 MarchError::Conflict(conflict) => conflict.fmt(f),
396 }
397 }
398}
399
400impl core::error::Error for MarchError<'_> {}
401
402#[cfg(test)]
403mod tests {
404 use super::*;
405
406 #[test]
407 fn version_suffixes() {
408 assert_eq!(leading_version("2p1m"), "2p1");
409 assert_eq!(leading_version("2m"), "2");
410 assert_eq!(leading_version("2pm"), "2");
411 assert_eq!(leading_version("mc2"), "");
412 assert_eq!(strip_version("2p1mc"), "mc");
413 assert_eq!(strip_version("mc2"), "mc2");
414 assert_eq!(trailing_version("zba1p0"), "1p0");
415 assert_eq!(trailing_version("zba1"), "1");
416 assert_eq!(trailing_version("zba"), "");
417 assert_eq!(trailing_version("zvl128b"), "");
418 }
419
420 #[test]
424 #[cfg(all(feature = "vendor-thead", feature = "vendor-corev"))]
425 fn vendor_conflicts_are_the_overlapping_encodings() {
426 use crate::opcodes::{MaskedOpcode, xcv, xthead};
427 let tables: [&[MaskedOpcode]; 2] = [&xthead::CUSTOM_0, &xcv::CUSTOM];
428 let entries = || tables.into_iter().flatten();
429 let vendor = |e: &MaskedOpcode| *e.op.requires.iter().find(|x| x.is_vendor()).unwrap();
430 let conflicts =
431 |a: Extension, b: Extension| CONFLICTS.contains(&(a, b)) || CONFLICTS.contains(&(b, a));
432 for (i, a) in entries().enumerate() {
433 for b in entries().skip(i + 1) {
434 let overlap =
435 (a.bits ^ b.bits) & a.mask & b.mask == 0 && a.op.isas & b.op.isas != 0;
436 if !overlap {
437 continue;
438 }
439 let (va, vb) = (vendor(a), vendor(b));
440 assert_ne!(va, vb, "{} and {} overlap", a.op.name, b.op.name);
441 assert!(conflicts(va, vb), "{} and {} overlap", a.op.name, b.op.name);
442 }
443 }
444 for &(a, b) in CONFLICTS.iter().filter(|(a, _)| a.is_vendor()) {
445 assert!(
446 entries().any(|x| vendor(x) == a
447 && entries().any(|y| vendor(y) == b
448 && (x.bits ^ y.bits) & x.mask & y.mask == 0
449 && x.op.isas & y.op.isas != 0)),
450 "{a} and {b} do not overlap"
451 );
452 }
453 }
454}