Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement table-based MAST #1349

Merged
merged 109 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
109 commits
Select commit Hold shift + click to select a range
13fab50
MastForest scaffolding
plafer Jun 5, 2024
ede129f
implement `CallNode` and `DynNode`
plafer Jun 5, 2024
2715666
Implement `BasicBlockNode` processing
plafer Jun 5, 2024
e36fb40
implement `External` processing
plafer Jun 5, 2024
eee816b
implement `execute_mast`
plafer Jun 5, 2024
fd77cf7
Add `MastForest::add_node()`
plafer Jun 5, 2024
e93889f
`BasicBlockNode` constructors
plafer Jun 5, 2024
408f082
JoinNode constructor
plafer Jun 5, 2024
464773a
SplitNode constructor
plafer Jun 5, 2024
7276593
LoopNode constructor
plafer Jun 5, 2024
6f607ae
`CallNode` constructors
plafer Jun 5, 2024
7fd1755
dyn and external constructors
plafer Jun 5, 2024
fde1c72
Remove comment
plafer Jun 5, 2024
f9a1d0d
`SpanBuilder` methods
plafer Jun 5, 2024
ae2bd01
`MastNodeId`: make `u32`
plafer Jun 6, 2024
2678a5a
fmt
plafer Jun 6, 2024
979e6a3
implement `Index` for `MastForex`, and mark `inline(always)`
plafer Jun 6, 2024
805be4a
Assembler: switch to use `MastForest`
plafer Jun 6, 2024
d5db165
`Assembler::assemble_test()`
plafer Jun 7, 2024
34fc0bb
Add `Display` to `MastForest`
plafer Jun 7, 2024
b408505
introduce `MastNodePrettyPrint`
plafer Jun 7, 2024
273bf76
Introduce `MastNodeDisplay`
plafer Jun 7, 2024
2babfde
fix nested_blocks test
plafer Jun 7, 2024
67106f3
BasicBlockNode: fix pretty print
plafer Jun 7, 2024
d99a915
revert `basic_block` change for now
plafer Jun 7, 2024
407f8ac
fix procedure invocation
plafer Jun 7, 2024
d93c304
Make `MastForest::get_node_id()` faillible, and use indexing everywhere
plafer Jun 7, 2024
93c11df
comment
plafer Jun 7, 2024
7411985
Rename `ExternalNode` -> `ProxyNode`
plafer Jun 7, 2024
d91bff1
update hash_memoization_control_blocks
plafer Jun 7, 2024
e7c06a3
update hash_memoization_control_blocks
plafer Jun 7, 2024
17a2b6f
update `build_bar_hash()`
plafer Jun 7, 2024
ac3691f
update decoder tests
plafer Jun 7, 2024
548009c
update processor tests
plafer Jun 7, 2024
3738d17
Merge branch 'next' into plafer-table-based-mast
plafer Jun 7, 2024
cae7bfe
processor tests
plafer Jun 10, 2024
93f79da
Remove `CodeBlock` and everything related
plafer Jun 10, 2024
424f029
Move `OpBatch`
plafer Jun 10, 2024
d79adf1
move tests
plafer Jun 10, 2024
710a142
Move `Kernel`
plafer Jun 10, 2024
26566bb
Move `ProgramInfo`
plafer Jun 10, 2024
7c26f85
rename `ensure_node()`
plafer Jun 10, 2024
76f63dc
Remove `combine_basic_blocks()`
plafer Jun 11, 2024
64a6b29
inline procedures on exec
plafer Jun 11, 2024
bce68f8
fix nested_blocks()
plafer Jun 11, 2024
63ac435
Remove `Proxy` node
plafer Jun 11, 2024
e9d2c92
Add comment in `invoke_mast_root()` about our assumptions
plafer Jun 11, 2024
354bbd3
move `MastNode` in submodule
plafer Jun 11, 2024
4f21406
rename `execute_mast_node()` and friends
plafer Jun 11, 2024
3dc679a
Introduce `Program`
plafer Jun 11, 2024
9a1146c
use `Program` everywhere
plafer Jun 11, 2024
8556210
fix `nested_control_blocks()` test
plafer Jun 11, 2024
7de110d
TestContext: use `assemble_test()`
plafer Jun 11, 2024
9bef046
program_with_nested_procedures fix
plafer Jun 11, 2024
b83bce5
fix program_with_one_procedure()
plafer Jun 11, 2024
57e1341
remove ignored test program_with_empty_procedure
plafer Jun 11, 2024
bc93877
fix program_with_one_import_and_hex_call
plafer Jun 11, 2024
494f4ec
ignore phantom mast call
plafer Jun 11, 2024
d616a81
fix comment_in_nested_control_blocks
plafer Jun 11, 2024
73c162f
fix test `module_alias()`
plafer Jun 11, 2024
22bcac7
fix `program_with_proc_locals()`
plafer Jun 11, 2024
df8709b
fix `program_with_reexported_proc_in_another/same_library`
plafer Jun 11, 2024
8546c21
Remove `procedure_cache()` test capability from `Assembler`
plafer Jun 11, 2024
f9c1d6e
Remove `Assembler::assemble_test()`
plafer Jun 11, 2024
8039cfb
assemble_with_options_in_context needs to consume self
plafer Jun 11, 2024
c8baccc
address TODOs
plafer Jun 11, 2024
7441749
`SplitNode`: move `to_pretty_print()` impl
plafer Jun 12, 2024
2773f37
Add `DynNode` digest test
plafer Jun 12, 2024
e50b961
verifier: export `ProgramInfo` and `Kernel`
plafer Jun 12, 2024
42b4b2a
execute: instrument("execute_program")
plafer Jun 12, 2024
aae29eb
`execute_mast_node`: use `get_node_id()` in case of errors
plafer Jun 12, 2024
c98029e
execute_call_node: use `get_node_by_id()`
plafer Jun 12, 2024
6dd2f0c
miden lib: restore all exports
plafer Jun 12, 2024
8e01659
fix `miden` crate
plafer Jun 12, 2024
4cdcffc
make `batch_ops` private
plafer Jun 12, 2024
fb151de
basic_block_node: fix docstring indentation
plafer Jun 12, 2024
e0d5cdf
fix mast module path
plafer Jun 12, 2024
2b0a44c
fix `Assembler::assemble_module()`
plafer Jun 12, 2024
fdad706
move `set_kernel` call
plafer Jun 12, 2024
9adc496
pretty print `basic_block` instead of `span`
plafer Jun 12, 2024
6960a56
cleanup MastNode.to_pretty_print
plafer Jun 12, 2024
d70308b
fix doc tests
plafer Jun 14, 2024
4c2af01
clippy
plafer Jun 14, 2024
ca59eb9
Update core/src/mast/mod.rs
plafer Jun 14, 2024
1f6a13e
Update core/src/mast/mod.rs
plafer Jun 14, 2024
a31ecd4
Use `as` operator for usize <-> u32
plafer Jun 14, 2024
4306ce2
inline(always) on `index()`
plafer Jun 14, 2024
6a06f32
use `Option.get()`
plafer Jun 14, 2024
2ef3a98
remove unused file
plafer Jun 14, 2024
ba75261
Use `binary_search` instead of linear search
plafer Jun 14, 2024
ab78daf
fix cli_run
plafer Jun 14, 2024
5b90c5b
fix asmop_repeat_test
plafer Jun 14, 2024
2fde2ba
docstring
plafer Jun 14, 2024
ceca4f6
fix test_falcon512_probabilistic_product_failure
plafer Jun 14, 2024
d3ff423
introduce `Assembler::assemble_program()`
plafer Jun 16, 2024
60428d4
Rename error variant to `MastNodeNotFoundInForest`
plafer Jun 16, 2024
5b70d83
test_utils: compile() returns `Program`
plafer Jun 16, 2024
bc8719d
fix `generate_blake3_program()`
plafer Jun 16, 2024
a062ba9
Use `program.get_node_by_id()` in the decoder
plafer Jun 16, 2024
fad9712
wrap at 100 and fix comment
plafer Jun 16, 2024
c34a532
rename `Procedure::code()` to `body_node` terminology
plafer Jun 16, 2024
4207e87
update changelog
plafer Jun 16, 2024
453f60f
clippy
plafer Jun 16, 2024
7ac1212
Remove disabled serialization tests
plafer Jun 18, 2024
c310a6f
Use `try_into()` for `usize -> u32` conversion
plafer Jun 18, 2024
d5c14c6
Remove `miette` dependency from `core`
plafer Jun 18, 2024
67343d1
fix README
plafer Jun 18, 2024
0d0e3e6
Remove `Program` type annotation
plafer Jun 18, 2024
39411a2
clippy
plafer Jun 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions assembly/src/assembler/span_builder.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use super::{AssemblyContext, BodyWrapper, Decorator, DecoratorList, Instruction};
use alloc::{borrow::Borrow, string::ToString, vec::Vec};
use vm_core::{code_blocks::CodeBlock, AdviceInjector, AssemblyOp, Operation};
use vm_core::{code_blocks::CodeBlock, mast::MastNode, AdviceInjector, AssemblyOp, Operation};

// SPAN BUILDER
// ================================================================================================

// TODOP: Rename `BasicBlockBuilder`
/// A helper struct for constructing SPAN blocks while compiling procedure bodies.
///
/// Operations and decorators can be added to a span builder via various `add_*()` and `push_*()`
Expand Down Expand Up @@ -65,7 +66,7 @@

/// Decorators
impl SpanBuilder {
/// Add ths specified decorator to the list of span decorators.
/// Add the specified decorator to the list of span decorators.
pub fn push_decorator(&mut self, decorator: Decorator) {
self.decorators.push((self.ops.len(), decorator));
}
Expand Down Expand Up @@ -144,4 +145,37 @@
self.ops.append(&mut self.epilogue);
self.extract_span_into(target);
}

/// Creates and returns a new BASIC BLOCK node from the operations and decorators currently in
/// this builder. If the builder is empty, then no node is created and `None` is returned.
///
/// This consumes all operations and decorators in the builder, but does not touch the
/// operations in the epilogue of the builder.
pub fn to_basic_block(&mut self) -> Option<MastNode> {

Check warning on line 154 in assembly/src/assembler/span_builder.rs

View workflow job for this annotation

GitHub Actions / Check Rust stable on ubuntu with --all-targets

methods `to_basic_block` and `into_basic_block` are never used

Check warning on line 154 in assembly/src/assembler/span_builder.rs

View workflow job for this annotation

GitHub Actions / Check Rust stable on ubuntu with --all-targets --all-features

methods `to_basic_block` and `into_basic_block` are never used

Check warning on line 154 in assembly/src/assembler/span_builder.rs

View workflow job for this annotation

GitHub Actions / Check Rust nightly on ubuntu with --all-targets --no-default-features

methods `to_basic_block` and `into_basic_block` are never used

Check warning on line 154 in assembly/src/assembler/span_builder.rs

View workflow job for this annotation

GitHub Actions / Check Rust nightly on ubuntu with --all-targets

methods `to_basic_block` and `into_basic_block` are never used

Check warning on line 154 in assembly/src/assembler/span_builder.rs

View workflow job for this annotation

GitHub Actions / Check Rust nightly on ubuntu with --all-targets --all-features

methods `to_basic_block` and `into_basic_block` are never used

Check warning on line 154 in assembly/src/assembler/span_builder.rs

View workflow job for this annotation

GitHub Actions / Check Rust stable on ubuntu with --all-targets --no-default-features

methods `to_basic_block` and `into_basic_block` are never used

Check warning on line 154 in assembly/src/assembler/span_builder.rs

View workflow job for this annotation

GitHub Actions / no-std (stable, wasm32-unknown-unknown)

methods `to_basic_block` and `into_basic_block` are never used

Check warning on line 154 in assembly/src/assembler/span_builder.rs

View workflow job for this annotation

GitHub Actions / no-std (nightly, wasm32-unknown-unknown)

methods `to_basic_block` and `into_basic_block` are never used

Check warning on line 154 in assembly/src/assembler/span_builder.rs

View workflow job for this annotation

GitHub Actions / Test Rust stable on ubuntu with --profile test-release --doc

methods `to_basic_block` and `into_basic_block` are never used

Check warning on line 154 in assembly/src/assembler/span_builder.rs

View workflow job for this annotation

GitHub Actions / Test Rust nightly on ubuntu with --profile test-release --doc

methods `to_basic_block` and `into_basic_block` are never used

Check warning on line 154 in assembly/src/assembler/span_builder.rs

View workflow job for this annotation

GitHub Actions / Test Rust stable on ubuntu with --profile test-release

methods `to_basic_block` and `into_basic_block` are never used

Check warning on line 154 in assembly/src/assembler/span_builder.rs

View workflow job for this annotation

GitHub Actions / Test Rust nightly on ubuntu with --profile test-release

methods `to_basic_block` and `into_basic_block` are never used
if !self.ops.is_empty() {
let ops = self.ops.drain(..).collect();
let decorators = self.decorators.drain(..).collect();

Some(MastNode::new_basic_block_with_decorators(ops, decorators))
} else if !self.decorators.is_empty() {
// this is a bug in the assembler. we shouldn't have decorators added without their
// associated operations
// TODO: change this to an error or allow decorators in empty span blocks
unreachable!("decorators in an empty SPAN block")
} else {
None
}
}

/// Creates and returns a new BASIC BLOCK node from the operations and decorators currently in
/// this builder. If the builder is empty, then no node is created and `None` is returned.
///
/// The main differences with [`Self::to_basic_block`] are:
/// - Operations contained in the epilogue of the builder are appended to the list of ops which
/// go into the new BASIC BLOCK node.
/// - The builder is consumed in the process.
pub fn into_basic_block(mut self) -> Option<MastNode> {
self.ops.append(&mut self.epilogue);
self.to_basic_block()
}
}
4 changes: 3 additions & 1 deletion core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ pub mod crypto {
}
}

pub mod mast;

pub use math::{
fields::{f64::BaseElement as Felt, QuadExtension},
polynom, ExtensionOf, FieldElement, StarkField, ToElements,
Expand All @@ -91,7 +93,7 @@ pub mod prettier {
pub use miden_formatting::{prettier::*, pretty_via_display, pretty_via_to_string};
}

mod program;
pub mod program;
pub use program::{blocks as code_blocks, CodeBlockTable, Kernel, Program, ProgramInfo};

mod operations;
Expand Down
94 changes: 94 additions & 0 deletions core/src/mast/basic_block_node.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use alloc::vec::Vec;
use miden_crypto::hash::rpo::RpoDigest;

use crate::{
code_blocks::{batch_ops, OpBatch},
DecoratorIterator, DecoratorList, Operation,
};

use super::MerkleTreeNode;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BasicBlockNode {
/// The primitive operations contained in this basic block.
///
/// The operations are broken up into batches of 8 groups,
/// with each group containing up to 9 operations, or a
/// single immediates. Thus the maximum size of each batch
/// is 72 operations. Multiple batches are used for blocks
/// consisting of more than 72 operations.
op_batches: Vec<OpBatch>,
digest: RpoDigest,
decorators: DecoratorList,
}

/// Constructors
impl BasicBlockNode {
/// Returns a new [`BasicBlockNode`] instantiated with the specified operations.
///
/// # Errors (TODO)
/// Returns an error if:
/// - `operations` vector is empty.
/// - `operations` vector contains any number of system operations.
pub fn new(operations: Vec<Operation>) -> Self {
assert!(!operations.is_empty()); // TODO: return error
Self::with_decorators(operations, DecoratorList::new())
}

/// Returns a new [`BasicBlockNode`] instantiated with the specified operations and decorators.
///
/// # Errors (TODO)
/// Returns an error if:
/// - `operations` vector is empty.
/// - `operations` vector contains any number of system operations.
pub fn with_decorators(operations: Vec<Operation>, decorators: DecoratorList) -> Self {
assert!(!operations.is_empty()); // TODO: return error

// validate decorators list (only in debug mode)
#[cfg(debug_assertions)]
validate_decorators(&operations, &decorators);

let (op_batches, digest) = batch_ops(operations);
Self {
op_batches,
digest,
decorators,
}
}
}

impl BasicBlockNode {
pub fn op_batches(&self) -> &[OpBatch] {
&self.op_batches
}

/// Returns a [`DecoratorIterator`] which allows us to iterate through the decorator list of
/// this span block while executing operation batches of this span block
pub fn decorator_iter(&self) -> DecoratorIterator {
DecoratorIterator::new(&self.decorators)
}
}

impl MerkleTreeNode for BasicBlockNode {
fn digest(&self) -> RpoDigest {
self.digest
}
}

/// Checks if a given decorators list is valid (only checked in debug mode)
/// - Assert the decorator list is in ascending order.
/// - Assert the last op index in decorator list is less than or equal to the number of operations.
#[cfg(debug_assertions)]
fn validate_decorators(operations: &[Operation], decorators: &DecoratorList) {
if !decorators.is_empty() {
// check if decorator list is sorted
for i in 0..(decorators.len() - 1) {
debug_assert!(decorators[i + 1].0 >= decorators[i].0, "unsorted decorators list");
}
// assert the last index in decorator list is less than operations vector length
debug_assert!(
operations.len() >= decorators.last().expect("empty decorators list").0,
"last op index in decorator list should be less than or equal to the number of ops"
);
}
}
79 changes: 79 additions & 0 deletions core/src/mast/call_node.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use miden_crypto::{hash::rpo::RpoDigest, Felt};

use crate::{chiplets::hasher, Operation};

use super::{MastForest, MastNodeId, MerkleTreeNode};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CallNode {
callee: MastNodeId,
is_syscall: bool,
digest: RpoDigest,
}

/// Constants
impl CallNode {
/// The domain of the call block (used for control block hashing).
pub const CALL_DOMAIN: Felt = Felt::new(Operation::Call.op_code() as u64);
/// The domain of the syscall block (used for control block hashing).
pub const SYSCALL_DOMAIN: Felt = Felt::new(Operation::SysCall.op_code() as u64);
}

/// Constructors
impl CallNode {
/// Returns a new [`CallNode`] instantiated with the specified callee.
pub fn new(callee: MastNodeId, mast_forest: &MastForest) -> Self {
let digest = {
let callee_digest = mast_forest.get_node_by_id(callee).digest();

hasher::merge_in_domain(&[callee_digest, RpoDigest::default()], Self::CALL_DOMAIN)
};

Self {
callee,
is_syscall: false,
digest,
}
}

/// Returns a new [`CallNode`] instantiated with the specified callee and marked as a kernel
/// call.
pub fn new_syscall(callee: MastNodeId, mast_forest: &MastForest) -> Self {
let digest = {
let callee_digest = mast_forest.get_node_by_id(callee).digest();

hasher::merge_in_domain(&[callee_digest, RpoDigest::default()], Self::SYSCALL_DOMAIN)
};

Self {
callee,
is_syscall: true,
digest,
}
}
}

impl CallNode {
pub fn callee(&self) -> MastNodeId {
self.callee
}

pub fn is_syscall(&self) -> bool {
self.is_syscall
}

/// Returns the domain of the call node.
pub fn hash_domain(&self) -> Felt {
if self.is_syscall() {
Self::SYSCALL_DOMAIN
} else {
Self::CALL_DOMAIN
}
}
}

impl MerkleTreeNode for CallNode {
fn digest(&self) -> RpoDigest {
self.digest
}
}
28 changes: 28 additions & 0 deletions core/src/mast/dyn_node.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use miden_crypto::{hash::rpo::RpoDigest, Felt};

use crate::Operation;

use super::MerkleTreeNode;

#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct DynNode;

/// Constants
impl DynNode {
/// The domain of the Dyn block (used for control block hashing).
pub const DOMAIN: Felt = Felt::new(Operation::Dyn.op_code() as u64);
}

impl MerkleTreeNode for DynNode {
fn digest(&self) -> RpoDigest {
// The Dyn node is represented by a constant, which is set to be the hash of two empty
// words ([ZERO, ZERO, ZERO, ZERO]) with a domain value of `DYN_DOMAIN`, i.e.
// hasher::merge_in_domain(&[Digest::default(), Digest::default()], DynNode::DOMAIN)
RpoDigest::new([
Felt::new(8115106948140260551),
Felt::new(13491227816952616836),
Felt::new(15015806788322198710),
Felt::new(16575543461540527115),
])
}
}
49 changes: 49 additions & 0 deletions core/src/mast/join_node.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use miden_crypto::{hash::rpo::RpoDigest, Felt};

use crate::{chiplets::hasher, Operation};

use super::{MastForest, MastNodeId, MerkleTreeNode};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct JoinNode {
children: [MastNodeId; 2],
digest: RpoDigest,
}

/// Constants
impl JoinNode {
/// The domain of the join block (used for control block hashing).
pub const DOMAIN: Felt = Felt::new(Operation::Join.op_code() as u64);
}

/// Constructors
impl JoinNode {
/// Returns a new [`JoinNode`] instantiated with the specified children nodes.
pub fn new(children: [MastNodeId; 2], mast_forest: &MastForest) -> Self {
let digest = {
let left_child_hash = mast_forest.get_node_by_id(children[0]).digest();
let right_child_hash = mast_forest.get_node_by_id(children[1]).digest();

hasher::merge_in_domain(&[left_child_hash, right_child_hash], Self::DOMAIN)
};

Self { children, digest }
}
}

/// Accessors
impl JoinNode {
pub fn first(&self) -> MastNodeId {
self.children[0]
}

pub fn second(&self) -> MastNodeId {
self.children[1]
}
}

impl MerkleTreeNode for JoinNode {
fn digest(&self) -> RpoDigest {
self.digest
}
}
42 changes: 42 additions & 0 deletions core/src/mast/loop_node.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use miden_crypto::{hash::rpo::RpoDigest, Felt};

use crate::{chiplets::hasher, Operation};

use super::{MastForest, MastNodeId, MerkleTreeNode};

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LoopNode {
body: MastNodeId,
digest: RpoDigest,
}

/// Constants
impl LoopNode {
/// The domain of the loop block (used for control block hashing).
pub const DOMAIN: Felt = Felt::new(Operation::Loop.op_code() as u64);
}

/// Constructors
impl LoopNode {
pub fn new(body: MastNodeId, mast_forest: &MastForest) -> Self {
let digest = {
let body_hash = mast_forest.get_node_by_id(body).digest();

hasher::merge_in_domain(&[body_hash, RpoDigest::default()], Self::DOMAIN)
};

Self { body, digest }
}
}

impl LoopNode {
pub fn body(&self) -> MastNodeId {
self.body
}
}

impl MerkleTreeNode for LoopNode {
fn digest(&self) -> RpoDigest {
self.digest
}
}
Loading
Loading