Skip to content

Commit

Permalink
refactor: turn python script for computing opcodes into a rust test
Browse files Browse the repository at this point in the history
  • Loading branch information
jan-ferdinand committed Nov 3, 2023
1 parent f380367 commit 92d49b6
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 140 deletions.
111 changes: 0 additions & 111 deletions opcodes.py

This file was deleted.

116 changes: 90 additions & 26 deletions triton-vm/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,13 @@ pub enum AnInstruction<Dest: PartialEq + Default> {
Swap(OpStackElement),

// Control flow
Halt,
Nop,
Skiz,
Call(Dest),
Return,
Recurse,
Assert,
Halt,

// Memory access
ReadMem,
Expand Down Expand Up @@ -161,13 +161,13 @@ impl<Dest: PartialEq + Default> AnInstruction<Dest> {
Divine(_) => 9,
Dup(_) => 17,
Swap(_) => 25,
Halt => 0,
Nop => 8,
Skiz => 2,
Call(_) => 33,
Return => 16,
Recurse => 24,
Assert => 10,
Halt => 0,
ReadMem => 32,
WriteMem => 18,
Hash => 26,
Expand Down Expand Up @@ -204,13 +204,13 @@ impl<Dest: PartialEq + Default> AnInstruction<Dest> {
Divine(_) => "divine",
Dup(_) => "dup",
Swap(_) => "swap",
Halt => "halt",
Nop => "nop",
Skiz => "skiz",
Call(_) => "call",
Return => "return",
Recurse => "recurse",
Assert => "assert",
Halt => "halt",
ReadMem => "read_mem",
WriteMem => "write_mem",
Hash => "hash",
Expand Down Expand Up @@ -273,13 +273,13 @@ impl<Dest: PartialEq + Default> AnInstruction<Dest> {
Divine(x) => Divine(*x),
Dup(x) => Dup(*x),
Swap(x) => Swap(*x),
Halt => Halt,
Nop => Nop,
Skiz => Skiz,
Call(label) => Call(f(label)),
Return => Return,
Recurse => Recurse,
Assert => Assert,
Halt => Halt,
ReadMem => ReadMem,
WriteMem => WriteMem,
Hash => Hash,
Expand Down Expand Up @@ -328,13 +328,13 @@ impl<Dest: PartialEq + Default> AnInstruction<Dest> {
Divine(n) => n.index() as i32,
Dup(_) => 1,
Swap(_) => 0,
Halt => 0,
Nop => 0,
Skiz => -1,
Call(_) => 0,
Return => 0,
Recurse => 0,
Assert => -1,
Halt => 0,
ReadMem => 1,
WriteMem => -1,
Hash => -5,
Expand Down Expand Up @@ -483,13 +483,13 @@ const fn all_instructions_without_args() -> [AnInstruction<BFieldElement>; Instr
Divine(ST0),
Dup(ST0),
Swap(ST0),
Halt,
Nop,
Skiz,
Call(BFIELD_ZERO),
Return,
Recurse,
Assert,
Halt,
ReadMem,
WriteMem,
Hash,
Expand Down Expand Up @@ -605,6 +605,88 @@ mod tests {
use crate::NonDeterminism;
use crate::Program;

#[derive(Debug, Clone, Copy, EnumCount, EnumIter)]
enum InstructionBucket {
HasArg,
ShrinksStack,
IsU32,
}

impl InstructionBucket {
fn contains(&self, instruction: Instruction) -> bool {
match self {
InstructionBucket::HasArg => instruction.has_arg(),
InstructionBucket::ShrinksStack => instruction.shrinks_op_stack(),
InstructionBucket::IsU32 => instruction.is_u32_instruction(),
}
}

fn bit_flag(&self) -> u32 {
match self {
InstructionBucket::HasArg => 0b001,
InstructionBucket::ShrinksStack => 0b010,
InstructionBucket::IsU32 => 0b100,
}
}
}

impl Instruction {
#[must_use]
fn replace_default_argument_if_illegal(self) -> Self {
match self {
Pop(ST0) => Pop(ST1),
Divine(ST0) => Divine(ST1),
Swap(ST0) => Swap(ST1),
_ => self,
}
}

fn flag_set(self) -> u32 {
InstructionBucket::iter()
.map(|bucket| match bucket.contains(self) {
true => bucket.bit_flag(),
false => 0,
})
.fold(0, |x, y| x | y)
}

fn computed_opcode(self) -> u32 {
let mut index_within_flag_set = 0;
for other_instruction in Instruction::iter() {
let other_instruction = other_instruction.replace_default_argument_if_illegal();
if other_instruction == self {
break;
}
if other_instruction.flag_set() == self.flag_set() {
index_within_flag_set += 1;
}
}

index_within_flag_set * 2_u32.pow(InstructionBucket::COUNT as u32) + self.flag_set()
}
}

#[test]
fn computed_and_actual_opcodes_are_identical() {
for instruction in Instruction::iter() {
let instruction = instruction.replace_default_argument_if_illegal();
println!(
"{opcode: >3} {name}",
opcode = instruction.computed_opcode(),
name = instruction.name()
)
}

for instruction in Instruction::iter() {
let instruction = instruction.replace_default_argument_if_illegal();
assert_eq!(
instruction.computed_opcode(),
instruction.opcode(),
"{instruction}"
);
}
}

#[test]
fn opcodes_are_unique() {
let mut opcodes_to_instruction_map = HashMap::new();
Expand Down Expand Up @@ -672,13 +754,6 @@ mod tests {
}
}

#[test]
fn print_all_instructions_and_opcodes() {
for instr in ALL_INSTRUCTIONS {
println!("{:>3} {: <10}", instr.opcode(), format!("{}", instr.name()));
}
}

#[test]
/// Serves no other purpose than to increase code coverage results.
fn run_constant_methods() {
Expand Down Expand Up @@ -750,7 +825,7 @@ mod tests {
fn opcodes_are_consistent_with_shrink_stack_indication_bit() {
let shrink_stack_indicator_bit_mask = 2;
for instruction in Instruction::iter() {
let instruction = replace_illegal_arguments_in_instruction(instruction);
let instruction = instruction.replace_default_argument_if_illegal();
let opcode = instruction.opcode();
println!("Testing instruction {instruction} with opcode {opcode}.");
assert_eq!(
Expand Down Expand Up @@ -800,7 +875,7 @@ mod tests {
#[test]
fn instructions_act_on_op_stack_as_indicated() {
for test_instruction in all_instructions_without_args() {
let test_instruction = replace_illegal_arguments_in_instruction(test_instruction);
let test_instruction = test_instruction.replace_default_argument_if_illegal();
let (program, stack_size_before_test_instruction) =
construct_test_program_for_instruction(test_instruction);
let stack_size_after_test_instruction = terminal_op_stack_size_for_program(program);
Expand All @@ -815,17 +890,6 @@ mod tests {
}
}

fn replace_illegal_arguments_in_instruction(
instruction: AnInstruction<BFieldElement>,
) -> AnInstruction<BFieldElement> {
match instruction {
Pop(ST0) => Pop(ST3),
Divine(ST0) => Divine(ST1),
Swap(ST0) => Swap(ST1),
_ => instruction,
}
}

fn construct_test_program_for_instruction(
instruction: AnInstruction<BFieldElement>,
) -> (Program, usize) {
Expand Down
2 changes: 1 addition & 1 deletion triton-vm/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,13 +220,13 @@ fn an_instruction(s: &str) -> ParseResult<AnInstruction<String>> {
let opstack_manipulation = alt((pop, push, dup, swap));

// Control flow
let halt = instruction("halt", Halt);
let nop = instruction("nop", Nop);
let skiz = instruction("skiz", Skiz);
let call = call_instruction();
let return_ = instruction("return", Return);
let recurse = instruction("recurse", Recurse);
let assert_ = instruction("assert", Assert);
let halt = instruction("halt", Halt);

let control_flow = alt((nop, skiz, call, return_, recurse, halt));

Expand Down
2 changes: 1 addition & 1 deletion triton-vm/src/table/processor_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2259,13 +2259,13 @@ impl ExtProcessorTable {
Divine(_) => ExtProcessorTable::instruction_divine(circuit_builder),
Dup(_) => ExtProcessorTable::instruction_dup(circuit_builder),
Swap(_) => ExtProcessorTable::instruction_swap(circuit_builder),
Halt => ExtProcessorTable::instruction_halt(circuit_builder),
Nop => ExtProcessorTable::instruction_nop(circuit_builder),
Skiz => ExtProcessorTable::instruction_skiz(circuit_builder),
Call(_) => ExtProcessorTable::instruction_call(circuit_builder),
Return => ExtProcessorTable::instruction_return(circuit_builder),
Recurse => ExtProcessorTable::instruction_recurse(circuit_builder),
Assert => ExtProcessorTable::instruction_assert(circuit_builder),
Halt => ExtProcessorTable::instruction_halt(circuit_builder),
ReadMem => ExtProcessorTable::instruction_read_mem(circuit_builder),
WriteMem => ExtProcessorTable::instruction_write_mem(circuit_builder),
Hash => ExtProcessorTable::instruction_hash(circuit_builder),
Expand Down
2 changes: 1 addition & 1 deletion triton-vm/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,13 +206,13 @@ impl<'pgm> VMState<'pgm> {
Divine(n) => self.divine(n)?,
Dup(stack_element) => self.dup(stack_element),
Swap(stack_element) => self.swap(stack_element)?,
Halt => self.halt(),
Nop => self.nop(),
Skiz => self.skiz()?,
Call(address) => self.call(address),
Return => self.return_from_call()?,
Recurse => self.recurse()?,
Assert => self.assert()?,
Halt => self.halt(),
ReadMem => self.read_mem(),
WriteMem => self.write_mem()?,
Hash => self.hash()?,
Expand Down

0 comments on commit 92d49b6

Please sign in to comment.