From b8f406566901560427f71afcaebd4b6449e3f049 Mon Sep 17 00:00:00 2001 From: Danny Willems Date: Tue, 19 Nov 2024 20:59:40 +0100 Subject: [PATCH 1/4] RISC-V32: aesthetic --- o1vm/src/interpreters/riscv32i/interpreter.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/o1vm/src/interpreters/riscv32i/interpreter.rs b/o1vm/src/interpreters/riscv32i/interpreter.rs index efe4a947ae..df923aa1af 100644 --- a/o1vm/src/interpreters/riscv32i/interpreter.rs +++ b/o1vm/src/interpreters/riscv32i/interpreter.rs @@ -106,7 +106,6 @@ pub enum UJInstruction { #[derive( Debug, Clone, Copy, Eq, PartialEq, EnumCount, EnumIter, Default, Hash, Ord, PartialOrd, )] - pub enum SyscallInstruction { #[default] SyscallSuccess, @@ -1101,7 +1100,8 @@ pub fn interpret_itype(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 = { From ac91cfe40946873ea685cc448ac0490d016df19b Mon Sep 17 00:00:00 2001 From: Danny Willems Date: Tue, 19 Nov 2024 21:00:23 +0100 Subject: [PATCH 2/4] RISC-V32: sketch support for M types In order to use https://doc.rust-lang.org/nightly/rustc/platform-support/riscv32im-risc0-zkvm-elf.html, we do need to support the m instruction types. --- o1vm/src/interpreters/riscv32i/column.rs | 16 +++- o1vm/src/interpreters/riscv32i/interpreter.rs | 78 +++++++++++++++++++ 2 files changed, 92 insertions(+), 2 deletions(-) diff --git a/o1vm/src/interpreters/riscv32i/column.rs b/o1vm/src/interpreters/riscv32i/column.rs index 577d8841d4..871db4a546 100644 --- a/o1vm/src/interpreters/riscv32i/column.rs +++ b/o1vm/src/interpreters/riscv32i/column.rs @@ -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, }; @@ -84,6 +84,18 @@ impl From 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 + } } } } diff --git a/o1vm/src/interpreters/riscv32i/interpreter.rs b/o1vm/src/interpreters/riscv32i/interpreter.rs index df923aa1af..8d3628471f 100644 --- a/o1vm/src/interpreters/riscv32i/interpreter.rs +++ b/o1vm/src/interpreters/riscv32i/interpreter.rs @@ -13,6 +13,7 @@ pub enum Instruction { UType(UInstruction), UJType(UJInstruction), SyscallType(SyscallInstruction), + MType(MInstruction), } // See @@ -111,6 +112,55 @@ pub enum SyscallInstruction { SyscallSuccess, } +/// M extension instructions +/// Following +#[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; @@ -166,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() + } } } } @@ -180,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), } } } @@ -265,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; @@ -1068,6 +1141,7 @@ pub fn interpret_instruction(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), } } @@ -1247,3 +1321,7 @@ pub fn interpret_syscall(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: &mut Env, _instr: MInstruction) { + unimplemented!("TODO") +} From fe054c80ceb7b7017b98efc79165fac848854d56 Mon Sep 17 00:00:00 2001 From: Danny Willems Date: Tue, 19 Nov 2024 21:02:53 +0100 Subject: [PATCH 3/4] o1vm: rename riscv32i in riscv32im In order to match the newly added M instruction type --- o1vm/src/interpreters/mod.rs | 4 ++-- o1vm/src/interpreters/{riscv32i => riscv32im}/column.rs | 0 o1vm/src/interpreters/{riscv32i => riscv32im}/constraints.rs | 2 +- o1vm/src/interpreters/{riscv32i => riscv32im}/interpreter.rs | 4 ++-- o1vm/src/interpreters/{riscv32i => riscv32im}/mod.rs | 0 o1vm/src/interpreters/{riscv32i => riscv32im}/registers.rs | 0 o1vm/src/interpreters/{riscv32i => riscv32im}/witness.rs | 0 o1vm/tests/test_riscv_elf.rs | 2 +- 8 files changed, 6 insertions(+), 6 deletions(-) rename o1vm/src/interpreters/{riscv32i => riscv32im}/column.rs (100%) rename o1vm/src/interpreters/{riscv32i => riscv32im}/constraints.rs (99%) rename o1vm/src/interpreters/{riscv32i => riscv32im}/interpreter.rs (99%) rename o1vm/src/interpreters/{riscv32i => riscv32im}/mod.rs (100%) rename o1vm/src/interpreters/{riscv32i => riscv32im}/registers.rs (100%) rename o1vm/src/interpreters/{riscv32i => riscv32im}/witness.rs (100%) diff --git a/o1vm/src/interpreters/mod.rs b/o1vm/src/interpreters/mod.rs index 799690c0a0..426192f514 100644 --- a/o1vm/src/interpreters/mod.rs +++ b/o1vm/src/interpreters/mod.rs @@ -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; diff --git a/o1vm/src/interpreters/riscv32i/column.rs b/o1vm/src/interpreters/riscv32im/column.rs similarity index 100% rename from o1vm/src/interpreters/riscv32i/column.rs rename to o1vm/src/interpreters/riscv32im/column.rs diff --git a/o1vm/src/interpreters/riscv32i/constraints.rs b/o1vm/src/interpreters/riscv32im/constraints.rs similarity index 99% rename from o1vm/src/interpreters/riscv32i/constraints.rs rename to o1vm/src/interpreters/riscv32im/constraints.rs index 300af50897..c8b7bec2c2 100644 --- a/o1vm/src/interpreters/riscv32i/constraints.rs +++ b/o1vm/src/interpreters/riscv32im/constraints.rs @@ -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}; diff --git a/o1vm/src/interpreters/riscv32i/interpreter.rs b/o1vm/src/interpreters/riscv32im/interpreter.rs similarity index 99% rename from o1vm/src/interpreters/riscv32i/interpreter.rs rename to o1vm/src/interpreters/riscv32im/interpreter.rs index 8d3628471f..ec10411db5 100644 --- a/o1vm/src/interpreters/riscv32i/interpreter.rs +++ b/o1vm/src/interpreters/riscv32im/interpreter.rs @@ -347,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 diff --git a/o1vm/src/interpreters/riscv32i/mod.rs b/o1vm/src/interpreters/riscv32im/mod.rs similarity index 100% rename from o1vm/src/interpreters/riscv32i/mod.rs rename to o1vm/src/interpreters/riscv32im/mod.rs diff --git a/o1vm/src/interpreters/riscv32i/registers.rs b/o1vm/src/interpreters/riscv32im/registers.rs similarity index 100% rename from o1vm/src/interpreters/riscv32i/registers.rs rename to o1vm/src/interpreters/riscv32im/registers.rs diff --git a/o1vm/src/interpreters/riscv32i/witness.rs b/o1vm/src/interpreters/riscv32im/witness.rs similarity index 100% rename from o1vm/src/interpreters/riscv32i/witness.rs rename to o1vm/src/interpreters/riscv32im/witness.rs diff --git a/o1vm/tests/test_riscv_elf.rs b/o1vm/tests/test_riscv_elf.rs index f6cf008924..6544736fac 100644 --- a/o1vm/tests/test_riscv_elf.rs +++ b/o1vm/tests/test_riscv_elf.rs @@ -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, From 241a86bccdf85fb0bb870babd885f5d5b79b4d02 Mon Sep 17 00:00:00 2001 From: Danny Willems Date: Tue, 19 Nov 2024 21:04:52 +0100 Subject: [PATCH 4/4] o1vm/ELF: rename parse_riscv32i in parse_riscv32 The ELF loader should be the same either it is riscv32i or riscv32im. Therefore, renaming into riscv32 (ISA + 32bits only support) makes sense. --- o1vm/src/elf_loader.rs | 4 ++-- o1vm/tests/test_elf_loader.rs | 2 +- o1vm/tests/test_riscv_elf.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/o1vm/src/elf_loader.rs b/o1vm/src/elf_loader.rs index 1a6dd7ccb8..0fc26a4788 100644 --- a/o1vm/src/elf_loader.rs +++ b/o1vm/src/elf_loader.rs @@ -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 { +pub fn parse_riscv32(path: &Path) -> Result { 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(); diff --git a/o1vm/tests/test_elf_loader.rs b/o1vm/tests/test_elf_loader.rs index 448fea92a0..67095cfd95 100644 --- a/o1vm/tests/test_elf_loader.rs +++ b/o1vm/tests/test_elf_loader.rs @@ -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); diff --git a/o1vm/tests/test_riscv_elf.rs b/o1vm/tests/test_riscv_elf.rs index 6544736fac..47a8c3251e 100644 --- a/o1vm/tests/test_riscv_elf.rs +++ b/o1vm/tests/test_riscv_elf.rs @@ -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::::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);