openvm_transpiler/
util.rs

1use std::collections::BTreeMap;
2
3use openvm_instructions::{
4    exe::SparseMemoryImage,
5    instruction::Instruction,
6    riscv::{RV32_MEMORY_AS, RV32_REGISTER_NUM_LIMBS},
7    utils::isize_to_field,
8    LocalOpcode, SystemOpcode, VmOpcode,
9};
10use openvm_stark_backend::p3_field::PrimeField32;
11use rrs_lib::instruction_formats::{BType, IType, ITypeShamt, JType, RType, SType, UType};
12
13fn i12_to_u24(imm: i32) -> u32 {
14    (imm as u32) & 0xffffff
15}
16
17/// Create a new [`Instruction`] from an R-type instruction.
18pub fn from_r_type<F: PrimeField32>(
19    opcode: usize,
20    e_as: usize,
21    dec_insn: &RType,
22    allow_rd_zero: bool,
23) -> Instruction<F> {
24    // If `rd` is not allowed to be zero, we transpile to `NOP` to prevent a write
25    // to `x0`. In the cases where `allow_rd_zero` is true, it is the responsibility of
26    // the caller to guarantee that the resulting instruction does not write to `rd`.
27    if !allow_rd_zero && dec_insn.rd == 0 {
28        return nop();
29    }
30    Instruction::new(
31        VmOpcode::from_usize(opcode),
32        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rd),
33        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rs1),
34        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rs2),
35        F::ONE,                        // rd and rs1 are registers
36        F::from_canonical_usize(e_as), // rs2 can be mem (eg modular arith)
37        F::ZERO,
38        F::ZERO,
39    )
40}
41
42/// Create a new [`Instruction`] from an I-type instruction. Should only be used for ALU
43/// instructions because `imm` is transpiled in a special way.
44pub fn from_i_type<F: PrimeField32>(opcode: usize, dec_insn: &IType) -> Instruction<F> {
45    if dec_insn.rd == 0 {
46        return nop();
47    }
48    Instruction::new(
49        VmOpcode::from_usize(opcode),
50        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rd),
51        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rs1),
52        F::from_canonical_u32(i12_to_u24(dec_insn.imm)),
53        F::ONE,  // rd and rs1 are registers
54        F::ZERO, // rs2 is an immediate
55        F::ZERO,
56        F::ZERO,
57    )
58}
59
60/// Create a new [`Instruction`] from a load operation
61pub fn from_load<F: PrimeField32>(opcode: usize, dec_insn: &IType) -> Instruction<F> {
62    Instruction::new(
63        VmOpcode::from_usize(opcode),
64        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rd),
65        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rs1),
66        F::from_canonical_u32((dec_insn.imm as u32) & 0xffff),
67        F::ONE,                         // rd is a register
68        F::TWO,                         // we load from memory
69        F::from_bool(dec_insn.rd != 0), // we may need to use this flag in the operation
70        F::from_bool(dec_insn.imm < 0), // flag for sign extension
71    )
72}
73
74/// Create a new [`Instruction`] from an I-type instruction with a shamt.
75/// It seems that shamt can only occur in SLLI, SRLI, SRAI.
76pub fn from_i_type_shamt<F: PrimeField32>(opcode: usize, dec_insn: &ITypeShamt) -> Instruction<F> {
77    if dec_insn.rd == 0 {
78        return nop();
79    }
80    Instruction::new(
81        VmOpcode::from_usize(opcode),
82        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rd),
83        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rs1),
84        F::from_canonical_u32(dec_insn.shamt),
85        F::ONE,  // rd and rs1 are registers
86        F::ZERO, // rs2 is an immediate
87        F::ZERO,
88        F::ZERO,
89    )
90}
91
92/// Create a new [`Instruction`] from an S-type instruction.
93pub fn from_s_type<F: PrimeField32>(opcode: usize, dec_insn: &SType) -> Instruction<F> {
94    Instruction::new(
95        VmOpcode::from_usize(opcode),
96        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rs2),
97        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rs1),
98        F::from_canonical_u32((dec_insn.imm as u32) & 0xffff),
99        F::ONE,
100        F::TWO,
101        F::ONE,
102        F::from_bool(dec_insn.imm < 0),
103    )
104}
105
106/// Create a new [`Instruction`] from a B-type instruction.
107pub fn from_b_type<F: PrimeField32>(opcode: usize, dec_insn: &BType) -> Instruction<F> {
108    Instruction::new(
109        VmOpcode::from_usize(opcode),
110        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rs1),
111        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rs2),
112        isize_to_field(dec_insn.imm as isize),
113        F::ONE, // rs1 is a register
114        F::ONE, // rs2 is a register
115        F::ZERO,
116        F::ZERO,
117    )
118}
119
120/// Create a new [`Instruction`] from a J-type instruction.
121pub fn from_j_type<F: PrimeField32>(opcode: usize, dec_insn: &JType) -> Instruction<F> {
122    Instruction::new(
123        VmOpcode::from_usize(opcode),
124        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rd),
125        F::ZERO,
126        isize_to_field(dec_insn.imm as isize),
127        F::ONE, // rd is a register
128        F::ZERO,
129        F::from_bool(dec_insn.rd != 0), // we may need to use this flag in the operation
130        F::ZERO,
131    )
132}
133
134/// Create a new [`Instruction`] from a U-type instruction.
135pub fn from_u_type<F: PrimeField32>(opcode: usize, dec_insn: &UType) -> Instruction<F> {
136    if dec_insn.rd == 0 {
137        return nop();
138    }
139    Instruction::new(
140        VmOpcode::from_usize(opcode),
141        F::from_canonical_usize(RV32_REGISTER_NUM_LIMBS * dec_insn.rd),
142        F::ZERO,
143        F::from_canonical_u32((dec_insn.imm as u32 >> 12) & 0xfffff),
144        F::ONE, // rd is a register
145        F::ZERO,
146        F::ZERO,
147        F::ZERO,
148    )
149}
150
151/// Create a new [`Instruction`] that exits with code 2. This is equivalent to program panic but
152/// with a special exit code for debugging.
153pub fn unimp<F: PrimeField32>() -> Instruction<F> {
154    Instruction {
155        opcode: SystemOpcode::TERMINATE.global_opcode(),
156        c: F::TWO,
157        ..Default::default()
158    }
159}
160
161pub fn nop<F: PrimeField32>() -> Instruction<F> {
162    Instruction {
163        opcode: SystemOpcode::PHANTOM.global_opcode(),
164        ..Default::default()
165    }
166}
167
168/// Converts our memory image (u32 -> [u8; 4]) into Vm memory image ((as=2, address) -> byte)
169pub fn elf_memory_image_to_openvm_memory_image(
170    memory_image: BTreeMap<u32, u32>,
171) -> SparseMemoryImage {
172    let mut result = SparseMemoryImage::new();
173    for (addr, word) in memory_image {
174        for (i, byte) in word.to_le_bytes().into_iter().enumerate() {
175            result.insert((RV32_MEMORY_AS, addr + i as u32), byte);
176        }
177    }
178    result
179}