Repository: rust-lang/rust
Pull request: debuginfo: emit DW_TAG_call_site entries

Set `FlagAllCallsDescribed` on function definition DIEs so LLVM emits DW_TAG_call_site entries, letting debuggers and analysis tools track tail calls.

<!-- homu-ignore:start -->
<!--
If this PR is related to an unstable feature or an otherwise tracked effort,
please link to the relevant tracking issue here. If you don't know of a related
tracking issue or there are none, feel free to ignore this.

This PR will get automatically assigned to a reviewer. In case you would like
a specific user to review your work, you can assign it to them by using

    r? <reviewer name>
-->
<!-- homu-ignore:end -->


Changed files:
filename: compiler/rustc_codegen_llvm/src/debuginfo/mod.rs
patch: @@ -471,7 +471,7 @@ impl<'ll, 'tcx> DebugInfoCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> {
         // FIXME(eddyb) does this need to be separate from `loc.line` for some reason?
         let scope_line = loc.line;
 
-        let mut flags = DIFlags::FlagPrototyped;
+        let mut flags = DIFlags::FlagPrototyped | DIFlags::FlagAllCallsDescribed;
 
         if fn_abi.ret.layout.is_uninhabited() {
             flags |= DIFlags::FlagNoReturn;
@@ -494,6 +494,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 +508,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,
             )

filename: compiler/rustc_codegen_llvm/src/llvm/ffi.rs
patch: @@ -781,6 +781,7 @@ pub(crate) mod debuginfo {
             const FlagNonTrivial          = (1 << 26);
             const FlagBigEndian           = (1 << 27);
             const FlagLittleEndian        = (1 << 28);
+            const FlagAllCallsDescribed   = (1 << 29);
         }
     }
 

filename: compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp
patch: @@ -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");
   }

filename: tests/codegen-llvm/debuginfo-callsite-flag.rs
patch: @@ -0,0 +1,20 @@
+// Check that DIFlagAllCallsDescribed is set on subprogram definitions.
+
+//@ ignore-msvc (CodeView does not use DIFlagAllCallsDescribed)
+//@ compile-flags: -C debuginfo=2 -C opt-level=1 -C no-prepopulate-passes
+
+// 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
+}
+
+fn main() {}