[llvm] [RFC] Emit dwarf data for signature-changed or new functions (PR #157349)

via llvm-commits llvm-commits at lists.llvm.org
Sun Sep 7 16:41:18 PDT 2025


https://github.com/yonghong-song updated https://github.com/llvm/llvm-project/pull/157349

>From 0455f8d5c368e4756700575167eb52308ac45fed Mon Sep 17 00:00:00 2001
From: Yonghong Song <yonghong.song at linux.dev>
Date: Sat, 6 Sep 2025 23:09:01 -0700
Subject: [PATCH 1/2] [ArgPromotion] Add DW_CC_nocall to DISubprogram

ArgumentPromotion pass may change function signatures.
If this happens and debuginfo is enabled, let us add
DW_CC_nocall to debuginfo so it is clear that the function
signature has changed.

Also fix a ArgumentPromotion test due to adding DW_CC_nocall
to debuginfo.
---
 llvm/lib/Transforms/IPO/ArgumentPromotion.cpp | 9 +++++++++
 llvm/test/Transforms/ArgumentPromotion/dbg.ll | 6 +++++-
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
index 262c902d40d2d..609e4f8e4d23a 100644
--- a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
+++ b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp
@@ -50,6 +50,7 @@
 #include "llvm/IR/BasicBlock.h"
 #include "llvm/IR/CFG.h"
 #include "llvm/IR/Constants.h"
+#include "llvm/IR/DIBuilder.h"
 #include "llvm/IR/DataLayout.h"
 #include "llvm/IR/DerivedTypes.h"
 #include "llvm/IR/Dominators.h"
@@ -432,6 +433,14 @@ doPromotion(Function *F, FunctionAnalysisManager &FAM,
     PromoteMemToReg(Allocas, DT, &AC);
   }
 
+  // DW_CC_nocall to DISubroutineType to inform debugger that it may not be safe
+  // to call this function.
+  DISubprogram *SP = NF->getSubprogram();
+  if (SP) {
+    auto Temp = SP->getType()->cloneWithCC(llvm::dwarf::DW_CC_nocall);
+    SP->replaceType(MDNode::replaceWithPermanent(std::move(Temp)));
+  }
+
   return NF;
 }
 
diff --git a/llvm/test/Transforms/ArgumentPromotion/dbg.ll b/llvm/test/Transforms/ArgumentPromotion/dbg.ll
index 6a14facfb36a2..ce86aaa3884de 100644
--- a/llvm/test/Transforms/ArgumentPromotion/dbg.ll
+++ b/llvm/test/Transforms/ArgumentPromotion/dbg.ll
@@ -53,7 +53,11 @@ define void @caller(ptr %Y, ptr %P) {
 
 !0 = !{i32 2, !"Debug Info Version", i32 3}
 !1 = !DILocation(line: 8, scope: !2)
-!2 = distinct !DISubprogram(name: "test", file: !5, line: 3, isLocal: true, isDefinition: true, virtualIndex: 6, flags: DIFlagPrototyped, isOptimized: false, unit: !3, scopeLine: 3, scope: null)
+!2 = distinct !DISubprogram(name: "test", file: !5, line: 3, type: !7, isLocal: true, isDefinition: true, flags: DIFlagPrototyped, isOptimized: false, unit: !3, scopeLine: 3, scope: null)
 !3 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, producer: "clang version 3.5.0 ", isOptimized: false, emissionKind: LineTablesOnly, file: !5)
 !5 = !DIFile(filename: "test.c", directory: "")
 !6 = !DILocation(line: 9, scope: !2)
+!7 = !DISubroutineType(types: !8)
+!8 = !{null, !9}
+!9 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !10)
+!10 = !DIBasicType(name: "int",  size: 32, encoding: DW_ATE_signed)

>From f8ee10adcb81a6e32f59f7929cdb0aa53b05eb78 Mon Sep 17 00:00:00 2001
From: Yonghong Song <yonghong.song at linux.dev>
Date: Tue, 29 Jul 2025 16:29:44 -0700
Subject: [PATCH 2/2] [LLVM] Emit dwarf data for changed-signature and new
 functions

Add a new pass EmitChangedFuncDebugInfo which will add dwarf for
additional functions including functions with signature change
and new functions.

The previous approach in [1] tries to add debuginfo for those
optimization passes which cause signature changes. Based on
discussion in [1], it is preferred to have a specific pass to
add debuginfo and later on dwarf generation can include those
new debuginfo.

The ultimate goal is to add new information to dwarf like below:

  DW_TAG_compile_unit
    ...
    // New functions with suffix
    DW_TAG_inlined_subroutine
      DW_AT_name      ("foo.1")
      DW_AT_type      (0x0000000000000091 "int")
      DW_AT_artificial (true)
      DW_AT_specificiation (original DW_TAG_subprogram)

      DW_TAG_formal_parameter
        DW_AT_name    ("b")
        DW_AT_type    (0x0000000000000091 "int")

      DW_TAG_formal_parameter
        DW_AT_name    ("c")
        DW_AT_type    (0x0000000000000095 "long")

    ...
    // Functions with changed signatures
    DW_TAG_inlined_subroutine
      DW_AT_name      ("bar")
      DW_AT_type      (0x0000000000000091 "int")
      DW_AT_artificial (true)
      DW_AT_specificiation (original DW_TAG_subprogram)

      DW_TAG_formal_parameter
        DW_AT_name    ("c")
        DW_AT_type    (0x0000000000000095 "unsigned int")

    ...
    // Functions not obtained function changed signatures yet
    // The DW_CC_nocall presence indicates such cases.
    DW_TAG_inlined_subroutine
      DW_AT_name      ("bar" or "bar.1")
      DW_AT_calling_convention        (DW_CC_nocall)
      DW_AT_artificial (true)
      DW_AT_specificiation (original DW_TAG_subprogram)

The parent tag of above DW_TAG_inlined_subroutine is
DW_TAG_compile_unit. This is a new feature for dwarf
so it won't cause issues with existing dwarf related tools.
Total three patterns are introduced as the above.
  . New functions with suffix, e.g., 'foo.1' or 'foo.llvm.<hash>'.
  . Functions with changed signature due to ArgumentPromotion
    or DeadArgumentElimination.
  . Functions the current implementation cannot get proper
    signature. For this case, DW_CC_nocall is set to indicate
    signature is lost. More details in the below.

A special CompileUnit with file name "<artificial>" is created
to hold special DISubprograms for the above three kinds of functions.
During actual dwarf generation, these special DISubprograms
will turn to above to proper DW_TAG_inlined_subroutine tags.

The below are some discussions with not handled cases and
some other alternative things:
 (1) Currently, there are three not handled signature changes.
     . During to ArgumentPromotion, we may have
       foo(..., struct foo *p, ...) => foo(..., int p.0.val, int p.4.val, ...)
     . Struct argument which expands to two actual arguments,
       foo(..., struct foo v, ...) => foo(..., v.coerce0, v.coerce1, ...)
     . Struct argument changed to struct pointer,
       foo(..., struct foo v, ...) => foo(..., struct foo *p, ...)
     I think by utilizing dbg_value/dbg_declare and instructions, we
     might be able to resolve the above and get proper signature.
     But any suggestions are welcome.
 (2) Currently, I am using a special CompileUnit "<artificial>" to hold
     newly created DISubprograms. But there is an alternative.
     For example, "llvm.dbg.cu" metadata is used to hold all CompileUnits.
     We could introduce "llvm.dbg.sp.extra" to hold all new
     DISubprograms instead of a new CompileUnit.

I have tested this patch set by building latest bpf-next linux kernel.
For no-lto case:
  65288 original number of functions
  910   new functions with this patch (including DW_CC_nocall case)
  7     new functions without signatures (with DW_CC_nocall)
For thin-lto case:
  65541 original number of functions
  2324  new functions with this patch (including DW_CC_nocall case)
  14    new functions without signatures (with DW_CC_nocall)

The following are some examples with thinlto with generated dwarf:

  ...
  0x0001707f:   DW_TAG_inlined_subroutine
                  DW_AT_name      ("msr_build_context")
                  DW_AT_type      (0x00004163 "int")
                  DW_AT_artificial        (true)
                  DW_AT_specification     (0x0000440b "msr_build_context")

  0x0001708b:     DW_TAG_formal_parameter
                    DW_AT_name    ("msr_id")
                    DW_AT_type    (0x0000e55c "const u32 *")

  0x00017093:     NULL
  ...
  0x004225e5:   DW_TAG_inlined_subroutine
                  DW_AT_name      ("__die_body.llvm.14794269134614576759")
                  DW_AT_type      (0x00418a14 "int")
                  DW_AT_artificial        (true)
                  DW_AT_specification     (0x00422348 "__die_body")

  0x004225f1:     DW_TAG_formal_parameter
                    DW_AT_name    ("")
                    DW_AT_type    (0x004181f3 "const char *")

  0x004225f9:     DW_TAG_formal_parameter
                    DW_AT_name    ("")
                    DW_AT_type    (0x00419118 "pt_regs *")

  0x00422601:     DW_TAG_formal_parameter
                    DW_AT_name    ("")
                    DW_AT_type    (0x0041af2f "long")

  0x00422609:     NULL
  ...
  0x013f5dac:   DW_TAG_inlined_subroutine
                  DW_AT_name      ("devkmsg_emit")
                  DW_AT_calling_convention        (DW_CC_nocall)
                  DW_AT_artificial        (true)
                  DW_AT_specification     (0x013ef75b "devkmsg_emit")

  [1] https://github.com/llvm/llvm-project/pull/127855
---
 .../Utils/EmitChangedFuncDebugInfo.h          |  33 ++
 llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp    |  72 ++++
 llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h      |   2 +
 llvm/lib/Passes/PassBuilder.cpp               |   1 +
 llvm/lib/Passes/PassBuilderPipelines.cpp      |  10 +-
 llvm/lib/Passes/PassRegistry.def              |   1 +
 llvm/lib/Transforms/Utils/CMakeLists.txt      |   1 +
 .../Utils/EmitChangedFuncDebugInfo.cpp        | 356 ++++++++++++++++++
 llvm/test/Other/new-pm-defaults.ll            |   2 +
 .../Other/new-pm-thinlto-postlink-defaults.ll |   1 +
 .../new-pm-thinlto-postlink-pgo-defaults.ll   |   1 +
 ...-pm-thinlto-postlink-samplepgo-defaults.ll |   1 +
 12 files changed, 478 insertions(+), 3 deletions(-)
 create mode 100644 llvm/include/llvm/Transforms/Utils/EmitChangedFuncDebugInfo.h
 create mode 100644 llvm/lib/Transforms/Utils/EmitChangedFuncDebugInfo.cpp

diff --git a/llvm/include/llvm/Transforms/Utils/EmitChangedFuncDebugInfo.h b/llvm/include/llvm/Transforms/Utils/EmitChangedFuncDebugInfo.h
new file mode 100644
index 0000000000000..8d569cd95d7f7
--- /dev/null
+++ b/llvm/include/llvm/Transforms/Utils/EmitChangedFuncDebugInfo.h
@@ -0,0 +1,33 @@
+//===- EmitChangedFuncDebugInfo.h - Emit Additional Debug Info -*- C++ --*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+/// Emit debug info for changed or new funcs.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TRANSFORMS_UTILS_EMITCHANGEDFUNCDEBUGINFO_H
+#define LLVM_TRANSFORMS_UTILS_EMITCHANGEDFUNCDEBUGINFO_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+
+class Module;
+
+// Pass that emits late dwarf.
+class EmitChangedFuncDebugInfoPass
+    : public PassInfoMixin<EmitChangedFuncDebugInfoPass> {
+public:
+  EmitChangedFuncDebugInfoPass() = default;
+
+  PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
+};
+
+} // end namespace llvm
+
+#endif // LLVM_TRANSFORMS_UTILS_EMITCHANGEDFUNCDEBUGINFO_H
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
index c27f100775625..00e917de4781d 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.cpp
@@ -1266,11 +1266,83 @@ void DwarfDebug::finishSubprogramDefinitions() {
   }
 }
 
+void DwarfDebug::addChangedSubprograms() {
+  // Generate additional dwarf for functions with signature changed.
+  NamedMDNode *NMD = MMI->getModule()->getNamedMetadata("llvm.dbg.cu");
+  DICompileUnit *ExtraCU = nullptr;
+  for (MDNode *N : NMD->operands()) {
+    auto *CU = dyn_cast<DICompileUnit>(N);
+    if (CU && CU->getFile()->getFilename() == "<artificial>") {
+      ExtraCU = CU;
+      break;
+    }
+  }
+  if (!ExtraCU)
+    return;
+
+  llvm::DebugInfoFinder DIF;
+  DIF.processModule(*MMI->getModule());
+  for (auto *ExtraSP : DIF.subprograms()) {
+    if (ExtraSP->getUnit() != ExtraCU)
+      continue;
+
+    DISubprogram *SP = cast<DISubprogram>(ExtraSP->getScope());
+    DwarfCompileUnit &Cu = getOrCreateDwarfCompileUnit(SP->getUnit());
+    DIE *ScopeDIE =
+        DIE::get(DIEValueAllocator, dwarf::DW_TAG_inlined_subroutine);
+    Cu.getUnitDie().addChild(ScopeDIE);
+
+    Cu.addString(*ScopeDIE, dwarf::DW_AT_name, ExtraSP->getName());
+
+    DITypeRefArray Args = ExtraSP->getType()->getTypeArray();
+
+    if (Args[0])
+      Cu.addType(*ScopeDIE, Args[0]);
+
+    if (ExtraSP->getType()->getCC() == llvm::dwarf::DW_CC_nocall) {
+      Cu.addUInt(*ScopeDIE, dwarf::DW_AT_calling_convention,
+                 dwarf::DW_FORM_data1, llvm::dwarf::DW_CC_nocall);
+    }
+
+    Cu.addFlag(*ScopeDIE, dwarf::DW_AT_artificial);
+
+    // dereference the DIE* for DIEEntry
+    DIE *OriginDIE = Cu.getOrCreateSubprogramDIE(SP);
+    Cu.addDIEEntry(*ScopeDIE, dwarf::DW_AT_specification, DIEEntry(*OriginDIE));
+
+    SmallVector<const DILocalVariable *> ArgVars(Args.size());
+    for (const DINode *DN : ExtraSP->getRetainedNodes()) {
+      if (const auto *DV = dyn_cast<DILocalVariable>(DN)) {
+        uint32_t Arg = DV->getArg();
+        if (Arg)
+          ArgVars[Arg - 1] = DV;
+      }
+    }
+
+    for (unsigned i = 1, N = Args.size(); i < N; ++i) {
+      const DIType *Ty = Args[i];
+      if (!Ty) {
+        assert(i == N - 1 && "Unspecified parameter must be the last argument");
+        Cu.createAndAddDIE(dwarf::DW_TAG_unspecified_parameters, *ScopeDIE);
+      } else {
+        DIE &Arg =
+            Cu.createAndAddDIE(dwarf::DW_TAG_formal_parameter, *ScopeDIE);
+        const DILocalVariable *DV = ArgVars[i - 1];
+        if (DV)
+          Cu.addString(Arg, dwarf::DW_AT_name, DV->getName());
+        Cu.addType(Arg, Ty);
+      }
+    }
+  }
+}
+
 void DwarfDebug::finalizeModuleInfo() {
   const TargetLoweringObjectFile &TLOF = Asm->getObjFileLowering();
 
   finishSubprogramDefinitions();
 
+  addChangedSubprograms();
+
   finishEntityDefinitions();
 
   bool HasEmittedSplitCU = false;
diff --git a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
index 89813dcf0fdab..417ffb19633c3 100644
--- a/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
+++ b/llvm/lib/CodeGen/AsmPrinter/DwarfDebug.h
@@ -565,6 +565,8 @@ class DwarfDebug : public DebugHandlerBase {
 
   void finishSubprogramDefinitions();
 
+  void addChangedSubprograms();
+
   /// Finish off debug information after all functions have been
   /// processed.
   void finalizeModuleInfo();
diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp
index 587f0ece0859b..40fbf5947c6dd 100644
--- a/llvm/lib/Passes/PassBuilder.cpp
+++ b/llvm/lib/Passes/PassBuilder.cpp
@@ -347,6 +347,7 @@
 #include "llvm/Transforms/Utils/DXILUpgrade.h"
 #include "llvm/Transforms/Utils/Debugify.h"
 #include "llvm/Transforms/Utils/DeclareRuntimeLibcalls.h"
+#include "llvm/Transforms/Utils/EmitChangedFuncDebugInfo.h"
 #include "llvm/Transforms/Utils/EntryExitInstrumenter.h"
 #include "llvm/Transforms/Utils/FixIrreducible.h"
 #include "llvm/Transforms/Utils/HelloWorld.h"
diff --git a/llvm/lib/Passes/PassBuilderPipelines.cpp b/llvm/lib/Passes/PassBuilderPipelines.cpp
index 98821bb1408a7..123041ea8cad8 100644
--- a/llvm/lib/Passes/PassBuilderPipelines.cpp
+++ b/llvm/lib/Passes/PassBuilderPipelines.cpp
@@ -133,6 +133,7 @@
 #include "llvm/Transforms/Utils/AssumeBundleBuilder.h"
 #include "llvm/Transforms/Utils/CanonicalizeAliases.h"
 #include "llvm/Transforms/Utils/CountVisits.h"
+#include "llvm/Transforms/Utils/EmitChangedFuncDebugInfo.h"
 #include "llvm/Transforms/Utils/EntryExitInstrumenter.h"
 #include "llvm/Transforms/Utils/ExtraPassManager.h"
 #include "llvm/Transforms/Utils/InjectTLIMappings.h"
@@ -1625,9 +1626,12 @@ PassBuilder::buildModuleOptimizationPipeline(OptimizationLevel Level,
   if (PTO.CallGraphProfile && !LTOPreLink)
     MPM.addPass(CGProfilePass(isLTOPostLink(LTOPhase)));
 
-  // RelLookupTableConverterPass runs later in LTO post-link pipeline.
-  if (!LTOPreLink)
+  // RelLookupTableConverterPass and EmitChangedFuncDebugInfoPass run later in
+  // LTO post-link pipeline.
+  if (!LTOPreLink) {
     MPM.addPass(RelLookupTableConverterPass());
+    MPM.addPass(EmitChangedFuncDebugInfoPass());
+  }
 
   return MPM;
 }
@@ -2355,4 +2359,4 @@ AAManager PassBuilder::buildDefaultAAPipeline() {
 bool PassBuilder::isInstrumentedPGOUse() const {
   return (PGOOpt && PGOOpt->Action == PGOOptions::IRUse) ||
          !UseCtxProfile.empty();
-}
\ No newline at end of file
+}
diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def
index 299aaa801439b..78ee4ca6f96a1 100644
--- a/llvm/lib/Passes/PassRegistry.def
+++ b/llvm/lib/Passes/PassRegistry.def
@@ -73,6 +73,7 @@ MODULE_PASS("debugify", NewPMDebugifyPass())
 MODULE_PASS("declare-runtime-libcalls", DeclareRuntimeLibcallsPass())
 MODULE_PASS("dfsan", DataFlowSanitizerPass())
 MODULE_PASS("dot-callgraph", CallGraphDOTPrinterPass())
+MODULE_PASS("dwarf-emit-late", EmitChangedFuncDebugInfoPass())
 MODULE_PASS("dxil-upgrade", DXILUpgradePass())
 MODULE_PASS("elim-avail-extern", EliminateAvailableExternallyPass())
 MODULE_PASS("extract-blocks", BlockExtractorPass({}, false))
diff --git a/llvm/lib/Transforms/Utils/CMakeLists.txt b/llvm/lib/Transforms/Utils/CMakeLists.txt
index e411d68570096..0b36693ce7975 100644
--- a/llvm/lib/Transforms/Utils/CMakeLists.txt
+++ b/llvm/lib/Transforms/Utils/CMakeLists.txt
@@ -22,6 +22,7 @@ add_llvm_component_library(LLVMTransformUtils
   Debugify.cpp
   DeclareRuntimeLibcalls.cpp
   DemoteRegToStack.cpp
+  EmitChangedFuncDebugInfo.cpp
   DXILUpgrade.cpp
   EntryExitInstrumenter.cpp
   EscapeEnumerator.cpp
diff --git a/llvm/lib/Transforms/Utils/EmitChangedFuncDebugInfo.cpp b/llvm/lib/Transforms/Utils/EmitChangedFuncDebugInfo.cpp
new file mode 100644
index 0000000000000..8c7b3ca54cd7b
--- /dev/null
+++ b/llvm/lib/Transforms/Utils/EmitChangedFuncDebugInfo.cpp
@@ -0,0 +1,356 @@
+//==- EmitChangedFuncDebugInfoPass - Emit Additional Debug Info -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements emitting debug info for functions with changed
+// signatures or new functions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Transforms/Utils/EmitChangedFuncDebugInfo.h"
+#include "llvm/IR/DIBuilder.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Module.h"
+
+using namespace llvm;
+
+#if 0
+static bool getArg(BasicBlock &FirstBB, unsigned Idx, DIBuilder &DIB,
+                   DIFile *NewFile, Function *F, DISubprogram *OldSP,
+                   SmallVector<Metadata *, 5> &TypeList,
+                   SmallVector<Metadata *, 5> &ArgList) {
+  for (Instruction &I : FirstBB) {
+    for (const DbgRecord &DR : I.getDbgRecordRange()) {
+      auto *DVR = dyn_cast<DbgVariableRecord>(&DR);
+      if (!DVR)
+        continue;
+      // All of DbgVariableRecord::LocationType::{Value,Assign,Declare}
+      // are covered.
+      Metadata *Loc = DVR->getRawLocation();
+      auto *ValueMDN = dyn_cast<ValueAsMetadata>(Loc);
+      if (!ValueMDN)
+        continue;
+
+      Value *MDNValue = ValueMDN->getValue();
+      if (!MDNValue)
+        continue;
+
+      if (true)
+        continue;
+
+      // A poison value may correspond to a unused argument.
+      if (isa<PoisonValue>(MDNValue)) {
+        Type *Ty = ValueMDN->getType();
+        auto *Var = dyn_cast<DILocalVariable>(DVR->getRawVariable());
+        if (!Var || Var->getArg() != (Idx + 1))
+          continue;
+
+        // Check for cases like below due to ArgumentPromotion
+        //   define internal ... i32 @add42_byref(i32 %p.0.val) ... {
+        //     #dbg_value(ptr poison, !17, !DIExpression(), !18)
+        //     ...
+        //   }
+        // TODO: one pointer expands to more than one argument is not
+        // supported yet. For example,
+        //   define internal ... i32 @add42_byref(i32 %p.0.val, i32 %p.4.val)
+        //   ...
+        if (Ty->isPointerTy() && F->getArg(Idx)->getType()->isIntegerTy()) {
+          // For such cases, a new argument is created.
+          auto *IntTy = cast<IntegerType>(F->getArg(Idx)->getType());
+          unsigned IntBitWidth = IntTy->getBitWidth();
+
+          DIType *IntDIType =
+              DIB.createBasicType("int" + std::to_string(IntBitWidth),
+                                  IntBitWidth, dwarf::DW_ATE_signed);
+          Var = DIB.createParameterVariable(OldSP, F->getArg(Idx)->getName(),
+                                            Idx + 1, NewFile, OldSP->getLine(),
+                                            IntDIType);
+        }
+
+        TypeList.push_back(Var->getType());
+        ArgList.push_back(Var);
+        return true;
+      }
+
+      // if (true) bad
+      //  continue;
+
+      // Handle the following pattern:
+      //   ... @vgacon_do_font_op(..., i32 noundef, i1 noundef zeroext %ch512)
+      //   ... {
+      //     ...
+      //       #dbg_value(i32 %set, !8568, !DIExpression(), !8589)
+      //     %storedv = zext i1 %ch512 to i8
+      //       #dbg_value(i8 %storedv, !8569, !DIExpression(), !8589)
+      //     ...
+      //   }
+      if (MDNValue != F->getArg(Idx)) {
+        Instruction *PrevI = I.getPrevNode();
+        if (!PrevI)
+          continue;
+        if (MDNValue != PrevI)
+          continue;
+        auto *ZExt = dyn_cast<ZExtInst>(PrevI);
+        if (!ZExt)
+          continue;
+        if (ZExt->getOperand(0) != F->getArg(Idx))
+          continue;
+      }
+
+      auto *Var = cast<DILocalVariable>(DVR->getRawVariable());
+
+      // Even we get dbg_*(...) for arguments, we still need to ensure
+      // compatible types between IR func argument types and debugInfo argument
+      // types.
+      Type *Ty = ValueMDN->getType();
+      DIType *DITy = Var->getType();
+      while (auto *DTy = dyn_cast<DIDerivedType>(DITy)) {
+        if (DTy->getTag() == dwarf::DW_TAG_pointer_type) {
+          DITy = DTy;
+          break;
+        }
+        DITy = DTy->getBaseType();
+      }
+
+      if (Ty->isIntegerTy()) {
+        if (auto *DTy = dyn_cast<DICompositeType>(DITy)) {
+          if (!Ty->isIntegerTy(DTy->getSizeInBits())) {
+            // TODO: A struct param breaks into two actual arguments like
+            //    static int count(struct user_arg_ptr argv, int max)
+            // and the actual func signature:
+            //    i32 @count(i8 range(i8 0, 2) %argv.coerce0, ptr %argv.coerce1)
+            //    {
+            //      #dbg_value(i8 %argv.coerce0, !14759,
+            //      !DIExpression(DW_OP_LLVM_fragment, 0, 8), !14768)
+            //      #dbg_value(ptr %argv.coerce1, !14759,
+            //      !DIExpression(DW_OP_LLVM_fragment, 64, 64), !14768)
+            //      ...
+            //    }
+            return false;
+          }
+        }
+      } else if (Ty->isPointerTy()) {
+        // TODO: A struct turned into a pointer to struct.
+        //   @rhashtable_lookup_fast(ptr noundef %key,
+        //      ptr noundef readonly byval(%struct.rhashtable_params)
+        //        align 8 captures(none) %params) {
+        //      ...
+        //      %MyAlloca = alloca [160 x i8], align 32
+        //      %0 = ptrtoint ptr %MyAlloca to i64
+        //      %1 = add i64 %0, 32
+        //      %2 = inttoptr i64 %1 to ptr
+        //      ...
+        //      call void @llvm.memcpy.p0.p0.i64(ptr align 8 %2, ptr align 8
+        //      %params, i64 40, i1 false)
+        //        #dbg_value(ptr @offdevs, !15308, !DIExpression(), !15312)
+        //        #dbg_value(ptr %key, !15309, !DIExpression(), !15312)
+        //        #dbg_declare(ptr %MyAlloca, !15310,
+        //        !DIExpression(DW_OP_plus_uconst, 32), !15313)
+        //      tail call void @__rcu_read_lock() #14, !dbg !15314
+        //   }
+        if (dyn_cast<DICompositeType>(DITy))
+          return false;
+
+        auto *DTy = dyn_cast<DIDerivedType>(DITy);
+        if (!DTy)
+          continue;
+        if (DTy->getTag() != dwarf::DW_TAG_pointer_type)
+          continue;
+      }
+
+      TypeList.push_back(Var->getType());
+      if (Var->getArg() != (Idx + 1) ||
+          Var->getName() != F->getArg(Idx)->getName()) {
+        Var = DIB.createParameterVariable(OldSP, F->getArg(Idx)->getName(),
+                                          Idx + 1, OldSP->getUnit()->getFile(),
+                                          OldSP->getLine(), Var->getType());
+      }
+      ArgList.push_back(Var);
+      return true;
+    }
+  }
+
+  return false;
+}
+#endif
+
+static bool getTypeArgList(DIBuilder &DIB, DIFile *NewFile, Function *F,
+                           FunctionType *FTy, DISubprogram *OldSP,
+                           SmallVector<Metadata *, 5> &TypeList,
+                           SmallVector<Metadata *, 5> &ArgList) {
+  Type *RetTy = FTy->getReturnType();
+  if (RetTy->isVoidTy()) {
+    // Void return type may be due to optimization.
+    TypeList.push_back(nullptr);
+  } else {
+    // Optimization does not change return type from one
+    // non-void type to another non-void type.
+    DITypeRefArray TyArray = OldSP->getType()->getTypeArray();
+    TypeList.push_back(TyArray[0]);
+  }
+#if 0
+  unsigned NumArgs = FTy->getNumParams();
+  BasicBlock &FirstBB = F->getEntryBlock();
+  for (unsigned i = 0; i < NumArgs; ++i) {
+    // if (!getArg(FirstBB, i, DIB, NewFile, F, OldSP, TypeList, ArgList))
+    return false;
+  }
+#endif
+  return true;
+}
+
+static void generateDebugInfo(Module &M, Function *F) {
+  // For this CU, we want generate the following three dwarf units:
+  // DW_TAG_compile_unit
+  //   ...
+  //   // New functions with suffix
+  //   DW_TAG_inlined_subroutine
+  //     DW_AT_name      ("foo.1")
+  //     DW_AT_type      (0x0000000000000091 "int")
+  //     DW_AT_artificial (true)
+  //     DW_AT_specificiation (original DW_TAG_subprogram)
+  //
+  //     DW_TAG_formal_parameter
+  //       DW_AT_name    ("b")
+  //       DW_AT_type    (0x0000000000000091 "int")
+  //
+  //     DW_TAG_formal_parameter
+  //       DW_AT_name    ("c")
+  //       DW_AT_type    (0x0000000000000095 "long")
+  //   ...
+  //   // Functions with changed signatures
+  //   DW_TAG_inlined_subroutine
+  //     DW_AT_name      ("bar")
+  //     DW_AT_type      (0x0000000000000091 "int")
+  //     DW_AT_artificial (true)
+  //     DW_AT_specificiation (original DW_TAG_subprogram)
+  //
+  //     DW_TAG_formal_parameter
+  //       DW_AT_name    ("c")
+  //       DW_AT_type    (0x0000000000000095 "unsigned int")
+  //   ...
+  //   // Functions not obtained function changed signatures yet
+  //   // The DW_CC_nocall presence indicates such cases.
+  //   DW_TAG_inlined_subroutine
+  //     DW_AT_name      ("bar" or "bar.1")
+  //     DW_AT_calling_convention        (DW_CC_nocall)
+  //     DW_AT_artificial (true)
+  //     DW_AT_specificiation (original DW_TAG_subprogram)
+  //   ...
+
+  // A new ComputeUnit is created with file name "<artificial>"
+  // to host newly-created DISubprogram's.
+  DICompileUnit *NewCU = nullptr;
+  NamedMDNode *CUs = M.getNamedMetadata("llvm.dbg.cu");
+  // Check whether the expected CU already there or not.
+  for (MDNode *Node : CUs->operands()) {
+    if (auto *CU = dyn_cast_or_null<DICompileUnit>(Node)) {
+      if (Metadata *Raw = CU->getRawFile()) {
+        if (auto *File = dyn_cast<DIFile>(Raw)) {
+          if (File->getFilename() != "<artificial>")
+            continue;
+          NewCU = CU;
+          break;
+        } else {
+          continue;
+        }
+      }
+    }
+  }
+
+  DISubprogram *OldSP = F->getSubprogram();
+  DIBuilder DIB(M, /*AllowUnresolved=*/false, NewCU);
+  DIFile *NewFile;
+
+  if (NewCU) {
+    NewFile = NewCU->getFile();
+  } else {
+    DICompileUnit *OldCU = OldSP->getUnit();
+    DIFile *OldFile = OldCU->getFile();
+    NewFile = DIB.createFile("<artificial>", OldFile->getDirectory());
+    NewCU = DIB.createCompileUnit(
+        OldCU->getSourceLanguage(), NewFile, OldCU->getProducer(),
+        OldCU->isOptimized(), OldCU->getFlags(), OldCU->getRuntimeVersion());
+  }
+
+  SmallVector<Metadata *, 5> TypeList;
+  SmallVector<Metadata *, 5> ArgList;
+
+  FunctionType *FTy = F->getFunctionType();
+  bool Success = getTypeArgList(DIB, NewFile, F, FTy, OldSP, TypeList, ArgList);
+  if (!Success) {
+    TypeList.clear();
+    TypeList.push_back(nullptr);
+    ArgList.clear();
+  }
+
+  DITypeRefArray DITypeArray = DIB.getOrCreateTypeArray(TypeList);
+  auto *SubroutineType = DIB.createSubroutineType(DITypeArray);
+  DINodeArray ArgArray = DIB.getOrCreateArray(ArgList);
+
+  Function *DummyF =
+      Function::Create(FTy, GlobalValue::AvailableExternallyLinkage,
+                       F->getName() + ".newsig", &M);
+
+  DISubprogram *NewSP =
+      DIB.createFunction(OldSP,                   // Scope
+                         F->getName(),            // Name
+                         OldSP->getLinkageName(), // Linkage name
+                         NewFile,                 // File
+                         OldSP->getLine(),        // Line
+                         SubroutineType,          // DISubroutineType
+                         OldSP->getScopeLine(),   // ScopeLine
+                         DINode::FlagZero, DISubprogram::SPFlagDefinition);
+  NewSP->replaceRetainedNodes(ArgArray);
+
+  if (!Success) {
+    auto Temp = NewSP->getType()->cloneWithCC(llvm::dwarf::DW_CC_nocall);
+    NewSP->replaceType(MDNode::replaceWithPermanent(std::move(Temp)));
+  }
+
+  DIB.finalizeSubprogram(NewSP);
+
+  // Add dummy return block
+  BasicBlock *BB = BasicBlock::Create(M.getContext(), "entry", DummyF);
+  IRBuilder<> IRB(BB);
+  IRB.CreateUnreachable();
+
+  DummyF->setSubprogram(NewSP);
+
+  DIB.finalize();
+}
+
+PreservedAnalyses EmitChangedFuncDebugInfoPass::run(Module &M,
+                                                    ModuleAnalysisManager &AM) {
+  SmallVector<Function *> ChangedFuncs;
+  for (auto &F : M) {
+    // Function must already have DebugInfo.
+    DISubprogram *SP = F.getSubprogram();
+    if (!SP)
+      continue;
+
+    // Ignore all intrinsics functions.
+    if (F.isIntrinsic())
+      continue;
+
+    StringRef FName = F.getName();
+    if (!FName.contains('.')) {
+      uint8_t cc = SP->getType()->getCC();
+      if (cc != llvm::dwarf::DW_CC_nocall)
+        continue;
+    }
+
+    ChangedFuncs.push_back(&F);
+  }
+
+  bool Changed = ChangedFuncs.size() != 0;
+  for (auto *F : ChangedFuncs)
+    generateDebugInfo(M, F);
+
+  return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
+}
diff --git a/llvm/test/Other/new-pm-defaults.ll b/llvm/test/Other/new-pm-defaults.ll
index c554fdbf4c799..c41f7e0b506b7 100644
--- a/llvm/test/Other/new-pm-defaults.ll
+++ b/llvm/test/Other/new-pm-defaults.ll
@@ -294,6 +294,8 @@
 ; CHECK-DEFAULT-NEXT: Running pass: CGProfilePass
 ; CHECK-DEFAULT-NEXT: Running pass: RelLookupTableConverterPass
 ; CHECK-LTO-NOT: Running pass: RelLookupTableConverterPass
+; CHECK-DEFAULT-NEXT: Running pass: EmitChangedFuncDebugInfoPass
+; CHECK-LTO-NOT: Running pass: EmitChangedFuncDebugInfoPass
 ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo
 ; CHECK-LTO-NEXT: Running pass: CanonicalizeAliasesPass
 ; CHECK-LTO-NEXT: Running pass: NameAnonGlobalPass
diff --git a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
index 62bb02d9b3c40..376ad023a0912 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-defaults.ll
@@ -207,6 +207,7 @@
 ; CHECK-POSTLINK-O-NEXT: Running pass: ConstantMergePass
 ; CHECK-POSTLINK-O-NEXT: Running pass: CGProfilePass
 ; CHECK-POSTLINK-O-NEXT: Running pass: RelLookupTableConverterPass
+; CHECK-POSTLINK-O-NEXT: Running pass: EmitChangedFuncDebugInfoPass
 ; CHECK-EP-OPT-EARLY-NEXT: Running pass: NoOpModulePass
 ; CHECK-EP-OPT-LAST-NEXT: Running pass: NoOpModulePass
 ; CHECK-O-NEXT:          Running pass: AnnotationRemarksPass on foo
diff --git a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
index 0da7a9f73bdce..a072107231d5c 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-pgo-defaults.ll
@@ -191,6 +191,7 @@
 ; CHECK-O-NEXT: Running pass: ConstantMergePass
 ; CHECK-O-NEXT: Running pass: CGProfilePass
 ; CHECK-O-NEXT: Running pass: RelLookupTableConverterPass
+; CHECK-O-NEXT: Running pass: EmitChangedFuncDebugInfoPass
 ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo
 ; CHECK-O-NEXT: Running pass: PrintModulePass
 
diff --git a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
index 38b7890682783..f076fb4428daf 100644
--- a/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
+++ b/llvm/test/Other/new-pm-thinlto-postlink-samplepgo-defaults.ll
@@ -200,6 +200,7 @@
 ; CHECK-O-NEXT: Running pass: ConstantMergePass
 ; CHECK-O-NEXT: Running pass: CGProfilePass
 ; CHECK-O-NEXT: Running pass: RelLookupTableConverterPass
+; CHECK-O-NEXT: Running pass: EmitChangedFuncDebugInfoPass
 ; CHECK-O-NEXT: Running pass: AnnotationRemarksPass on foo
 ; CHECK-O-NEXT: Running pass: PrintModulePass
 



More information about the llvm-commits mailing list