Skip to main content

tsffs/arch/
risc_v.rs

1// Copyright (C) 2024 Intel Corporation
2// SPDX-License-Identifier: Apache-2.0
3
4//! Architecture-specific implementation for RISCV architecture
5
6use anyhow::{anyhow, bail, Result};
7use libafl::prelude::CmpValues;
8use raw_cstr::AsRawCstr;
9use simics::api::{
10    get_interface, read_phys_memory, sys::instruction_handle_t, Access, ConfObject,
11    CpuInstructionQueryInterface, CpuInstrumentationSubscribeInterface, CycleInterface,
12    IntRegisterInterface, ProcessorInfoV2Interface,
13};
14use std::{ffi::CStr, mem::size_of, slice::from_raw_parts};
15use yaxpeax_arch::{Decoder, U8Reader};
16use yaxpeax_riscv::{Instruction, Opcode, Operand, RiscVDecoder};
17
18use crate::{
19    tracer::{CmpExpr, CmpType, CmpValue, TraceEntry},
20    traits::TracerDisassembler,
21};
22
23use super::ArchitectureOperations;
24
25pub(crate) struct RISCVArchitectureOperations {
26    cpu: *mut ConfObject,
27    disassembler: Disassembler,
28    int_register: IntRegisterInterface,
29    processor_info_v2: ProcessorInfoV2Interface,
30    cpu_instruction_query: CpuInstructionQueryInterface,
31    #[allow(dead_code)]
32    cpu_instrumentation_subscribe: CpuInstrumentationSubscribeInterface,
33    cycle: CycleInterface,
34}
35
36impl ArchitectureOperations for RISCVArchitectureOperations {
37    const INDEX_SELECTOR_REGISTER: &'static str = "x10";
38
39    const ARGUMENT_REGISTER_0: &'static str = "x11";
40
41    const ARGUMENT_REGISTER_1: &'static str = "x12";
42
43    const ARGUMENT_REGISTER_2: &'static str = "x13";
44
45    fn new(cpu: *mut ConfObject) -> Result<Self> {
46        let mut processor_info_v2: ProcessorInfoV2Interface = get_interface(cpu)?;
47
48        let arch = unsafe { CStr::from_ptr(processor_info_v2.architecture()?) }
49            .to_str()?
50            .to_string();
51
52        if arch == "risc-v" || arch == "riscv" || arch == "riscv32" || arch == "riscv64" {
53            Ok(Self {
54                cpu,
55                disassembler: Disassembler::new(),
56                int_register: get_interface(cpu)?,
57                processor_info_v2,
58                cpu_instruction_query: get_interface(cpu)?,
59                cpu_instrumentation_subscribe: get_interface(cpu)?,
60                cycle: get_interface(cpu)?,
61            })
62        } else {
63            bail!("Architecture {} is not risc-v", arch);
64        }
65    }
66
67    fn new_unchecked(cpu: *mut ConfObject) -> Result<Self>
68    where
69        Self: Sized,
70    {
71        Ok(Self {
72            cpu,
73            disassembler: Disassembler::new(),
74            int_register: get_interface(cpu)?,
75            processor_info_v2: get_interface(cpu)?,
76            cpu_instruction_query: get_interface(cpu)?,
77            cpu_instrumentation_subscribe: get_interface(cpu)?,
78            cycle: get_interface(cpu)?,
79        })
80    }
81
82    fn cpu(&self) -> *mut ConfObject {
83        self.cpu
84    }
85
86    fn disassembler(&mut self) -> &mut dyn TracerDisassembler {
87        &mut self.disassembler
88    }
89
90    fn int_register(&mut self) -> &mut IntRegisterInterface {
91        &mut self.int_register
92    }
93
94    fn processor_info_v2(&mut self) -> &mut ProcessorInfoV2Interface {
95        &mut self.processor_info_v2
96    }
97
98    fn cpu_instruction_query(&mut self) -> &mut CpuInstructionQueryInterface {
99        &mut self.cpu_instruction_query
100    }
101
102    fn cpu_instrumentation_subscribe(&mut self) -> &mut CpuInstrumentationSubscribeInterface {
103        &mut self.cpu_instrumentation_subscribe
104    }
105
106    fn cycle(&mut self) -> &mut CycleInterface {
107        &mut self.cycle
108    }
109
110    fn trace_pc(&mut self, instruction_query: *mut instruction_handle_t) -> Result<TraceEntry> {
111        let instruction_bytes = self
112            .cpu_instruction_query
113            .get_instruction_bytes(instruction_query)?;
114
115        self.disassembler.disassemble(unsafe {
116            from_raw_parts(instruction_bytes.data, instruction_bytes.size)
117        })?;
118
119        if self.disassembler.last_was_call()
120            || self.disassembler.last_was_control_flow()
121            || self.disassembler.last_was_ret()
122        {
123            Ok(TraceEntry::builder()
124                .edge(self.processor_info_v2.get_program_counter()?)
125                .build())
126        } else {
127            Ok(TraceEntry::default())
128        }
129    }
130
131    fn trace_cmp(&mut self, instruction_query: *mut instruction_handle_t) -> Result<TraceEntry> {
132        let instruction_bytes = self
133            .cpu_instruction_query
134            .get_instruction_bytes(instruction_query)?;
135        self.disassembler.disassemble(unsafe {
136            from_raw_parts(instruction_bytes.data, instruction_bytes.size)
137        })?;
138
139        let pc = self.processor_info_v2.get_program_counter()?;
140
141        let mut cmp_values = Vec::new();
142
143        for expr in self.disassembler.cmp() {
144            if let Ok(value) = self.simplify(&expr) {
145                cmp_values.push(value);
146            }
147        }
148
149        let cmp_value = if let (Some(l), Some(r)) = (cmp_values.first(), cmp_values.get(1)) {
150            match (l, r) {
151                (CmpValue::U8(l), CmpValue::U8(r)) => Some(CmpValues::U8((*l, *r))),
152                (CmpValue::I8(l), CmpValue::I8(r)) => Some(CmpValues::U8((
153                    u8::from_le_bytes(l.to_le_bytes()),
154                    u8::from_le_bytes(r.to_le_bytes()),
155                ))),
156                (CmpValue::U16(l), CmpValue::U16(r)) => Some(CmpValues::U16((*l, *r))),
157                (CmpValue::I16(l), CmpValue::I16(r)) => Some(CmpValues::U16((
158                    u16::from_le_bytes(l.to_le_bytes()),
159                    u16::from_le_bytes(r.to_le_bytes()),
160                ))),
161                (CmpValue::U32(l), CmpValue::U32(r)) => Some(CmpValues::U32((*l, *r))),
162                (CmpValue::I32(l), CmpValue::I32(r)) => Some(CmpValues::U32((
163                    u32::from_le_bytes(l.to_le_bytes()),
164                    u32::from_le_bytes(r.to_le_bytes()),
165                ))),
166                (CmpValue::U64(l), CmpValue::U64(r)) => Some(CmpValues::U64((*l, *r))),
167                (CmpValue::I64(l), CmpValue::I64(r)) => Some(CmpValues::U64((
168                    u64::from_le_bytes(l.to_le_bytes()),
169                    u64::from_le_bytes(r.to_le_bytes()),
170                ))),
171                (CmpValue::Expr(_), CmpValue::Expr(_)) => None,
172                _ => None,
173            }
174        } else {
175            None
176        };
177
178        Ok(TraceEntry::builder()
179            .cmp((
180                pc,
181                self.disassembler.cmp_type(),
182                cmp_value.ok_or_else(|| anyhow!("No cmp value available"))?,
183            ))
184            .build())
185    }
186}
187
188impl RISCVArchitectureOperations {
189    fn simplify(&mut self, expr: &CmpExpr) -> Result<CmpValue> {
190        match expr {
191            CmpExpr::Deref((b, _)) => {
192                let v = self.simplify(b)?;
193                match v {
194                    CmpValue::U64(a) => {
195                        let address = self
196                            .processor_info_v2
197                            .logical_to_physical(a, Access::Sim_Access_Read)?;
198                        Ok(CmpValue::U64(read_phys_memory(
199                            self.cpu,
200                            address.address,
201                            size_of::<u64>() as i32,
202                        )?))
203                    }
204                    CmpValue::U32(a) => {
205                        let address = self
206                            .processor_info_v2
207                            .logical_to_physical(a as u64, Access::Sim_Access_Read)?;
208                        Ok(CmpValue::U64(read_phys_memory(
209                            self.cpu,
210                            address.address,
211                            size_of::<u32>() as i32,
212                        )?))
213                    }
214                    _ => bail!("Invalid dereference size {:?}", v),
215                }
216            }
217            CmpExpr::Reg((n, _)) => {
218                let regno = self.int_register.get_number(n.as_raw_cstr()?)?;
219                let value = self.int_register.read(regno)?;
220                if self.processor_info_v2.get_logical_address_width()? as u32 / u8::BITS == 8 {
221                    Ok(CmpValue::U64(value))
222                } else {
223                    Ok(CmpValue::U32(value as u32))
224                }
225            }
226            CmpExpr::Add((l, r)) => {
227                let lv = self.simplify(l)?;
228                let rv = self.simplify(r)?;
229
230                match (lv, rv) {
231                    (CmpValue::U8(lu), CmpValue::U8(ru)) => Ok(CmpValue::U8(lu.wrapping_add(ru))),
232                    (CmpValue::U8(lu), CmpValue::I8(ru)) => {
233                        Ok(CmpValue::U8(lu.wrapping_add_signed(ru)))
234                    }
235                    (CmpValue::U8(lu), CmpValue::U16(ru)) => {
236                        Ok(CmpValue::U8((lu as u16).wrapping_add(ru) as u8))
237                    }
238                    (CmpValue::U8(lu), CmpValue::I16(ru)) => {
239                        Ok(CmpValue::U8((lu as u16).wrapping_add_signed(ru) as u8))
240                    }
241                    (CmpValue::U8(lu), CmpValue::U32(ru)) => {
242                        Ok(CmpValue::U8((lu as u32).wrapping_add(ru) as u8))
243                    }
244                    (CmpValue::U8(lu), CmpValue::I32(ru)) => {
245                        Ok(CmpValue::U8((lu as u32).wrapping_add_signed(ru) as u8))
246                    }
247                    (CmpValue::U8(lu), CmpValue::U64(ru)) => {
248                        Ok(CmpValue::U8((lu as u64).wrapping_add(ru) as u8))
249                    }
250                    (CmpValue::U8(lu), CmpValue::I64(ru)) => {
251                        Ok(CmpValue::U8((lu as u64).wrapping_add_signed(ru) as u8))
252                    }
253                    (CmpValue::I8(lu), CmpValue::U8(ru)) => {
254                        Ok(CmpValue::I8(lu.wrapping_add_unsigned(ru)))
255                    }
256                    (CmpValue::I8(lu), CmpValue::I8(ru)) => Ok(CmpValue::I8(lu.wrapping_add(ru))),
257                    (CmpValue::I8(lu), CmpValue::U16(ru)) => {
258                        Ok(CmpValue::I8((lu as i16).wrapping_add_unsigned(ru) as i8))
259                    }
260                    (CmpValue::I8(lu), CmpValue::I16(ru)) => {
261                        Ok(CmpValue::I8((lu as i16).wrapping_add(ru) as i8))
262                    }
263                    (CmpValue::I8(lu), CmpValue::U32(ru)) => {
264                        Ok(CmpValue::I8((lu as i32).wrapping_add_unsigned(ru) as i8))
265                    }
266                    (CmpValue::I8(lu), CmpValue::I32(ru)) => {
267                        Ok(CmpValue::I8((lu as i32).wrapping_add(ru) as i8))
268                    }
269                    (CmpValue::I8(lu), CmpValue::U64(ru)) => {
270                        Ok(CmpValue::I8((lu as i64).wrapping_add_unsigned(ru) as i8))
271                    }
272                    (CmpValue::I8(lu), CmpValue::I64(ru)) => {
273                        Ok(CmpValue::I8((lu as i64).wrapping_add(ru) as i8))
274                    }
275                    (CmpValue::U16(lu), CmpValue::U8(ru)) => {
276                        Ok(CmpValue::U16(lu.wrapping_add(ru as u16)))
277                    }
278                    (CmpValue::U16(lu), CmpValue::I8(ru)) => {
279                        Ok(CmpValue::U16(lu.wrapping_add_signed(ru as i16)))
280                    }
281                    (CmpValue::U16(lu), CmpValue::U16(ru)) => {
282                        Ok(CmpValue::U16(lu.wrapping_add(ru)))
283                    }
284                    (CmpValue::U16(lu), CmpValue::I16(ru)) => {
285                        Ok(CmpValue::U16(lu.wrapping_add_signed(ru)))
286                    }
287                    (CmpValue::U16(lu), CmpValue::U32(ru)) => {
288                        Ok(CmpValue::U16((lu as u32).wrapping_add(ru) as u16))
289                    }
290                    (CmpValue::U16(lu), CmpValue::I32(ru)) => {
291                        Ok(CmpValue::U16((lu as u32).wrapping_add_signed(ru) as u16))
292                    }
293                    (CmpValue::U16(lu), CmpValue::U64(ru)) => {
294                        Ok(CmpValue::U16((lu as u64).wrapping_add(ru) as u16))
295                    }
296                    (CmpValue::U16(lu), CmpValue::I64(ru)) => {
297                        Ok(CmpValue::U16((lu as u64).wrapping_add_signed(ru) as u16))
298                    }
299                    (CmpValue::I16(lu), CmpValue::U8(ru)) => {
300                        Ok(CmpValue::I16(lu.wrapping_add_unsigned(ru as u16)))
301                    }
302                    (CmpValue::I16(lu), CmpValue::I8(ru)) => {
303                        Ok(CmpValue::I16(lu.wrapping_add(ru as i16)))
304                    }
305                    (CmpValue::I16(lu), CmpValue::U16(ru)) => {
306                        Ok(CmpValue::I16(lu.wrapping_add_unsigned(ru)))
307                    }
308                    (CmpValue::I16(lu), CmpValue::I16(ru)) => {
309                        Ok(CmpValue::I16(lu.wrapping_add(ru)))
310                    }
311                    (CmpValue::I16(lu), CmpValue::U32(ru)) => {
312                        Ok(CmpValue::I16((lu as i32).wrapping_add_unsigned(ru) as i16))
313                    }
314                    (CmpValue::I16(lu), CmpValue::I32(ru)) => {
315                        Ok(CmpValue::I16((lu as i32).wrapping_add(ru) as i16))
316                    }
317                    (CmpValue::I16(lu), CmpValue::U64(ru)) => {
318                        Ok(CmpValue::I16((lu as i64).wrapping_add_unsigned(ru) as i16))
319                    }
320                    (CmpValue::I16(lu), CmpValue::I64(ru)) => {
321                        Ok(CmpValue::I16((lu as i64).wrapping_add(ru) as i16))
322                    }
323                    (CmpValue::U32(lu), CmpValue::U8(ru)) => {
324                        Ok(CmpValue::U32(lu.wrapping_add(ru as u32)))
325                    }
326                    (CmpValue::U32(lu), CmpValue::I8(ru)) => {
327                        Ok(CmpValue::U32(lu.wrapping_add_signed(ru as i32)))
328                    }
329                    (CmpValue::U32(lu), CmpValue::U16(ru)) => {
330                        Ok(CmpValue::U32(lu.wrapping_add(ru as u32)))
331                    }
332                    (CmpValue::U32(lu), CmpValue::I16(ru)) => {
333                        Ok(CmpValue::U32(lu.wrapping_add_signed(ru as i32)))
334                    }
335                    (CmpValue::U32(lu), CmpValue::U32(ru)) => {
336                        Ok(CmpValue::U32(lu.wrapping_add(ru)))
337                    }
338                    (CmpValue::U32(lu), CmpValue::I32(ru)) => {
339                        Ok(CmpValue::U32(lu.wrapping_add_signed(ru)))
340                    }
341                    (CmpValue::U32(lu), CmpValue::U64(ru)) => {
342                        Ok(CmpValue::U32((lu as u64).wrapping_add(ru) as u32))
343                    }
344                    (CmpValue::U32(lu), CmpValue::I64(ru)) => {
345                        Ok(CmpValue::U32((lu as u64).wrapping_add_signed(ru) as u32))
346                    }
347                    (CmpValue::I32(lu), CmpValue::U8(ru)) => {
348                        Ok(CmpValue::I32(lu.wrapping_add_unsigned(ru as u32)))
349                    }
350                    (CmpValue::I32(lu), CmpValue::I8(ru)) => {
351                        Ok(CmpValue::I32(lu.wrapping_add(ru as i32)))
352                    }
353                    (CmpValue::I32(lu), CmpValue::U16(ru)) => {
354                        Ok(CmpValue::I32(lu.wrapping_add_unsigned(ru as u32)))
355                    }
356                    (CmpValue::I32(lu), CmpValue::I16(ru)) => {
357                        Ok(CmpValue::I32(lu.wrapping_add(ru as i32)))
358                    }
359                    (CmpValue::I32(lu), CmpValue::U32(ru)) => {
360                        Ok(CmpValue::I32(lu.wrapping_add_unsigned(ru)))
361                    }
362                    (CmpValue::I32(lu), CmpValue::I32(ru)) => {
363                        Ok(CmpValue::I32(lu.wrapping_add(ru)))
364                    }
365                    (CmpValue::I32(lu), CmpValue::U64(ru)) => {
366                        Ok(CmpValue::I32((lu as i64).wrapping_add_unsigned(ru) as i32))
367                    }
368                    (CmpValue::I32(lu), CmpValue::I64(ru)) => {
369                        Ok(CmpValue::I32((lu as i64).wrapping_add(ru) as i32))
370                    }
371                    (CmpValue::U64(lu), CmpValue::U8(ru)) => {
372                        Ok(CmpValue::U64(lu.wrapping_add(ru as u64)))
373                    }
374                    (CmpValue::U64(lu), CmpValue::I8(ru)) => {
375                        Ok(CmpValue::U64(lu.wrapping_add_signed(ru as i64)))
376                    }
377                    (CmpValue::U64(lu), CmpValue::U16(ru)) => {
378                        Ok(CmpValue::U64(lu.wrapping_add(ru as u64)))
379                    }
380                    (CmpValue::U64(lu), CmpValue::I16(ru)) => {
381                        Ok(CmpValue::U64(lu.wrapping_add_signed(ru as i64)))
382                    }
383                    (CmpValue::U64(lu), CmpValue::U32(ru)) => {
384                        Ok(CmpValue::U64(lu.wrapping_add(ru as u64)))
385                    }
386                    (CmpValue::U64(lu), CmpValue::I32(ru)) => {
387                        Ok(CmpValue::U64(lu.wrapping_add_signed(ru as i64)))
388                    }
389                    (CmpValue::U64(lu), CmpValue::U64(ru)) => {
390                        Ok(CmpValue::U64(lu.wrapping_add(ru)))
391                    }
392                    (CmpValue::U64(lu), CmpValue::I64(ru)) => {
393                        Ok(CmpValue::U64(lu.wrapping_add_signed(ru)))
394                    }
395                    (CmpValue::I64(lu), CmpValue::U8(ru)) => {
396                        Ok(CmpValue::I64(lu.wrapping_add_unsigned(ru as u64)))
397                    }
398                    (CmpValue::I64(lu), CmpValue::I8(ru)) => {
399                        Ok(CmpValue::I64(lu.wrapping_add(ru as i64)))
400                    }
401                    (CmpValue::I64(lu), CmpValue::U16(ru)) => {
402                        Ok(CmpValue::I64(lu.wrapping_add_unsigned(ru as u64)))
403                    }
404                    (CmpValue::I64(lu), CmpValue::I16(ru)) => {
405                        Ok(CmpValue::I64(lu.wrapping_add(ru as i64)))
406                    }
407                    (CmpValue::I64(lu), CmpValue::U32(ru)) => {
408                        Ok(CmpValue::I64(lu.wrapping_add_unsigned(ru as u64)))
409                    }
410                    (CmpValue::I64(lu), CmpValue::I32(ru)) => {
411                        Ok(CmpValue::I64(lu.wrapping_add(ru as i64)))
412                    }
413                    (CmpValue::I64(lu), CmpValue::U64(ru)) => {
414                        Ok(CmpValue::I64(lu.wrapping_add_unsigned(ru)))
415                    }
416                    (CmpValue::I64(lu), CmpValue::I64(ru)) => {
417                        Ok(CmpValue::I64(lu.wrapping_add(ru)))
418                    }
419                    _ => bail!("Cannot multiply non-integral types"),
420                }
421            }
422            CmpExpr::I16(i) => Ok(CmpValue::I16(*i)),
423            CmpExpr::U32(u) => Ok(CmpValue::U32(*u)),
424            CmpExpr::I32(i) => Ok(CmpValue::I32(*i)),
425            _ => bail!("Unsupported expression {:?}", expr),
426        }
427    }
428}
429
430pub(crate) struct Disassembler {
431    decoder: RiscVDecoder,
432    last: Option<Instruction>,
433}
434
435impl Disassembler {
436    pub fn new() -> Self {
437        Self {
438            decoder: RiscVDecoder::default(),
439            last: None,
440        }
441    }
442}
443
444impl Default for Disassembler {
445    fn default() -> Self {
446        Self::new()
447    }
448}
449
450impl TracerDisassembler for Disassembler {
451    fn disassemble(&mut self, bytes: &[u8]) -> Result<()> {
452        let mut r = U8Reader::new(bytes);
453
454        if let Ok(insn) = self.decoder.decode(&mut r) {
455            self.last = Some(insn);
456        } else {
457            bail!("Could not disassemble {:?}", bytes);
458        }
459
460        Ok(())
461    }
462
463    fn disassemble_to_string(&mut self, bytes: &[u8]) -> Result<String> {
464        let mut r = U8Reader::new(bytes);
465
466        if let Ok(insn) = self.decoder.decode(&mut r) {
467            Ok(insn.to_string())
468        } else {
469            bail!("Could not disassemble {:?}", bytes);
470        }
471    }
472
473    fn last_was_control_flow(&self) -> bool {
474        if let Some(last) = self.last.as_ref() {
475            if matches!(last.opcode(), |Opcode::BEQ| Opcode::BNE
476                | Opcode::BLT
477                | Opcode::BGE
478                | Opcode::BLTU
479                | Opcode::BGEU)
480            {
481                return true;
482            }
483        }
484
485        false
486    }
487
488    // TODO: Make call/ret distinction more accurate, all three can ret/call far or near, but
489    // there are semantic versions based on operands:
490    // https://inst.eecs.berkeley.edu/~cs61c/fa20/pdfs/lectures/lec12-bw.pdf
491
492    fn last_was_call(&self) -> bool {
493        if let Some(last) = self.last.as_ref() {
494            return matches!(last.opcode(), Opcode::JALR | Opcode::JAL | Opcode::AUIPC);
495        }
496
497        false
498    }
499
500    fn last_was_ret(&self) -> bool {
501        if let Some(last) = self.last.as_ref() {
502            return matches!(last.opcode(), Opcode::JALR | Opcode::JAL | Opcode::AUIPC);
503        }
504
505        false
506    }
507
508    fn last_was_cmp(&self) -> bool {
509        if let Some(last) = self.last.as_ref() {
510            return matches!(
511                last.opcode(),
512                Opcode::SLT
513                    | Opcode::SLTI
514                    | Opcode::SLTU
515                    | Opcode::SLTIU
516                    | Opcode::BEQ
517                    | Opcode::BNE
518                    | Opcode::BGE
519                    | Opcode::BLTU
520                    | Opcode::BGEU
521            );
522        }
523
524        false
525    }
526
527    fn cmp(&self) -> Vec<CmpExpr> {
528        let mut cmp_exprs = Vec::new();
529        if self.last_was_cmp() {
530            if let Some(last) = self.last.as_ref() {
531                for operand in last.operands() {
532                    match operand {
533                        Some(Operand::Reg(r)) => {
534                            let regname = format!("x{}", r);
535                            // NOTE: We don't give a width to regs here, it's defined by the
536                            // arch subtype in the archops
537                            cmp_exprs.push(CmpExpr::Reg((regname, 0)));
538                        }
539                        Some(Operand::Imm(i)) => {
540                            // NOTE: Not technically correct, can be 12I/S or 20U
541                            cmp_exprs.push(CmpExpr::I32(i));
542                        }
543                        Some(Operand::BaseOffset(b, o)) => {
544                            let regname = format!("x{}", b);
545                            cmp_exprs.push(CmpExpr::Deref((
546                                Box::new(CmpExpr::Add((
547                                    Box::new(CmpExpr::Reg((regname, 0))),
548                                    Box::new(CmpExpr::I16(o)),
549                                ))),
550                                None,
551                            )))
552                        }
553                        Some(Operand::LongImm(u)) => cmp_exprs.push(CmpExpr::U32(u)),
554                        _ => {}
555                    }
556                }
557            }
558        }
559
560        cmp_exprs
561    }
562
563    fn cmp_type(&self) -> Vec<CmpType> {
564        if self.last_was_cmp() {
565            if let Some(last) = self.last.as_ref() {
566                return match last.opcode() {
567                    Opcode::SLT => vec![CmpType::Lesser],
568                    Opcode::SLTI => vec![CmpType::Lesser],
569                    Opcode::SLTU => vec![CmpType::Lesser],
570                    Opcode::SLTIU => vec![CmpType::Lesser],
571                    Opcode::BEQ => vec![CmpType::Equal],
572                    Opcode::BNE => vec![CmpType::Equal],
573                    Opcode::BGE => vec![CmpType::Greater, CmpType::Equal],
574                    Opcode::BLTU => vec![CmpType::Lesser],
575                    Opcode::BGEU => vec![CmpType::Greater, CmpType::Equal],
576                    _ => vec![],
577                };
578            }
579        }
580
581        vec![]
582    }
583}