Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
- Fixed the release dry-run publish cycle between `miden-air` and `miden-ace-codegen`, and preserved leaf-only DAG imports with explicit snapshots ([#2931](https://github.com/0xMiden/miden-vm/pull/2931)).
- Added regression coverage for the exact `max_num_continuations` continuation-stack boundary ([#2995](https://github.com/0xMiden/miden-vm/pull/2995)).
- Fixed AEAD padding handling so encrypt does not overwrite memory next to the plaintext buffer and decrypt leaves the plaintext output tail untouched ([#3008](https://github.com/0xMiden/miden-vm/pull/3008)).
- Fixed MAST compaction after debug info is cleared so compiler-generated packages do not grow ([#3044](https://github.com/0xMiden/miden-vm/pull/3044)).

#### Changes

Expand Down
6 changes: 6 additions & 0 deletions core/src/mast/debuginfo/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,12 @@ impl DebugInfo {
pub(crate) fn op_decorator_storage(&self) -> &OpToDecoratorIds {
&self.op_decorator_storage
}

/// Returns the node decorator storage.
#[cfg(test)]
pub(crate) fn node_decorator_storage(&self) -> &NodeToDecoratorIds {
&self.node_decorator_storage
}
}

impl Serializable for DebugInfo {
Expand Down
33 changes: 33 additions & 0 deletions core/src/mast/merger/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,39 @@ fn mast_forest_merge_preserves_dyn_callness_and_digest() {
assert_eq!(merged_dyncall.digest(), dyncall_digest, "dyncall digest should be preserved");
}

#[test]
fn mast_forest_merge_preserves_padded_basic_block_batches() {
let mut forest = MastForest::new();

let operations = vec![Operation::Add, Operation::Push(Felt::new_unchecked(100))];
let block_id = BasicBlockNodeBuilder::new(operations.clone(), Vec::new())
.add_to_forest(&mut forest)
.unwrap();
forest.make_root(block_id);

let original_block = forest[block_id].unwrap_basic_block();
assert!(
original_block.operations().count() > original_block.raw_operations().count(),
"test input must create padded operations"
);
let original_batches = original_block.op_batches().to_vec();

let (merged, root_maps) = MastForest::merge([&forest]).unwrap();

let merged_block_id = root_maps.map_root(0, &block_id).unwrap();
let merged_block = merged[merged_block_id].unwrap_basic_block();
assert_eq!(
merged_block.raw_operations().copied().collect::<Vec<_>>(),
operations,
"merge must not treat padded operations as raw operations"
);
assert_eq!(
merged_block.op_batches(),
original_batches,
"merge must preserve the original batch layout"
);
}

/// Tests that Call(bar) still correctly calls the remapped bar block.
///
/// [Block(foo), Call(foo)]
Expand Down
4 changes: 4 additions & 0 deletions core/src/mast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,10 @@ impl MastForest {
before_enter: &[DecoratorId],
after_exit: &[DecoratorId],
) {
if before_enter.is_empty() && after_exit.is_empty() {
return;
}

self.debug_info.register_node_decorators(node_id, before_enter, after_exit);
}

Expand Down
25 changes: 25 additions & 0 deletions core/src/mast/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,31 @@ fn test_clear_debug_info_multiple_node_types() {
assert!(forest.decorator_links_for_node(block_id).unwrap().into_iter().next().is_none());
}

#[test]
fn test_compact_after_clear_debug_info_does_not_materialize_empty_node_decorators() {
let mut forest = MastForest::new();
let decorator = forest.add_decorator(Decorator::Trace(1)).unwrap();
let block_id = BasicBlockNodeBuilder::new(
vec![Operation::Push(Felt::new_unchecked(1)), Operation::Add],
vec![(0, decorator)],
)
.with_before_enter(vec![decorator])
.add_to_forest(&mut forest)
.unwrap();
let call_id = CallNodeBuilder::new(block_id).add_to_forest(&mut forest).unwrap();
forest.make_root(call_id);

forest.clear_debug_info();
let (compacted, _) = forest.compact();

assert!(compacted.debug_info.node_decorator_storage().is_empty());
for node_idx in 0..compacted.nodes().len() {
let node_id = crate::mast::MastNodeId::new_unchecked(node_idx as u32);
assert!(compacted.before_enter_decorators(node_id).is_empty());
assert!(compacted.after_exit_decorators(node_id).is_empty());
}
}

#[test]
fn test_mast_forest_roundtrip_with_basic_blocks_and_decorators() {
use crate::mast::MastNode;
Expand Down
58 changes: 58 additions & 0 deletions crates/assembly/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4990,6 +4990,64 @@ fn regression_empty_kernel_library_is_rejected() {
assert_diagnostic_lines!(err, "library must contain at least one exported procedure");
}

/// Reproduces issue #3035: a MAST with padded basic blocks grows when debug info is cleared and the
/// forest is compacted via self-merge.
#[test]
fn issue_3035_compact_after_clear_debug_info_does_not_grow_mast() -> TestResult {
let context = TestContext::default();
let module = context.parse_module_with_path(
"issue_3035::repro",
source_file!(
&context,
"
pub proc repro
add
push.100
end
"
),
)?;

let library = Assembler::new(context.source_manager()).assemble_library([module])?;
let mut forest = library.mast_forest().as_ref().clone();
assert!(
forest
.nodes()
.iter()
.filter_map(|node| node.get_basic_block())
.any(|block| { block.operations().count() > block.raw_operations().count() }),
"test input must create at least one padded basic block"
);

forest.clear_debug_info();
let stripped_size = forest.to_bytes().len();
let stripped_without_debug_info_size = {
let mut bytes = Vec::new();
forest.write_stripped(&mut bytes);
bytes.len()
};
let stripped_nodes = forest.nodes().len();
let (compacted, _) = forest.compact();
let compacted_size = compacted.to_bytes().len();
let compacted_without_debug_info_size = {
let mut bytes = Vec::new();
compacted.write_stripped(&mut bytes);
bytes.len()
};
let compacted_nodes = compacted.nodes().len();

assert!(
compacted_size <= stripped_size,
"MastForest::compact increased serialized size after clear_debug_info(): \
stripped={stripped_size}, compacted={compacted_size}, \
stripped_without_debug_info={stripped_without_debug_info_size}, \
compacted_without_debug_info={compacted_without_debug_info_size}, \
stripped_nodes={stripped_nodes}, compacted_nodes={compacted_nodes}"
);

Ok(())
}

/// Test for issue #1644: verify that single-forest merge doesn't preserves node digests
#[test]
fn issue_1644_single_forest_merge_identity() -> TestResult {
Expand Down
Loading