Description
I ran into this as part of #128913
Compile this example program extracted from tests/debuginfo/function-arg-initialization.rs
fn binding(a: i64, b: u64, c: f64) {
let x = 0;
}
fn main() {
binding(19, 20, 21.5);
}
with rustc test.rs -g
then load it into gdb with gdb test
and behold:
(gdb) b test.rs:2
Breakpoint 1 at 0x15c90: file test.rs, line 2.
(gdb) run
Starting program: /tmp/test
Downloading separate debug info for system-supplied DSO at 0x7ffff7fc5000
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
Breakpoint 1, test::binding (a=56, b=8, c=6.9533558074966682e-310) at test.rs:2
2 let x = 0;
(gdb) disas
Dump of assembler code for function _ZN4test7binding17h4d177e65956385d9E:
=> 0x0000555555569c90 <+0>: movl $0x0,-0x1c(%rsp)
0x0000555555569c98 <+8>: mov %rdi,-0x18(%rsp)
0x0000555555569c9d <+13>: mov %rsi,-0x10(%rsp)
0x0000555555569ca2 <+18>: movsd %xmm0,-0x8(%rsp)
0x0000555555569ca8 <+24>: ret
End of assembler dump.
The bug here is that a breakpoint set inside a function can land on an instruction before the function prelude.
As far as I can tell, this is only possible with single-use-consts. You can get the right codegen in current rustc by setting -Zmir-enable-passes=-SingleUseConsts
. But I think blaming that MIR pass would be mistake because that MIR pass is just a reimplementation of the same concept that used to be called ConstDebugInfo
.
Naive attempts to bisect this using the breakpoint method above will all land on #107404. But I think that is a wrong bisection, because that PR just fixed an existing MIR pass so that we produce the right span information in our debuginfo.
I think the root cause or the PR that first introduced the bug is #73210. You can see in this godbolt demo that rustc 1.50.0 is the version where we start initializing x
before the function prelude: https://godbolt.org/z/r5W3M4sG9. It's likely that nobody noticed or appreciated how codegen has changed because the debuginfo test for this scenario had been disabled.
This problem also afflicts tests/debuginfo/macro-stepping.rs
, because its strange combination of single-use-consts on the same line as function calls makes stepping through the function hit all those lines before the prelude when the consts are initialized, then again when the functions are called.