Skip to content

Commit

Permalink
refactor: compile advice injectors down to Operation::Emit
Browse files Browse the repository at this point in the history
  • Loading branch information
plafer committed Nov 21, 2024
1 parent 17b5dff commit 8cab631
Show file tree
Hide file tree
Showing 14 changed files with 176 additions and 261 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
- [BREAKING] `DYNCALL` operation fixed, and now expects a memory address pointing to the procedure hash (#1535).
- Permit child `MastNodeId`s to exceed the `MastNodeId`s of their parents (#1542).
- Don't validate export names on `Library` deserialization (#1554)
- Compile advice injectors down to `Emit` operations (#1581)

#### Fixes

Expand Down
11 changes: 6 additions & 5 deletions assembly/src/assembler/basic_block_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ impl BasicBlockBuilder<'_> {
let new_len = self.ops.len() + n;
self.ops.resize(new_len, op);
}

/// Converts the advice injector into its corresponding event ID, and adds an `Emit` operation
/// to the list of basic block operations.
pub fn push_advice_injector(&mut self, injector: AdviceInjector) {
self.push_op(Operation::Emit(injector.into_event_id()))
}
}

/// Decorators
Expand All @@ -105,11 +111,6 @@ impl BasicBlockBuilder<'_> {
Ok(())
}

/// Adds the specified advice injector to the list of basic block decorators.
pub fn push_advice_injector(&mut self, injector: AdviceInjector) -> Result<(), AssemblyError> {
self.push_decorator(Decorator::Advice(injector))
}

/// Adds an AsmOp decorator to the list of basic block decorators.
///
/// This indicates that the provided instruction should be tracked and the cycle count for
Expand Down
5 changes: 1 addition & 4 deletions assembly/src/assembler/instruction/adv_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ pub fn adv_push(block_builder: &mut BasicBlockBuilder, n: u8) -> Result<(), Asse
// ================================================================================================

/// Appends advice injector decorator to the span.
pub fn adv_inject(
block_builder: &mut BasicBlockBuilder,
injector: &AdviceInjectorNode,
) -> Result<(), AssemblyError> {
pub fn adv_inject(block_builder: &mut BasicBlockBuilder, injector: &AdviceInjectorNode) {
block_builder.push_advice_injector(injector.into())
}
22 changes: 8 additions & 14 deletions assembly/src/assembler/instruction/crypto_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,10 @@ pub(super) fn hmerge(block_builder: &mut BasicBlockBuilder) {
/// - root of the tree, 4 elements.
///
/// This operation takes 9 VM cycles.
pub(super) fn mtree_get(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> {
pub(super) fn mtree_get(block_builder: &mut BasicBlockBuilder) {
// stack: [d, i, R, ...]
// pops the value of the node we are looking for from the advice stack
read_mtree_node(block_builder)?;
read_mtree_node(block_builder);
#[rustfmt::skip]
let ops = [
// verify the node V for root R with depth d and index i
Expand All @@ -128,8 +128,6 @@ pub(super) fn mtree_get(block_builder: &mut BasicBlockBuilder) -> Result<(), Ass
MovUp4, Drop, MovUp4, Drop,
];
block_builder.push_ops(ops);

Ok(())
}

/// Appends the MRUPDATE op with a parameter of "false" and stack manipulations to the span block
Expand Down Expand Up @@ -165,18 +163,16 @@ pub(super) fn mtree_set(block_builder: &mut BasicBlockBuilder) -> Result<(), Ass
/// It is not checked whether the provided roots exist as Merkle trees in the advide providers.
///
/// This operation takes 16 VM cycles.
pub(super) fn mtree_merge(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> {
pub(super) fn mtree_merge(block_builder: &mut BasicBlockBuilder) {
// stack input: [R_rhs, R_lhs, ...]
// stack output: [R_merged, ...]

// invoke the advice provider function to merge 2 Merkle trees defined by the roots on the top
// of the operand stack
block_builder.push_advice_injector(AdviceInjector::MerkleNodeMerge)?;
block_builder.push_advice_injector(AdviceInjector::MerkleNodeMerge);

// perform the `hmerge`, updating the operand stack
hmerge(block_builder);

Ok(())
hmerge(block_builder)
}

// MERKLE TREES - HELPERS
Expand All @@ -199,20 +195,18 @@ pub(super) fn mtree_merge(block_builder: &mut BasicBlockBuilder) -> Result<(), A
/// - new value of the node, 4 elements (only in the case of mtree_set)
///
/// This operation takes 4 VM cycles.
fn read_mtree_node(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> {
fn read_mtree_node(block_builder: &mut BasicBlockBuilder) {
// The stack should be arranged in the following way: [d, i, R, ...] so that the decorator
// can fetch the node value from the root. In the `mtree.get` operation we have the stack in
// the following format: [d, i, R], whereas in the case of `mtree.set` we would also have the
// new node value post the tree root: [d, i, R, V_new]
//
// pops the value of the node we are looking for from the advice stack
block_builder.push_advice_injector(AdviceInjector::MerkleNodeToStack)?;
block_builder.push_advice_injector(AdviceInjector::MerkleNodeToStack);

// pops the old node value from advice the stack => MPVERIFY: [V_old, d, i, R, ...]
// MRUPDATE: [V_old, d, i, R, V_new, ...]
block_builder.push_op_many(AdvPop, 4);

Ok(())
}

/// Update a node in the merkle tree. This operation will always copy the tree into a new instance,
Expand All @@ -225,7 +219,7 @@ fn update_mtree(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyErr

// Inject the old node value onto the stack for the call to MRUPDATE.
// stack: [V_old, d, i, R_old, V_new, ...] (4 cycles)
read_mtree_node(block_builder)?;
read_mtree_node(block_builder);

#[rustfmt::skip]
let ops = [
Expand Down
8 changes: 3 additions & 5 deletions assembly/src/assembler/instruction/ext2_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ pub fn ext2_mul(block_builder: &mut BasicBlockBuilder) {
/// operations outputs the result c = (c1, c0) where c = a * b^-1.
///
/// This operation takes 11 VM cycles.
pub fn ext2_div(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> {
block_builder.push_advice_injector(Ext2Inv)?;
pub fn ext2_div(block_builder: &mut BasicBlockBuilder) {
block_builder.push_advice_injector(Ext2Inv);
#[rustfmt::skip]
let ops = [
AdvPop, // [b0', b1, b0, a1, a0, ...]
Expand All @@ -70,8 +70,6 @@ pub fn ext2_div(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyErr
Drop // [a1*b1', a0*b0'...]
];
block_builder.push_ops(ops);

Ok(())
}

/// Given a stack with initial configuration given by [a1, a0, ...] where a = (a0, a1) represents
Expand Down Expand Up @@ -116,7 +114,7 @@ pub fn ext2_neg(block_builder: &mut BasicBlockBuilder) {
///
/// This operation takes 8 VM cycles.
pub fn ext2_inv(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> {
block_builder.push_advice_injector(Ext2Inv)?;
block_builder.push_advice_injector(Ext2Inv);
#[rustfmt::skip]
let ops = [
AdvPop, // [a0', a1, a0, ...]
Expand Down
6 changes: 2 additions & 4 deletions assembly/src/assembler/instruction/field_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,8 @@ fn perform_exp_for_small_power(span_builder: &mut BasicBlockBuilder, pow: u64) {
///
/// # Errors
/// Returns an error if the logarithm argument (top stack element) equals ZERO.
pub fn ilog2(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> {
block_builder.push_advice_injector(AdviceInjector::ILog2)?;
pub fn ilog2(block_builder: &mut BasicBlockBuilder) {
block_builder.push_advice_injector(AdviceInjector::ILog2);
block_builder.push_op(AdvPop); // [ilog2, n, ...]

// compute the power-of-two for the value given in the advice tape (17 cycles)
Expand Down Expand Up @@ -290,8 +290,6 @@ pub fn ilog2(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError>
];

block_builder.push_ops(ops);

Ok(())
}

// COMPARISON OPERATIONS
Expand Down
18 changes: 9 additions & 9 deletions assembly/src/assembler/instruction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ impl Assembler {
Instruction::ExpBitLength(num_pow_bits) => {
field_ops::exp(block_builder, *num_pow_bits)?
},
Instruction::ILog2 => field_ops::ilog2(block_builder)?,
Instruction::ILog2 => field_ops::ilog2(block_builder),

Instruction::Not => block_builder.push_op(Not),
Instruction::And => block_builder.push_op(And),
Expand All @@ -111,7 +111,7 @@ impl Assembler {
Instruction::Ext2Add => ext2_ops::ext2_add(block_builder),
Instruction::Ext2Sub => ext2_ops::ext2_sub(block_builder),
Instruction::Ext2Mul => ext2_ops::ext2_mul(block_builder),
Instruction::Ext2Div => ext2_ops::ext2_div(block_builder)?,
Instruction::Ext2Div => ext2_ops::ext2_div(block_builder),
Instruction::Ext2Neg => ext2_ops::ext2_neg(block_builder),
Instruction::Ext2Inv => ext2_ops::ext2_inv(block_builder)?,

Expand Down Expand Up @@ -190,10 +190,10 @@ impl Assembler {
Instruction::U32Rotr => u32_ops::u32rotr(block_builder, None)?,
Instruction::U32RotrImm(v) => u32_ops::u32rotr(block_builder, Some(v.expect_value()))?,
Instruction::U32Popcnt => u32_ops::u32popcnt(block_builder),
Instruction::U32Clz => u32_ops::u32clz(block_builder)?,
Instruction::U32Ctz => u32_ops::u32ctz(block_builder)?,
Instruction::U32Clo => u32_ops::u32clo(block_builder)?,
Instruction::U32Cto => u32_ops::u32cto(block_builder)?,
Instruction::U32Clz => u32_ops::u32clz(block_builder),
Instruction::U32Ctz => u32_ops::u32ctz(block_builder),
Instruction::U32Clo => u32_ops::u32clo(block_builder),
Instruction::U32Cto => u32_ops::u32cto(block_builder),
Instruction::U32Lt => u32_ops::u32lt(block_builder),
Instruction::U32Lte => u32_ops::u32lte(block_builder),
Instruction::U32Gt => u32_ops::u32gt(block_builder),
Expand Down Expand Up @@ -361,15 +361,15 @@ impl Assembler {
false,
)?,

Instruction::AdvInject(injector) => adv_ops::adv_inject(block_builder, injector)?,
Instruction::AdvInject(injector) => adv_ops::adv_inject(block_builder, injector),

// ----- cryptographic instructions ---------------------------------------------------
Instruction::Hash => crypto_ops::hash(block_builder),
Instruction::HPerm => block_builder.push_op(HPerm),
Instruction::HMerge => crypto_ops::hmerge(block_builder),
Instruction::MTreeGet => crypto_ops::mtree_get(block_builder)?,
Instruction::MTreeGet => crypto_ops::mtree_get(block_builder),
Instruction::MTreeSet => crypto_ops::mtree_set(block_builder)?,
Instruction::MTreeMerge => crypto_ops::mtree_merge(block_builder)?,
Instruction::MTreeMerge => crypto_ops::mtree_merge(block_builder),
Instruction::MTreeVerify => block_builder.push_op(MpVerify(0)),
Instruction::MTreeVerifyWithError(err_code) => {
block_builder.push_op(MpVerify(err_code.expect_value()))
Expand Down
20 changes: 8 additions & 12 deletions assembly/src/assembler/instruction/u32_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,51 +298,47 @@ pub fn u32popcnt(span_builder: &mut BasicBlockBuilder) {
/// provider).
///
/// This operation takes 42 VM cycles.
pub fn u32clz(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> {
block_builder.push_advice_injector(AdviceInjector::U32Clz)?;
pub fn u32clz(block_builder: &mut BasicBlockBuilder) {
block_builder.push_advice_injector(AdviceInjector::U32Clz);
block_builder.push_op(AdvPop); // [clz, n, ...]

verify_clz(block_builder);
Ok(())
}

/// Translates `u32ctz` assembly instruction to VM operations. `u32ctz` counts the number of
/// trailing zeros of the value using non-deterministic technique (i.e. it takes help of advice
/// provider).
///
/// This operation takes 34 VM cycles.
pub fn u32ctz(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> {
block_builder.push_advice_injector(AdviceInjector::U32Ctz)?;
pub fn u32ctz(block_builder: &mut BasicBlockBuilder) {
block_builder.push_advice_injector(AdviceInjector::U32Ctz);
block_builder.push_op(AdvPop); // [ctz, n, ...]

verify_ctz(block_builder);
Ok(())
}

/// Translates `u32clo` assembly instruction to VM operations. `u32clo` counts the number of
/// leading ones of the value using non-deterministic technique (i.e. it takes help of advice
/// provider).
///
/// This operation takes 41 VM cycles.
pub fn u32clo(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> {
block_builder.push_advice_injector(AdviceInjector::U32Clo)?;
pub fn u32clo(block_builder: &mut BasicBlockBuilder) {
block_builder.push_advice_injector(AdviceInjector::U32Clo);
block_builder.push_op(AdvPop); // [clo, n, ...]

verify_clo(block_builder);
Ok(())
}

/// Translates `u32cto` assembly instruction to VM operations. `u32cto` counts the number of
/// trailing ones of the value using non-deterministic technique (i.e. it takes help of advice
/// provider).
///
/// This operation takes 33 VM cycles.
pub fn u32cto(block_builder: &mut BasicBlockBuilder) -> Result<(), AssemblyError> {
block_builder.push_advice_injector(AdviceInjector::U32Cto)?;
pub fn u32cto(block_builder: &mut BasicBlockBuilder) {
block_builder.push_advice_injector(AdviceInjector::U32Cto);
block_builder.push_op(AdvPop); // [cto, n, ...]

verify_cto(block_builder);
Ok(())
}

/// Specifically handles these specific inputs per the spec.
Expand Down
Loading

0 comments on commit 8cab631

Please sign in to comment.