diff --git a/assembler/grammar/assembly.pest b/assembler/grammar/assembly.pest index 82cea9e2..2265213d 100644 --- a/assembler/grammar/assembly.pest +++ b/assembler/grammar/assembly.pest @@ -3,7 +3,7 @@ comment = { ";" ~ (!"\n" ~ ANY)* ~ "\n" } label = { (!":" ~ !"\n" ~ ANY)+ ~ ":" ~ "\n" } instruction = { mnemonic ~ (operand ~ ", "?)+? ~ "\n"? } mnemonic = { - "lw" | "sw" | "loadu8" | "loads8" | "storeu8" | "jalv" | "jal" | "beqi" | "beq" | "bnei" | "bne" | "imm32" | "stop" | + "lw" | "sw" | "loadu8" | "tloadu8" | "loads8" | "storeu8" | "jalv" | "jal" | "beqi" | "beq" | "bnei" | "bne" | "imm32" | "stop" | "advread" | "advwrite" | "addi" | "add" | "subi" | "sub" | "muli" | "mul" | "mulhsi"| "mulhui"| "mulhs"| "mulhu" | "divi" | "div" | "sdiv" | "sdivi" | "ilte" | "ltei" | "lte" | "ilt" | "lti" | "lt" | "shli" | "shl" | "shri" | "shr" | "srai" | "sra" | diff --git a/assembler/src/lib.rs b/assembler/src/lib.rs index b3ed64be..5154c9a6 100644 --- a/assembler/src/lib.rs +++ b/assembler/src/lib.rs @@ -59,6 +59,7 @@ pub fn assemble(input: &str) -> Result, String> { // Core CPU "lw" => LOAD32, "loadu8" => LOADU8, + "tloadu8" => TLOADU8, "loads8" => LOADS8, "sw" => STORE32, "storeu8" => STOREU8, @@ -104,7 +105,7 @@ pub fn assemble(input: &str) -> Result, String> { // Insert zero operands if necessary match mnemonic { - "lw" | "loadu8" | "loads8" => { + "lw" | "loadu8" | "tloadu8" | "loads8" => { // (a, 0, c, 0, 0) operands.insert(1, 0); operands.extend(vec![0; 2]); diff --git a/basic/src/lib.rs b/basic/src/lib.rs index 29aef541..8b5e3335 100644 --- a/basic/src/lib.rs +++ b/basic/src/lib.rs @@ -42,6 +42,7 @@ use valida_cpu::{ BeqInstruction, BneInstruction, Imm32Instruction, JalInstruction, JalvInstruction, Load32Instruction, LoadFpInstruction, LoadS8Instruction, LoadU8Instruction, ReadAdviceInstruction, StopInstruction, Store32Instruction, StoreU8Instruction, + TLoadU8Instruction, }; use valida_cpu::{CpuChip, MachineWithCpuChip}; use valida_machine::__internal::p3_challenger::{CanObserve, FieldChallenger}; @@ -67,6 +68,7 @@ pub struct BasicMachine { // Core instructions load32: Load32Instruction, loadu8: LoadU8Instruction, + tloadu8: TLoadU8Instruction, loads8: LoadS8Instruction, store32: Store32Instruction, storeu8: StoreU8Instruction, @@ -1081,6 +1083,9 @@ impl Machine for BasicMachine { >::OPCODE => { LoadU8Instruction::execute_with_advice::(self, ops, advice) } + >::OPCODE => { + TLoadU8Instruction::execute_with_advice::(self, ops, advice) + } >::OPCODE => { LoadS8Instruction::execute_with_advice::(self, ops, advice) } diff --git a/cpu/src/columns.rs b/cpu/src/columns.rs index 589cb999..0c8333ca 100644 --- a/cpu/src/columns.rs +++ b/cpu/src/columns.rs @@ -49,6 +49,7 @@ pub struct OpcodeFlagCols { pub is_imm_op: T, pub is_load: T, pub is_load_u8: T, + pub is_tload_u8: T, pub is_load_s8: T, pub is_store: T, pub is_store_u8: T, diff --git a/cpu/src/lib.rs b/cpu/src/lib.rs index 424affc0..b65d612a 100644 --- a/cpu/src/lib.rs +++ b/cpu/src/lib.rs @@ -18,7 +18,7 @@ use valida_machine::{ use valida_memory::{MachineWithMemoryChip, Operation as MemoryOperation}; use valida_opcodes::{ BEQ, BNE, BYTES_PER_INSTR, IMM32, JAL, JALV, LOAD32, LOADFP, LOADS8, LOADU8, READ_ADVICE, STOP, - STORE32, STOREU8, + STORE32, STOREU8, TLOADU8, }; use p3_air::VirtualPairCol; @@ -37,6 +37,7 @@ pub enum Operation { StoreU8, Load32, LoadU8, + TLoadU8, LoadS8, Jal, Jalv, @@ -187,6 +188,9 @@ impl CpuChip { Operation::LoadU8 => { cols.opcode_flags.is_load_u8 = SC::Val::one(); } + Operation::TLoadU8 => { + cols.opcode_flags.is_tload_u8 = SC::Val::one(); + } Operation::LoadS8 => { cols.opcode_flags.is_load_s8 = SC::Val::one(); } @@ -365,6 +369,7 @@ pub trait MachineWithCpuChip: MachineWithMemoryChip { instructions!( Load32Instruction, LoadU8Instruction, + TLoadU8Instruction, LoadS8Instruction, Store32Instruction, StoreU8Instruction, @@ -475,6 +480,68 @@ where } } +impl Instruction for TLoadU8Instruction +where + M: MachineWithCpuChip, + F: Field, +{ + const OPCODE: u32 = TLOADU8; + + fn execute(state: &mut M, ops: Operands) { + let opcode = >::OPCODE; + let clk = state.cpu().clock; + let pc = state.cpu().pc; + let fp = state.cpu().fp; + + let read_addr_loc = (fp as i32 + ops.c()) as u32; + + let read_addr = state + .mem_mut() + .read(clk, read_addr_loc, true, pc, opcode, 0, ""); + let read_addr_index = addr_of_word(read_addr.into()); + + // The word from the read address. + let cell = state.mem_mut().read( + clk, + read_addr_index, + true, + pc, + opcode, + 1, + &format!( + "fp = {}, c = {}, [fp+c] = {:?}", + fp as i32, + ops.c() as u32, + read_addr_index + ), + ); + + // The array index of the word for the byte to read from + let index_of_read = index_of_byte(read_addr.into()); + // The byte from the read cell. + let cell_byte: u8 = cell[index_of_read]; + + let write_addr = (state.cpu().fp as i32 + ops.a()) as u32; + // The address, converted to a multiple of 4. + let write_addr_index = addr_of_word(write_addr); + + // The array index of the word for the byte to write to + let index_of_write = index_of_byte(write_addr); + + // The original content of the cell to write to. If the cell is empty, initiate it with a default value. + let cell_write = state.mem_mut().read_or_init(clk, write_addr_index, true); + + // The Word to write, with one byte overwritten to the read byte + let cell_to_write = cell_write.update_byte(cell_byte, index_of_write); + + state + .mem_mut() + .write(clk, write_addr_index, cell_to_write, true); + state.cpu_mut().pc += 1; + state.cpu_mut().push_op(Operation::TLoadU8, opcode, ops); + } +} + impl Instruction for LoadU8Instruction where M: MachineWithCpuChip, @@ -520,7 +587,6 @@ where // The address, converted to a multiple of 4. let write_addr_index = addr_of_word(write_addr); - // The Word to write, with one byte overwritten to the read byte state .mem_mut() .write(clk, write_addr_index, Word::from_u8(cell_byte), true); @@ -575,7 +641,7 @@ where // The address, converted to a multiple of 4. let write_addr_index = addr_of_word(write_addr); - // The Word to write, with one byte overwritten to the read byte + // The Word to write, with the read byte sign extended to 32 bits. let cell_to_write = Word::sign_extend_byte(cell_byte); state diff --git a/machine/src/core.rs b/machine/src/core.rs index c0c25975..2df20383 100644 --- a/machine/src/core.rs +++ b/machine/src/core.rs @@ -42,7 +42,8 @@ impl Word { } } -// The cell is stored in little endian format in the compiler. But the VM stores it in big endian. +/// Update the cell with one byte overwritten to the input byte. +/// The cell is stored in little endian format in the compiler. But the VM stores it in big endian. impl Word { pub fn update_byte(self, byte: u8, loc: usize) -> Self { let result_little_end: [u8; MEMORY_CELL_BYTES] = self.0; diff --git a/machine/src/program.rs b/machine/src/program.rs index 3032f8cf..5872ac2d 100644 --- a/machine/src/program.rs +++ b/machine/src/program.rs @@ -105,6 +105,9 @@ impl InstructionWord { valida_opcodes::LOADU8 => { format!("{}(fp), {}(fp)", self.operands.0[0], self.operands.0[2]) } + valida_opcodes::TLOADU8 => { + format!("{}(fp), {}(fp)", self.operands.0[0], self.operands.0[2]) + } valida_opcodes::LOADS8 => { format!("{}(fp), {}(fp)", self.operands.0[0], self.operands.0[2]) } diff --git a/opcodes/src/lib.rs b/opcodes/src/lib.rs index fee6411f..f91af9d5 100644 --- a/opcodes/src/lib.rs +++ b/opcodes/src/lib.rs @@ -18,6 +18,7 @@ pub enum Opcode { LOADU8 = 11, LOADS8 = 12, STOREU8 = 13, + TLOADU8 = 16, ADD32 = 100, SUB32 = 101, @@ -65,6 +66,7 @@ declare_opcode!(LOADFP); declare_opcode!(LOADU8); declare_opcode!(LOADS8); declare_opcode!(STOREU8); +declare_opcode!(TLOADU8); /// NONDETERMINISTIC declare_opcode!(READ_ADVICE);