diff --git a/clang/include/clang/Basic/TargetInfo.h b/clang/include/clang/Basic/TargetInfo.h --- a/clang/include/clang/Basic/TargetInfo.h +++ b/clang/include/clang/Basic/TargetInfo.h @@ -1538,8 +1538,8 @@ virtual void setAuxTarget(const TargetInfo *Aux) {} - /// Whether target allows debuginfo types for decl only variables. - virtual bool allowDebugInfoForExternalVar() const { return false; } + /// Whether target allows debuginfo types for decl only variables/functions. + virtual bool allowDebugInfoForExternalRef() const { return false; } protected: /// Copy type and layout related info. diff --git a/clang/lib/Basic/Targets/BPF.h b/clang/lib/Basic/Targets/BPF.h --- a/clang/lib/Basic/Targets/BPF.h +++ b/clang/lib/Basic/Targets/BPF.h @@ -76,7 +76,7 @@ return None; } - bool allowDebugInfoForExternalVar() const override { return true; } + bool allowDebugInfoForExternalRef() const override { return true; } CallingConvCheckResult checkCallingConvention(CallingConv CC) const override { switch (CC) { diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -2834,8 +2834,20 @@ return LV; } - if (const auto *FD = dyn_cast(ND)) - return EmitFunctionDeclLValue(*this, E, FD); + if (const auto *FD = dyn_cast(ND)) { + LValue LV = EmitFunctionDeclLValue(*this, E, FD); + + // Emit debuginfo for the function declaration if the target wants to. + if (getContext().getTargetInfo().allowDebugInfoForExternalRef()) { + auto *Fn = dyn_cast(LV.getPointer(*this)); + if (!FD->isDefined() && !Fn->getSubprogram()) { + CGM.getModuleDebugInfo()->EmitFunctionDecl(FD, FD->getLocation(), T, + Fn); + } + } + + return LV; + } // FIXME: While we're emitting a binding from an enclosing scope, all other // DeclRefExprs we see should be implicitly treated as if they also refer to diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -12667,7 +12667,7 @@ Diag(Var->getLocation(), diag::note_private_extern); } - if (Context.getTargetInfo().allowDebugInfoForExternalVar() && + if (Context.getTargetInfo().allowDebugInfoForExternalRef() && !Var->isInvalidDecl() && !getLangOpts().CPlusPlus) ExternalDeclarations.push_back(Var); diff --git a/clang/test/CodeGen/debug-info-extern-callback.c b/clang/test/CodeGen/debug-info-extern-callback.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/debug-info-extern-callback.c @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -x c -debug-info-kind=limited -triple bpf-linux-gnu -emit-llvm %s -o - | FileCheck %s + +extern int do_work1(int); +long bpf_helper1(void *callback_fn); +long prog1() { + return bpf_helper1(&do_work1); +} + +extern int do_work2(int); +long bpf_helper2(void *callback_fn); +int do_work2(int arg) { + return arg; +} +long prog2() { + return bpf_helper2(&do_work2); +} + +extern int do_work3(int); +long bpf_helper3(void *callback_fn); +long prog3() { + return bpf_helper3(&do_work3); +} +int do_work3(int arg) { + return arg; +} + +// CHECK: declare !dbg ![[FUNC1:[0-9]+]] i32 @do_work1(i32) +// CHECK: define dso_local i32 @do_work2(i32 %arg) #{{[0-9]+}} !dbg ![[FUNC2:[0-9]+]] +// CHECK: define dso_local i32 @do_work3(i32 %arg) #{{[0-9]+}} !dbg ![[FUNC3:[0-9]+]] + +// CHECK: ![[FUNC1]] = !DISubprogram(name: "do_work1" +// CHECK: ![[FUNC2]] = distinct !DISubprogram(name: "do_work2" +// CHECK: ![[FUNC3]] = distinct !DISubprogram(name: "do_work3"