[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-21 Thread Vlad Serebrennikov via cfe-commits

https://github.com/Endilll commented:

`Sema.h` changes look good to me.

https://github.com/llvm/llvm-project/pull/91310
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-20 Thread Eli Friedman via cfe-commits


@@ -1950,8 +1950,22 @@ ConstantLValueEmitter::tryEmitBase(const 
APValue::LValueBase ) {
 if (D->hasAttr())
   return CGM.GetWeakRefReference(D).getPointer();
 
-if (auto FD = dyn_cast(D))
-  return CGM.GetAddrOfFunction(FD);
+if (auto FD = dyn_cast(D)) {
+  auto *C = CGM.GetAddrOfFunction(FD);
+
+  // we don't normally emit debug info for extern fns referenced via
+  // variable initialisers; BPF needs it since it generates BTF from
+  // debug info and bpftool demands BTF for every symbol linked
+  if (CGM.getTarget().getTriple().isBPF() && FD->getStorageClass() == 
SC_Extern) {

efriedma-quic wrote:

Current approach seems fine.

> Sema::ActOnEndOfTranslationUnit checks whether a declaration is used (does it 
> mean referenced?)

"Used" in this context corresponds to odr-used in the C++ standard... which is 
stuff that Sema thinks the code generator might need to emit.  In some cases we 
manage to skip actually emitting some declarations due to optimizations.

https://github.com/llvm/llvm-project/pull/91310
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-18 Thread via cfe-commits


@@ -1950,8 +1950,22 @@ ConstantLValueEmitter::tryEmitBase(const 
APValue::LValueBase ) {
 if (D->hasAttr())
   return CGM.GetWeakRefReference(D).getPointer();
 
-if (auto FD = dyn_cast(D))
-  return CGM.GetAddrOfFunction(FD);
+if (auto FD = dyn_cast(D)) {
+  auto *C = CGM.GetAddrOfFunction(FD);
+
+  // we don't normally emit debug info for extern fns referenced via
+  // variable initialisers; BPF needs it since it generates BTF from
+  // debug info and bpftool demands BTF for every symbol linked
+  if (CGM.getTarget().getTriple().isBPF() && FD->getStorageClass() == 
SC_Extern) {

yonghong-song wrote:

Thanks @mejedi I think we do not need to worry about cases where external 
functions are in the code but removed by optimizer later as this should not 
that frequent. Your patch looks good to me, which follows what we did for 
VarDecl so adding FunctionDecl is a natural addition. BPF side of the change 
looks good to me too. @efriedma-quic could you take a look as well?

https://github.com/llvm/llvm-project/pull/91310
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-17 Thread Nick Zavaritsky via cfe-commits

https://github.com/mejedi updated 
https://github.com/llvm/llvm-project/pull/91310

>From 88f895cc1a761681edd1dff15170ab42d563da49 Mon Sep 17 00:00:00 2001
From: Nick Zavaritsky 
Date: Sun, 5 May 2024 10:20:52 +
Subject: [PATCH] [BPF] Fix linking issues in static map initializers

When BPF object files are linked with bpftool, every symbol must be
accompanied by BTF info. Ensure that extern functions referenced by
global variable initializers are included in BTF.

The primary motivation is "static" initialization of PROG maps:

  extern int elsewhere(struct xdp_md *);

  struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(max_entries, 1);
__type(key, int);
__type(value, int);
__array(values, int (struct xdp_md *));
  } prog_map SEC(".maps") = { .values = { elsewhere } };

BPF backend needs debug info to produce BTF. Debug info is not
normally generated for external variables and functions. Previously, it
was solved differently for variables (collecting variable declarations
in ExternalDeclarations vector) and functions (logic invoked during
codegen in CGExpr.cpp).

This patch generalises ExternalDefclarations to include both function
and variable declarations. This change ensures that function references
are not missed no matter the context. Previously external functions
referenced in constant expressions lacked debug info.
---
 clang/include/clang/AST/ASTConsumer.h |  3 +-
 .../clang/Frontend/MultiplexConsumer.h|  2 +-
 clang/include/clang/Sema/Sema.h   |  2 +-
 clang/lib/CodeGen/BackendConsumer.h   |  2 +-
 clang/lib/CodeGen/CGExpr.cpp  | 17 +-
 clang/lib/CodeGen/CodeGenAction.cpp   |  2 +-
 clang/lib/CodeGen/CodeGenModule.cpp   | 19 ++-
 clang/lib/CodeGen/CodeGenModule.h |  3 +-
 clang/lib/CodeGen/ModuleBuilder.cpp   |  2 +-
 clang/lib/Frontend/MultiplexConsumer.cpp  |  2 +-
 clang/lib/Interpreter/IncrementalParser.cpp   |  2 +-
 clang/lib/Sema/SemaDecl.cpp   |  8 +++
 clang/test/bpf-debug-info-extern-func.c   |  9 
 llvm/lib/Target/BPF/BTFDebug.cpp  | 23 
 llvm/lib/Target/BPF/BTFDebug.h|  4 ++
 llvm/test/CodeGen/BPF/BTF/extern-var-func2.ll | 54 +++
 16 files changed, 128 insertions(+), 26 deletions(-)
 create mode 100644 clang/test/bpf-debug-info-extern-func.c
 create mode 100644 llvm/test/CodeGen/BPF/BTF/extern-var-func2.ll

diff --git a/clang/include/clang/AST/ASTConsumer.h 
b/clang/include/clang/AST/ASTConsumer.h
index ebcd8059284d8..447f2592d2359 100644
--- a/clang/include/clang/AST/ASTConsumer.h
+++ b/clang/include/clang/AST/ASTConsumer.h
@@ -23,6 +23,7 @@ namespace clang {
   class ASTDeserializationListener; // layering violation because void* is ugly
   class SemaConsumer; // layering violation required for safe SemaConsumer
   class TagDecl;
+  class DeclaratorDecl;
   class VarDecl;
   class FunctionDecl;
   class ImportDecl;
@@ -105,7 +106,7 @@ class ASTConsumer {
   /// CompleteExternalDeclaration - Callback invoked at the end of a 
translation
   /// unit to notify the consumer that the given external declaration should be
   /// completed.
-  virtual void CompleteExternalDeclaration(VarDecl *D) {}
+  virtual void CompleteExternalDeclaration(DeclaratorDecl *D) {}
 
   /// Callback invoked when an MSInheritanceAttr has been attached to a
   /// CXXRecordDecl.
diff --git a/clang/include/clang/Frontend/MultiplexConsumer.h 
b/clang/include/clang/Frontend/MultiplexConsumer.h
index 4ed0d86d3cdfb..e49e3392d1f31 100644
--- a/clang/include/clang/Frontend/MultiplexConsumer.h
+++ b/clang/include/clang/Frontend/MultiplexConsumer.h
@@ -67,7 +67,7 @@ class MultiplexConsumer : public SemaConsumer {
   void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override;
   void HandleImplicitImportDecl(ImportDecl *D) override;
   void CompleteTentativeDefinition(VarDecl *D) override;
-  void CompleteExternalDeclaration(VarDecl *D) override;
+  void CompleteExternalDeclaration(DeclaratorDecl *D) override;
   void AssignInheritanceModel(CXXRecordDecl *RD) override;
   void HandleVTable(CXXRecordDecl *RD) override;
   ASTMutationListener *GetASTMutationListener() override;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 873592a2d430a..adc716681bfe6 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2611,7 +2611,7 @@ class Sema final : public SemaBase {
   TentativeDefinitionsType TentativeDefinitions;
 
   /// All the external declarations encoutered and used in the TU.
-  SmallVector ExternalDeclarations;
+  SmallVector ExternalDeclarations;
 
   /// Generally null except when we temporarily switch decl contexts,
   /// like in \see SemaObjC::ActOnObjCTemporaryExitContainerContext.
diff --git a/clang/lib/CodeGen/BackendConsumer.h 
b/clang/lib/CodeGen/BackendConsumer.h
index 0fe9929dca2b3..92ff19ab30844 100644
--- 

[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-17 Thread Nick Zavaritsky via cfe-commits


@@ -1950,8 +1950,22 @@ ConstantLValueEmitter::tryEmitBase(const 
APValue::LValueBase ) {
 if (D->hasAttr())
   return CGM.GetWeakRefReference(D).getPointer();
 
-if (auto FD = dyn_cast(D))
-  return CGM.GetAddrOfFunction(FD);
+if (auto FD = dyn_cast(D)) {
+  auto *C = CGM.GetAddrOfFunction(FD);
+
+  // we don't normally emit debug info for extern fns referenced via
+  // variable initialisers; BPF needs it since it generates BTF from
+  // debug info and bpftool demands BTF for every symbol linked
+  if (CGM.getTarget().getTriple().isBPF() && FD->getStorageClass() == 
SC_Extern) {

mejedi wrote:

@efriedma-quic @yonghong-song I ended up generalising `ExternalDeclarations` to 
include both `VarDecl`s and `FunctionDecl`s.
Since `GetAddrOfFunction` is invoked in so many different contexts, I found it 
difficult to emit debug info only for the subset of calls we care about.
Personally, I find it cleaner to collect external declarations on a list and 
handle them separately rather than plugging directly into codegen. Please let 
me know if I am missing something.
Concerning referenced/all debacle -
 * `Sema::ActOnEndOfTranslationUnit` checks whether a declaration is used (does 
it mean referenced?) before calling `Consumer.CompleteExternalDeclaration`;
 * plugging into codegen ensures that we notice any reference that made it into 
llvm bitcode BUT they could be removed by the optimiser later;
 * BPF backend ensures that only referenced entities make it to BTF.

It looks like both approaches are quite similar?

https://github.com/llvm/llvm-project/pull/91310
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-17 Thread Nick Zavaritsky via cfe-commits

https://github.com/mejedi edited https://github.com/llvm/llvm-project/pull/91310
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-17 Thread Nick Zavaritsky via cfe-commits

https://github.com/mejedi edited https://github.com/llvm/llvm-project/pull/91310
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-17 Thread Nick Zavaritsky via cfe-commits

https://github.com/mejedi edited https://github.com/llvm/llvm-project/pull/91310
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-17 Thread Nick Zavaritsky via cfe-commits

https://github.com/mejedi updated 
https://github.com/llvm/llvm-project/pull/91310

>From 440f62d438810fbd1166a9eb3dbfbe10957fba58 Mon Sep 17 00:00:00 2001
From: Nick Zavaritsky 
Date: Sun, 5 May 2024 10:20:52 +
Subject: [PATCH] [BPF] Fix linking issues in static map initializers

When BPF object files are linked with bpftool, every symbol must be
accompanied by BTF info. Ensure that extern functions referenced by
global variable initializers are included in BTF.

The primary motivation is "static" initialization of PROG maps:

  extern int elsewhere(struct xdp_md *);

  struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(max_entries, 1);
__type(key, int);
__type(value, int);
__array(values, int (struct xdp_md *));
  } prog_map SEC(".maps") = { .values = { elsewhere } };

BPF backend needs debug info to produce BTF. Debug info is not
normally generated for external variables and functions. Previously, it
was solved differently for variables (collecting variable declarations
in ExternalDeclarations vector) and functions (logic invoked during
codegen in CGExpr.cpp).

This patch generalises ExternalDefclarations to include both function
and variable declarations. This change ensures that function references
are not missed no matter the context. Previously external functions
referenced in constant expressions lacked debug info.
---
 clang/include/clang/AST/ASTConsumer.h |  3 +-
 .../clang/Frontend/MultiplexConsumer.h|  2 +-
 clang/include/clang/Sema/Sema.h   |  2 +-
 clang/lib/CodeGen/BackendConsumer.h   |  2 +-
 clang/lib/CodeGen/CGExpr.cpp  | 17 +-
 clang/lib/CodeGen/CodeGenAction.cpp   |  2 +-
 clang/lib/CodeGen/CodeGenModule.cpp   | 19 ++-
 clang/lib/CodeGen/CodeGenModule.h |  3 +-
 clang/lib/CodeGen/ModuleBuilder.cpp   |  2 +-
 clang/lib/Frontend/MultiplexConsumer.cpp  |  2 +-
 clang/lib/Interpreter/IncrementalParser.cpp   |  2 +-
 clang/lib/Sema/SemaDecl.cpp   |  8 +++
 clang/test/bpf-debug-info-extern-func.c   |  9 
 llvm/lib/Target/BPF/BTFDebug.cpp  | 23 
 llvm/lib/Target/BPF/BTFDebug.h|  4 ++
 llvm/test/CodeGen/BPF/BTF/extern-var-func2.ll | 54 +++
 16 files changed, 128 insertions(+), 26 deletions(-)
 create mode 100644 clang/test/bpf-debug-info-extern-func.c
 create mode 100644 llvm/test/CodeGen/BPF/BTF/extern-var-func2.ll

diff --git a/clang/include/clang/AST/ASTConsumer.h 
b/clang/include/clang/AST/ASTConsumer.h
index ebcd8059284d8..447f2592d2359 100644
--- a/clang/include/clang/AST/ASTConsumer.h
+++ b/clang/include/clang/AST/ASTConsumer.h
@@ -23,6 +23,7 @@ namespace clang {
   class ASTDeserializationListener; // layering violation because void* is ugly
   class SemaConsumer; // layering violation required for safe SemaConsumer
   class TagDecl;
+  class DeclaratorDecl;
   class VarDecl;
   class FunctionDecl;
   class ImportDecl;
@@ -105,7 +106,7 @@ class ASTConsumer {
   /// CompleteExternalDeclaration - Callback invoked at the end of a 
translation
   /// unit to notify the consumer that the given external declaration should be
   /// completed.
-  virtual void CompleteExternalDeclaration(VarDecl *D) {}
+  virtual void CompleteExternalDeclaration(DeclaratorDecl *D) {}
 
   /// Callback invoked when an MSInheritanceAttr has been attached to a
   /// CXXRecordDecl.
diff --git a/clang/include/clang/Frontend/MultiplexConsumer.h 
b/clang/include/clang/Frontend/MultiplexConsumer.h
index 4ed0d86d3cdfb..e49e3392d1f31 100644
--- a/clang/include/clang/Frontend/MultiplexConsumer.h
+++ b/clang/include/clang/Frontend/MultiplexConsumer.h
@@ -67,7 +67,7 @@ class MultiplexConsumer : public SemaConsumer {
   void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override;
   void HandleImplicitImportDecl(ImportDecl *D) override;
   void CompleteTentativeDefinition(VarDecl *D) override;
-  void CompleteExternalDeclaration(VarDecl *D) override;
+  void CompleteExternalDeclaration(DeclaratorDecl *D) override;
   void AssignInheritanceModel(CXXRecordDecl *RD) override;
   void HandleVTable(CXXRecordDecl *RD) override;
   ASTMutationListener *GetASTMutationListener() override;
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 873592a2d430a..adc716681bfe6 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -2611,7 +2611,7 @@ class Sema final : public SemaBase {
   TentativeDefinitionsType TentativeDefinitions;
 
   /// All the external declarations encoutered and used in the TU.
-  SmallVector ExternalDeclarations;
+  SmallVector ExternalDeclarations;
 
   /// Generally null except when we temporarily switch decl contexts,
   /// like in \see SemaObjC::ActOnObjCTemporaryExitContainerContext.
diff --git a/clang/lib/CodeGen/BackendConsumer.h 
b/clang/lib/CodeGen/BackendConsumer.h
index 0fe9929dca2b3..92ff19ab30844 100644
--- 

[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-14 Thread via cfe-commits


@@ -1950,8 +1950,22 @@ ConstantLValueEmitter::tryEmitBase(const 
APValue::LValueBase ) {
 if (D->hasAttr())
   return CGM.GetWeakRefReference(D).getPointer();
 
-if (auto FD = dyn_cast(D))
-  return CGM.GetAddrOfFunction(FD);
+if (auto FD = dyn_cast(D)) {
+  auto *C = CGM.GetAddrOfFunction(FD);
+
+  // we don't normally emit debug info for extern fns referenced via
+  // variable initialisers; BPF needs it since it generates BTF from
+  // debug info and bpftool demands BTF for every symbol linked
+  if (CGM.getTarget().getTriple().isBPF() && FD->getStorageClass() == 
SC_Extern) {

yonghong-song wrote:

@mejedi @efriedma-quic Yes, we only want referenced functions/variables. The 
support for external variable has been implemented here 
https://github.com/llvm/llvm-project/commit/d77ae1552fc21a9f3877f3ed7e13d631f517c825.
 Thanks!

https://github.com/llvm/llvm-project/pull/91310
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-12 Thread Eli Friedman via cfe-commits


@@ -1950,8 +1950,22 @@ ConstantLValueEmitter::tryEmitBase(const 
APValue::LValueBase ) {
 if (D->hasAttr())
   return CGM.GetWeakRefReference(D).getPointer();
 
-if (auto FD = dyn_cast(D))
-  return CGM.GetAddrOfFunction(FD);
+if (auto FD = dyn_cast(D)) {
+  auto *C = CGM.GetAddrOfFunction(FD);
+
+  // we don't normally emit debug info for extern fns referenced via
+  // variable initialisers; BPF needs it since it generates BTF from
+  // debug info and bpftool demands BTF for every symbol linked
+  if (CGM.getTarget().getTriple().isBPF() && FD->getStorageClass() == 
SC_Extern) {

efriedma-quic wrote:

Looking at the code again, I guess the ultimate question is whether we want to 
emit debug info for all external functions/variables, or only 
functions/variables that are actually referenced.

If we want all functions/variables, we want to extend ExternalDeclarations to 
include them.  Maybe put the code in Sema::ActOnFunctionDeclarator, or 
something like that.

If you just want referenced functions/variables, probably the code should be in 
GetAddrOfFunction() (and something similar for variables).

https://github.com/llvm/llvm-project/pull/91310
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-08 Thread Nick Zavaritsky via cfe-commits


@@ -1950,8 +1950,22 @@ ConstantLValueEmitter::tryEmitBase(const 
APValue::LValueBase ) {
 if (D->hasAttr())
   return CGM.GetWeakRefReference(D).getPointer();
 
-if (auto FD = dyn_cast(D))
-  return CGM.GetAddrOfFunction(FD);
+if (auto FD = dyn_cast(D)) {
+  auto *C = CGM.GetAddrOfFunction(FD);
+
+  // we don't normally emit debug info for extern fns referenced via
+  // variable initialisers; BPF needs it since it generates BTF from
+  // debug info and bpftool demands BTF for every symbol linked
+  if (CGM.getTarget().getTriple().isBPF() && FD->getStorageClass() == 
SC_Extern) {

mejedi wrote:

@efriedma-quic Thank you for insightful comments! As a newbie, I need more 
guidance.

It is pretty much clear that `allowDebugInfoForExternalRef` is a preferred way 
to check whether we should emit additional debug info. It is currently only 
overriden by BPF target and is being used for exact same purpose we have here.

I need help getting up to speed irt. `CompleteExternalDeclaration` proposal. 
Having grepped through the code, I can see that BPF faced the same challenge 
with `extern` variables. At the moment, there's a vector of `VariableDecl`s 
populated in `SemaDecl.cpp` and later used to attach debug info to variables.

A different mechanism is applied to discovering `extern` functions in need for 
debug info (prior to my patch, `extern` functions referenced through code were 
handled). This is accomplished by code in `CGExpr.cpp`. I am proposing a 
similar extension in `CGExprConstant.cpp`.

Would you like to have functions on `ExternalDeclarations` list instead?

What would be a good spot to get ahold of `FunctionDecl` to put it on 
`ExternalDeclarations` list?

https://github.com/llvm/llvm-project/pull/91310
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-07 Thread Nick Zavaritsky via cfe-commits

mejedi wrote:

cc @anakryiko

https://github.com/llvm/llvm-project/pull/91310
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-07 Thread Eli Friedman via cfe-commits

https://github.com/efriedma-quic edited 
https://github.com/llvm/llvm-project/pull/91310
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-07 Thread Eli Friedman via cfe-commits

https://github.com/efriedma-quic commented:

Is it possible you could put this code into CompleteExternalDeclaration(), 
instead of writing it out in every place that can refer to the address of a 
function?

https://github.com/llvm/llvm-project/pull/91310
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-07 Thread Eli Friedman via cfe-commits


@@ -1950,8 +1950,22 @@ ConstantLValueEmitter::tryEmitBase(const 
APValue::LValueBase ) {
 if (D->hasAttr())
   return CGM.GetWeakRefReference(D).getPointer();
 
-if (auto FD = dyn_cast(D))
-  return CGM.GetAddrOfFunction(FD);
+if (auto FD = dyn_cast(D)) {
+  auto *C = CGM.GetAddrOfFunction(FD);
+
+  // we don't normally emit debug info for extern fns referenced via
+  // variable initialisers; BPF needs it since it generates BTF from
+  // debug info and bpftool demands BTF for every symbol linked
+  if (CGM.getTarget().getTriple().isBPF() && FD->getStorageClass() == 
SC_Extern) {

efriedma-quic wrote:

There's an existing hook allowDebugInfoForExternalRef() for which targets want 
this.

You almost never want getStorageClass(); if you care about linkage, we have 
different methods for that.

https://github.com/llvm/llvm-project/pull/91310
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-07 Thread Nick Zavaritsky via cfe-commits

mejedi wrote:

CC @yonghong-song 

https://github.com/llvm/llvm-project/pull/91310
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-07 Thread Nick Zavaritsky via cfe-commits

https://github.com/mejedi updated 
https://github.com/llvm/llvm-project/pull/91310

>From e518bdfb86b06fa8bc447934293623a803a7f630 Mon Sep 17 00:00:00 2001
From: Nick Zavaritsky 
Date: Sun, 5 May 2024 10:20:52 +
Subject: [PATCH] [BPF] Fix linking issues in static map initializers

When BPF object files are linked with bpftool, every symbol must be
accompanied by BTF info. Ensure that extern functions referenced by
global variable initializers are included in BTF.

The primary motivation is "static" initialization of PROG maps:

  extern int elsewhere(struct xdp_md *);

  struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(max_entries, 1);
__type(key, int);
__type(value, int);
__array(values, int (struct xdp_md *));
  } prog_map SEC(".maps") = { .values = { elsewhere } };

Signed-off-by: Nick Zavaritsky 
---
 clang/lib/CodeGen/CGExprConstant.cpp  | 18 ++-
 clang/test/bpf-debug-info-extern-func.c   |  9 
 llvm/lib/Target/BPF/BTFDebug.cpp  | 23 
 llvm/lib/Target/BPF/BTFDebug.h|  4 ++
 llvm/test/CodeGen/BPF/BTF/extern-var-func2.ll | 54 +++
 5 files changed, 106 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/bpf-debug-info-extern-func.c
 create mode 100644 llvm/test/CodeGen/BPF/BTF/extern-var-func2.ll

diff --git a/clang/lib/CodeGen/CGExprConstant.cpp 
b/clang/lib/CodeGen/CGExprConstant.cpp
index 94962091116a..945f2e222b23 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -1950,8 +1950,22 @@ ConstantLValueEmitter::tryEmitBase(const 
APValue::LValueBase ) {
 if (D->hasAttr())
   return CGM.GetWeakRefReference(D).getPointer();
 
-if (auto FD = dyn_cast(D))
-  return CGM.GetAddrOfFunction(FD);
+if (auto FD = dyn_cast(D)) {
+  auto *C = CGM.GetAddrOfFunction(FD);
+
+  // we don't normally emit debug info for extern fns referenced via
+  // variable initialisers; BPF needs it since it generates BTF from
+  // debug info and bpftool demands BTF for every symbol linked
+  if (CGM.getTarget().getTriple().isBPF() && FD->getStorageClass() == 
SC_Extern) {
+if (CGDebugInfo *DI = CGM.getModuleDebugInfo()) {
+  auto *Fn = dyn_cast(C);
+  if (Fn && !Fn->getSubprogram())
+DI->EmitFunctionDecl(FD, FD->getLocation(), D->getType(), Fn);
+}
+  }
+
+  return C;
+}
 
 if (auto VD = dyn_cast(D)) {
   // We can never refer to a variable with local storage.
diff --git a/clang/test/bpf-debug-info-extern-func.c 
b/clang/test/bpf-debug-info-extern-func.c
new file mode 100644
index ..da324630a314
--- /dev/null
+++ b/clang/test/bpf-debug-info-extern-func.c
@@ -0,0 +1,9 @@
+// RUN: %clang -g -O2 -target bpf -S -emit-llvm %s -o - | FileCheck %s
+//
+// When linking BPF object files via bpftool, BTF info is required for
+// every symbol. BTF is generated from debug info. Ensure that debug info
+// is emitted for extern functions referenced via variable initializers.
+//
+// CHECK: !DISubprogram(name: "fn"
+extern void fn(void);
+void (*pfn) (void) = 
diff --git a/llvm/lib/Target/BPF/BTFDebug.cpp b/llvm/lib/Target/BPF/BTFDebug.cpp
index b6d3b460005c..7d64f2e04bc2 100644
--- a/llvm/lib/Target/BPF/BTFDebug.cpp
+++ b/llvm/lib/Target/BPF/BTFDebug.cpp
@@ -1494,6 +1494,29 @@ void BTFDebug::processGlobals(bool ProcessingMapDef) {
 
 DataSecEntries[std::string(SecName)]->addDataSecEntry(VarId,
 Asm->getSymbol(), Size);
+
+if (Global.hasInitializer())
+  processGlobalInitializer(Global.getInitializer());
+  }
+}
+
+/// Process global variable initializer in pursuit for function
+/// pointers. Add discovered (extern) functions to BTF. Some (extern)
+/// functions might have been missed otherwise. Every symbol needs BTF
+/// info when linking with bpftool. Primary use case: "static"
+/// initialization of BPF maps.
+///
+/// struct {
+///   __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
+///   ...
+/// } prog_map SEC(".maps") = { .values = { extern_func } };
+///
+void BTFDebug::processGlobalInitializer(const Constant *C) {
+  if (auto *Fn = dyn_cast(C))
+processFuncPrototypes(Fn);
+  if (auto *CA = dyn_cast(C)) {
+for (unsigned I = 0, N = CA->getNumOperands(); I < N; ++I)
+  processGlobalInitializer(CA->getOperand(I));
   }
 }
 
diff --git a/llvm/lib/Target/BPF/BTFDebug.h b/llvm/lib/Target/BPF/BTFDebug.h
index 11a0c59ba6c9..02181d3013be 100644
--- a/llvm/lib/Target/BPF/BTFDebug.h
+++ b/llvm/lib/Target/BPF/BTFDebug.h
@@ -352,6 +352,10 @@ class BTFDebug : public DebugHandlerBase {
   /// Generate types and variables for globals.
   void processGlobals(bool ProcessingMapDef);
 
+  /// Process global variable initializer in pursuit for function
+  /// pointers.
+  void processGlobalInitializer(const Constant *C);
+
   /// Generate types for function prototypes.
   void processFuncPrototypes(const Function *);
 
diff --git 

[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-07 Thread via cfe-commits

llvmbot wrote:




@llvm/pr-subscribers-clang

Author: Nick Zavaritsky (mejedi)


Changes

When BPF object files are linked with bpftool, every symbol must be accompanied 
by BTF info. Ensure that extern functions referenced by global variable 
initializers are included in BTF.

The primary motivation is "static" initialization of PROG maps:

  extern int elsewhere(struct xdp_md *);

  struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(max_entries, 1);
__type(key, int);
__type(value, int);
__array(values, int (struct xdp_md *));
  } prog_map SEC(".maps") = { .values = { elsewhere } };

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


5 Files Affected:

- (modified) clang/lib/CodeGen/CGExprConstant.cpp (+16-2) 
- (added) clang/test/bpf-debug-info-extern-func.c (+9) 
- (modified) llvm/lib/Target/BPF/BTFDebug.cpp (+23) 
- (modified) llvm/lib/Target/BPF/BTFDebug.h (+4) 
- (added) llvm/test/CodeGen/BPF/BTF/extern-var-func2.ll (+54) 


``diff
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp 
b/clang/lib/CodeGen/CGExprConstant.cpp
index 94962091116a..945f2e222b23 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -1950,8 +1950,22 @@ ConstantLValueEmitter::tryEmitBase(const 
APValue::LValueBase ) {
 if (D->hasAttr())
   return CGM.GetWeakRefReference(D).getPointer();
 
-if (auto FD = dyn_cast(D))
-  return CGM.GetAddrOfFunction(FD);
+if (auto FD = dyn_cast(D)) {
+  auto *C = CGM.GetAddrOfFunction(FD);
+
+  // we don't normally emit debug info for extern fns referenced via
+  // variable initialisers; BPF needs it since it generates BTF from
+  // debug info and bpftool demands BTF for every symbol linked
+  if (CGM.getTarget().getTriple().isBPF() && FD->getStorageClass() == 
SC_Extern) {
+if (CGDebugInfo *DI = CGM.getModuleDebugInfo()) {
+  auto *Fn = dyn_cast(C);
+  if (Fn && !Fn->getSubprogram())
+DI->EmitFunctionDecl(FD, FD->getLocation(), D->getType(), Fn);
+}
+  }
+
+  return C;
+}
 
 if (auto VD = dyn_cast(D)) {
   // We can never refer to a variable with local storage.
diff --git a/clang/test/bpf-debug-info-extern-func.c 
b/clang/test/bpf-debug-info-extern-func.c
new file mode 100644
index ..da324630a314
--- /dev/null
+++ b/clang/test/bpf-debug-info-extern-func.c
@@ -0,0 +1,9 @@
+// RUN: %clang -g -O2 -target bpf -S -emit-llvm %s -o - | FileCheck %s
+//
+// When linking BPF object files via bpftool, BTF info is required for
+// every symbol. BTF is generated from debug info. Ensure that debug info
+// is emitted for extern functions referenced via variable initializers.
+//
+// CHECK: !DISubprogram(name: "fn"
+extern void fn(void);
+void (*pfn) (void) = 
diff --git a/llvm/lib/Target/BPF/BTFDebug.cpp b/llvm/lib/Target/BPF/BTFDebug.cpp
index b6d3b460005c..7d64f2e04bc2 100644
--- a/llvm/lib/Target/BPF/BTFDebug.cpp
+++ b/llvm/lib/Target/BPF/BTFDebug.cpp
@@ -1494,6 +1494,29 @@ void BTFDebug::processGlobals(bool ProcessingMapDef) {
 
 DataSecEntries[std::string(SecName)]->addDataSecEntry(VarId,
 Asm->getSymbol(), Size);
+
+if (Global.hasInitializer())
+  processGlobalInitializer(Global.getInitializer());
+  }
+}
+
+/// Process global variable initializer in pursuit for function
+/// pointers. Add discovered (extern) functions to BTF. Some (extern)
+/// functions might have been missed otherwise. Every symbol needs BTF
+/// info when linking with bpftool. Primary use case: "static"
+/// initialization of BPF maps.
+///
+/// struct {
+///   __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
+///   ...
+/// } prog_map SEC(".maps") = { .values = { extern_func } };
+///
+void BTFDebug::processGlobalInitializer(const Constant *C) {
+  if (auto *Fn = dyn_cast(C))
+processFuncPrototypes(Fn);
+  if (auto *CA = dyn_cast(C)) {
+for (unsigned I = 0, N = CA->getNumOperands(); I < N; ++I)
+  processGlobalInitializer(CA->getOperand(I));
   }
 }
 
diff --git a/llvm/lib/Target/BPF/BTFDebug.h b/llvm/lib/Target/BPF/BTFDebug.h
index 11a0c59ba6c9..02181d3013be 100644
--- a/llvm/lib/Target/BPF/BTFDebug.h
+++ b/llvm/lib/Target/BPF/BTFDebug.h
@@ -352,6 +352,10 @@ class BTFDebug : public DebugHandlerBase {
   /// Generate types and variables for globals.
   void processGlobals(bool ProcessingMapDef);
 
+  /// Process global variable initializer in pursuit for function
+  /// pointers.
+  void processGlobalInitializer(const Constant *C);
+
   /// Generate types for function prototypes.
   void processFuncPrototypes(const Function *);
 
diff --git a/llvm/test/CodeGen/BPF/BTF/extern-var-func2.ll 
b/llvm/test/CodeGen/BPF/BTF/extern-var-func2.ll
new file mode 100644
index ..700486d9f351
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/extern-var-func2.ll
@@ -0,0 +1,54 @@
+; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck 
-check-prefixes=CHECK %s
+; RUN: llc -march=bpfeb -filetype=asm -o - %s 

[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-07 Thread via cfe-commits

llvmbot wrote:




@llvm/pr-subscribers-clang-codegen

Author: Nick Zavaritsky (mejedi)


Changes

When BPF object files are linked with bpftool, every symbol must be accompanied 
by BTF info. Ensure that extern functions referenced by global variable 
initializers are included in BTF.

The primary motivation is "static" initialization of PROG maps:

  extern int elsewhere(struct xdp_md *);

  struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(max_entries, 1);
__type(key, int);
__type(value, int);
__array(values, int (struct xdp_md *));
  } prog_map SEC(".maps") = { .values = { elsewhere } };

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


5 Files Affected:

- (modified) clang/lib/CodeGen/CGExprConstant.cpp (+16-2) 
- (added) clang/test/bpf-debug-info-extern-func.c (+9) 
- (modified) llvm/lib/Target/BPF/BTFDebug.cpp (+23) 
- (modified) llvm/lib/Target/BPF/BTFDebug.h (+4) 
- (added) llvm/test/CodeGen/BPF/BTF/extern-var-func2.ll (+54) 


``diff
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp 
b/clang/lib/CodeGen/CGExprConstant.cpp
index 94962091116af..945f2e222b23f 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -1950,8 +1950,22 @@ ConstantLValueEmitter::tryEmitBase(const 
APValue::LValueBase ) {
 if (D->hasAttr())
   return CGM.GetWeakRefReference(D).getPointer();
 
-if (auto FD = dyn_cast(D))
-  return CGM.GetAddrOfFunction(FD);
+if (auto FD = dyn_cast(D)) {
+  auto *C = CGM.GetAddrOfFunction(FD);
+
+  // we don't normally emit debug info for extern fns referenced via
+  // variable initialisers; BPF needs it since it generates BTF from
+  // debug info and bpftool demands BTF for every symbol linked
+  if (CGM.getTarget().getTriple().isBPF() && FD->getStorageClass() == 
SC_Extern) {
+if (CGDebugInfo *DI = CGM.getModuleDebugInfo()) {
+  auto *Fn = dyn_cast(C);
+  if (Fn && !Fn->getSubprogram())
+DI->EmitFunctionDecl(FD, FD->getLocation(), D->getType(), Fn);
+}
+  }
+
+  return C;
+}
 
 if (auto VD = dyn_cast(D)) {
   // We can never refer to a variable with local storage.
diff --git a/clang/test/bpf-debug-info-extern-func.c 
b/clang/test/bpf-debug-info-extern-func.c
new file mode 100644
index 0..da324630a314d
--- /dev/null
+++ b/clang/test/bpf-debug-info-extern-func.c
@@ -0,0 +1,9 @@
+// RUN: %clang -g -O2 -target bpf -S -emit-llvm %s -o - | FileCheck %s
+//
+// When linking BPF object files via bpftool, BTF info is required for
+// every symbol. BTF is generated from debug info. Ensure that debug info
+// is emitted for extern functions referenced via variable initializers.
+//
+// CHECK: !DISubprogram(name: "fn"
+extern void fn(void);
+void (*pfn) (void) = 
diff --git a/llvm/lib/Target/BPF/BTFDebug.cpp b/llvm/lib/Target/BPF/BTFDebug.cpp
index b6d3b460005c9..7d64f2e04bc26 100644
--- a/llvm/lib/Target/BPF/BTFDebug.cpp
+++ b/llvm/lib/Target/BPF/BTFDebug.cpp
@@ -1494,6 +1494,29 @@ void BTFDebug::processGlobals(bool ProcessingMapDef) {
 
 DataSecEntries[std::string(SecName)]->addDataSecEntry(VarId,
 Asm->getSymbol(), Size);
+
+if (Global.hasInitializer())
+  processGlobalInitializer(Global.getInitializer());
+  }
+}
+
+/// Process global variable initializer in pursuit for function
+/// pointers. Add discovered (extern) functions to BTF. Some (extern)
+/// functions might have been missed otherwise. Every symbol needs BTF
+/// info when linking with bpftool. Primary use case: "static"
+/// initialization of BPF maps.
+///
+/// struct {
+///   __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
+///   ...
+/// } prog_map SEC(".maps") = { .values = { extern_func } };
+///
+void BTFDebug::processGlobalInitializer(const Constant *C) {
+  if (auto *Fn = dyn_cast(C))
+processFuncPrototypes(Fn);
+  if (auto *CA = dyn_cast(C)) {
+for (unsigned I = 0, N = CA->getNumOperands(); I < N; ++I)
+  processGlobalInitializer(CA->getOperand(I));
   }
 }
 
diff --git a/llvm/lib/Target/BPF/BTFDebug.h b/llvm/lib/Target/BPF/BTFDebug.h
index 11a0c59ba6c90..02181d3013be9 100644
--- a/llvm/lib/Target/BPF/BTFDebug.h
+++ b/llvm/lib/Target/BPF/BTFDebug.h
@@ -352,6 +352,10 @@ class BTFDebug : public DebugHandlerBase {
   /// Generate types and variables for globals.
   void processGlobals(bool ProcessingMapDef);
 
+  /// Process global variable initializer in pursuit for function
+  /// pointers.
+  void processGlobalInitializer(const Constant *C);
+
   /// Generate types for function prototypes.
   void processFuncPrototypes(const Function *);
 
diff --git a/llvm/test/CodeGen/BPF/BTF/extern-var-func2.ll 
b/llvm/test/CodeGen/BPF/BTF/extern-var-func2.ll
new file mode 100644
index 0..700486d9f3515
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/BTF/extern-var-func2.ll
@@ -0,0 +1,54 @@
+; RUN: llc -march=bpfel -filetype=asm -o - %s | FileCheck 
-check-prefixes=CHECK %s
+; RUN: llc -march=bpfeb 

[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-07 Thread via cfe-commits

github-actions[bot] wrote:



Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be
notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this 
page.

If this is not working for you, it is probably because you do not have write
permissions for the repository. In which case you can instead tag reviewers by
name in a comment by using `@` followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review
by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate
is once a week. Please remember that you are asking for valuable time from 
other developers.

If you have further questions, they may be answered by the [LLVM GitHub User 
Guide](https://llvm.org/docs/GitHub.html).

You can also ask questions in a comment on this PR, on the [LLVM 
Discord](https://discord.com/invite/xS7Z362) or on the 
[forums](https://discourse.llvm.org/).

https://github.com/llvm/llvm-project/pull/91310
___
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


[clang] [llvm] [BPF] Fix linking issues in static map initializers (PR #91310)

2024-05-07 Thread Nick Zavaritsky via cfe-commits

https://github.com/mejedi created 
https://github.com/llvm/llvm-project/pull/91310

When BPF object files are linked with bpftool, every symbol must be accompanied 
by BTF info. Ensure that extern functions referenced by global variable 
initializers are included in BTF.

The primary motivation is "static" initialization of PROG maps:

  extern int elsewhere(struct xdp_md *);

  struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(max_entries, 1);
__type(key, int);
__type(value, int);
__array(values, int (struct xdp_md *));
  } prog_map SEC(".maps") = { .values = { elsewhere } };

>From 6fb034ef2bf370358b1acd1cee30cf2154ef46c3 Mon Sep 17 00:00:00 2001
From: Nick Zavaritsky 
Date: Sun, 5 May 2024 10:20:52 +
Subject: [PATCH] [BPF] Fix linking issues in static map initializers

When BPF object files are linked with bpftool, every symbol must be
accompanied by BTF info. Ensure that extern functions referenced by
global variable initializers are included in BTF.

The primary motivation is "static" initialization of PROG maps:

  extern int elsewhere(struct xdp_md *);

  struct {
__uint(type, BPF_MAP_TYPE_PROG_ARRAY);
__uint(max_entries, 1);
__type(key, int);
__type(value, int);
__array(values, int (struct xdp_md *));
  } prog_map SEC(".maps") = { .values = { elsewhere } };
---
 clang/lib/CodeGen/CGExprConstant.cpp  | 18 ++-
 clang/test/bpf-debug-info-extern-func.c   |  9 
 llvm/lib/Target/BPF/BTFDebug.cpp  | 23 
 llvm/lib/Target/BPF/BTFDebug.h|  4 ++
 llvm/test/CodeGen/BPF/BTF/extern-var-func2.ll | 54 +++
 5 files changed, 106 insertions(+), 2 deletions(-)
 create mode 100644 clang/test/bpf-debug-info-extern-func.c
 create mode 100644 llvm/test/CodeGen/BPF/BTF/extern-var-func2.ll

diff --git a/clang/lib/CodeGen/CGExprConstant.cpp 
b/clang/lib/CodeGen/CGExprConstant.cpp
index 94962091116af..945f2e222b23f 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -1950,8 +1950,22 @@ ConstantLValueEmitter::tryEmitBase(const 
APValue::LValueBase ) {
 if (D->hasAttr())
   return CGM.GetWeakRefReference(D).getPointer();
 
-if (auto FD = dyn_cast(D))
-  return CGM.GetAddrOfFunction(FD);
+if (auto FD = dyn_cast(D)) {
+  auto *C = CGM.GetAddrOfFunction(FD);
+
+  // we don't normally emit debug info for extern fns referenced via
+  // variable initialisers; BPF needs it since it generates BTF from
+  // debug info and bpftool demands BTF for every symbol linked
+  if (CGM.getTarget().getTriple().isBPF() && FD->getStorageClass() == 
SC_Extern) {
+if (CGDebugInfo *DI = CGM.getModuleDebugInfo()) {
+  auto *Fn = dyn_cast(C);
+  if (Fn && !Fn->getSubprogram())
+DI->EmitFunctionDecl(FD, FD->getLocation(), D->getType(), Fn);
+}
+  }
+
+  return C;
+}
 
 if (auto VD = dyn_cast(D)) {
   // We can never refer to a variable with local storage.
diff --git a/clang/test/bpf-debug-info-extern-func.c 
b/clang/test/bpf-debug-info-extern-func.c
new file mode 100644
index 0..da324630a314d
--- /dev/null
+++ b/clang/test/bpf-debug-info-extern-func.c
@@ -0,0 +1,9 @@
+// RUN: %clang -g -O2 -target bpf -S -emit-llvm %s -o - | FileCheck %s
+//
+// When linking BPF object files via bpftool, BTF info is required for
+// every symbol. BTF is generated from debug info. Ensure that debug info
+// is emitted for extern functions referenced via variable initializers.
+//
+// CHECK: !DISubprogram(name: "fn"
+extern void fn(void);
+void (*pfn) (void) = 
diff --git a/llvm/lib/Target/BPF/BTFDebug.cpp b/llvm/lib/Target/BPF/BTFDebug.cpp
index b6d3b460005c9..7d64f2e04bc26 100644
--- a/llvm/lib/Target/BPF/BTFDebug.cpp
+++ b/llvm/lib/Target/BPF/BTFDebug.cpp
@@ -1494,6 +1494,29 @@ void BTFDebug::processGlobals(bool ProcessingMapDef) {
 
 DataSecEntries[std::string(SecName)]->addDataSecEntry(VarId,
 Asm->getSymbol(), Size);
+
+if (Global.hasInitializer())
+  processGlobalInitializer(Global.getInitializer());
+  }
+}
+
+/// Process global variable initializer in pursuit for function
+/// pointers. Add discovered (extern) functions to BTF. Some (extern)
+/// functions might have been missed otherwise. Every symbol needs BTF
+/// info when linking with bpftool. Primary use case: "static"
+/// initialization of BPF maps.
+///
+/// struct {
+///   __uint(type, BPF_MAP_TYPE_PROG_ARRAY);
+///   ...
+/// } prog_map SEC(".maps") = { .values = { extern_func } };
+///
+void BTFDebug::processGlobalInitializer(const Constant *C) {
+  if (auto *Fn = dyn_cast(C))
+processFuncPrototypes(Fn);
+  if (auto *CA = dyn_cast(C)) {
+for (unsigned I = 0, N = CA->getNumOperands(); I < N; ++I)
+  processGlobalInitializer(CA->getOperand(I));
   }
 }
 
diff --git a/llvm/lib/Target/BPF/BTFDebug.h b/llvm/lib/Target/BPF/BTFDebug.h
index 11a0c59ba6c90..02181d3013be9 100644
---