Skip to content

Conversation

@djolertrk
Copy link
Contributor

@djolertrk djolertrk commented Dec 8, 2025

This demonstrates that simple "assert-based debugging" does not work.
The reason is that there is no source locations attached to ub.unreachable.

It can be confirmed by using following commands:

  # Build the example
  cargo build --release --target wasm32-unknown-unknown \
    --manifest-path examples/assert-debug-test/Cargo.toml

  # Check HIR with source locations  
  ./bin/midenc examples/assert-debug-test/target/wasm32-unknown-unknown/release/assert_debug_test.wasm \
    --entrypoint=assert_debug_test::test_assert \
    -Ztrim-path-prefix=examples/assert-debug-test \
    -Zprint-hir-source-locations \
    --debug full --emit=hir=-

And to run the integration test:

cargo test --release -p miden-integration-tests debug_source_locations -- --nocapture

This addresses #806.

@djolertrk djolertrk marked this pull request as draft December 8, 2025 22:46
@djolertrk
Copy link
Contributor Author

Similar situation is when an assert_eq is being used, since in that case an intrinsic is being used that also has a default/unknown source location attached, a solution for that case can be inlining of the call to intrinsic itself, so we can find a reasonable span for it,.

@djolertrk
Copy link
Contributor Author

cc @greenhat @bitwalker

@greenhat
Copy link
Contributor

greenhat commented Dec 10, 2025

This demonstrates that simple "assert-based debugging" does not work. The reason is that there is no source locations attached to ub.unreachable.

Interesting. I see we set the passed span for the unreachable at

builder.unreachable(span);

I wonder how it ends up being lost. Or do we pass an empty span here? Any ideas?

EDIT: Oh, I'm just slow. #812 (comment) fixes it, right?

@djolertrk
Copy link
Contributor Author

I double checked now, without changes from #812 (comment) (so basically with this branch only), we have this situation:

        public builtin.function @test_assert(v1: i32) -> i32 {
        ^block8(v1: i32):
            v3 = arith.constant 100 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":23:1);
            v4 = hir.bitcast v1 : u32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":24:13);
            v5 = hir.bitcast v3 : u32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":24:13);
            v6 = arith.gt v4, v5 : i1 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":24:13);
            v7 = arith.zext v6 : u32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":24:13);
            v8 = hir.bitcast v7 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":24:13);
            v9 = arith.constant 0 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":24:13);
            v10 = arith.neq v8, v9 : i1 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":24:13);
            cf.cond_br v10 ^block10, ^block11 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":24:13);
        ^block9(v2: i32):
            builtin.ret v2 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":27:1);
        ^block10:
            cf.br ^block9(v1) #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":27:1);
        ^block11:
            v11 = arith.constant 1048576 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":24:5);
            v12 = arith.constant 25 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":24:5);
            v13 = arith.constant 1048612 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":24:5);
            hir.exec @root_ns:root@1.0.0/assert_debug_test/_ZN4core9panicking5panic17h4cab4a0cdc8b5cb0E(v11, v12, v13)
            ub.unreachable  #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":24:5);
        };
        
         public builtin.function @test_multiple_asserts(v14: i32, v15: i32) -> i32 {
        ^block12(v14: i32, v15: i32):
            v17 = arith.constant 0 : i32;
            v18 = arith.eq v14, v17 : i1;
            v19 = arith.zext v18 : u32;
            v20 = hir.bitcast v19 : i32;
            v21 = arith.constant 0 : i32;
            v22 = arith.neq v20, v21 : i1;
            cf.cond_br v22 ^block16, ^block17;
        ^block13(v16: i32):

        ^block14:
            v41 = arith.constant 1048708 : i32;
            v42 = arith.constant 24 : i32;
            v43 = arith.constant 1048732 : i32;
            hir.exec @root_ns:root@1.0.0/assert_debug_test/_ZN4core9panicking5panic17h4cab4a0cdc8b5cb0E(v41, v42, v43)
            ub.unreachable ;
        ^block15:
            v38 = arith.constant 1048668 : i32;
            v39 = arith.constant 23 : i32;
            v40 = arith.constant 1048692 : i32;
            hir.exec @root_ns:root@1.0.0/assert_debug_test/_ZN4core9panicking5panic17h4cab4a0cdc8b5cb0E(v38, v39, v40)
            ub.unreachable ;
        ^block16:
            v35 = arith.constant 1048628 : i32;
            v36 = arith.constant 23 : i32;
            v37 = arith.constant 1048652 : i32;
            hir.exec @root_ns:root@1.0.0/assert_debug_test/_ZN4core9panicking5panic17h4cab4a0cdc8b5cb0E(v35, v36, v37)
            ub.unreachable ;
        ^block17:
            v23 = arith.constant 0 : i32;
            v24 = arith.eq v15, v23 : i1;
            v25 = arith.zext v24 : u32;
            v26 = hir.bitcast v25 : i32;
            v27 = arith.constant 0 : i32;
            v28 = arith.neq v26, v27 : i1;
            cf.cond_br v28 ^block15, ^block18;
        ^block18:
            v29 = arith.eq v14, v15 : i1;
            v30 = arith.zext v29 : u32;
            v31 = hir.bitcast v30 : i32;
            v32 = arith.constant 0 : i32;
            v33 = arith.neq v31, v32 : i1;
            cf.cond_br v33 ^block14, ^block19;
        ^block19:
            v34 = arith.add v15, v14 : i32 #[overflow = wrapping];
            builtin.ret v34;
        };

As you can see test_assert has valid source location attachments, but test_multiple_asserts has no location at all -- all of them are UNKNOWN.
However, I was able to find the root cause of the issue -- the problem is caused by the comments in the rust example (https://github.com/walnuthq/compiler/blob/pr/assert-debugging/examples/assert-debug-test/src/lib.rs#L4)... The compute_line_starts() incorrectly parses those (in a C-like style), causing source manager to incorrectly compute source locations, and that is why we ended up with no locations at all in the test_multiple_asserts.

Here is the fix 0xMiden/miden-vm#2449. So, only with this fix (we can completely forget about #812 for now), we see:

        public builtin.function @test_multiple_asserts(v14: i32, v15: i32) -> i32 {
        ^block12(v14: i32, v15: i32):
            v17 = arith.constant 0 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":30:13);
            v18 = arith.eq v14, v17 : i1 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":30:13);
            v19 = arith.zext v18 : u32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":30:13);
            v20 = hir.bitcast v19 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":30:13);
            v21 = arith.constant 0 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":30:13);
            v22 = arith.neq v20, v21 : i1 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":30:13);
            cf.cond_br v22 ^block16, ^block17 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":30:13);
        ^block13(v16: i32):

        ^block14:
            v41 = arith.constant 1048708 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":34:5);
            v42 = arith.constant 24 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":34:5);
            v43 = arith.constant 1048732 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":34:5);
            hir.exec @root_ns:root@1.0.0/assert_debug_test/_ZN4core9panicking5panic17h4cab4a0cdc8b5cb0E(v41, v42, v43)
            ub.unreachable  #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":34:5);
        ^block15:
            v38 = arith.constant 1048668 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":32:5);
            v39 = arith.constant 23 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":32:5);
            v40 = arith.constant 1048692 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":32:5);
            hir.exec @root_ns:root@1.0.0/assert_debug_test/_ZN4core9panicking5panic17h4cab4a0cdc8b5cb0E(v38, v39, v40)
            ub.unreachable  #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":32:5);
        ^block16:
            v35 = arith.constant 1048628 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":30:5);
            v36 = arith.constant 23 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":30:5);
            v37 = arith.constant 1048652 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":30:5);
            hir.exec @root_ns:root@1.0.0/assert_debug_test/_ZN4core9panicking5panic17h4cab4a0cdc8b5cb0E(v35, v36, v37)
            ub.unreachable  #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":30:5);
        ^block17:
            v23 = arith.constant 0 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":32:13);
            v24 = arith.eq v15, v23 : i1 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":32:13);
            v25 = arith.zext v24 : u32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":32:13);
            v26 = hir.bitcast v25 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":32:13);
            v27 = arith.constant 0 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":32:13);
            v28 = arith.neq v26, v27 : i1 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":32:13);
            cf.cond_br v28 ^block15, ^block18 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":32:13);
        ^block18:
            v29 = arith.eq v14, v15 : i1 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":34:13);
            v30 = arith.zext v29 : u32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":34:13);
            v31 = hir.bitcast v30 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":34:13);
            v32 = arith.constant 0 : i32 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":34:13);
            v33 = arith.neq v31, v32 : i1 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":34:13);
            cf.cond_br v33 ^block14, ^block19 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":34:13);
        ^block19:
            v34 = arith.add v15, v14 : i32 #[overflow = wrapping] #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":36:5);
            builtin.ret v34 #loc("/path/to/compiler/examples/assert-debug-test/src/lib.rs":37:2);
        };

But, for the test I added here, the miden-debug still prints:

Stack Trace:
 |-> root_ns:root@1.0.0::init in <unavailable>
 |-> root_ns:root@1.0.0::assert_debug_test::test_assert in <unavailable>
 |-> root_ns:root@1.0.0::assert_debug_test::core::panicking::panic in <unavailable>
 `-> root_ns:root@1.0.0::assert_debug_test::core::panicking::panic_fmt in <unavailable>:

Last 3 Instructions (of current frame):
 |   drop
 |   drop
 |   pad
 `-> <error occured here>


Last 5 Instructions (any frame):
 |   u32add
 |   drop
 |   noop
 |   drop
 |   pad
 `-> <error occured here>

@greenhat
Copy link
Contributor

So we have source location for the panic function call but no source code for the unreachable op inside it. I wonder if miden-debug could go up the stack and find the first source span. But it would not help when we're running in the VM through the miden-client. So whatever solution we find needs to be implemented in the VM.
@bitwalker thoughts?

@greenhat
Copy link
Contributor

@djolertrk good catch! Great job!

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.

2 participants