Skip to content

Commit

Permalink
Merge branch 'main' into wasm-doc
Browse files Browse the repository at this point in the history
  • Loading branch information
sebasti810 authored Jul 22, 2024
2 parents e87dcfe + 9842060 commit 5aff30f
Show file tree
Hide file tree
Showing 42 changed files with 2,399 additions and 712 deletions.
22 changes: 11 additions & 11 deletions common/src/rv_trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,11 +348,11 @@ impl ELFInstruction {
None => false
};
flags[10] = matches!(self.opcode,
RV32IM::VIRTUAL_ASSERT_EQ |
RV32IM::VIRTUAL_ASSERT_LTE |
RV32IM::VIRTUAL_ASSERT_LTU |
RV32IM::VIRTUAL_ASSERT_LT_ABS |
RV32IM::VIRTUAL_ASSERT_EQ_SIGNS,
RV32IM::VIRTUAL_ASSERT_EQ |
RV32IM::VIRTUAL_ASSERT_LTE |
RV32IM::VIRTUAL_ASSERT_VALID_SIGNED_REMAINDER |
RV32IM::VIRTUAL_ASSERT_VALID_UNSIGNED_REMAINDER |
RV32IM::VIRTUAL_ASSERT_VALID_DIV0,
);

flags
Expand Down Expand Up @@ -445,11 +445,11 @@ pub enum RV32IM {
// Virtual instructions
VIRTUAL_MOVSIGN,
VIRTUAL_ADVICE,
VIRTUAL_ASSERT_LTU,
VIRTUAL_ASSERT_LTE,
VIRTUAL_ASSERT_LT_ABS,
VIRTUAL_ASSERT_EQ_SIGNS,
VIRTUAL_ASSERT_VALID_UNSIGNED_REMAINDER,
VIRTUAL_ASSERT_VALID_SIGNED_REMAINDER,
VIRTUAL_ASSERT_EQ,
VIRTUAL_ASSERT_VALID_DIV0,
}

impl FromStr for RV32IM {
Expand Down Expand Up @@ -577,9 +577,9 @@ impl RV32IM {
RV32IM::BGEU |
RV32IM::VIRTUAL_ASSERT_EQ |
RV32IM::VIRTUAL_ASSERT_LTE |
RV32IM::VIRTUAL_ASSERT_LTU |
RV32IM::VIRTUAL_ASSERT_LT_ABS |
RV32IM::VIRTUAL_ASSERT_EQ_SIGNS => RV32InstructionFormat::SB,
RV32IM::VIRTUAL_ASSERT_VALID_DIV0 |
RV32IM::VIRTUAL_ASSERT_VALID_SIGNED_REMAINDER |
RV32IM::VIRTUAL_ASSERT_VALID_UNSIGNED_REMAINDER => RV32InstructionFormat::SB,

RV32IM::LUI |
RV32IM::AUIPC |
Expand Down
7 changes: 5 additions & 2 deletions jolt-core/src/host/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use core::str::FromStr;
use std::{
fs::{self, File},
io::{self, Write},
io::{self, Read, Write},
path::PathBuf,
process::Command,
};
Expand Down Expand Up @@ -159,7 +159,10 @@ impl Program {
pub fn decode(&mut self) -> (Vec<ELFInstruction>, Vec<(u64, u8)>) {
self.build();
let elf = self.elf.as_ref().unwrap();
tracer::decode(elf)
let mut elf_file = File::open(elf).unwrap();
let mut elf_contents = Vec::new();
elf_file.read_to_end(&mut elf_contents).unwrap();
tracer::decode(&elf_contents)
}

// TODO(moodlezoup): Make this generic over InstructionSet
Expand Down
8 changes: 4 additions & 4 deletions jolt-core/src/jolt/instruction/bge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use super::{slt::SLTInstruction, JoltInstruction, SubtableIndices};
use crate::{
field::JoltField,
jolt::subtable::{
eq::EqSubtable, eq_abs::EqAbsSubtable, eq_msb::EqMSBSubtable, gt_msb::GtMSBSubtable,
lt_abs::LtAbsSubtable, ltu::LtuSubtable, LassoSubtable,
eq::EqSubtable, eq_abs::EqAbsSubtable, left_msb::LeftMSBSubtable, lt_abs::LtAbsSubtable,
ltu::LtuSubtable, right_msb::RightMSBSubtable, LassoSubtable,
},
utils::instruction_utils::chunk_and_concatenate_operands,
};
Expand Down Expand Up @@ -35,8 +35,8 @@ impl JoltInstruction for BGEInstruction {
_: usize,
) -> Vec<(Box<dyn LassoSubtable<F>>, SubtableIndices)> {
vec![
(Box::new(GtMSBSubtable::new()), SubtableIndices::from(0)),
(Box::new(EqMSBSubtable::new()), SubtableIndices::from(0)),
(Box::new(LeftMSBSubtable::new()), SubtableIndices::from(0)),
(Box::new(RightMSBSubtable::new()), SubtableIndices::from(0)),
(Box::new(LtuSubtable::new()), SubtableIndices::from(1..C)),
(Box::new(EqSubtable::new()), SubtableIndices::from(1..C)),
(Box::new(LtAbsSubtable::new()), SubtableIndices::from(0)),
Expand Down
287 changes: 287 additions & 0 deletions jolt-core/src/jolt/instruction/div.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,287 @@
use common::constants::virtual_register_index;
use tracer::{ELFInstruction, RVTraceRow, RegisterState, RV32IM};

use super::VirtualInstructionSequence;
use crate::jolt::instruction::{
add::ADDInstruction, beq::BEQInstruction, mul::MULInstruction,
virtual_advice::ADVICEInstruction, virtual_assert_valid_div0::AssertValidDiv0Instruction,
virtual_assert_valid_signed_remainder::AssertValidSignedRemainderInstruction, JoltInstruction,
};
/// Perform signed division and return the result
pub struct DIVInstruction<const WORD_SIZE: usize>;

impl<const WORD_SIZE: usize> VirtualInstructionSequence for DIVInstruction<WORD_SIZE> {
fn virtual_sequence(trace_row: RVTraceRow) -> Vec<RVTraceRow> {
assert_eq!(trace_row.instruction.opcode, RV32IM::DIV);
// DIV operands
let x = trace_row.register_state.rs1_val.unwrap();
let y = trace_row.register_state.rs2_val.unwrap();
// DIV source registers
let r_x = trace_row.instruction.rs1;
let r_y = trace_row.instruction.rs2;
// Virtual registers used in sequence
let v_0 = Some(virtual_register_index(0));
let v_r: Option<u64> = Some(virtual_register_index(1));
let v_qy = Some(virtual_register_index(2));

let mut virtual_sequence = vec![];

let (quotient, remainder) = match WORD_SIZE {
32 => {
let mut quotient = x as i32 / y as i32;
let mut remainder = x as i32 % y as i32;
if (remainder < 0 && (y as i32) > 0) || (remainder > 0 && (y as i32) < 0) {
remainder += y as i32;
quotient -= 1;
}
(quotient as u32 as u64, remainder as u32 as u64)
}
64 => {
let mut quotient = x as i64 / y as i64;
let mut remainder = x as i64 % y as i64;
if (remainder < 0 && (y as i64) > 0) || (remainder > 0 && (y as i64) < 0) {
remainder += y as i64;
quotient -= 1;
}
(quotient as u64, remainder as u64)
}
_ => panic!("Unsupported WORD_SIZE: {}", WORD_SIZE),
};

let q = ADVICEInstruction::<WORD_SIZE>(quotient).lookup_entry();
virtual_sequence.push(RVTraceRow {
instruction: ELFInstruction {
address: trace_row.instruction.address,
opcode: RV32IM::VIRTUAL_ADVICE,
rs1: None,
rs2: None,
rd: trace_row.instruction.rd,
imm: None,
virtual_sequence_index: Some(virtual_sequence.len()),
},
register_state: RegisterState {
rs1_val: None,
rs2_val: None,
rd_post_val: Some(q),
},
memory_state: None,
advice_value: Some(quotient), // What should advice value be here?
});

let r = ADVICEInstruction::<WORD_SIZE>(remainder).lookup_entry();
virtual_sequence.push(RVTraceRow {
instruction: ELFInstruction {
address: trace_row.instruction.address,
opcode: RV32IM::VIRTUAL_ADVICE,
rs1: None,
rs2: None,
rd: v_r,
imm: None,
virtual_sequence_index: Some(virtual_sequence.len()),
},
register_state: RegisterState {
rs1_val: None,
rs2_val: None,
rd_post_val: Some(r),
},
memory_state: None,
advice_value: Some(remainder), // What should advice value be here?
});

let is_valid: u64 = AssertValidSignedRemainderInstruction::<WORD_SIZE>(r, y).lookup_entry();
assert_eq!(is_valid, 1);
virtual_sequence.push(RVTraceRow {
instruction: ELFInstruction {
address: trace_row.instruction.address,
opcode: RV32IM::VIRTUAL_ASSERT_VALID_SIGNED_REMAINDER,
rs1: v_r,
rs2: r_y,
rd: None,
imm: None,
virtual_sequence_index: Some(virtual_sequence.len()),
},
register_state: RegisterState {
rs1_val: Some(r),
rs2_val: Some(y),
rd_post_val: None,
},
memory_state: None,
advice_value: None,
});

let is_valid: u64 = AssertValidDiv0Instruction::<WORD_SIZE>(y, q).lookup_entry();
assert_eq!(is_valid, 1);
virtual_sequence.push(RVTraceRow {
instruction: ELFInstruction {
address: trace_row.instruction.address,
opcode: RV32IM::VIRTUAL_ASSERT_VALID_DIV0,
rs1: r_y,
rs2: trace_row.instruction.rd,
rd: None,
imm: None,
virtual_sequence_index: Some(virtual_sequence.len()),
},
register_state: RegisterState {
rs1_val: Some(y),
rs2_val: Some(q),
rd_post_val: None,
},
memory_state: None,
advice_value: None,
});

let q_y = MULInstruction::<WORD_SIZE>(q, y).lookup_entry();
virtual_sequence.push(RVTraceRow {
instruction: ELFInstruction {
address: trace_row.instruction.address,
opcode: RV32IM::MUL,
rs1: trace_row.instruction.rd,
rs2: r_y,
rd: v_qy,
imm: None,
virtual_sequence_index: Some(virtual_sequence.len()),
},
register_state: RegisterState {
rs1_val: Some(q),
rs2_val: Some(y),
rd_post_val: Some(q_y),
},
memory_state: None,
advice_value: None,
});

let add_0 = ADDInstruction::<WORD_SIZE>(q_y, r).lookup_entry();
virtual_sequence.push(RVTraceRow {
instruction: ELFInstruction {
address: trace_row.instruction.address,
opcode: RV32IM::ADD,
rs1: v_qy,
rs2: v_r,
rd: v_0,
imm: None,
virtual_sequence_index: Some(virtual_sequence.len()),
},
register_state: RegisterState {
rs1_val: Some(q_y),
rs2_val: Some(r),
rd_post_val: Some(add_0),
},
memory_state: None,
advice_value: None,
});

let _assert_eq = BEQInstruction(add_0, x).lookup_entry();
virtual_sequence.push(RVTraceRow {
instruction: ELFInstruction {
address: trace_row.instruction.address,
opcode: RV32IM::VIRTUAL_ASSERT_EQ,
rs1: v_0,
rs2: r_x,
rd: None,
imm: None,
virtual_sequence_index: Some(virtual_sequence.len()),
},
register_state: RegisterState {
rs1_val: Some(add_0),
rs2_val: Some(x),
rd_post_val: None,
},
memory_state: None,
advice_value: None,
});

virtual_sequence
}
}

#[cfg(test)]
mod test {
use ark_std::test_rng;
use common::constants::REGISTER_COUNT;
use rand_chacha::rand_core::RngCore;

use crate::jolt::vm::rv32i_vm::RV32I;

use super::*;

#[test]
// TODO(moodlezoup): Turn this into a macro, similar to the `jolt_instruction_test` macro
fn div_virtual_sequence_32() {
let mut rng = test_rng();

let r_x = rng.next_u64() % 32;
let r_y = rng.next_u64() % 32;
let rd = rng.next_u64() % 32;

let x = rng.next_u32() as u64;
let y = if r_y == r_x { x } else { rng.next_u32() as u64 };

let mut quotient = x as i32 / y as i32;
let remainder = x as i32 % y as i32;
if (remainder < 0 && (y as i32) > 0) || (remainder > 0 && (y as i32) < 0) {
quotient -= 1;
}
let result = quotient as u32 as u64;

let div_trace_row = RVTraceRow {
instruction: ELFInstruction {
address: rng.next_u64(),
opcode: RV32IM::DIV,
rs1: Some(r_x),
rs2: Some(r_y),
rd: Some(rd),
imm: None,
virtual_sequence_index: None,
},
register_state: RegisterState {
rs1_val: Some(x),
rs2_val: Some(y),
rd_post_val: Some(result as u64),
},
memory_state: None,
advice_value: None,
};

let virtual_sequence = DIVInstruction::<32>::virtual_sequence(div_trace_row);
let mut registers = vec![0u64; REGISTER_COUNT as usize];
registers[r_x as usize] = x;
registers[r_y as usize] = y;

for row in virtual_sequence {
if let Some(rs1_val) = row.register_state.rs1_val {
assert_eq!(registers[row.instruction.rs1.unwrap() as usize], rs1_val);
}
if let Some(rs2_val) = row.register_state.rs2_val {
assert_eq!(registers[row.instruction.rs2.unwrap() as usize], rs2_val);
}

let lookup = RV32I::try_from(&row).unwrap();
let output = lookup.lookup_entry();
if let Some(rd) = row.instruction.rd {
registers[rd as usize] = output;
assert_eq!(
registers[rd as usize],
row.register_state.rd_post_val.unwrap()
);
} else {
assert!(output == 1)
}
}

for (index, val) in registers.iter().enumerate() {
if index as u64 == r_x {
// Check that r_x hasn't been clobbered
assert_eq!(*val, x);
} else if index as u64 == r_y {
// Check that r_y hasn't been clobbered
assert_eq!(*val, y);
} else if index as u64 == rd {
// Check that result was written to rd
assert_eq!(*val, result as u64);
} else if index < 32 {
// None of the other "real" registers were touched
assert_eq!(*val, 0);
}
}
}
}
Loading

0 comments on commit 5aff30f

Please sign in to comment.