Skip to content

feat: make the Assembler work in debug mode, remove optionality#2396

Merged
bobbinth merged 9 commits intonextfrom
huitseeker/issue-1821
Dec 5, 2025
Merged

feat: make the Assembler work in debug mode, remove optionality#2396
bobbinth merged 9 commits intonextfrom
huitseeker/issue-1821

Conversation

@huitseeker
Copy link
Contributor

@huitseeker huitseeker commented Nov 28, 2025

On top of #2366.

This removes the optionality of "turning on" debug mode in the Assembler, fixing #1821. As an unavoidable consequence, we lose the ability to deduplicate between nodes, which are now all equipped with unique AsmOps. — though @plafer may have other ideas on this topic, and this PR could serve as a conversation starter.

We will recover compactness in MastForest representation when we purge such MastForests of decorators and solve #2368.

Adds a test to check the CLI still controls whether the processor (unchanged) still executes in debug mode depending on the --release parameter (this feature is orthogonal to the scope of this PR, we're just documenting its working with this test).

Fixes #1821

@huitseeker huitseeker changed the title Huitseeker/issue 1821 feat: make the Assembler work in debug mode, remove optionality Nov 28, 2025
@huitseeker huitseeker force-pushed the huitseeker/issue-1821 branch from 7e7a386 to a167f8d Compare November 28, 2025 04:56
@huitseeker huitseeker marked this pull request as draft November 28, 2025 05:50
@huitseeker huitseeker force-pushed the huitseeker/issue-1821 branch 2 times, most recently from 8800947 to 1e1ff22 Compare November 28, 2025 05:59
@huitseeker huitseeker marked this pull request as ready for review November 28, 2025 06:00
Base automatically changed from huitseeker/issue-1776 to next November 28, 2025 20:39
@bobbinth
Copy link
Contributor

Sorry - I merged #2366 before looking at this one. Could you merge the latest next into this PR?

@huitseeker huitseeker force-pushed the huitseeker/issue-1821 branch from 1e1ff22 to c986bb5 Compare November 29, 2025 13:41
@huitseeker
Copy link
Contributor Author

huitseeker commented Nov 29, 2025

@bobbinth No worries, I never intend to merge feature branches into each other anyway, all my PRs are always meant to be seen as a queue of things that all merge to main (or in this case next) in a FIFO manner. The rebase needed here is a consequence of the squash merge policy, and that's fine.

Copy link
Contributor

@bobbinth bobbinth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Thank you! I didn't really review the .snap file changes, but covered pretty much everything else and left a few small comments inline.

@plafer
Copy link
Contributor

plafer commented Dec 2, 2025

As an unavoidable consequence, we lose the ability to deduplicate between nodes, which are now all equipped with unique AsmOps.

I'm not sure we fully realized this when we opened the issue. So with #2368 what we're saying is:

  • By default, we essentially make all nodes unique so that we can attach some debug information to each.
  • If you want release mode for production, you need to compactify the resulting MastForest remove all duplicates.

Is this really what we want? Looking back at the initial conversation, I think what really we wanted was for debug mode to be the default so that we don't need any build.rs shenanigans; but not so much lose the ability to compile directly to release mode. In both cases, we still end up with 2 "types" of MastForest:

  • debug mode: larger, set up in a way that you can attach debug infos to each node
  • release mode: all duplicate nodes merged, removing the ability to attach debug infos

With this new approach, I just feel like we've made it harder for ourselves to obtain the "release mode MastForest".

@bitwalker
Copy link
Collaborator

Just to add some follow up after the sync call today: I think we may be able to get away from needing decorators entirely now that we have operation indexing/ranging. Doing so would probably mean removing the existing debug.* "instructions" entirely, or lower them some other way (especially since there isn't really any advantage to using decorators for them, we could just as easily implement them using events). The trace instruction is all but dead anyway, and the last remaining uses of it by the compiler can be transitioned to other approaches.

So, TL;DR, without decorators, we could move to a model without the need for node fingerprinting (or at least, fingerprinting would only need to deal with side-effecting operations like those that manipulate the advice provider), and all debugging-related information could be stored in a separate data structure from the MAST itself (only needing to provide some means to map operation indices/ranges to associated debug info).

That said, we could make those changes after this is merged, so I don't see it as blocking or anything, just a logical next step.

@bobbinth
Copy link
Contributor

bobbinth commented Dec 2, 2025

I'll hold off on merging this now (though, I think there are still arguments for doing so) - let's discuss this in more detail during our debug sync (hopefully tomorrow).

@huitseeker huitseeker mentioned this pull request Dec 2, 2025
@huitseeker huitseeker force-pushed the huitseeker/issue-1821 branch 2 times, most recently from 68f9849 to 1b08e16 Compare December 4, 2025 02:14
@huitseeker
Copy link
Contributor Author

huitseeker commented Dec 4, 2025

As requested, here's the script to measure the MastForest size. (use as an example in miden-vm)
Before (6049c56):

=== Miden Core Library Serialized MastForest Size ===
  Total serialized library size: 260,209 bytes
  Total serialized library size: 254.11 KB
  Total serialized library size: 0.25 MB
  Number of MAST nodes: 649
  Number of procedures: 272
  Bytes per MAST node: 400.94
  Bytes per procedure: 956.65

After:

=== Miden Core Library Serialized MastForest Size ===
Total serialized library size: 826039 bytes
Total serialized library size: 806.68 KB
Total serialized library size: 0.79 MB
Number of MAST nodes: 682
Number of procedures: 273
Bytes per MAST node: 1211.20
Bytes per procedure: 3025.78

Copy link
Contributor

@plafer plafer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

From the serialization size, we see that only 30 MAST nodes were removed by compaction (~4%), i.e. we don't get a lot of benefit from compacting. Makes sense to me to ask the user to call an explicit compact() method if they really want compaction.

@bobbinth
Copy link
Contributor

bobbinth commented Dec 5, 2025

=== Miden Core Library Serialized MastForest Size ===
  Total serialized library size: 260,209 bytes
  Total serialized library size: 254.11 KB
  Total serialized library size: 0.25 MB
  Number of MAST nodes: 649
  Number of procedures: 272
  Bytes per MAST node: 400.94
  Bytes per procedure: 956.65

After:

=== Miden Core Library Serialized MastForest Size ===
Total serialized library size: 826039 bytes
Total serialized library size: 806.68 KB
Total serialized library size: 0.79 MB
Number of MAST nodes: 682
Number of procedures: 273
Bytes per MAST node: 1211.20
Bytes per procedure: 3025.78

Very interesting! Thank you for running this comparison! So, the serialized size roughly tripled, but given that the overall size is pretty small, I think this is fine.

A couple of things I found surprising:

  • There are under 700 nodes in the entire core library. I would have expected there to be thousands of nodes - but maybe I overestimate our code complexity (the number of procedures looks roughly right though).
  • It is interesting that one extra procedure appeared after this PR. Is this because there are two modules somewhere with identical procedures, and previously these were getting naturally de-duplicated?

One other thing that would be good to do is to run our deserialize_libcore() benchmark (in miden-vm crate) to see how this has affected deserialization times. Assuming the impact is not too bad, we should go ahead resolve the conflicts and merge.

…nality

- Remove conditional debug logic from assembler compile_body (if/else, while loops)
- Always enable instruction tracking and debug decorators in instruction/mod.rs
- Update testing utilities to remove debug mode control
- Fix external callers across codebase to remove with_debug_mode() calls
- Preserve all debug functionality but make it permanently enabled

Progress on #1821
…g functionality

- Removed in_debug_mode field from Assembler struct
- Removed debug mode accessor and setter methods
- Always execute debug decorator code paths (if/else, while loops, instruction tracking)
- Updated testing utilities to remove debug mode control
- Fixed external callers across codebase to remove debug mode dependencies
- Updated test assertions to reflect new expected behavior with debug decorators
- Updated all snapshot tests to include asmOp decorators
- Fixed linting warnings

Resolves #1821: always include debug info when assembling
- Update get_masm_program() to always use assembler debug mode
- Remove Debug enum and related CLI functionality
- Remove with-debug-info feature from stdlib and Makefile
- Update all CLI callers to remove debug mode parameters
- Preserve VM debug mode via --release flag for ExecutionOptions
- All tests pass - assembler debug mode always enabled, VM debug mode optional

Fully addresses issue #1821: always include debug info when assembling
Test that demonstrates how the CLI --release flag controls processor debug mode.

The tests show that:
- CLI without --release: debug mode = ON (enable_debugging() = true)
- CLI with --release: debug mode = OFF (enable_debugging() = false)
- CLI with --trace: both tracing = true AND debug mode = true
- Trace decorators execute when tracing is enabled, regardless of debug mode
- Convert test_assembler_debug_info_conditional to test_assembler_debug_info_present:
  Now tests that debug info is always present instead of conditionally testing debug modes
- Update duplicate_nodes test to duplicate_nodes_with_debug_decorators:
  Now tests functional equivalence while accounting for more nodes due to unique debug decorators
- Add proper imports and fix API calls for test execution with processor dependencies
- Remove unused imports and improve test accuracy for always-enabled debug mode behavior

These changes make tests more robust and accurate for the post-issue-1821 codebase
where assembler debug mode is always enabled, providing better test coverage.
- Remove unused TESTS section from miden-vm/src/cli/data.rs
- Remove obsolete with_debug_info method from crates/assembly/src/testing.rs
- Extract duplicate code into create_asmop_decorator helper function in assembler.rs
- Fix test that used removed with_debug_info method
@huitseeker huitseeker force-pushed the huitseeker/issue-1821 branch from 1b08e16 to a0b2583 Compare December 5, 2025 02:44
@huitseeker
Copy link
Contributor Author

huitseeker commented Dec 5, 2025

Comments

Is this because there are two modules somewhere with identical procedures, and previously these were getting naturally de-duplicated?

I would assume so, yes.

Deserialize benchmark (on M4)

origin/next (baseline):

  • Time: 11.082 ms - 11.105 ms (average ~11.1 ms)

huitseeker/issue-1821 (this branch):

  • Time: 13.413 ms - 13.590 ms (average ~13.5 ms)

Performance Impact: 18% performance degradation; ~2.4ms increase in deserialization time.

@bobbinth
Copy link
Contributor

bobbinth commented Dec 5, 2025

Performance Impact: 18% performance degradation; ~2.4ms increase in deserialization time.

This looks acceptable - so, I'll merge this PR.

Maybe at some point later on we could experiment with "smart deserialization" - e.g., indicate during deserialization whether we want the debug info or now, and if not, then we'd skip deserializing this part of the MastForest which maybe brings us roughly back to the original numbers.

@bobbinth bobbinth merged commit 6e65482 into next Dec 5, 2025
12 checks passed
@bobbinth bobbinth deleted the huitseeker/issue-1821 branch December 5, 2025 06:12
huitseeker added a commit to huitseeker/miden-vm that referenced this pull request Jan 1, 2026
…corator optionality

This commit manually re-implements the ability to conditionally disable
decorators by reverting the changes from PR 0xMiden#2396 (6e65482) on the
origin/next branch (Winterfell v0.21.0, commit c8e1833).

Changes:
- Added in_debug_mode field to Assembler (defaults to false)
- Added with_debug_mode(), set_debug_mode(), in_debug_mode() methods
- Made decorator creation conditional in instruction/mod.rs
- Made Breakpoint and Debug instructions conditional
- Made if/loop node decorators conditional in assembler.rs
- Updated CLI files to pass debug_on parameter based on --release flag

Performance results:
- WITH --release: 3154 ms (decorators disabled)
- WITHOUT --release: 3168 ms (decorators enabled)

This confirms the reversion logic is correct and Winterfell baseline
performance is ~3.2s regardless of decorator presence.
huitseeker added a commit to huitseeker/miden-vm that referenced this pull request Jan 1, 2026
…conditional

This commit completes the reversion of PR 0xMiden#2396 by making instruction-level
decorators conditional on debug mode. The previous reversion (15d26d7 and
46ecb01) only made control flow decorators (if/loop) conditional, but left
instruction tracking and cycle count decorators unconditional.

Changes in instruction/mod.rs:
- Wrap instruction tracking with if self.in_debug_mode()
- Wrap decorator creation for new nodes with if self.in_debug_mode()
- Make cycle count setting conditional: if self.in_debug_mode() && !can_create_node
- Make Breakpoint instruction conditional
- Make Debug instruction conditional

This matches the complete reversion in test-revert-on-next (commit 3aeff6a).

Expected impact: With --release flag, NO decorators should be created,
resulting in smaller MastForest and potentially better performance.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

always include debug info when assembling

4 participants