Skip to content

[MLIR][LLVM] Exporter skip over inlined frame without debug scope #90915

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

zyx-billy
Copy link
Contributor

Followup to #90759.

Instead of just returning null when the caller scope is not translatable, "jump over" the current caller scope and use the outer scope as the caller if that is available. This means that in an inlined call stack if there are frames without debug scope, those frames are skipped to preserve what is available. In the original example where

func A {
  foo loc(fused<#A>["a":1:1])
}
func B {
  call @A loc("b":1:1)
}
func C {
  call @B loc(fused<#C>["c":1:1])
}

is inlined into

func C {
  foo loc(callsite(
            callsite(fused<#A>["a":1:1] at loc("b":1:1))
           at
            fused<#C>["c":1:1]))
}

The translated result would be !1:

!0 = !DILocation(line: 1, column: 1, scope: !C)
!1 = !DILocation(line: 1, column: 1, scope: !A, inlinedAt: !0)

This has a neat benefit in maintaining callsite associativity: No matter if we have callsite(callsite(A at B) at C) or callsite(A at callsite(B at C)), the translation now is the same. The previous solution did not provide this guarantee, which meant the callsite construction would somehow impact this translation.

@zyx-billy zyx-billy changed the title [MLIR][LLVM] Exporter skip over caller frame without debug scope [MLIR][LLVM] Exporter skip over inlined frame without debug scope May 2, 2024
@zyx-billy
Copy link
Contributor Author

Apologies for the churn, but I realize the previously solution was a bit incomplete. LMK if this makes sense. Thank you!

@zyx-billy zyx-billy marked this pull request as ready for review May 3, 2024 00:01
@zyx-billy zyx-billy requested a review from gysit May 3, 2024 00:01
@llvmbot
Copy link
Member

llvmbot commented May 3, 2024

@llvm/pr-subscribers-mlir

@llvm/pr-subscribers-mlir-llvm

Author: Billy Zhu (zyx-billy)

Changes

Followup to #90759.

Instead of just returning null when the caller scope is not translatable, "jump over" the current caller scope and use the outer scope as the caller if that is available. This means that in an inlined call stack if there are frames without debug scope, those frames are skipped to preserve what is available. In the original example where

func A {
  foo loc(fused&lt;#A&gt;["a":1:1])
}
func B {
  call @<!-- -->A loc("b":1:1)
}
func C {
  call @<!-- -->B loc(fused&lt;#C&gt;["c":1:1])
}

is inlined into

func C {
  foo loc(callsite(
            callsite(fused&lt;#A&gt;["a":1:1] at loc("b":1:1))
           at
            fused&lt;#C&gt;["c":1:1]))
}

The translated result would be !1:

!0 = !DILocation(line: 1, column: 1, scope: !C)
!1 = !DILocation(line: 1, column: 1, scope: !A, inlinedAt: !0)

This has a neat benefit in maintaining callsite associativity: No matter if we have callsite(callsite(A at B) at C) or callsite(A at callsite(B at C)), the translation now is the same. The previous solution did not provide this guarantee, which meant the callsite construction would somehow impact this translation.


Full diff: https://github.com/llvm/llvm-project/pull/90915.diff

2 Files Affected:

  • (modified) mlir/lib/Target/LLVMIR/DebugTranslation.cpp (+9-4)
  • (modified) mlir/test/Target/LLVMIR/llvmir-debug.mlir (+4-4)
diff --git a/mlir/lib/Target/LLVMIR/DebugTranslation.cpp b/mlir/lib/Target/LLVMIR/DebugTranslation.cpp
index f02939abc5fe18..2aa1b6b85ac02e 100644
--- a/mlir/lib/Target/LLVMIR/DebugTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/DebugTranslation.cpp
@@ -406,10 +406,15 @@ llvm::DILocation *DebugTranslation::translateLoc(Location loc,
   if (auto callLoc = dyn_cast<CallSiteLoc>(loc)) {
     // For callsites, the caller is fed as the inlinedAt for the callee.
     auto *callerLoc = translateLoc(callLoc.getCaller(), scope, inlinedAt);
-    // If the caller scope does not exist, the callsite cannot be represented
-    // in LLVM (the callee scope may not match the function it is in).
-    if (!callerLoc)
-      return nullptr;
+    // If the caller scope is not translatable, the overall callsite cannot be
+    // represented in LLVM (the callee scope may not match the parent function).
+    if (!callerLoc) {
+      // If there is an inlinedAt scope (an outer caller), skip to that
+      // directly. Otherwise, cannot translate.
+      if (!inlinedAt)
+        return nullptr;
+      callerLoc = inlinedAt;
+    }
     llvmLoc = translateLoc(callLoc.getCallee(), nullptr, callerLoc);
     // Fallback: Ignore callee if it has no debug scope.
     if (!llvmLoc)
diff --git a/mlir/test/Target/LLVMIR/llvmir-debug.mlir b/mlir/test/Target/LLVMIR/llvmir-debug.mlir
index e8a3d65209afde..1f0fc969364ac8 100644
--- a/mlir/test/Target/LLVMIR/llvmir-debug.mlir
+++ b/mlir/test/Target/LLVMIR/llvmir-debug.mlir
@@ -114,13 +114,13 @@ llvm.func @func_with_debug(%arg: i64) {
   // CHECK: call void @func_no_debug(), !dbg ![[MY_SOURCE_LOC]]
   llvm.call @func_no_debug() : () -> () loc(callsite("nodebug.cc":3:4 at fused<#sp0>["mysource.cc":5:6]))
 
-  // CHECK: call void @func_no_debug(), !dbg ![[MY_SOURCE_LOC]]
-  llvm.call @func_no_debug() : () -> () loc(callsite(callsite(fused<#callee>["nodebug.cc":3:4] at "foo.mlir":2:4) at fused<#sp0>["mysource.cc":5:6]))
-
   // CHECK: call void @func_no_debug(), !dbg ![[FUSED_LOC:[0-9]+]]
   llvm.call @func_no_debug() : () -> () loc(fused[callsite(fused<#callee>["mysource.cc":5:6] at "mysource.cc":1:1), "mysource.cc":1:1])
 
-  // CHECK: add i64 %[[ARG]], %[[ARG]], !dbg ![[FUSEDWITH_LOC:[0-9]+]]
+  // CHECK: call void @func_no_debug(), !dbg ![[FUSEDWITH_LOC:[0-9]+]]
+  llvm.call @func_no_debug() : () -> () loc(callsite(callsite(fused<#callee>["foo.mlir":2:4] at "foo.mlir":1:1) at fused<#sp0>["foo.mlir":28:5]))
+
+  // CHECK: add i64 %[[ARG]], %[[ARG]], !dbg ![[FUSEDWITH_LOC]]
   %sum = llvm.add %arg, %arg : i64 loc(callsite(fused<#callee>["foo.mlir":2:4] at fused<#sp0>["foo.mlir":28:5]))
 
   llvm.return

Copy link
Contributor

@gysit gysit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

That makes sense 👍

@zyx-billy zyx-billy merged commit 5fc5769 into llvm:main May 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants