Skip to content

Commit

Permalink
Merge pull request #2762 from o1-labs/dw/riscv32-support-m
Browse files Browse the repository at this point in the history
o1vm/riscv32: sketch M instruction type support
  • Loading branch information
dannywillems authored Nov 20, 2024
2 parents 119b5a1 + 241a86b commit 0249bbc
Show file tree
Hide file tree
Showing 10 changed files with 104 additions and 14 deletions.
4 changes: 2 additions & 2 deletions o1vm/src/elf_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ use log::debug;
use std::{collections::HashMap, path::Path};

/// Parse an ELF file and return the parsed data as a structure that is expected
/// by the o1vm RISC-V 32i edition.
/// by the o1vm RISC-V 32 bits edition.
// FIXME: parametrize by an architecture. We should return a state depending on the
// architecture. In the meantime, we can have parse_riscv32i and parse_mips.
// FIXME: for now, we return a State structure, either for RISC-V 32i or MIPS.
// We should return a structure specifically built for the o1vm, and not tight
// to Cannon. It will be done in a future PR to avoid breaking the current code
// and have a huge diff.
pub fn parse_riscv32i(path: &Path) -> Result<State, String> {
pub fn parse_riscv32(path: &Path) -> Result<State, String> {
debug!("Start parsing the ELF file to load a RISC-V 32i compatible state");
let file_data = std::fs::read(path).expect("Could not read file.");
let slice = file_data.as_slice();
Expand Down
4 changes: 2 additions & 2 deletions o1vm/src/interpreters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub mod keccak;
/// An interpreter for the MIPS instruction set.
pub mod mips;

/// An interpreter for the RISC-V 32I instruction set, following the specification
/// An interpreter for the RISC-V 32IM instruction set, following the specification
/// on
/// [riscv.org](https://riscv.org/wp-content/uploads/2019/12/riscv-spec-20191213.pdf).
pub mod riscv32i;
pub mod riscv32im;
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use super::{
interpreter::{
IInstruction,
Instruction::{self, IType, RType, SBType, SType, SyscallType, UJType, UType},
RInstruction, SBInstruction, SInstruction, UInstruction, UJInstruction,
Instruction::{self, IType, MType, RType, SBType, SType, SyscallType, UJType, UType},
RInstruction, SBInstruction, SInstruction, SyscallInstruction, UInstruction, UJInstruction,
},
INSTRUCTION_SET_SIZE, SCRATCH_SIZE,
};
Expand Down Expand Up @@ -84,6 +84,18 @@ impl From<Instruction> for usize {
+ UJInstruction::COUNT
+ syscalltype as usize
}
MType(mtype) => {
SCRATCH_SIZE
+ 1
+ RInstruction::COUNT
+ IInstruction::COUNT
+ SInstruction::COUNT
+ SBInstruction::COUNT
+ UInstruction::COUNT
+ UJInstruction::COUNT
+ SyscallInstruction::COUNT
+ mtype as usize
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use super::{
INSTRUCTION_SET_SIZE,
};
use crate::{
interpreters::riscv32i::{constraints::ConstantTerm::Literal, SCRATCH_SIZE},
interpreters::riscv32im::{constraints::ConstantTerm::Literal, SCRATCH_SIZE},
lookups::Lookup,
};
use ark_ff::{Field, One};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub enum Instruction {
UType(UInstruction),
UJType(UJInstruction),
SyscallType(SyscallInstruction),
MType(MInstruction),
}

// See
Expand Down Expand Up @@ -106,12 +107,60 @@ pub enum UJInstruction {
#[derive(
Debug, Clone, Copy, Eq, PartialEq, EnumCount, EnumIter, Default, Hash, Ord, PartialOrd,
)]

pub enum SyscallInstruction {
#[default]
SyscallSuccess,
}

/// M extension instructions
/// Following <https://msyksphinz-self.github.io/riscv-isadoc/html/rvm.html>
#[derive(
Debug, Clone, Copy, Eq, PartialEq, EnumCount, EnumIter, Default, Hash, Ord, PartialOrd,
)]
pub enum MInstruction {
/// Format: `mul rd, rs1, rs2`
/// Description: performs an 32-bit 32-bit multiplication of signed rs1
/// by signed rs2 and places the lower 32 bits in the destination register.
/// Implementation: `x[rd] = x[rs1] * x[rs2]`
#[default]
Mul, // mul
/// Format: `mulh rd, rs1, rs2`
/// Description: performs an 32-bit 32-bit multiplication of signed rs1 by
/// signed rs2 and places the upper 32 bits in the destination register.
/// Implementation: `x[rd] = (x[rs1] * x[rs2]) >> 32`
Mulh, // mulh
/// Format: `mulhsu rd, rs1, rs2`
/// Description: performs an 32-bit 32-bit multiplication of signed rs1 by
/// unsigned rs2 and places the upper 32 bits in the destination register.
/// Implementation: `x[rd] = (x[rs1] * x[rs2]) >> 32`
Mulhsu, // mulhsu
/// Format: `mulhu rd, rs1, rs2`
/// Description: performs an 32-bit 32-bit multiplication of unsigned rs1 by
/// unsigned rs2 and places the upper 32 bits in the destination register.
/// Implementation: `x[rd] = (x[rs1] * x[rs2]) >> 32`
Mulhu, // mulhu
/// Format: `div rd, rs1, rs2`
/// Description: perform an 32 bits by 32 bits signed integer division of
/// rs1 by rs2, rounding towards zero
/// Implementation: `x[rd] = x[rs1] /s x[rs2]`
Div, // div
/// Format: `divu rd, rs1, rs2`
/// Description: performs an 32 bits by 32 bits unsigned integer division of
/// rs1 by rs2, rounding towards zero.
/// Implementation: `x[rd] = x[rs1] /u x[rs2]`
Divu, // divu
/// Format: `rem rd, rs1, rs2`
/// Description: performs an 32 bits by 32 bits signed integer reminder of
/// rs1 by rs2.
/// Implementation: `x[rd] = x[rs1] %s x[rs2]`
Rem, // rem
/// Format: `remu rd, rs1, rs2`
/// Description: performs an 32 bits by 32 bits unsigned integer reminder of
/// rs1 by rs2.
/// Implementation: `x[rd] = x[rs1] %u x[rs2]`
Remu, // remu
}

impl IntoIterator for Instruction {
type Item = Instruction;
type IntoIter = std::vec::IntoIter<Instruction>;
Expand Down Expand Up @@ -167,6 +216,13 @@ impl IntoIterator for Instruction {
}
iter_contents.into_iter()
}
Instruction::MType(_) => {
let mut iter_contents = Vec::with_capacity(MInstruction::COUNT);
for mtype in MInstruction::iter() {
iter_contents.push(Instruction::MType(mtype));
}
iter_contents.into_iter()
}
}
}
}
Expand All @@ -181,6 +237,7 @@ impl std::fmt::Display for Instruction {
Instruction::UType(utype) => write!(f, "{}", utype),
Instruction::UJType(ujtype) => write!(f, "{}", ujtype),
Instruction::SyscallType(_syscall) => write!(f, "ecall"),
Instruction::MType(mtype) => write!(f, "{}", mtype),
}
}
}
Expand Down Expand Up @@ -266,6 +323,21 @@ impl std::fmt::Display for UJInstruction {
}
}

impl std::fmt::Display for MInstruction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MInstruction::Mul => write!(f, "mul"),
MInstruction::Mulh => write!(f, "mulh"),
MInstruction::Mulhsu => write!(f, "mulhsu"),
MInstruction::Mulhu => write!(f, "mulhu"),
MInstruction::Div => write!(f, "div"),
MInstruction::Divu => write!(f, "divu"),
MInstruction::Rem => write!(f, "rem"),
MInstruction::Remu => write!(f, "remu"),
}
}
}

pub trait InterpreterEnv {
/// A position can be seen as an indexed variable
type Position;
Expand All @@ -275,10 +347,10 @@ pub trait InterpreterEnv {
/// The variables are "freed" after each step/instruction.
/// The variable allocation can be seen as an allocation on a stack that is
/// popped after each step execution.
/// At the moment, [crate::interpreters::riscv32i::SCRATCH_SIZE]
/// At the moment, [crate::interpreters::riscv32im::SCRATCH_SIZE]
/// elements can be allocated. If more temporary variables are required for
/// an instruction, increase the value
/// [crate::interpreters::riscv32i::SCRATCH_SIZE]
/// [crate::interpreters::riscv32im::SCRATCH_SIZE]
fn alloc_scratch(&mut self) -> Self::Position;

type Variable: Clone
Expand Down Expand Up @@ -1069,6 +1141,7 @@ pub fn interpret_instruction<Env: InterpreterEnv>(env: &mut Env, instr: Instruct
Instruction::UType(utype) => interpret_utype(env, utype),
Instruction::UJType(ujtype) => interpret_ujtype(env, ujtype),
Instruction::SyscallType(syscall) => interpret_syscall(env, syscall),
Instruction::MType(mtype) => interpret_mtype(env, mtype),
}
}

Expand Down Expand Up @@ -1101,7 +1174,8 @@ pub fn interpret_itype<Env: InterpreterEnv>(env: &mut Env, instr: IInstruction)
/* verify opcode is 7 bits */
env.range_check8(&opcode, 7);

/* decode and parse bits from the full 32 bits instruction in accordance with the Rtype riscV spec
/* decode and parse bits from the full 32 bits instruction in accordance
* with the Rtype RISC-V spec
https://www.cs.cornell.edu/courses/cs3410/2024fa/assignments/cpusim/riscv-instructions.pdf
*/
let rd = {
Expand Down Expand Up @@ -1247,3 +1321,7 @@ pub fn interpret_syscall<Env: InterpreterEnv>(env: &mut Env, _instr: SyscallInst
// FIXME: check if it is syscall success. There is only one syscall atm
env.set_halted(Env::constant(1));
}

pub fn interpret_mtype<Env: InterpreterEnv>(_env: &mut Env, _instr: MInstruction) {
unimplemented!("TODO")
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion o1vm/tests/test_elf_loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ fn test_correctly_parsing_elf() {
let path = curr_dir.join(std::path::PathBuf::from(
"resources/programs/riscv32i/fibonacci",
));
let state = o1vm::elf_loader::parse_riscv32i(&path).unwrap();
let state = o1vm::elf_loader::parse_riscv32(&path).unwrap();

// This is the output we get by running objdump -d fibonacci
assert_eq!(state.pc, 69932);
Expand Down
4 changes: 2 additions & 2 deletions o1vm/tests/test_riscv_elf.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use mina_curves::pasta::Fp;
use o1vm::interpreters::riscv32i::{
use o1vm::interpreters::riscv32im::{
interpreter::{IInstruction, Instruction, RInstruction},
witness::Env,
PAGE_SIZE,
Expand Down Expand Up @@ -29,7 +29,7 @@ fn test_no_action() {
let path = curr_dir.join(std::path::PathBuf::from(
"resources/programs/riscv32i/no-action",
));
let state = o1vm::elf_loader::parse_riscv32i(&path).unwrap();
let state = o1vm::elf_loader::parse_riscv32(&path).unwrap();
let mut witness = Env::<Fp>::create(PAGE_SIZE.try_into().unwrap(), state);
// This is the output we get by running objdump -d no-action
assert_eq!(witness.registers.current_instruction_pointer, 69844);
Expand Down

0 comments on commit 0249bbc

Please sign in to comment.