mirror of
https://github.com/powdr-labs/powdr.git
synced 2026-01-10 09:47:58 -05:00
Share dummy periphery across all APCs (#2870)
All APCs need access to periphery chips such as the range checker or the xor chip. These chips are expensive to create in memory, but they are not needed in the end and just get thrown away. Before this PR, each APC creates one instance of them. After this PR, a single instance is created and then shared across all APCs.
This commit is contained in:
committed by
GitHub
parent
a515b323f9
commit
3454b41d88
49
Cargo.toml
49
Cargo.toml
@@ -83,33 +83,38 @@ powdr-autoprecompiles = { path = "./autoprecompiles", version = "0.1.4" }
|
||||
powdr-openvm = { path = "./openvm", version = "0.1.4" }
|
||||
|
||||
# openvm
|
||||
openvm = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc" }
|
||||
openvm-build = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc" }
|
||||
openvm-rv32im-circuit = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc" }
|
||||
openvm-rv32im-transpiler = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc" }
|
||||
openvm-rv32im-guest = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc", default-features = false }
|
||||
openvm-transpiler = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc" }
|
||||
openvm-circuit = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc" }
|
||||
openvm-circuit-derive = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc" }
|
||||
openvm-circuit-primitives = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc" }
|
||||
openvm-circuit-primitives-derive = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc" }
|
||||
openvm-instructions = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc" }
|
||||
openvm-instructions-derive = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc" }
|
||||
openvm-sdk = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc", default-features = false, features = [
|
||||
openvm = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-build = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-rv32im-circuit = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-rv32im-transpiler = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-rv32im-guest = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865", default-features = false }
|
||||
openvm-transpiler = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-circuit = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-circuit-derive = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-circuit-primitives = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-circuit-primitives-derive = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-instructions = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-instructions-derive = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-sdk = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865", default-features = false, features = [
|
||||
"parallel",
|
||||
"jemalloc",
|
||||
"nightly-features",
|
||||
"bench-metrics",
|
||||
] }
|
||||
openvm-ecc-transpiler = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc" }
|
||||
openvm-keccak256-circuit = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc" }
|
||||
openvm-keccak256-transpiler = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc" }
|
||||
openvm-sha256-circuit = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc" }
|
||||
openvm-sha256-transpiler = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc" }
|
||||
openvm-algebra-transpiler = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc" }
|
||||
openvm-bigint-transpiler = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc" }
|
||||
openvm-native-circuit = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc", default-features = false }
|
||||
openvm-native-recursion = { git = "https://github.com/powdr-labs/openvm.git", rev = "f12a2fc", default-features = false }
|
||||
openvm-ecc-circuit = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-ecc-transpiler = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-keccak256-circuit = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-keccak256-transpiler = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-sha256-circuit = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-sha256-transpiler = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-algebra-circuit = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-algebra-transpiler = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-bigint-circuit = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-bigint-transpiler = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-pairing-circuit = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-pairing-transpiler = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865" }
|
||||
openvm-native-circuit = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865", default-features = false }
|
||||
openvm-native-recursion = { git = "https://github.com/powdr-labs/openvm.git", rev = "7f85865", default-features = false }
|
||||
|
||||
# stark-backend
|
||||
openvm-stark-sdk = { git = "https://github.com/powdr-labs/stark-backend.git", rev = "fe1c5a8", default-features = false, features = [
|
||||
|
||||
@@ -20,13 +20,18 @@ openvm-circuit-primitives-derive.workspace = true
|
||||
openvm-instructions.workspace = true
|
||||
openvm-instructions-derive.workspace = true
|
||||
openvm-sdk.workspace = true
|
||||
openvm-ecc-circuit.workspace = true
|
||||
openvm-ecc-transpiler.workspace = true
|
||||
openvm-keccak256-circuit.workspace = true
|
||||
openvm-keccak256-transpiler.workspace = true
|
||||
openvm-sha256-circuit.workspace = true
|
||||
openvm-sha256-transpiler.workspace = true
|
||||
openvm-algebra-circuit.workspace = true
|
||||
openvm-algebra-transpiler.workspace = true
|
||||
openvm-bigint-circuit.workspace = true
|
||||
openvm-bigint-transpiler.workspace = true
|
||||
openvm-pairing-circuit.workspace = true
|
||||
openvm-pairing-transpiler.workspace = true
|
||||
openvm-native-circuit.workspace = true
|
||||
openvm-native-recursion.workspace = true
|
||||
|
||||
|
||||
@@ -5,7 +5,10 @@ use std::{
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use crate::{traits::OpenVmField, utils::algebraic_to_symbolic, IntoOpenVm};
|
||||
use crate::{
|
||||
powdr_extension::executor::PowdrPeripheryInstances, traits::OpenVmField,
|
||||
utils::algebraic_to_symbolic, IntoOpenVm,
|
||||
};
|
||||
|
||||
use super::{executor::PowdrExecutor, opcode::PowdrOpcode, PowdrPrecompile};
|
||||
use itertools::Itertools;
|
||||
@@ -14,10 +17,6 @@ use openvm_circuit::{
|
||||
arch::{ExecutionState, InstructionExecutor, Result as ExecutionResult},
|
||||
system::memory::OfflineMemory,
|
||||
};
|
||||
use openvm_circuit_primitives::{
|
||||
bitwise_op_lookup::SharedBitwiseOperationLookupChip, range_tuple::SharedRangeTupleCheckerChip,
|
||||
var_range::SharedVariableRangeCheckerChip,
|
||||
};
|
||||
use openvm_instructions::{instruction::Instruction, LocalOpcode};
|
||||
use openvm_sdk::config::SdkVmConfig;
|
||||
use openvm_stark_backend::{
|
||||
@@ -53,95 +52,12 @@ pub struct PowdrChip<P: IntoOpenVm> {
|
||||
pub air: Arc<PowdrAir<P>>,
|
||||
}
|
||||
|
||||
/// The shared chips which can be used by the PowdrChip.
|
||||
pub struct SharedChips {
|
||||
bitwise_lookup_8: SharedBitwiseOperationLookupChip<8>,
|
||||
pub range_checker: SharedVariableRangeCheckerChip,
|
||||
tuple_range_checker: Option<SharedRangeTupleCheckerChip<2>>,
|
||||
}
|
||||
|
||||
impl SharedChips {
|
||||
pub fn new(
|
||||
bitwise_lookup_8: SharedBitwiseOperationLookupChip<8>,
|
||||
range_checker: SharedVariableRangeCheckerChip,
|
||||
tuple_range_checker: Option<SharedRangeTupleCheckerChip<2>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
bitwise_lookup_8,
|
||||
range_checker,
|
||||
tuple_range_checker,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl SharedChips {
|
||||
/// Sends concrete values to the shared chips using a given bus id.
|
||||
/// Panics if the bus id doesn't match any of the chips' bus ids.
|
||||
pub fn apply(&self, bus_id: u16, mult: u32, mut args: impl Iterator<Item = u32>) {
|
||||
match bus_id {
|
||||
id if id == self.bitwise_lookup_8.bus().inner.index => {
|
||||
// bitwise operation lookup
|
||||
// interpret the arguments, see `Air<AB> for BitwiseOperationLookupAir<NUM_BITS>`
|
||||
let [x, y, x_xor_y, selector] = [
|
||||
args.next().unwrap(),
|
||||
args.next().unwrap(),
|
||||
args.next().unwrap(),
|
||||
args.next().unwrap(),
|
||||
];
|
||||
|
||||
for _ in 0..mult {
|
||||
match selector {
|
||||
0 => {
|
||||
self.bitwise_lookup_8.request_range(x, y);
|
||||
}
|
||||
1 => {
|
||||
let res = self.bitwise_lookup_8.request_xor(x, y);
|
||||
debug_assert_eq!(res, x_xor_y);
|
||||
}
|
||||
_ => {
|
||||
unreachable!("Invalid selector");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
id if id == self.range_checker.bus().index() => {
|
||||
// interpret the arguments, see `Air<AB> for VariableRangeCheckerAir`
|
||||
let [value, max_bits] = [args.next().unwrap(), args.next().unwrap()];
|
||||
|
||||
for _ in 0..mult {
|
||||
self.range_checker.add_count(value, max_bits as usize);
|
||||
}
|
||||
}
|
||||
id if Some(id)
|
||||
== self
|
||||
.tuple_range_checker
|
||||
.as_ref()
|
||||
.map(|c| c.bus().inner.index) =>
|
||||
{
|
||||
// tuple range checker
|
||||
// We pass a slice. It is checked inside `add_count`.
|
||||
let args = args.collect_vec();
|
||||
for _ in 0..mult {
|
||||
self.tuple_range_checker.as_ref().unwrap().add_count(&args);
|
||||
}
|
||||
}
|
||||
0..=2 => {
|
||||
// execution bridge, memory, pc lookup
|
||||
// do nothing
|
||||
}
|
||||
_ => {
|
||||
unreachable!("Bus interaction {} not implemented", bus_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: IntoOpenVm> PowdrChip<P> {
|
||||
pub(crate) fn new(
|
||||
precompile: PowdrPrecompile<P>,
|
||||
memory: Arc<Mutex<OfflineMemory<OpenVmField<P>>>>,
|
||||
base_config: SdkVmConfig,
|
||||
periphery: SharedChips,
|
||||
periphery: PowdrPeripheryInstances,
|
||||
) -> Self {
|
||||
let PowdrPrecompile {
|
||||
machine,
|
||||
|
||||
126
openvm/src/powdr_extension/executor/inventory.rs
Normal file
126
openvm/src/powdr_extension/executor/inventory.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
use derive_more::From;
|
||||
use openvm_circuit::{
|
||||
arch::{SystemExecutor, SystemPeriphery, VmChipComplex, VmInventory},
|
||||
system::phantom::PhantomChip,
|
||||
};
|
||||
use openvm_circuit_derive::{AnyEnum, InstructionExecutor};
|
||||
use openvm_circuit_primitives::{
|
||||
bitwise_op_lookup::SharedBitwiseOperationLookupChip, range_tuple::SharedRangeTupleCheckerChip,
|
||||
var_range::SharedVariableRangeCheckerChip, Chip, ChipUsageGetter,
|
||||
};
|
||||
use openvm_sdk::config::{SdkVmConfigExecutor, SdkVmConfigPeriphery};
|
||||
use openvm_stark_backend::p3_field::PrimeField32;
|
||||
|
||||
/// A dummy inventory used for execution of autoprecompiles
|
||||
/// It extends the `SdkVmConfigExecutor` and `SdkVmConfigPeriphery`, providing them with shared, pre-loaded periphery chips to avoid memory allocations by each SDK chip
|
||||
pub type DummyInventory<F> = VmInventory<DummyExecutor<F>, DummyPeriphery<F>>;
|
||||
pub type DummyChipComplex<F> = VmChipComplex<F, DummyExecutor<F>, DummyPeriphery<F>>;
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(ChipUsageGetter, Chip, InstructionExecutor, AnyEnum, From)]
|
||||
pub enum DummyExecutor<F: PrimeField32> {
|
||||
#[any_enum]
|
||||
Sdk(SdkVmConfigExecutor<F>),
|
||||
#[any_enum]
|
||||
Shared(SharedExecutor<F>),
|
||||
#[any_enum]
|
||||
/// We keep the `SystemExecutor` variant to allow for system-level operations. Failing to do this compiles, but at runtime since the `PhantomChip` cannot be found
|
||||
/// This seems like a bug in openvm, as it breaks abstraction: wrapping an executor should not require the user to know about the system executor.
|
||||
System(SystemExecutor<F>),
|
||||
}
|
||||
|
||||
#[derive(ChipUsageGetter, Chip, AnyEnum, From)]
|
||||
pub enum DummyPeriphery<F: PrimeField32> {
|
||||
#[any_enum]
|
||||
Sdk(SdkVmConfigPeriphery<F>),
|
||||
#[any_enum]
|
||||
Shared(SharedPeriphery<F>),
|
||||
#[any_enum]
|
||||
System(SystemPeriphery<F>),
|
||||
}
|
||||
|
||||
#[derive(ChipUsageGetter, Chip, InstructionExecutor, From, AnyEnum)]
|
||||
pub enum SharedExecutor<F: PrimeField32> {
|
||||
Phantom(PhantomChip<F>),
|
||||
}
|
||||
|
||||
#[derive(From, ChipUsageGetter, Chip, AnyEnum)]
|
||||
pub enum SharedPeriphery<F: PrimeField32> {
|
||||
BitwiseLookup8(SharedBitwiseOperationLookupChip<8>),
|
||||
RangeChecker(SharedRangeTupleCheckerChip<2>),
|
||||
VariableRangeChecker(SharedVariableRangeCheckerChip),
|
||||
Phantom(PhantomChip<F>),
|
||||
}
|
||||
|
||||
mod from_implementations {
|
||||
|
||||
use super::{DummyExecutor, DummyPeriphery};
|
||||
use openvm_sdk::config::{SdkVmConfigExecutor, SdkVmConfigPeriphery};
|
||||
use openvm_stark_backend::p3_field::PrimeField32;
|
||||
|
||||
// Import all the relevant executor and periphery types
|
||||
use openvm_algebra_circuit::{
|
||||
Fp2ExtensionExecutor, Fp2ExtensionPeriphery, ModularExtensionExecutor,
|
||||
ModularExtensionPeriphery,
|
||||
};
|
||||
use openvm_bigint_circuit::{Int256Executor, Int256Periphery};
|
||||
use openvm_ecc_circuit::{WeierstrassExtensionExecutor, WeierstrassExtensionPeriphery};
|
||||
use openvm_keccak256_circuit::{Keccak256Executor, Keccak256Periphery};
|
||||
use openvm_native_circuit::{
|
||||
CastFExtensionExecutor, CastFExtensionPeriphery, NativeExecutor, NativePeriphery,
|
||||
};
|
||||
use openvm_pairing_circuit::{PairingExtensionExecutor, PairingExtensionPeriphery};
|
||||
use openvm_rv32im_circuit::{
|
||||
Rv32IExecutor, Rv32IPeriphery, Rv32IoExecutor, Rv32IoPeriphery, Rv32MExecutor,
|
||||
Rv32MPeriphery,
|
||||
};
|
||||
use openvm_sha256_circuit::{Sha256Executor, Sha256Periphery};
|
||||
|
||||
/// Defines `From<T> for DummyExecutor` and `From<T> for DummyPeriphery`
|
||||
/// by mapping to the appropriate `SdkVmConfigExecutor` and `SdkVmConfigPeriphery` variant.
|
||||
/// This cannot be derived because we have a custom implementation of this conversion for the `SystemExecutor`, avoiding this wrapping.
|
||||
macro_rules! impl_zero_cost_conversions {
|
||||
($(($variant:ident, $executor_ty:ty, $periphery_ty:ty)),* $(,)?) => {
|
||||
$(
|
||||
impl<F: PrimeField32> From<$executor_ty> for DummyExecutor<F> {
|
||||
fn from(executor: $executor_ty) -> Self {
|
||||
DummyExecutor::Sdk(SdkVmConfigExecutor::$variant(executor))
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: PrimeField32> From<$periphery_ty> for DummyPeriphery<F> {
|
||||
fn from(periphery: $periphery_ty) -> Self {
|
||||
DummyPeriphery::Sdk(SdkVmConfigPeriphery::$variant(periphery))
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
impl_zero_cost_conversions!(
|
||||
(Rv32i, Rv32IExecutor<F>, Rv32IPeriphery<F>),
|
||||
(Io, Rv32IoExecutor<F>, Rv32IoPeriphery<F>),
|
||||
(Keccak, Keccak256Executor<F>, Keccak256Periphery<F>),
|
||||
(Sha256, Sha256Executor<F>, Sha256Periphery<F>),
|
||||
(Native, NativeExecutor<F>, NativePeriphery<F>),
|
||||
(Rv32m, Rv32MExecutor<F>, Rv32MPeriphery<F>),
|
||||
(BigInt, Int256Executor<F>, Int256Periphery<F>),
|
||||
(
|
||||
Modular,
|
||||
ModularExtensionExecutor<F>,
|
||||
ModularExtensionPeriphery<F>
|
||||
),
|
||||
(Fp2, Fp2ExtensionExecutor<F>, Fp2ExtensionPeriphery<F>),
|
||||
(
|
||||
Pairing,
|
||||
PairingExtensionExecutor<F>,
|
||||
PairingExtensionPeriphery<F>
|
||||
),
|
||||
(
|
||||
Ecc,
|
||||
WeierstrassExtensionExecutor<F>,
|
||||
WeierstrassExtensionPeriphery<F>
|
||||
),
|
||||
(CastF, CastFExtensionExecutor<F>, CastFExtensionPeriphery<F>),
|
||||
);
|
||||
}
|
||||
@@ -3,31 +3,31 @@ use std::{
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use crate::OpenVmField;
|
||||
use crate::{
|
||||
powdr_extension::executor::{
|
||||
inventory::{DummyChipComplex, DummyInventory},
|
||||
periphery::SharedPeripheryChips,
|
||||
},
|
||||
OpenVmField,
|
||||
};
|
||||
|
||||
use super::{
|
||||
chip::{RangeCheckerSend, RowEvaluator, SharedChips},
|
||||
chip::{RangeCheckerSend, RowEvaluator},
|
||||
vm::OriginalInstruction,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use openvm_circuit::{
|
||||
arch::{
|
||||
ExecutionState, InstructionExecutor, Result as ExecutionResult, VmChipComplex,
|
||||
VmInventoryError,
|
||||
},
|
||||
arch::VmConfig, system::memory::MemoryController, utils::next_power_of_two_or_zero,
|
||||
};
|
||||
use openvm_circuit::{
|
||||
arch::{ExecutionState, InstructionExecutor, Result as ExecutionResult, VmInventoryError},
|
||||
system::memory::{
|
||||
online::{ApcRange, MemoryLogEntry},
|
||||
OfflineMemory,
|
||||
},
|
||||
};
|
||||
use openvm_circuit::{
|
||||
arch::{VmConfig, VmInventory},
|
||||
system::memory::MemoryController,
|
||||
utils::next_power_of_two_or_zero,
|
||||
};
|
||||
use openvm_circuit_primitives::var_range::SharedVariableRangeCheckerChip;
|
||||
use openvm_native_circuit::CastFExtension;
|
||||
use openvm_sdk::config::{SdkVmConfig, SdkVmConfigExecutor, SdkVmConfigPeriphery};
|
||||
use openvm_sdk::config::SdkVmConfig;
|
||||
use openvm_stark_backend::{
|
||||
p3_field::FieldAlgebra, p3_matrix::Matrix, p3_maybe_rayon::prelude::ParallelIterator,
|
||||
};
|
||||
@@ -47,16 +47,21 @@ use openvm_stark_backend::{
|
||||
use openvm_stark_backend::{p3_maybe_rayon::prelude::IndexedParallelIterator, ChipUsageGetter};
|
||||
use powdr_autoprecompiles::{powdr::Column, SymbolicBusInteraction, SymbolicMachine};
|
||||
|
||||
type SdkVmInventory<F> = VmInventory<SdkVmConfigExecutor<F>, SdkVmConfigPeriphery<F>>;
|
||||
/// The inventory of the PowdrExecutor, which contains the executors for each opcode.
|
||||
mod inventory;
|
||||
/// The shared periphery chips used by the PowdrExecutor
|
||||
mod periphery;
|
||||
|
||||
pub use periphery::PowdrPeripheryInstances;
|
||||
|
||||
/// A struct which holds the state of the execution based on the original instructions in this block and a dummy inventory.
|
||||
pub struct PowdrExecutor<P: IntoOpenVm> {
|
||||
instructions: Vec<OriginalInstruction<OpenVmField<P>>>,
|
||||
air_by_opcode_id: BTreeMap<usize, SymbolicMachine<P>>,
|
||||
is_valid_poly_id: u64,
|
||||
inventory: SdkVmInventory<OpenVmField<P>>,
|
||||
inventory: DummyInventory<OpenVmField<P>>,
|
||||
number_of_calls: usize,
|
||||
periphery: SharedChips,
|
||||
periphery: SharedPeripheryChips,
|
||||
}
|
||||
|
||||
impl<P: IntoOpenVm> PowdrExecutor<P> {
|
||||
@@ -66,7 +71,7 @@ impl<P: IntoOpenVm> PowdrExecutor<P> {
|
||||
is_valid_column: Column,
|
||||
memory: Arc<Mutex<OfflineMemory<OpenVmField<P>>>>,
|
||||
base_config: SdkVmConfig,
|
||||
periphery: SharedChips,
|
||||
periphery: PowdrPeripheryInstances,
|
||||
) -> Self {
|
||||
Self {
|
||||
instructions,
|
||||
@@ -74,13 +79,13 @@ impl<P: IntoOpenVm> PowdrExecutor<P> {
|
||||
is_valid_poly_id: is_valid_column.id.id,
|
||||
inventory: create_chip_complex_with_memory(
|
||||
memory,
|
||||
periphery.range_checker.clone(),
|
||||
periphery.dummy,
|
||||
base_config.clone(),
|
||||
)
|
||||
.unwrap()
|
||||
.inventory,
|
||||
number_of_calls: 0,
|
||||
periphery,
|
||||
periphery: periphery.real,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,26 +383,27 @@ fn global_index<F>(
|
||||
Ok(*variable_index)
|
||||
}
|
||||
|
||||
// Extracted from openvm, extended to create an inventory with the correct memory
|
||||
// Extracted from openvm, extended to create an inventory with the correct memory and periphery chips.
|
||||
fn create_chip_complex_with_memory<F: PrimeField32>(
|
||||
memory: Arc<Mutex<OfflineMemory<F>>>,
|
||||
range_checker: SharedVariableRangeCheckerChip,
|
||||
shared_chips: SharedPeripheryChips,
|
||||
base_config: SdkVmConfig,
|
||||
) -> std::result::Result<
|
||||
VmChipComplex<F, SdkVmConfigExecutor<F>, SdkVmConfigPeriphery<F>>,
|
||||
VmInventoryError,
|
||||
> {
|
||||
) -> std::result::Result<DummyChipComplex<F>, VmInventoryError> {
|
||||
use openvm_keccak256_circuit::Keccak256;
|
||||
use openvm_native_circuit::Native;
|
||||
use openvm_rv32im_circuit::{Rv32I, Rv32Io};
|
||||
use openvm_sha256_circuit::Sha256;
|
||||
|
||||
let this = base_config;
|
||||
let mut complex = this.system.config.create_chip_complex()?.transmute();
|
||||
let mut complex: DummyChipComplex<F> = this.system.config.create_chip_complex()?.transmute();
|
||||
|
||||
// CHANGE: inject the correct memory here to be passed to the chips, to be accessible in their get_proof_input
|
||||
complex.base.memory_controller.offline_memory = memory.clone();
|
||||
complex.base.range_checker_chip = range_checker;
|
||||
complex.base.range_checker_chip = shared_chips.range_checker.clone();
|
||||
// END CHANGE
|
||||
|
||||
// CHANGE: inject the periphery chips so that they are not created by the extensions. This is done for memory footprint: the dummy periphery chips are thrown away anyway, so we reuse a single one for all APCs.
|
||||
complex = complex.extend(&shared_chips)?;
|
||||
// END CHANGE
|
||||
|
||||
if this.rv32i.is_some() {
|
||||
153
openvm/src/powdr_extension/executor/periphery.rs
Normal file
153
openvm/src/powdr_extension/executor/periphery.rs
Normal file
@@ -0,0 +1,153 @@
|
||||
use crate::powdr_extension::executor::inventory::{SharedExecutor, SharedPeriphery};
|
||||
use itertools::Itertools;
|
||||
use openvm_circuit::arch::VmExtension;
|
||||
use openvm_circuit_primitives::{
|
||||
bitwise_op_lookup::SharedBitwiseOperationLookupChip, range_tuple::SharedRangeTupleCheckerChip,
|
||||
var_range::SharedVariableRangeCheckerChip,
|
||||
};
|
||||
use openvm_stark_backend::p3_field::PrimeField32;
|
||||
|
||||
/// The shared chips which can be used by the PowdrChip.
|
||||
#[derive(Clone)]
|
||||
pub struct PowdrPeripheryInstances {
|
||||
/// The real chips used for the main execution.
|
||||
pub real: SharedPeripheryChips,
|
||||
/// The dummy chips used for all APCs. They share the range checker but create new instances of the bitwise lookup chip and the tuple range checker.
|
||||
pub dummy: SharedPeripheryChips,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SharedPeripheryChips {
|
||||
pub bitwise_lookup_8: SharedBitwiseOperationLookupChip<8>,
|
||||
pub range_checker: SharedVariableRangeCheckerChip,
|
||||
pub tuple_range_checker: Option<SharedRangeTupleCheckerChip<2>>,
|
||||
}
|
||||
|
||||
impl PowdrPeripheryInstances {
|
||||
pub(crate) fn new(
|
||||
range_checker: &SharedVariableRangeCheckerChip,
|
||||
bitwise_8: &SharedBitwiseOperationLookupChip<8>,
|
||||
tuple_range_checker: Option<&SharedRangeTupleCheckerChip<2>>,
|
||||
) -> Self {
|
||||
Self {
|
||||
real: SharedPeripheryChips {
|
||||
bitwise_lookup_8: bitwise_8.clone(),
|
||||
range_checker: range_checker.clone(),
|
||||
tuple_range_checker: tuple_range_checker.cloned(),
|
||||
},
|
||||
// Bitwise lookup and tuple range checker do not need to be shared with the main execution:
|
||||
// If we did share, we'd have to roll back the side effects of execution and apply the side effects from the apc air onto the main periphery.
|
||||
// By not sharing them, we can throw away the dummy ones after execution and only apply the side effects from the apc air onto the main periphery.
|
||||
dummy: SharedPeripheryChips {
|
||||
bitwise_lookup_8: SharedBitwiseOperationLookupChip::new(bitwise_8.bus()),
|
||||
range_checker: range_checker.clone(),
|
||||
tuple_range_checker: tuple_range_checker.map(|tuple_range_checker| {
|
||||
SharedRangeTupleCheckerChip::new(*tuple_range_checker.bus())
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// We implement an extension to make it easy to pre-load the shared chips into the VM inventory.
|
||||
impl<F> VmExtension<F> for SharedPeripheryChips
|
||||
where
|
||||
F: PrimeField32,
|
||||
{
|
||||
type Executor = SharedExecutor<F>;
|
||||
|
||||
type Periphery = SharedPeriphery<F>;
|
||||
|
||||
fn build(
|
||||
&self,
|
||||
builder: &mut openvm_circuit::arch::VmInventoryBuilder<F>,
|
||||
) -> Result<
|
||||
openvm_circuit::arch::VmInventory<Self::Executor, Self::Periphery>,
|
||||
openvm_circuit::arch::VmInventoryError,
|
||||
> {
|
||||
let mut inventory = openvm_circuit::arch::VmInventory::new();
|
||||
|
||||
// Sanity check that the shared chips are not already present in the builder.
|
||||
assert!(builder
|
||||
.find_chip::<SharedBitwiseOperationLookupChip<8>>()
|
||||
.is_empty());
|
||||
inventory.add_periphery_chip(self.bitwise_lookup_8.clone());
|
||||
|
||||
if let Some(tuple_checker) = &self.tuple_range_checker {
|
||||
assert!(builder
|
||||
.find_chip::<SharedRangeTupleCheckerChip<2>>()
|
||||
.is_empty());
|
||||
inventory.add_periphery_chip(tuple_checker.clone());
|
||||
}
|
||||
|
||||
// The range checker is already present in the builder because it's is used by the system, so we don't add it again.
|
||||
assert_eq!(
|
||||
builder.find_chip::<SharedVariableRangeCheckerChip>().len(),
|
||||
1
|
||||
);
|
||||
|
||||
Ok(inventory)
|
||||
}
|
||||
}
|
||||
|
||||
impl SharedPeripheryChips {
|
||||
/// Sends concrete values to the shared chips using a given bus id.
|
||||
/// Panics if the bus id doesn't match any of the chips' bus ids.
|
||||
pub fn apply(&self, bus_id: u16, mult: u32, mut args: impl Iterator<Item = u32>) {
|
||||
match bus_id {
|
||||
id if id == self.bitwise_lookup_8.bus().inner.index => {
|
||||
// bitwise operation lookup
|
||||
// interpret the arguments, see `Air<AB> for BitwiseOperationLookupAir<NUM_BITS>`
|
||||
let [x, y, x_xor_y, selector] = [
|
||||
args.next().unwrap(),
|
||||
args.next().unwrap(),
|
||||
args.next().unwrap(),
|
||||
args.next().unwrap(),
|
||||
];
|
||||
|
||||
for _ in 0..mult {
|
||||
match selector {
|
||||
0 => {
|
||||
self.bitwise_lookup_8.request_range(x, y);
|
||||
}
|
||||
1 => {
|
||||
let res = self.bitwise_lookup_8.request_xor(x, y);
|
||||
debug_assert_eq!(res, x_xor_y);
|
||||
}
|
||||
_ => {
|
||||
unreachable!("Invalid selector");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
id if id == self.range_checker.bus().index() => {
|
||||
// interpret the arguments, see `Air<AB> for VariableRangeCheckerAir`
|
||||
let [value, max_bits] = [args.next().unwrap(), args.next().unwrap()];
|
||||
|
||||
for _ in 0..mult {
|
||||
self.range_checker.add_count(value, max_bits as usize);
|
||||
}
|
||||
}
|
||||
id if Some(id)
|
||||
== self
|
||||
.tuple_range_checker
|
||||
.as_ref()
|
||||
.map(|c| c.bus().inner.index) =>
|
||||
{
|
||||
// tuple range checker
|
||||
// We pass a slice. It is checked inside `add_count`.
|
||||
let args = args.collect_vec();
|
||||
for _ in 0..mult {
|
||||
self.tuple_range_checker.as_ref().unwrap().add_count(&args);
|
||||
}
|
||||
}
|
||||
0..=2 => {
|
||||
// execution bridge, memory, pc lookup
|
||||
// do nothing
|
||||
}
|
||||
_ => {
|
||||
unreachable!("Bus interaction {} not implemented", bus_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
/// The core logic of our extension
|
||||
pub mod chip;
|
||||
/// The executor for the powdr instructions
|
||||
mod executor;
|
||||
/// The opcodes for the powdr instructions, which is used in the chip implementation and contains the opcode ID
|
||||
pub mod opcode;
|
||||
/// The integration of our extension with the VM
|
||||
mod vm;
|
||||
|
||||
mod executor;
|
||||
mod plonk;
|
||||
|
||||
pub use opcode::PowdrOpcode;
|
||||
|
||||
@@ -4,10 +4,10 @@ use std::sync::{Arc, Mutex};
|
||||
|
||||
use crate::plonk::air_to_plonkish::build_circuit;
|
||||
use crate::plonk::{Gate, Variable};
|
||||
use crate::powdr_extension::executor::PowdrExecutor;
|
||||
use crate::powdr_extension::executor::{PowdrExecutor, PowdrPeripheryInstances};
|
||||
use crate::powdr_extension::plonk::air::PlonkColumns;
|
||||
use crate::powdr_extension::PowdrOpcode;
|
||||
use crate::powdr_extension::{chip::SharedChips, PowdrPrecompile};
|
||||
use crate::powdr_extension::PowdrPrecompile;
|
||||
use crate::{BusMap, IntoOpenVm, OpenVmField};
|
||||
use itertools::Itertools;
|
||||
use openvm_circuit::utils::next_power_of_two_or_zero;
|
||||
@@ -50,7 +50,7 @@ impl<P: IntoOpenVm> PlonkChip<P> {
|
||||
precompile: PowdrPrecompile<P>,
|
||||
memory: Arc<Mutex<OfflineMemory<OpenVmField<P>>>>,
|
||||
base_config: SdkVmConfig,
|
||||
periphery: SharedChips,
|
||||
periphery: PowdrPeripheryInstances,
|
||||
bus_map: BusMap,
|
||||
) -> Self {
|
||||
let PowdrPrecompile {
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::iter::once;
|
||||
|
||||
use derive_more::From;
|
||||
|
||||
use crate::powdr_extension::executor::PowdrPeripheryInstances;
|
||||
use crate::{IntoOpenVm, OpenVmField};
|
||||
use openvm_circuit::arch::{InstructionExecutor, VmInventoryError};
|
||||
use openvm_circuit::{
|
||||
@@ -31,7 +32,6 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{BusMap, PrecompileImplementation};
|
||||
|
||||
use super::chip::SharedChips;
|
||||
use super::plonk::chip::PlonkChip;
|
||||
use super::{chip::PowdrChip, PowdrOpcode};
|
||||
|
||||
@@ -196,28 +196,24 @@ impl<P: IntoOpenVm> VmExtension<OpenVmField<P>> for PowdrExtension<P> {
|
||||
.first()
|
||||
.cloned();
|
||||
|
||||
// Create the shared chips and the dummy shared chips
|
||||
let shared_chips_pair =
|
||||
PowdrPeripheryInstances::new(range_checker, bitwise_lookup, tuple_range_checker);
|
||||
|
||||
for precompile in &self.precompiles {
|
||||
let powdr_chip: PowdrExecutor<P> = match self.implementation {
|
||||
PrecompileImplementation::SingleRowChip => PowdrChip::new(
|
||||
precompile.clone(),
|
||||
offline_memory.clone(),
|
||||
self.base_config.clone(),
|
||||
SharedChips::new(
|
||||
bitwise_lookup.clone(),
|
||||
range_checker.clone(),
|
||||
tuple_range_checker.cloned(),
|
||||
),
|
||||
shared_chips_pair.clone(),
|
||||
)
|
||||
.into(),
|
||||
PrecompileImplementation::PlonkChip => PlonkChip::new(
|
||||
precompile.clone(),
|
||||
offline_memory.clone(),
|
||||
self.base_config.clone(),
|
||||
SharedChips::new(
|
||||
bitwise_lookup.clone(),
|
||||
range_checker.clone(),
|
||||
tuple_range_checker.cloned(),
|
||||
),
|
||||
shared_chips_pair.clone(),
|
||||
self.bus_map.clone(),
|
||||
)
|
||||
.into(),
|
||||
|
||||
@@ -128,7 +128,7 @@ pub fn symbolic_to_algebraic<T: PrimeField32, P: FieldElement>(
|
||||
_ => unimplemented!(),
|
||||
},
|
||||
SymbolicExpression::IsFirstRow => AlgebraicExpression::Reference(AlgebraicReference {
|
||||
name: "is_first_row".to_string(),
|
||||
name: String::new(),
|
||||
poly_id: PolyID {
|
||||
id: 0,
|
||||
ptype: PolynomialType::Constant,
|
||||
@@ -136,7 +136,7 @@ pub fn symbolic_to_algebraic<T: PrimeField32, P: FieldElement>(
|
||||
next: false,
|
||||
}),
|
||||
SymbolicExpression::IsLastRow => AlgebraicExpression::Reference(AlgebraicReference {
|
||||
name: "is_last_row".to_string(),
|
||||
name: String::new(),
|
||||
poly_id: PolyID {
|
||||
id: 1,
|
||||
ptype: PolynomialType::Constant,
|
||||
@@ -144,7 +144,7 @@ pub fn symbolic_to_algebraic<T: PrimeField32, P: FieldElement>(
|
||||
next: false,
|
||||
}),
|
||||
SymbolicExpression::IsTransition => AlgebraicExpression::Reference(AlgebraicReference {
|
||||
name: "is_transition".to_string(),
|
||||
name: String::new(),
|
||||
poly_id: PolyID {
|
||||
id: 2,
|
||||
ptype: PolynomialType::Constant,
|
||||
|
||||
Reference in New Issue
Block a user