diff --git a/vm/src/arch/chips.rs b/vm/src/arch/chips.rs index 3c9f48e55d..26227c8eed 100644 --- a/vm/src/arch/chips.rs +++ b/vm/src/arch/chips.rs @@ -29,6 +29,8 @@ use crate::{ modular_addsub::ModularAddSubChip, modular_multdiv::ModularMultDivChip, new_alu::Rv32ArithmeticLogicChip, + new_lt::Rv32LessThanChip, + new_shift::Rv32ShiftChip, program::{ExecutionError, Instruction, ProgramChip}, shift::ShiftChip, ui::UiChip, @@ -183,9 +185,11 @@ pub enum InstructionExecutorVariant { Keccak256(Rc>>), ModularAddSub(Rc>>), ModularMultDiv(Rc>>), - ArithmeticLogicUnit256(Rc>>), ArithmeticLogicUnitRv32(Rc>>), + ArithmeticLogicUnit256(Rc>>), + LessThanRv32(Rc>>), U256Multiplication(Rc>>), + ShiftRv32(Rc>>), Shift256(Rc>>), Ui(Rc>>), CastF(Rc>>), @@ -206,9 +210,11 @@ pub enum MachineChipVariant { RangeTupleChecker(Arc), Keccak256(Rc>>), ByteXor(Arc>), - ArithmeticLogicUnit256(Rc>>), ArithmeticLogicUnitRv32(Rc>>), + ArithmeticLogicUnit256(Rc>>), + LessThanRv32(Rc>>), U256Multiplication(Rc>>), + ShiftRv32(Rc>>), Shift256(Rc>>), Ui(Rc>>), CastF(Rc>>), diff --git a/vm/src/arch/instructions.rs b/vm/src/arch/instructions.rs index 7749386082..9592988309 100644 --- a/vm/src/arch/instructions.rs +++ b/vm/src/arch/instructions.rs @@ -192,3 +192,26 @@ pub enum AluOpcode { OR, AND, } + +#[derive( + Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, EnumCount, EnumIter, FromRepr, UsizeOpcode, +)] +#[opcode_offset = 0x305] +#[repr(usize)] +#[allow(non_camel_case_types)] +pub enum ShiftOpcode { + SLL, + SRL, + SRA, +} + +#[derive( + Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, EnumCount, EnumIter, FromRepr, UsizeOpcode, +)] +#[opcode_offset = 0x310] +#[repr(usize)] +#[allow(non_camel_case_types)] +pub enum LessThanOpcode { + SLT, + SLTU, +} diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 270c37f400..0e6204af2e 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -10,6 +10,8 @@ pub mod memory; pub mod modular_addsub; pub mod modular_multdiv; pub mod new_alu; +pub mod new_lt; +pub mod new_shift; pub mod program; /// SDK functions for running and proving programs in the VM. #[cfg(feature = "sdk")] diff --git a/vm/src/new_lt/integration.rs b/vm/src/new_lt/integration.rs new file mode 100644 index 0000000000..84a2d319b7 --- /dev/null +++ b/vm/src/new_lt/integration.rs @@ -0,0 +1,163 @@ +use std::{mem::size_of, sync::Arc}; + +use afs_derive::AlignedBorrow; +use afs_primitives::xor::{bus::XorBus, lookup::XorLookupChip}; +use afs_stark_backend::interaction::InteractionBuilder; +use p3_air::{Air, AirBuilderWithPublicValues, BaseAir, PairBuilder}; +use p3_field::{Field, PrimeField32}; + +use crate::{ + arch::{ + instructions::{LessThanOpcode, UsizeOpcode}, + InstructionOutput, IntegrationInterface, MachineAdapter, MachineAdapterInterface, + MachineIntegration, Result, + }, + program::Instruction, +}; + +#[repr(C)] +#[derive(AlignedBorrow)] +pub struct LessThanCols { + pub b: [T; NUM_LIMBS], + pub c: [T; NUM_LIMBS], + pub cmp_result: T, + + pub opcode_slt_flag: T, + pub opcode_sltu_flag: T, + + pub x_sign: T, + pub y_sign: T, + + // 1 at the most significant index i such that b[i] != c[i], otherwise 0. If such + // an i exists, diff_val = c[i] - b[i] + pub diff_marker: [T; LIMB_BITS], + pub diff_val: T, +} + +impl LessThanCols { + pub fn width() -> usize { + size_of::>() + } +} + +#[derive(Copy, Clone, Debug)] +pub struct LessThanAir { + pub bus: XorBus, +} + +impl BaseAir + for LessThanAir +{ + fn width(&self) -> usize { + LessThanCols::::width() + } +} + +impl Air + for LessThanAir +{ + fn eval(&self, _builder: &mut AB) { + todo!(); + } +} + +#[derive(Debug)] +pub struct LessThanIntegration { + pub air: LessThanAir, + pub xor_lookup_chip: Arc>, + offset: usize, +} + +impl LessThanIntegration { + pub fn new(xor_lookup_chip: Arc>, offset: usize) -> Self { + Self { + air: LessThanAir { + bus: xor_lookup_chip.bus(), + }, + xor_lookup_chip, + offset, + } + } +} + +impl, const NUM_LIMBS: usize, const LIMB_BITS: usize> + MachineIntegration for LessThanIntegration +where + A::Interface: MachineAdapterInterface, + as MachineAdapterInterface>::Reads: Into<[[F; NUM_LIMBS]; 2]>, + as MachineAdapterInterface>::Writes: From<[F; NUM_LIMBS]>, +{ + // TODO: update for trace generation + type Record = u32; + type Cols = LessThanCols; + type Air = LessThanAir; + + #[allow(clippy::type_complexity)] + fn execute_instruction( + &self, + instruction: &Instruction, + from_pc: F, + reads: as MachineAdapterInterface>::Reads, + ) -> Result<(InstructionOutput>, Self::Record)> { + let Instruction { opcode, .. } = instruction; + let opcode = LessThanOpcode::from_usize(opcode - self.offset); + + let data: [[F; NUM_LIMBS]; 2] = reads.into(); + let x = data[0].map(|x| x.as_canonical_u32()); + let y = data[1].map(|y| y.as_canonical_u32()); + let (cmp_result, _diff_idx, _x_sign, _y_sign) = + solve_less_than::(opcode, &x, &y); + + let mut writes = [0u32; NUM_LIMBS]; + writes[0] = cmp_result as u32; + + // Integration doesn't modify PC directly, so we let Adapter handle the increment + let output: InstructionOutput> = InstructionOutput { + to_pc: from_pc, + writes: writes.map(F::from_canonical_u32).into(), + }; + + // TODO: send XorLookupChip requests + // TODO: create Record and return + + Ok((output, 0)) + } + + fn get_opcode_name(&self, _opcode: usize) -> String { + todo!() + } + + fn generate_trace_row(&self, _row_slice: &mut Self::Cols, _record: Self::Record) { + todo!() + } + + /// Returns `(to_pc, interface)`. + fn eval_primitive + PairBuilder + AirBuilderWithPublicValues>( + _air: &Self::Air, + _builder: &mut AB, + _local: &Self::Cols, + _local_adapter: &A::Cols, + ) -> IntegrationInterface> { + todo!() + } + + fn air(&self) -> Self::Air { + self.air + } +} + +// Returns (cmp_result, diff_idx, x_sign, y_sign) +pub(super) fn solve_less_than( + opcode: LessThanOpcode, + x: &[u32; NUM_LIMBS], + y: &[u32; NUM_LIMBS], +) -> (bool, usize, bool, bool) { + let x_sign = (x[NUM_LIMBS - 1] >> (LIMB_BITS - 1) == 1) && opcode == LessThanOpcode::SLT; + let y_sign = (y[NUM_LIMBS - 1] >> (LIMB_BITS - 1) == 1) && opcode == LessThanOpcode::SLT; + for i in (0..NUM_LIMBS).rev() { + if x[i] != y[i] { + return ((x[i] < y[i]) ^ x_sign ^ y_sign, i, x_sign, y_sign); + } + } + (false, 0, x_sign, y_sign) +} diff --git a/vm/src/new_lt/mod.rs b/vm/src/new_lt/mod.rs new file mode 100644 index 0000000000..1ed512b843 --- /dev/null +++ b/vm/src/new_lt/mod.rs @@ -0,0 +1,10 @@ +use crate::arch::{MachineChipWrapper, Rv32AluAdapter}; + +mod integration; +pub use integration::*; + +#[cfg(test)] +mod tests; + +// TODO: Replace current ALU less than commands upon completion +pub type Rv32LessThanChip = MachineChipWrapper, LessThanIntegration<4, 8>>; diff --git a/vm/src/new_lt/tests.rs b/vm/src/new_lt/tests.rs new file mode 100644 index 0000000000..cb16648b2d --- /dev/null +++ b/vm/src/new_lt/tests.rs @@ -0,0 +1,41 @@ +use super::integration::solve_less_than; +use crate::arch::instructions::LessThanOpcode; + +const RV32_NUM_LIMBS: usize = 4; +const RV32_LIMB_BITS: usize = 8; + +#[test] +fn solve_sltu_sanity_test() { + let x: [u32; RV32_NUM_LIMBS] = [145, 34, 25, 205]; + let y: [u32; RV32_NUM_LIMBS] = [73, 35, 25, 205]; + let (cmp_result, diff_idx, x_sign, y_sign) = + solve_less_than::(LessThanOpcode::SLTU, &x, &y); + assert!(cmp_result); + assert_eq!(diff_idx, 1); + assert!(!x_sign); // unsigned + assert!(!y_sign); // unsigned +} + +#[test] +fn solve_slt_same_sign_sanity_test() { + let x: [u32; RV32_NUM_LIMBS] = [145, 34, 25, 205]; + let y: [u32; RV32_NUM_LIMBS] = [73, 35, 25, 205]; + let (cmp_result, diff_idx, x_sign, y_sign) = + solve_less_than::(LessThanOpcode::SLT, &x, &y); + assert!(cmp_result); + assert_eq!(diff_idx, 1); + assert!(x_sign); // negative + assert!(y_sign); // negative +} + +#[test] +fn solve_slt_diff_sign_sanity_test() { + let x: [u32; RV32_NUM_LIMBS] = [45, 35, 25, 55]; + let y: [u32; RV32_NUM_LIMBS] = [173, 34, 25, 205]; + let (cmp_result, diff_idx, x_sign, y_sign) = + solve_less_than::(LessThanOpcode::SLT, &x, &y); + assert!(!cmp_result); + assert_eq!(diff_idx, 3); + assert!(!x_sign); // positive + assert!(y_sign); // negative +} diff --git a/vm/src/new_shift/integration.rs b/vm/src/new_shift/integration.rs new file mode 100644 index 0000000000..a883e9757f --- /dev/null +++ b/vm/src/new_shift/integration.rs @@ -0,0 +1,236 @@ +use std::{mem::size_of, sync::Arc}; + +use afs_derive::AlignedBorrow; +use afs_primitives::{ + var_range::{bus::VariableRangeCheckerBus, VariableRangeCheckerChip}, + xor::{bus::XorBus, lookup::XorLookupChip}, +}; +use afs_stark_backend::interaction::InteractionBuilder; +use p3_air::{Air, AirBuilderWithPublicValues, BaseAir, PairBuilder}; +use p3_field::{Field, PrimeField32}; + +use crate::{ + arch::{ + instructions::{ShiftOpcode, UsizeOpcode}, + InstructionOutput, IntegrationInterface, MachineAdapter, MachineAdapterInterface, + MachineIntegration, Result, + }, + program::Instruction, +}; + +#[repr(C)] +#[derive(AlignedBorrow)] +pub struct ShiftCols { + pub a: [T; NUM_LIMBS], + pub b: [T; NUM_LIMBS], + pub c: [T; NUM_LIMBS], + + pub opcode_sll_flag: T, + pub opcode_srl_flag: T, + pub opcode_sra_flag: T, + + // bit_multiplier = 2^bit_shift + pub bit_shift: T, + pub bit_multiplier_left: T, + pub bit_multiplier_right: T, + + // Sign of x for SRA + pub x_sign: T, + + // Boolean columns that are 1 exactly at the index of the bit/limb shift amount + pub bit_shift_marker: [T; LIMB_BITS], + pub limb_shift_marker: [T; NUM_LIMBS], + + // Part of each x[i] that gets bit shifted to the next limb + pub bit_shift_carry: [T; NUM_LIMBS], +} + +impl ShiftCols { + pub fn width() -> usize { + size_of::>() + } +} + +#[derive(Copy, Clone, Debug)] +pub struct ShiftAir { + pub xor_bus: XorBus, + pub range_bus: VariableRangeCheckerBus, +} + +impl BaseAir + for ShiftAir +{ + fn width(&self) -> usize { + ShiftCols::::width() + } +} + +impl Air + for ShiftAir +{ + fn eval(&self, _builder: &mut AB) { + todo!(); + } +} + +#[derive(Debug)] +pub struct ShiftIntegration { + pub air: ShiftAir, + pub xor_lookup_chip: Arc>, + pub range_checker_chip: Arc, + offset: usize, +} + +impl ShiftIntegration { + pub fn new( + xor_lookup_chip: Arc>, + range_checker_chip: Arc, + offset: usize, + ) -> Self { + Self { + air: ShiftAir { + xor_bus: xor_lookup_chip.bus(), + range_bus: range_checker_chip.bus(), + }, + xor_lookup_chip, + range_checker_chip, + offset, + } + } +} + +impl, const NUM_LIMBS: usize, const LIMB_BITS: usize> + MachineIntegration for ShiftIntegration +where + A::Interface: MachineAdapterInterface, + as MachineAdapterInterface>::Reads: Into<[[F; NUM_LIMBS]; 2]>, + as MachineAdapterInterface>::Writes: From<[F; NUM_LIMBS]>, +{ + // TODO: update for trace generation + type Record = u32; + type Cols = ShiftCols; + type Air = ShiftAir; + + #[allow(clippy::type_complexity)] + fn execute_instruction( + &self, + instruction: &Instruction, + from_pc: F, + reads: as MachineAdapterInterface>::Reads, + ) -> Result<(InstructionOutput>, Self::Record)> { + let Instruction { opcode, .. } = instruction; + let opcode = ShiftOpcode::from_usize(opcode - self.offset); + + let data: [[F; NUM_LIMBS]; 2] = reads.into(); + let x = data[0].map(|x| x.as_canonical_u32()); + let y = data[1].map(|y| y.as_canonical_u32()); + let (z, _limb_shift, _bit_shift) = solve_shift::(opcode, &x, &y); + + // Integration doesn't modify PC directly, so we let Adapter handle the increment + let output: InstructionOutput> = InstructionOutput { + to_pc: from_pc, + writes: z.map(F::from_canonical_u32).into(), + }; + + // TODO: send XorLookupChip and VariableRangeCheckerChip requests + // TODO: create Record and return + + Ok((output, 0)) + } + + fn get_opcode_name(&self, _opcode: usize) -> String { + todo!() + } + + fn generate_trace_row(&self, _row_slice: &mut Self::Cols, _record: Self::Record) { + todo!() + } + + /// Returns `(to_pc, interface)`. + fn eval_primitive + PairBuilder + AirBuilderWithPublicValues>( + _air: &Self::Air, + _builder: &mut AB, + _local: &Self::Cols, + _local_adapter: &A::Cols, + ) -> IntegrationInterface> { + todo!() + } + + fn air(&self) -> Self::Air { + self.air + } +} + +pub(super) fn solve_shift( + opcode: ShiftOpcode, + x: &[u32; NUM_LIMBS], + y: &[u32; NUM_LIMBS], +) -> ([u32; NUM_LIMBS], usize, usize) { + match opcode { + ShiftOpcode::SLL => solve_shift_left::(x, y), + ShiftOpcode::SRL => solve_shift_right::(x, y, true), + ShiftOpcode::SRA => solve_shift_right::(x, y, false), + } +} + +fn solve_shift_left( + x: &[u32; NUM_LIMBS], + y: &[u32; NUM_LIMBS], +) -> ([u32; NUM_LIMBS], usize, usize) { + let mut result = [0u32; NUM_LIMBS]; + + let (is_zero, limb_shift, bit_shift) = get_shift::(y); + if is_zero { + return (result, limb_shift, bit_shift); + } + + for i in limb_shift..NUM_LIMBS { + result[i] = if i > limb_shift { + ((x[i - limb_shift] << bit_shift) + (x[i - limb_shift - 1] >> (LIMB_BITS - bit_shift))) + % (1 << LIMB_BITS) + } else { + (x[i - limb_shift] << bit_shift) % (1 << LIMB_BITS) + }; + } + (result, limb_shift, bit_shift) +} + +fn solve_shift_right( + x: &[u32; NUM_LIMBS], + y: &[u32; NUM_LIMBS], + logical: bool, +) -> ([u32; NUM_LIMBS], usize, usize) { + let fill = if logical { + 0 + } else { + ((1 << LIMB_BITS) - 1) * (x[NUM_LIMBS - 1] >> (LIMB_BITS - 1)) + }; + let mut result = [fill; NUM_LIMBS]; + + let (is_zero, limb_shift, bit_shift) = get_shift::(y); + if is_zero { + return (result, limb_shift, bit_shift); + } + + for i in 0..(NUM_LIMBS - limb_shift) { + result[i] = if i + limb_shift + 1 < NUM_LIMBS { + ((x[i + limb_shift] >> bit_shift) + (x[i + limb_shift + 1] << (LIMB_BITS - bit_shift))) + % (1 << LIMB_BITS) + } else { + ((x[i + limb_shift] >> bit_shift) + (fill << (LIMB_BITS - bit_shift))) + % (1 << LIMB_BITS) + } + } + (result, limb_shift, bit_shift) +} + +fn get_shift(y: &[u32]) -> (bool, usize, usize) { + // We assume `NUM_LIMBS * LIMB_BITS < 2^(2*LIMB_BITS)` so if there are any higher limbs, + // the shifted value is zero. + let shift = (y[0] + (y[1] * (1 << LIMB_BITS))) as usize; + if shift < NUM_LIMBS * LIMB_BITS && y[2..].iter().all(|&val| val == 0) { + (false, shift / LIMB_BITS, shift % LIMB_BITS) + } else { + (true, NUM_LIMBS, shift % LIMB_BITS) + } +} diff --git a/vm/src/new_shift/mod.rs b/vm/src/new_shift/mod.rs new file mode 100644 index 0000000000..44ac82970c --- /dev/null +++ b/vm/src/new_shift/mod.rs @@ -0,0 +1,10 @@ +use crate::arch::{MachineChipWrapper, Rv32AluAdapter}; + +mod integration; +pub use integration::*; + +#[cfg(test)] +mod tests; + +// TODO: Replace current Shift256 module upon completion +pub type Rv32ShiftChip = MachineChipWrapper, ShiftIntegration<4, 8>>; diff --git a/vm/src/new_shift/tests.rs b/vm/src/new_shift/tests.rs new file mode 100644 index 0000000000..1f0ea2743c --- /dev/null +++ b/vm/src/new_shift/tests.rs @@ -0,0 +1,47 @@ +use super::integration::solve_shift; +use crate::arch::instructions::ShiftOpcode; + +const RV32_NUM_LIMBS: usize = 4; +const RV32_LIMB_BITS: usize = 8; + +#[test] +fn solve_sll_sanity_test() { + let x: [u32; RV32_NUM_LIMBS] = [45, 7, 61, 186]; + let y: [u32; RV32_NUM_LIMBS] = [27, 0, 0, 0]; + let z: [u32; RV32_NUM_LIMBS] = [0, 0, 0, 104]; + let (result, limb_shift, bit_shift) = + solve_shift::(ShiftOpcode::SLL, &x, &y); + for i in 0..RV32_NUM_LIMBS { + assert_eq!(z[i], result[i]) + } + assert_eq!((y[0] as usize) / RV32_LIMB_BITS, limb_shift); + assert_eq!((y[0] as usize) % RV32_LIMB_BITS, bit_shift); +} + +#[test] +fn solve_srl_sanity_test() { + let x: [u32; RV32_NUM_LIMBS] = [31, 190, 221, 200]; + let y: [u32; RV32_NUM_LIMBS] = [17, 0, 0, 0]; + let z: [u32; RV32_NUM_LIMBS] = [110, 100, 0, 0]; + let (result, limb_shift, bit_shift) = + solve_shift::(ShiftOpcode::SRL, &x, &y); + for i in 0..RV32_NUM_LIMBS { + assert_eq!(z[i], result[i]) + } + assert_eq!((y[0] as usize) / RV32_LIMB_BITS, limb_shift); + assert_eq!((y[0] as usize) % RV32_LIMB_BITS, bit_shift); +} + +#[test] +fn solve_sra_sanity_test() { + let x: [u32; RV32_NUM_LIMBS] = [31, 190, 221, 200]; + let y: [u32; RV32_NUM_LIMBS] = [17, 0, 0, 0]; + let z: [u32; RV32_NUM_LIMBS] = [110, 228, 255, 255]; + let (result, limb_shift, bit_shift) = + solve_shift::(ShiftOpcode::SRA, &x, &y); + for i in 0..RV32_NUM_LIMBS { + assert_eq!(z[i], result[i]) + } + assert_eq!((y[0] as usize) / RV32_LIMB_BITS, limb_shift); + assert_eq!((y[0] as usize) % RV32_LIMB_BITS, bit_shift); +} diff --git a/vm/src/vm/config.rs b/vm/src/vm/config.rs index 282337ed61..950d7c76ca 100644 --- a/vm/src/vm/config.rs +++ b/vm/src/vm/config.rs @@ -72,21 +72,31 @@ fn default_executor_range(executor: ExecutorName) -> (Range, usize) { 2, ModularArithmeticOpcode::default_offset(), ), + ExecutorName::ArithmeticLogicUnitRv32 => ( + AluOpcode::default_offset(), + AluOpcode::COUNT, + AluOpcode::default_offset(), + ), ExecutorName::ArithmeticLogicUnit256 => ( U256Opcode::default_offset(), 8, U256Opcode::default_offset(), ), - ExecutorName::ArithmeticLogicUnitRv32 => ( - AluOpcode::default_offset(), - AluOpcode::COUNT, - AluOpcode::default_offset(), + ExecutorName::LessThanRv32 => ( + LessThanOpcode::default_offset(), + LessThanOpcode::COUNT, + LessThanOpcode::default_offset(), ), ExecutorName::U256Multiplication => ( U256Opcode::default_offset() + 11, 1, U256Opcode::default_offset(), ), + ExecutorName::ShiftRv32 => ( + ShiftOpcode::default_offset(), + ShiftOpcode::COUNT, + ShiftOpcode::default_offset(), + ), ExecutorName::Shift256 => ( U256Opcode::default_offset() + 8, 3, diff --git a/vm/src/vm/segment.rs b/vm/src/vm/segment.rs index 7d62eb97dd..d6bad91099 100644 --- a/vm/src/vm/segment.rs +++ b/vm/src/vm/segment.rs @@ -46,6 +46,8 @@ use crate::{ modular_addsub::ModularAddSubChip, modular_multdiv::ModularMultDivChip, new_alu::{ArithmeticLogicIntegration, Rv32ArithmeticLogicChip}, + new_lt::{LessThanIntegration, Rv32LessThanChip}, + new_shift::{Rv32ShiftChip, ShiftIntegration}, program::{bridge::ProgramBus, DebugInfo, ExecutionError, Program, ProgramChip}, shift::ShiftChip, ui::UiChip, @@ -258,6 +260,17 @@ impl ExecutionSegment { } chips.push(MachineChipVariant::ArithmeticLogicUnit256(chip)); } + ExecutorName::LessThanRv32 => { + let chip = Rc::new(RefCell::new(Rv32LessThanChip::new( + Rv32AluAdapter::new(execution_bus, program_bus, memory_chip.clone()), + LessThanIntegration::new(byte_xor_chip.clone(), offset), + memory_chip.clone(), + ))); + for opcode in range { + executors.insert(opcode, chip.clone().into()); + } + chips.push(MachineChipVariant::LessThanRv32(chip)); + } ExecutorName::U256Multiplication => { let range_tuple_bus = RangeTupleCheckerBus::new( RANGE_TUPLE_CHECKER_BUS, @@ -276,6 +289,17 @@ impl ExecutionSegment { } chips.push(MachineChipVariant::U256Multiplication(chip)); } + ExecutorName::ShiftRv32 => { + let chip = Rc::new(RefCell::new(Rv32ShiftChip::new( + Rv32AluAdapter::new(execution_bus, program_bus, memory_chip.clone()), + ShiftIntegration::new(byte_xor_chip.clone(), range_checker.clone(), offset), + memory_chip.clone(), + ))); + for opcode in range { + executors.insert(opcode, chip.clone().into()); + } + chips.push(MachineChipVariant::ShiftRv32(chip)); + } ExecutorName::Shift256 => { let chip = Rc::new(RefCell::new(ShiftChip::new( execution_bus,