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];
22
23#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
47pub struct Decoder {
48 isa: Isa,
49 extensions: Extensions,
50}
51
52impl Decoder {
53 pub const RV32GC: Decoder = Decoder::unchecked(Isa::Rv32, Extensions::GC);
55
56 pub const RV64GC: Decoder = Decoder::unchecked(Isa::Rv64, Extensions::GC);
58
59 pub const RVA23U64: Decoder = Decoder::unchecked(Isa::Rv64, Extensions::RVA23U64);
61
62 pub const RVA23S64: Decoder = Decoder::unchecked(Isa::Rv64, Extensions::RVA23S64);
65
66 pub const fn new(isa: Isa) -> Decoder {
69 Decoder::unchecked(isa, Extensions::DEFAULT)
70 }
71
72 const fn unchecked(isa: Isa, extensions: Extensions) -> Decoder {
73 Decoder { isa, extensions }
74 }
75
76 pub fn with_only(isa: Isa, extensions: impl Into<Extensions>) -> Result<Decoder, Conflict> {
87 let extensions = extensions.into();
88 match find_conflict(extensions) {
89 Some(conflict) => Err(conflict),
90 None => Ok(Decoder { isa, extensions }),
91 }
92 }
93
94 pub fn with(self, ext: Extension) -> Result<Decoder, Conflict> {
97 Decoder::with_only(self.isa, self.extensions.with(ext))
98 }
99
100 pub fn try_with(self, ext: Extension) -> Decoder {
104 self.with(ext).unwrap_or(self)
105 }
106
107 pub fn without(self, ext: Extension) -> Decoder {
109 Decoder {
110 extensions: self.extensions.without(ext),
111 ..self
112 }
113 }
114
115 pub fn isa(&self) -> Isa {
117 self.isa
118 }
119
120 pub fn extensions(&self) -> Extensions {
122 self.extensions
123 }
124
125 pub fn decode(&self, pc: u64, inst: u64) -> Instruction {
128 crate::decode::decode(self.isa, self.extensions, pc, inst)
129 }
130
131 pub fn decode_bytes(&self, pc: u64, bytes: &[u8]) -> Option<Instruction> {
134 let first = u16::from_le_bytes([*bytes.first()?, *bytes.get(1)?]);
135 let len = inst_length(first).unwrap_or(2);
136 let mut word = [0; 8];
137 word[..len].copy_from_slice(bytes.get(..len)?);
138 Some(self.decode(pc, u64::from_le_bytes(word)))
139 }
140
141 pub fn disassemble<'a>(&self, pc: u64, bytes: &'a [u8]) -> Disassembler<'a> {
144 Disassembler {
145 decoder: *self,
146 pc,
147 bytes,
148 }
149 }
150
151 pub fn from_march(march: &str) -> Result<Decoder, MarchError<'_>> {
175 let (isa, rest) = [
176 ("rv32", Isa::Rv32),
177 ("rv64", Isa::Rv64),
178 ("rv128", Isa::Rv128),
179 ]
180 .into_iter()
181 .find_map(|(prefix, isa)| {
182 let (head, rest) = march.split_at_checked(prefix.len())?;
183 head.eq_ignore_ascii_case(prefix).then_some((isa, rest))
184 })
185 .ok_or(MarchError::InvalidBase)?;
186 let mut extensions = Extensions::EMPTY;
187 for (i, token) in rest.split(['_']).enumerate() {
190 let first = token.chars().next().map(|c| c.to_ascii_lowercase());
191 match first {
192 Some('z' | 's' | 'x') if i > 0 => {
193 extensions = extensions.union(multi_letter(token)?);
194 }
195 _ => {
196 if i == 0 && !matches!(first, Some('i' | 'g' | 'e')) {
197 return Err(MarchError::InvalidBase);
198 }
199 extensions = extensions.union(single_letters(token)?);
200 }
201 }
202 }
203 Decoder::with_only(isa, extensions).map_err(MarchError::Conflict)
204 }
205}
206
207fn single_letters(token: &str) -> Result<Extensions, MarchError<'_>> {
209 use Extension::*;
210 let mut extensions = Extensions::EMPTY;
211 let mut rest = token;
212 while let Some(c) = rest.chars().next() {
213 let Some((name, after)) = rest.split_at_checked(c.len_utf8()) else {
214 break;
215 };
216 rest = strip_version(after);
217 extensions = extensions.union(match c.to_ascii_lowercase() {
218 'i' => Extensions::EMPTY,
219 'e' => E.into(),
221 'g' => Extensions::of(&[M, A, F, D, Zicsr, Zifencei]),
222 'b' => Extensions::of(&[Zba, Zbb, Zbs]),
223 c => match Extension::from_name(name) {
224 Some(ext) if c.is_ascii_alphabetic() => ext.into(),
225 _ => return Err(MarchError::UnsupportedExtension(name)),
226 },
227 });
228 }
229 Ok(extensions)
230}
231
232fn multi_letter(token: &str) -> Result<Extensions, MarchError<'_>> {
234 use Extension::*;
235 let name = token.strip_suffix(trailing_version(token)).unwrap_or(token);
236 if let Some(ext) = Extension::from_name(name) {
237 return Ok(ext.into());
238 }
239 let lower = |s: &str| name.eq_ignore_ascii_case(s);
240 let has_prefix = |p: &str| {
241 name.get(..p.len())
242 .is_some_and(|h| h.eq_ignore_ascii_case(p))
243 };
244 Ok(if lower("zmmul") {
245 M.into()
246 } else if lower("zaamo") || lower("zalrsc") {
247 A.into()
248 } else if lower("zce") {
249 Extensions::of(&[Zca, Zcb, Zcmp, Zcmt])
250 } else if has_prefix("zve") {
251 V.into()
252 } else if lower("zkn") {
253 Extensions::of(&[Zbkb, Zbkc, Zbkx, Zkne, Zknd, Zknh])
254 } else if lower("zks") {
255 Extensions::of(&[Zbkb, Zbkc, Zbkx, Zksed, Zksh])
256 } else if lower("zk") {
257 Extensions::of(&[Zbkb, Zbkc, Zbkx, Zkne, Zknd, Zknh])
258 } else if lower("sha") {
259 H.into()
260 } else if lower("smctr") {
261 Ssctr.into()
262 } else if has_prefix("zvl") || NO_INSTRUCTIONS.iter().any(|n| lower(n)) {
263 Extensions::EMPTY
264 } else if has_prefix("s") {
265 Extensions::EMPTY
268 } else {
269 return Err(MarchError::UnsupportedExtension(name));
270 })
271}
272
273const NO_INSTRUCTIONS: &[&str] = &[
275 "zicntr", "zihpm", "zkt", "zvkt", "zvfh", "zvfhmin", "ztso", "za64rs", "za128rs", "zama16b",
276 "zic64b", "ziccamoa", "ziccif", "zicclsm", "ziccrse", "supm", "zkr",
277];
278
279fn strip_version(s: &str) -> &str {
281 s.strip_prefix(leading_version(s)).unwrap_or(s)
282}
283
284fn leading_version(s: &str) -> &str {
287 fn skip_digits(s: &str) -> &str {
288 s.trim_start_matches(|c: char| c.is_ascii_digit())
289 }
290 let after_major = skip_digits(s);
291 if after_major.len() == s.len() {
292 return "";
293 }
294 let after = match after_major.strip_prefix(['p', 'P']) {
295 Some(minor) if minor.starts_with(|c: char| c.is_ascii_digit()) => skip_digits(minor),
296 _ => after_major,
297 };
298 s.strip_suffix(after).unwrap_or("")
299}
300
301fn trailing_version(s: &str) -> &str {
304 fn skip_digits(s: &str) -> &str {
305 s.trim_end_matches(|c: char| c.is_ascii_digit())
306 }
307 let before_minor = skip_digits(s);
308 if before_minor.len() == s.len() {
309 return "";
310 }
311 let before = match before_minor.strip_suffix(['p', 'P']) {
312 Some(major) if major.ends_with(|c: char| c.is_ascii_digit()) => skip_digits(major),
313 _ => before_minor,
314 };
315 s.strip_prefix(before).unwrap_or("")
316}
317
318fn find_conflict(extensions: Extensions) -> Option<Conflict> {
320 CONFLICTS
321 .iter()
322 .find(|(a, b)| extensions.contains(*a) && extensions.contains(*b))
323 .map(|&(extension, conflicts_with)| Conflict {
324 extension,
325 conflicts_with,
326 })
327}
328
329#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
332pub struct Conflict {
333 extension: Extension,
334 conflicts_with: Extension,
335}
336
337impl Conflict {
338 pub fn extension(&self) -> Extension {
340 self.extension
341 }
342
343 pub fn conflicts_with(&self) -> Extension {
345 self.conflicts_with
346 }
347}
348
349impl fmt::Display for Conflict {
350 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
351 write!(
352 f,
353 "{} conflicts with {}",
354 self.extension, self.conflicts_with
355 )
356 }
357}
358
359impl core::error::Error for Conflict {}
360
361#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
363#[non_exhaustive]
364pub enum MarchError<'a> {
365 InvalidBase,
368 UnsupportedExtension(&'a str),
370 Conflict(Conflict),
372}
373
374impl fmt::Display for MarchError<'_> {
375 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
376 match self {
377 MarchError::InvalidBase => {
378 f.write_str("ISA string does not start with rv32, rv64 or rv128 and i, e or g")
379 }
380 MarchError::UnsupportedExtension(name) => write!(f, "unsupported extension {name}"),
381 MarchError::Conflict(conflict) => conflict.fmt(f),
382 }
383 }
384}
385
386impl core::error::Error for MarchError<'_> {}
387
388#[cfg(test)]
389mod tests {
390 use super::*;
391
392 #[test]
393 fn version_suffixes() {
394 assert_eq!(leading_version("2p1m"), "2p1");
395 assert_eq!(leading_version("2m"), "2");
396 assert_eq!(leading_version("2pm"), "2");
397 assert_eq!(leading_version("mc2"), "");
398 assert_eq!(strip_version("2p1mc"), "mc");
399 assert_eq!(strip_version("mc2"), "mc2");
400 assert_eq!(trailing_version("zba1p0"), "1p0");
401 assert_eq!(trailing_version("zba1"), "1");
402 assert_eq!(trailing_version("zba"), "");
403 assert_eq!(trailing_version("zvl128b"), "");
404 }
405}