feat: support debug FunC compilation with source maps #2923
+37
−3
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Debug information in the form of source maps is crucial for tools such as coverage analyzers and debuggers. This PR is a step toward creating those tools, but first we need a way to extract source maps from FunC.
A new WASM build based on this PR is available here: tact-lang/ton#3
In debug mode, the FunC compiler inserts pseudo-instructions called
DEBUGMARK
, each tagged with a unique ID corresponding to its source statement. After compilation, the FunC compiler returns a mapping from each ID to its source location, allowing us to correlate specific TVM instructions with FunC code.Because
DEBUGMARK
isn’t a real TVM instruction, we can’t run the compiled BoC directly. In subsequent PRs, we’ll introduce TASM and an assembler for it. This assembler will leverage theDEBUGMARK
instructions to build the instruction-to-statement mapping and then strip them out, producing a valid BoC ready for executionPseudo-instruction??
Why do we need a pseudo-instruction? Why not just reuse the original PR’s
DEBUGSTR
approach?Embedding additional real instructions in the executable code creates several problems:
Gas overhead
Every extra instruction consumes gas, so debug-mode binaries will behave (and cost) differently than usual builds.
Code-size bloat
Inserting additional instructions can push the BoC into a new TVM cell, which then costs an extra 110 gas just to unpack.
Tooling complexity
Debuggers and coverage tools must include special logic to recognize and skip these “debug” instructions at runtime.
Previous attempts
We’ve already explored collecting instruction mappings directly in Tact code. The old approach worked like this:
PUSHINT X DROP
sequence into the bytecode for every statement, safely marking its location.PUSHINT X
calls, and a post-processing step reconstructed the mapping.The key drawback was that it relied on executing instructions at runtime. In contrast, the new solution performs all mapping work at compile time, without adding any code to the BoC