From 7adf1d04349c5d044f829c083474a80657b4d5a0 Mon Sep 17 00:00:00 2001 From: Scott Young Date: Sun, 29 Mar 2026 01:13:50 -0400 Subject: [PATCH] debuginfo: emit DW_TAG_call_site entries on optimized builds --- .../rustc_codegen_llvm/src/debuginfo/mod.rs | 9 +++++++- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 1 + .../rustc_llvm/llvm-wrapper/RustWrapper.cpp | 3 ++- .../debuginfo-callsite-flag-noopt.rs | 23 +++++++++++++++++++ tests/codegen-llvm/debuginfo-callsite-flag.rs | 21 +++++++++++++++++ 5 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 tests/codegen-llvm/debuginfo-callsite-flag-noopt.rs create mode 100644 tests/codegen-llvm/debuginfo-callsite-flag.rs diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs index c3fa86f8a2ad3..a5f6c9685b632 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/mod.rs @@ -483,6 +483,10 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { } if self.sess().opts.optimize != config::OptLevel::No { spflags |= DISPFlags::SPFlagOptimized; + // Emit DW_TAG_call_site entries for optimized builds, matching + // Clang's behavior. At -O0 no tail-call optimization occurs, so + // the debugger can reconstruct the call stack without them. + flags |= DIFlags::FlagAllCallsDescribed; } if let Some((id, _)) = tcx.entry_fn(()) { if id == def_id { @@ -494,6 +498,9 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { // LLVM LTO can't unify type definitions when a child DIE is a full subprogram definition. // When we use this `decl` below, the subprogram definition gets created at the CU level // with a DW_AT_specification pointing back to the type's declaration. + // FlagAllCallsDescribed cannot appear on the method declaration DIE + // because it has no body, which LLVM's verifier rejects. + let decl_flags = flags & !DIFlags::FlagAllCallsDescribed; let decl = is_method.then(|| unsafe { llvm::LLVMRustDIBuilderCreateMethod( DIB(self), @@ -505,7 +512,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { file_metadata, loc.line, function_type_metadata, - flags, + decl_flags, spflags & !DISPFlags::SPFlagDefinition, template_parameters, ) diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 7355d11367920..c6c04acc4631a 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -781,6 +781,7 @@ pub(crate) mod debuginfo { const FlagNonTrivial = (1 << 26); const FlagBigEndian = (1 << 27); const FlagLittleEndian = (1 << 28); + const FlagAllCallsDescribed = (1 << 29); } } diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 63ff0b2a0a0df..392cd0601391d 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -779,6 +779,7 @@ ASSERT_DIFLAG_VALUE(FlagThunk, 1 << 25); ASSERT_DIFLAG_VALUE(FlagNonTrivial, 1 << 26); ASSERT_DIFLAG_VALUE(FlagBigEndian, 1 << 27); ASSERT_DIFLAG_VALUE(FlagLittleEndian, 1 << 28); +static_assert(DINode::DIFlags::FlagAllCallsDescribed == (1 << 29)); ASSERT_DIFLAG_VALUE(FlagIndirectVirtualBase, (1 << 2) | (1 << 5)); #undef ASSERT_DIFLAG_VALUE @@ -791,7 +792,7 @@ ASSERT_DIFLAG_VALUE(FlagIndirectVirtualBase, (1 << 2) | (1 << 5)); // to copying each bit/subvalue. static DINode::DIFlags fromRust(LLVMDIFlags Flags) { // Check that all set bits are covered by the static assertions above. - const unsigned UNKNOWN_BITS = (1 << 31) | (1 << 30) | (1 << 29) | (1 << 21); + const unsigned UNKNOWN_BITS = (1 << 31) | (1 << 30) | (1 << 21); if (Flags & UNKNOWN_BITS) { report_fatal_error("bad LLVMDIFlags"); } diff --git a/tests/codegen-llvm/debuginfo-callsite-flag-noopt.rs b/tests/codegen-llvm/debuginfo-callsite-flag-noopt.rs new file mode 100644 index 0000000000000..d7e9930074daf --- /dev/null +++ b/tests/codegen-llvm/debuginfo-callsite-flag-noopt.rs @@ -0,0 +1,23 @@ +// Check that DIFlagAllCallsDescribed is NOT set in unoptimized builds. +// At -O0 no tail-call optimization occurs, so the debugger can +// reconstruct the call stack without DW_TAG_call_site entries. + +//@ ignore-msvc (CodeView does not use DIFlagAllCallsDescribed) +//@ compile-flags: -C debuginfo=2 -C opt-level=0 -C no-prepopulate-passes + +#![crate_type = "lib"] + +// CHECK: {{.*}}DISubprogram{{.*}}name: "foo" +// CHECK-NOT: DIFlagAllCallsDescribed + +#[no_mangle] +#[inline(never)] +pub fn foo(x: i32) -> i32 { + bar(x + 1) +} + +#[no_mangle] +#[inline(never)] +pub fn bar(x: i32) -> i32 { + x * 2 +} diff --git a/tests/codegen-llvm/debuginfo-callsite-flag.rs b/tests/codegen-llvm/debuginfo-callsite-flag.rs new file mode 100644 index 0000000000000..04e99eba34613 --- /dev/null +++ b/tests/codegen-llvm/debuginfo-callsite-flag.rs @@ -0,0 +1,21 @@ +// Check that DIFlagAllCallsDescribed is set on subprogram definitions +// in optimized builds, so LLVM emits DW_TAG_call_site entries. + +//@ ignore-msvc (CodeView does not use DIFlagAllCallsDescribed) +//@ compile-flags: -C debuginfo=2 -C opt-level=1 -C no-prepopulate-passes + +#![crate_type = "lib"] + +// CHECK: {{.*}}DISubprogram{{.*}}name: "foo"{{.*}}DIFlagAllCallsDescribed{{.*}} + +#[no_mangle] +#[inline(never)] +pub fn foo(x: i32) -> i32 { + bar(x + 1) +} + +#[no_mangle] +#[inline(never)] +pub fn bar(x: i32) -> i32 { + x * 2 +}