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