[llvm] 3db0dd7 - Implement support for NSDI DebugFunctionDefinition. (#211853)

via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 3 04:44:33 PDT 2026


Author: Manuel Carrasco
Date: 2026-08-03T11:44:27Z
New Revision: 3db0dd71c8579fb825bdd2c6148b53496e485375

URL: https://github.com/llvm/llvm-project/commit/3db0dd71c8579fb825bdd2c6148b53496e485375
DIFF: https://github.com/llvm/llvm-project/commit/3db0dd71c8579fb825bdd2c6148b53496e485375.diff

LOG: Implement support for NSDI DebugFunctionDefinition. (#211853)

This PR depends on the DebugFunction PR:
https://github.com/llvm/llvm-project/pull/211760. This PR implements
support for
[DebugFunctionDefinition](https://github.khronos.org/SPIRV-Registry/nonsemantic/NonSemantic.Shader.DebugInfo.html#DebugFunctionDefinition).

DebugFunctionDefinition must be emitted within the instruction sequence
of its corresponding OpFunction. The current implementation inserts
DebugFunctionDefinition immediately after the last OpVariable, if one
exists, or otherwise immediately after the first OpLabel. The goal is to
satisfy the following
[requirement](https://github.khronos.org/SPIRV-Registry/nonsemantic/NonSemantic.Shader.DebugInfo.html#_binary_form):

> DebugScope, DebugNoScope, DebugDeclare, DebugValue, DebugLine,
DebugNoLine, and DebugFunctionDefinition instructions may interleave
with instructions inside a function, but they must appear at valid
locations within a block as required by SPV_KHR_non_semantic_info. In
particular, they cannot appear before any OpPhi or function-level
variable declarations in a block, and they cannot appear after a merge
instruction.

To support this, I updated SPIRVAsmPrinter to notify the debug handler
whenever an instruction is emitted. The debug handler maintains a small
amount of state so it can detect when the last OpVariable or the first
OpLabel has been emitted and insert DebugFunctionDefinition at the
appropriate location.

Added: 
    llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-after-opvariable.ll
    llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-call-before-alloca.ll
    llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-calls.ll
    llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-external-call.ll
    llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition.ll

Modified: 
    llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
    llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
    llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
index 31953bf811982..040a6858b9009 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
@@ -322,16 +322,24 @@ void SPIRVAsmPrinter::emitInstruction(const MachineInstr *MI) {
   SPIRV_MC::verifyInstructionPredicates(MI->getOpcode(),
                                         getSubtargetInfo().getFeatureBits());
 
-  if (!MAI->getSkipEmission(MI))
+  bool InstructionEmitted = !MAI->getSkipEmission(MI);
+  if (InstructionEmitted)
     outputInstruction(MI);
 
   // Output OpLabel after OpFunction and OpFunctionParameter in the first MBB.
   const MachineInstr *NextMI = MI->getNextNode();
-  if (!LabeledMBB.contains(MI->getParent()) && isFuncOrHeaderInstr(MI, TII) &&
-      (!NextMI || !isFuncOrHeaderInstr(NextMI, TII))) {
+  bool BlockHasLabel = LabeledMBB.contains(MI->getParent());
+  bool IsFunctionPreambleInstruction = isFuncOrHeaderInstr(MI, TII);
+  bool IsNextInstructionFunctionPreamble =
+      NextMI && isFuncOrHeaderInstr(NextMI, TII);
+  bool ShouldEmitEntryLabel = !BlockHasLabel && IsFunctionPreambleInstruction &&
+                              !IsNextInstructionFunctionPreamble;
+  if (ShouldEmitEntryLabel) {
     assert(MI->getParent()->getNumber() == MF->front().getNumber() &&
            "OpFunction is not in the front MBB of MF");
     emitOpLabel(*MI->getParent());
+    if (NSDebugHandler && !isHidden())
+      NSDebugHandler->notifyEntryLabelEmitted(*MF);
   }
 }
 

diff  --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
index d0b7f67866acb..f921df3912d38 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.cpp
@@ -15,6 +15,8 @@
 #include "llvm/ADT/Twine.h"
 #include "llvm/BinaryFormat/Dwarf.h"
 #include "llvm/CodeGen/AsmPrinter.h"
+#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/MachineInstr.h"
 #include "llvm/IR/DebugInfo.h"
 #include "llvm/IR/DebugInfoMetadata.h"
 #include "llvm/IR/GlobalVariable.h"
@@ -193,6 +195,35 @@ static uint32_t mapCompositeTypeTag(unsigned Tag) {
   }
 }
 
+static const MachineInstr *
+findLastFunctionOpVariableDeclaration(const MachineFunction &MF,
+                                      SPIRV::ModuleAnalysisInfo &MAI) {
+
+  // We iterate over the instructions to find the last OpVariable instruction if
+  // any. The following SPIRV rule is used to terminate the traversal earlier:
+  // SPIR-V 2.16.1, Function Structure: "All OpVariable instructions in a
+  // function must be in the first block in the function. These instructions,
+  // together with any intermixed OpLine and OpNoLine instructions, must be the
+  // first instructions in that block."
+  const MachineInstr *LastOpVariable = nullptr;
+  bool SeenOpVariable = false;
+  for (const MachineInstr &MI : MF.front()) {
+    if (MI.getOpcode() == SPIRV::OpVariable) {
+      SeenOpVariable = true;
+      if (!MAI.getSkipEmission(&MI))
+        LastOpVariable = &MI;
+      continue;
+    }
+
+    bool CanInterleaveWithOpVariable =
+        MI.getOpcode() == SPIRV::OpLine || MI.getOpcode() == SPIRV::OpNoLine;
+    if (SeenOpVariable && !CanInterleaveWithOpVariable &&
+        !MAI.getSkipEmission(&MI))
+      break;
+  }
+  return LastOpVariable;
+}
+
 } // namespace
 
 SPIRVNonSemanticDebugHandler::SPIRVNonSemanticDebugHandler(AsmPrinter &AP)
@@ -242,6 +273,7 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
   SubprogramDefinitions.clear();
   GlobalVariableDebugInfoMap.clear();
   DebugFunctionDeclarationRegs.clear();
+  DebugFunctionRegs.clear();
   ScopeToPathOpStringReg.clear();
   CUToCompilationUnitDbgReg.clear();
   DebugSourceRegByFileStr.clear();
@@ -250,6 +282,8 @@ void SPIRVNonSemanticDebugHandler::beginModule(Module *M) {
   I32ConstantCache.clear();
   DebugTypeFunctionCache.clear();
   GlobalDIEmitted = false;
+  GlobalNSDIEnabled = false;
+  CurrentMAI = nullptr;
 #ifndef NDEBUG
   NonSemanticOpStringsSectionEmitted = false;
 #endif
@@ -336,8 +370,6 @@ void SPIRVNonSemanticDebugHandler::prepareModuleOutput(
   // Add the NonSemantic.Shader.DebugInfo.100 entry to ExtInstSetMap so that
   // outputOpExtInstImports() emits the OpExtInstImport instruction. Allocate a
   // fresh result ID for it now; the same ID is used in emitExtInst() operands.
-  constexpr unsigned NSSet = static_cast<unsigned>(
-      SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100);
   if (!MAI.ExtInstSetMap.count(NSSet))
     MAI.ExtInstSetMap[NSSet] = MAI.getNextIDRegister();
 }
@@ -992,8 +1024,6 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticDebugStrings(
   // Check that prepareModuleOutput() registered the extended instruction set.
   // If the subtarget does not support the extension, neither strings nor ext
   // insts are emitted.
-  constexpr unsigned NSSet = static_cast<unsigned>(
-      SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100);
   if (!MAI.getExtInstSetReg(NSSet).isValid())
     return;
 
@@ -1051,18 +1081,136 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticDebugStrings(
 #endif
 }
 
+void SPIRVNonSemanticDebugHandler::emitDebugFunctionDefinition(
+    MCRegister DebugFunctionReg, MCRegister OpFunctionReg,
+    SPIRV::ModuleAnalysisInfo &MAI) {
+  assert(DebugFunctionReg.isValid() && OpFunctionReg.isValid() &&
+         "DebugFunctionDefinition operands must be valid");
+  MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
+  MCRegister ExtInstSetReg = MAI.getExtInstSetReg(NSSet);
+  emitExtInst(SPIRV::NonSemanticExtInst::DebugFunctionDefinition, VoidTypeReg,
+              ExtInstSetReg, {DebugFunctionReg, OpFunctionReg}, MAI);
+}
+
+void SPIRVNonSemanticDebugHandler::resetPerFunctionDebugState() {
+  CurrentMF = nullptr;
+  LastFunctionOpVariable = nullptr;
+  DebugFunctionDefinitionEmitted = false;
+}
+
+void SPIRVNonSemanticDebugHandler::preparePerFunctionDebug(
+    const MachineFunction *MF) {
+  resetPerFunctionDebugState();
+  if (!GlobalNSDIEnabled || !CurrentMAI)
+    return;
+
+  CurrentMF = MF;
+
+  if (MF->getFunction()
+          .getFnAttribute(SPIRV_BACKEND_SERVICE_FUN_NAME)
+          .isValid())
+    return;
+
+  const DISubprogram *SP = MF->getFunction().getSubprogram();
+  if (!SP || !SP->isDefinition())
+    return;
+
+  // DebugFunctionDefinition is emitted after the last function-level
+  // OpVariable. If there are none, it is emitted after the entry OpLabel.
+  LastFunctionOpVariable =
+      findLastFunctionOpVariableDeclaration(*MF, *CurrentMAI);
+}
+
+void SPIRVNonSemanticDebugHandler::tryEmitDebugFunctionDefinition(
+    SPIRV::ModuleAnalysisInfo &MAI) {
+  if (DebugFunctionDefinitionEmitted || !GlobalNSDIEnabled)
+    return;
+
+  assert(CurrentMF && "no current MachineFunction");
+  const Function &F = CurrentMF->getFunction();
+  const DISubprogram *SP = F.getSubprogram();
+  if (!SP || !SP->isDefinition())
+    return;
+
+  auto DFIt = DebugFunctionRegs.find(SP);
+  if (DFIt == DebugFunctionRegs.end())
+    return;
+
+  MCRegister OpFunctionReg = MAI.getGlobalObjReg(&F);
+  if (!OpFunctionReg.isValid())
+    return;
+
+  emitDebugFunctionDefinition(DFIt->second, OpFunctionReg, MAI);
+  DebugFunctionDefinitionEmitted = true;
+}
+
+void SPIRVNonSemanticDebugHandler::beginFunctionImpl(
+    const MachineFunction *MF) {
+  preparePerFunctionDebug(MF);
+}
+
+void SPIRVNonSemanticDebugHandler::endFunctionImpl(const MachineFunction *MF) {
+  (void)MF;
+  resetPerFunctionDebugState();
+}
+
+void SPIRVNonSemanticDebugHandler::beginInstruction(const MachineInstr *MI) {
+  assert(CurMI == nullptr && "CurMI must be null");
+  CurMI = MI;
+}
+
+void SPIRVNonSemanticDebugHandler::endInstruction() {
+  const MachineInstr *MI = CurMI;
+  CurMI = nullptr;
+
+  if (!MI || !GlobalNSDIEnabled || DebugFunctionDefinitionEmitted || !CurrentMF)
+    return;
+
+  if (MI != LastFunctionOpVariable)
+    return;
+
+  // If this is the last function-level OpVariable, emit the
+  // DebugFunctionDefinition. Otherwise, we had already done it before right
+  // after the OpLabel (see notifyEntryLabelEmitted).
+  assert(CurrentMAI && "CurrentMAI must be set");
+  tryEmitDebugFunctionDefinition(*CurrentMAI);
+}
+
+void SPIRVNonSemanticDebugHandler::notifyEntryLabelEmitted(
+    const MachineFunction &MF) {
+  if (!GlobalNSDIEnabled || DebugFunctionDefinitionEmitted || !CurrentMF)
+    return;
+
+  assert(CurrentMF == &MF &&
+         "notification does not match the current MachineFunction");
+
+  if (LastFunctionOpVariable)
+    return;
+
+  // If there are no function-level OpVariables, emit the
+  // DebugFunctionDefinition. Otherwise, DebugFunctionDefinition is emitted
+  // after the last OpVariable (see endInstruction).
+  tryEmitDebugFunctionDefinition(*CurrentMAI);
+}
+
 void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
     SPIRV::ModuleAnalysisInfo &MAI) {
-  if (GlobalDIEmitted || CompileUnits.empty())
+  if (GlobalDIEmitted)
     return;
+
   GlobalDIEmitted = true;
 
+  if (CompileUnits.empty()) {
+    GlobalNSDIEnabled = false;
+    return;
+  }
+
   // Retrieve the ext inst set register allocated by prepareModuleOutput().
-  constexpr unsigned NSSet = static_cast<unsigned>(
-      SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100);
   MCRegister ExtInstSetReg = MAI.getExtInstSetReg(NSSet);
-  if (!ExtInstSetReg.isValid())
-    return; // Extension not available.
+  if (!ExtInstSetReg.isValid()) {
+    GlobalNSDIEnabled = false;
+    return;
+  }
 
 #ifndef NDEBUG
   assert(NonSemanticOpStringsSectionEmitted &&
@@ -1070,6 +1218,8 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
          "emitNonSemanticGlobalDebugInfo()");
 #endif
 
+  CurrentMAI = &MAI;
+
   MCRegister VoidTypeReg = getOrEmitOpTypeVoidReg(MAI);
   MCRegister I32TypeReg = getOrEmitOpTypeInt32Reg(MAI);
 
@@ -1223,13 +1373,18 @@ void SPIRVNonSemanticDebugHandler::emitNonSemanticGlobalDebugInfo(
   }
 
   // Emit DebugFunction for DISubprogram definitions.
-  for (const DISubprogram *SP : SubprogramDefinitions)
-    emitDebugFunction(SP, VoidTypeReg, I32TypeReg, ExtInstSetReg, MAI);
+  for (const DISubprogram *SP : SubprogramDefinitions) {
+    if (auto FnReg =
+            emitDebugFunction(SP, VoidTypeReg, I32TypeReg, ExtInstSetReg, MAI))
+      DebugFunctionRegs[SP] = *FnReg;
+  }
 
   // Emit DebugGlobalVariable for each collected DIGlobalVariable.
   for (const auto &[GV, Info] : GlobalVariableDebugInfoMap)
     emitDebugGlobalVariable(GV, Info, VoidTypeReg, I32TypeReg, ExtInstSetReg,
                             MAI);
+
+  GlobalNSDIEnabled = true;
 }
 
 SmallString<128>

diff  --git a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
index 5da526587df91..4fce1b73004e1 100644
--- a/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
+++ b/llvm/lib/Target/SPIRV/SPIRVNonSemanticDebugHandler.h
@@ -40,17 +40,20 @@ class SPIRVSubtarget;
 /// the module contains debug info (llvm.dbg.cu).
 ///
 /// Call sequence:
-///   beginModule()                    -- collect compile-unit metadata.
-///   prepareModuleOutput()            -- add extension + ext inst set to MAI.
-///   emitNonSemanticDebugStrings()    -- OpString for NSDI strings (sec. 7).
-///   emitNonSemanticGlobalDebugInfo() -- emit DebugSource,
-///                                       DebugCompilationUnit, DebugTypeBasic,
-///                                       DebugTypePointer, DebugTypeFunction,
-///                                       DebugFunctionDeclaration,
-///                                       DebugFunction.
-///   beginFunctionImpl()              -- no-op (no per-function DI yet).
-///   endFunctionImpl()                -- no-op.
+/// - beginModule() collects compile-unit metadata.
+/// - prepareModuleOutput() adds the extension and ext-inst set to MAI.
+/// - emitNonSemanticDebugStrings() emits NSDI OpStrings in section 7.
+/// - emitNonSemanticGlobalDebugInfo() emits module-scope NSDI and sets
+///   GlobalNSDIEnabled.
+/// - beginFunctionImpl() prepares per-function DebugFunctionDefinition state.
+/// - endInstruction() emits DebugFunctionDefinition after the last function-
+///   level OpVariable; SPIRVAsmPrinter calls notifyEntryLabelEmitted() after
+///   the synthesized entry OpLabel when there are no OpVariables.
+/// - endFunctionImpl() resets per-function state.
 class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
+  static constexpr unsigned NSSet = static_cast<unsigned>(
+      SPIRV::InstructionSet::NonSemantic_Shader_DebugInfo_100);
+
   struct CompileUnitInfo {
     const DICompileUnit *TheCU = nullptr;
     SmallString<128> FilePath;
@@ -101,6 +104,10 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   // (only entries where emission succeeded).
   DenseMap<const DISubprogram *, MCRegister> DebugFunctionDeclarationRegs;
 
+  // DebugFunction result id per emitted definition DISubprogram (only entries
+  // where emission succeeded).
+  DenseMap<const DISubprogram *, MCRegister> DebugFunctionRegs;
+
   // Path \c OpString result id per \c DIScope (CU, \c DIFile, declaration
   // \c DISubprogram, …). Filled during \c emitNonSemanticDebugStrings() using
   // \c getDebugFullPath + \c emitOpStringIfNew; section 10 uses it for
@@ -148,6 +155,18 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   // change.
   bool GlobalDIEmitted = false;
 
+  // True when emitNonSemanticGlobalDebugInfo() completed module-scope NSDI
+  // emission for this module.
+  bool GlobalNSDIEnabled = false;
+
+  SPIRV::ModuleAnalysisInfo *CurrentMAI = nullptr;
+
+  const MachineFunction *CurrentMF = nullptr;
+
+  const MachineInstr *LastFunctionOpVariable = nullptr;
+
+  bool DebugFunctionDefinitionEmitted = false;
+
 public:
   explicit SPIRVNonSemanticDebugHandler(AsmPrinter &AP);
 
@@ -177,8 +196,12 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   /// SPIRVAsmPrinter::outputModuleSections() at section 10 in place of
   /// outputModuleSection(MB_NonSemanticGlobalDI). Requires
   /// emitNonSemanticDebugStrings() to have run first when NSDI strings apply.
+  /// Sets \c GlobalNSDIEnabled when module-scope NSDI emission completes.
   void emitNonSemanticGlobalDebugInfo(SPIRV::ModuleAnalysisInfo &MAI);
 
+  /// Called after the synthesized entry \c OpLabel has been emitted.
+  void notifyEntryLabelEmitted(const MachineFunction &MF);
+
 protected:
   // All module-level output is driven by emitNonSemanticGlobalDebugInfo(),
   // called explicitly from SPIRVAsmPrinter::outputModuleSections(). Nothing
@@ -188,24 +211,29 @@ class SPIRVNonSemanticDebugHandler : public DebugHandlerBase {
   // DebugHandlerBase stores MMI as a pointer copy from Asm->MMI at construction
   // time (DebugHandlerBase.cpp: `MMI(Asm->MMI)`). The handler is constructed
   // before AsmPrinter::doInitialization() runs, so Asm->MMI is null at that
-  // point and MMI remains null for this handler's entire lifetime. The
-  // base-class beginInstruction/endInstruction dereference MMI to create temp
-  // symbols for label tracking and would crash. Override them as no-ops.
-  // When per-function NSDI is implemented, use Asm->OutStreamer->getContext()
-  // for MCContext access rather than MMI->getContext().
-  void beginInstruction(const MachineInstr *MI) override {}
-  void endInstruction() override {}
-
-  // TODO: Emit DebugFunctionDefinition here once per-function NSDI emission is
-  // implemented. DebugHandlerBase::beginFunction() populates LScopes and
-  // DbgValues, which are needed for DebugLine emission. Do not override
-  // beginFunction() until that work is in place.
-  void beginFunctionImpl(const MachineFunction *MF) override {}
-  // TODO: Add per-function cleanup when DebugFunctionDefinition emission is in
-  // place.
-  void endFunctionImpl(const MachineFunction *MF) override {}
+  // point and MMI remains null for this handler's entire lifetime. Do not call
+  // the base-class beginInstruction/endInstruction — they dereference MMI to
+  // create temp symbols for label tracking and would crash.
+  // Future local NSDI that needs MCContext must use
+  // Asm->OutStreamer->getContext() rather than MMI->getContext().
+  void beginInstruction(const MachineInstr *MI) override;
+  void endInstruction() override;
+
+  // Override beginFunctionImpl(), not beginFunction():
+  // DebugHandlerBase::beginFunction() populates LScopes and DbgValues needed
+  // for future DebugLine emission.
+  void beginFunctionImpl(const MachineFunction *MF) override;
+  void endFunctionImpl(const MachineFunction *MF) override;
 
 private:
+  void emitDebugFunctionDefinition(MCRegister DebugFunctionReg,
+                                   MCRegister OpFunctionReg,
+                                   SPIRV::ModuleAnalysisInfo &MAI);
+
+  void resetPerFunctionDebugState();
+  void preparePerFunctionDebug(const MachineFunction *MF);
+  void tryEmitDebugFunctionDefinition(SPIRV::ModuleAnalysisInfo &MAI);
+
   void emitMCInst(MCInst &Inst);
   MCRegister emitOpString(StringRef S, SPIRV::ModuleAnalysisInfo &MAI);
 

diff  --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-after-opvariable.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-after-opvariable.ll
new file mode 100644
index 0000000000000..ec3c93c5115b6
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-after-opvariable.ll
@@ -0,0 +1,36 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; DebugFunctionDefinition must follow function-local OpVariable instructions.
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[DF:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction {{.*}}
+; CHECK: [[FOO:%[0-9]+]] = OpFunction
+; CHECK: OpVariable {{.*}} Function
+; CHECK-NEXT: OpVariable {{.*}} Function
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF]] [[FOO]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func void @foo() !dbg !4 {
+entry:
+  %x = alloca i32, align 4
+  %y = alloca i32, align 4
+  store i32 0, ptr %x
+  store i32 1, ptr %y
+  ret void, !dbg !7
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-function-definition-after-opvariable.c", directory: "/src")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+
+!4 = distinct !DISubprogram(name: "foo", scope: !1, file: !1, line: 1, type: !5, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!5 = !DISubroutineType(types: !6)
+!6 = !{null}
+!7 = !DILocation(line: 2, column: 1, scope: !4)

diff  --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-call-before-alloca.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-call-before-alloca.ll
new file mode 100644
index 0000000000000..19d9c1b09db2d
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-call-before-alloca.ll
@@ -0,0 +1,88 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; External call appears before the alloca in LLVM IR source order. The local
+; OpVariable is still inserted in the function preamble; DebugFunctionDefinition
+; must follow it, not the entry OpLabel.
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[NAME:%[0-9]+]] = OpString "caller"
+; CHECK-DAG: [[NAME_NOARGS:%[0-9]+]] = OpString "caller_no_args"
+; CHECK-DAG: [[DF:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction [[NAME]]
+; CHECK-DAG: [[DF_NOARGS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction [[NAME_NOARGS]]
+; CHECK-DAG: OpName [[EXT_HELPER:%[0-9]+]] "external_helper"
+; CHECK-DAG: OpDecorate [[EXT_HELPER]] LinkageAttributes "external_helper" Import
+; CHECK-DAG: OpDecorate [[CALLER:%[0-9]+]] LinkageAttributes "caller" Export
+; CHECK-DAG: OpName [[EXT_HELPER_NOARGS:%[0-9]+]] "external_helper_no_args"
+; CHECK-DAG: OpDecorate [[EXT_HELPER_NOARGS]] LinkageAttributes "external_helper_no_args" Import
+; CHECK-DAG: OpDecorate [[CALLER_NOARGS:%[0-9]+]] LinkageAttributes "caller_no_args" Export
+
+; external_helper: hoisted declaration in the declarations section.
+; CHECK: [[EXT_HELPER]] = OpFunction %{{.*}}
+; CHECK-NEXT: OpFunctionParameter
+; CHECK-NEXT: OpFunctionEnd
+
+; CHECK: [[EXT_HELPER_NOARGS]] = OpFunction %{{.*}}
+; CHECK-NEXT: OpFunctionEnd
+
+; CHECK: [[CALLER]] = OpFunction %{{.*}} ; -- Begin function caller
+; CHECK-NEXT: OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpVariable {{.*}} Function
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF]] [[CALLER]]
+; CHECK-NEXT: OpFunctionCall
+; CHECK-NEXT: OpStore
+; CHECK-NEXT: OpReturnValue
+; CHECK-NEXT: OpFunctionEnd
+
+; caller_no_args: same placement rules, but no function parameters.
+; CHECK: [[CALLER_NOARGS]] = OpFunction %{{.*}} ; -- Begin function caller_no_args
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpVariable {{.*}} Function
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF_NOARGS]] [[CALLER_NOARGS]]
+; CHECK-NEXT: OpFunctionCall
+; CHECK-NEXT: OpStore
+; CHECK-NEXT: OpReturn
+; CHECK-NEXT: OpFunctionEnd
+
+target triple = "spirv64-unknown-unknown"
+
+declare spir_func i32 @external_helper(i32)
+declare spir_func i32 @external_helper_no_args()
+
+define spir_func i32 @caller(i32 %x) !dbg !5 {
+entry:
+  %r = call i32 @external_helper(i32 %x)
+  %a = alloca i32, align 4
+  store i32 %r, ptr %a
+  ret i32 %r, !dbg !8
+}
+
+define spir_func void @caller_no_args() !dbg !9 {
+entry:
+  %r = call i32 @external_helper_no_args()
+  %a = alloca i32, align 4
+  store i32 %r, ptr %a
+  ret void, !dbg !13
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-function-definition-call-before-alloca.c", directory: "/src")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+
+!4 = !DISubroutineType(types: !6)
+!6 = !{!7, !7}
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!5 = distinct !DISubprogram(name: "caller", linkageName: "caller", scope: !1, file: !1, line: 1, type: !4, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!8 = !DILocation(line: 6, column: 3, scope: !5)
+
+!9 = distinct !DISubprogram(name: "caller_no_args", linkageName: "caller_no_args", scope: !1, file: !1, line: 10, type: !10, scopeLine: 10, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!10 = !DISubroutineType(types: !11)
+!11 = !{null}
+!13 = !DILocation(line: 15, column: 3, scope: !9)

diff  --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-calls.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-calls.ll
new file mode 100644
index 0000000000000..19189b8d5bca7
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-calls.ll
@@ -0,0 +1,124 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Exercise DebugFunctionDefinition placement across multiple defined functions
+; that call each other, with and without function-local OpVariable instructions.
+; Each function's definition must reference its own OpFunction id, placed after
+; the entry OpLabel (no locals) or after the last function-local OpVariable.
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[NAME_LEAF:%[0-9]+]] = OpString "leaf_no_vars"
+; CHECK-DAG: [[NAME_WITH:%[0-9]+]] = OpString "helper_with_vars"
+; CHECK-DAG: [[NAME_CALLER:%[0-9]+]] = OpString "caller_no_vars"
+; CHECK-DAG: [[NAME_ORCH:%[0-9]+]] = OpString "orchestrator"
+; CHECK-DAG: [[DF_LEAF:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction [[NAME_LEAF]]
+; CHECK-DAG: [[DF_WITH:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction [[NAME_WITH]]
+; CHECK-DAG: [[DF_CALLER:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction [[NAME_CALLER]]
+; CHECK-DAG: [[DF_ORCH:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction [[NAME_ORCH]]
+; CHECK-DAG: OpDecorate [[LEAF:%[0-9]+]] LinkageAttributes "leaf_no_vars" Export
+; CHECK-DAG: OpDecorate [[WITH:%[0-9]+]] LinkageAttributes "helper_with_vars" Export
+; CHECK-DAG: OpDecorate [[CALLER:%[0-9]+]] LinkageAttributes "caller_no_vars" Export
+; CHECK-DAG: OpDecorate [[ORCH:%[0-9]+]] LinkageAttributes "orchestrator" Export
+
+; leaf_no_vars: no local variables -> DebugFunctionDefinition after OpLabel.
+; CHECK: [[LEAF]] = OpFunction %{{.*}} ; -- Begin function leaf_no_vars
+; CHECK-NEXT: OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF_LEAF]] [[LEAF]]
+; CHECK-NEXT: OpReturnValue
+; CHECK-NEXT: OpFunctionEnd
+
+; helper_with_vars: two local variables -> DebugFunctionDefinition after them.
+; CHECK: [[WITH]] = OpFunction %{{.*}} ; -- Begin function helper_with_vars
+; CHECK-NEXT: OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpVariable {{.*}} Function
+; CHECK-NEXT: OpVariable {{.*}} Function
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF_WITH]] [[WITH]]
+; CHECK: OpReturnValue
+; CHECK-NEXT: OpFunctionEnd
+
+; caller_no_vars: calls both helpers, no locals -> after OpLabel.
+; CHECK: [[CALLER]] = OpFunction %{{.*}} ; -- Begin function caller_no_vars
+; CHECK-NEXT: OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF_CALLER]] [[CALLER]]
+; CHECK-NEXT: OpFunctionCall
+; CHECK-NEXT: OpFunctionCall
+; CHECK-NEXT: OpReturnValue
+; CHECK-NEXT: OpFunctionEnd
+
+; orchestrator: no args, no locals, only calls -> after OpLabel.
+; CHECK: [[ORCH]] = OpFunction %{{.*}} ; -- Begin function orchestrator
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF_ORCH]] [[ORCH]]
+; CHECK-NEXT: OpFunctionCall
+; CHECK-NEXT: OpFunctionCall
+; CHECK-NEXT: OpReturn
+; CHECK-NEXT: OpFunctionEnd
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func i32 @leaf_no_vars(i32 %value) !dbg !5 {
+entry:
+  ret i32 %value, !dbg !8
+}
+
+define spir_func i32 @helper_with_vars(i32 %value) !dbg !9 {
+entry:
+  %x = alloca i32, align 4
+  %y = alloca i32, align 4
+  store i32 %value, ptr %x
+  store i32 0, ptr %y
+  %tmp = call spir_func i32 @leaf_no_vars(i32 %value), !dbg !12
+  %sum = add i32 %tmp, 1
+  ret i32 %sum, !dbg !13
+}
+
+define spir_func i32 @caller_no_vars(i32 %value) !dbg !14 {
+entry:
+  %a = call spir_func i32 @helper_with_vars(i32 %value), !dbg !17
+  %b = call spir_func i32 @leaf_no_vars(i32 %a), !dbg !18
+  ret i32 %b, !dbg !19
+}
+
+define spir_func void @orchestrator() !dbg !20 {
+entry:
+  %unused1 = call spir_func i32 @leaf_no_vars(i32 0), !dbg !23
+  %unused2 = call spir_func i32 @helper_with_vars(i32 1), !dbg !24
+  ret void, !dbg !25
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-function-definition-calls.c", directory: "/src")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+
+!4 = !DISubroutineType(types: !6)
+!6 = !{!7, !7}
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!5 = distinct !DISubprogram(name: "leaf_no_vars", linkageName: "leaf_no_vars", scope: !1, file: !1, line: 1, type: !4, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!8 = !DILocation(line: 3, column: 3, scope: !5)
+
+!9 = distinct !DISubprogram(name: "helper_with_vars", linkageName: "helper_with_vars", scope: !1, file: !1, line: 5, type: !4, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!10 = !DILocalVariable(name: "x", scope: !9, file: !1, line: 6, type: !7)
+!11 = !DILocalVariable(name: "y", scope: !9, file: !1, line: 7, type: !7)
+!12 = !DILocation(line: 10, column: 10, scope: !9)
+!13 = !DILocation(line: 12, column: 3, scope: !9)
+
+!14 = distinct !DISubprogram(name: "caller_no_vars", linkageName: "caller_no_vars", scope: !1, file: !1, line: 14, type: !4, scopeLine: 14, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!17 = !DILocation(line: 16, column: 8, scope: !14)
+!18 = !DILocation(line: 17, column: 8, scope: !14)
+!19 = !DILocation(line: 18, column: 3, scope: !14)
+
+!20 = distinct !DISubprogram(name: "orchestrator", linkageName: "orchestrator", scope: !1, file: !1, line: 20, type: !21, scopeLine: 20, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!21 = !DISubroutineType(types: !22)
+!22 = !{null}
+!23 = !DILocation(line: 22, column: 13, scope: !20)
+!24 = !DILocation(line: 23, column: 13, scope: !20)
+!25 = !DILocation(line: 24, column: 3, scope: !20)

diff  --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-external-call.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-external-call.ll
new file mode 100644
index 0000000000000..da5deeb43ac1f
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition-external-call.ll
@@ -0,0 +1,56 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Forward-declared external callee plus function-local variables: the hoisted
+; OpFunction declaration must not cause DebugFunctionDefinition to be emitted
+; before the last function-local OpVariable.
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[NAME:%[0-9]+]] = OpString "caller_with_vars"
+; CHECK-DAG: [[DF:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction [[NAME]]
+; CHECK-DAG: OpName [[EXT_HELPER:%[0-9]+]] "external_helper"
+; CHECK-DAG: OpDecorate [[EXT_HELPER]] LinkageAttributes "external_helper" Import
+; CHECK-DAG: OpDecorate [[CALLER:%[0-9]+]] LinkageAttributes "caller_with_vars" Export
+
+; external_helper: hoisted declaration in the declarations section.
+; CHECK: [[EXT_HELPER]] = OpFunction %{{.*}}
+; CHECK-NEXT: OpFunctionParameter
+; CHECK-NEXT: OpFunctionEnd
+
+; CHECK: [[CALLER]] = OpFunction %{{.*}} ; -- Begin function caller_with_vars
+; CHECK-NEXT: OpFunctionParameter
+; CHECK-NEXT: OpLabel
+; CHECK-NEXT: OpVariable {{.*}} Function
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF]] [[CALLER]]
+; CHECK-NEXT: OpStore
+; CHECK-NEXT: OpFunctionCall
+; CHECK-NEXT: OpReturnValue
+; CHECK-NEXT: OpFunctionEnd
+
+target triple = "spirv64-unknown-unknown"
+
+declare spir_func i32 @external_helper(i32)
+
+define spir_func i32 @caller_with_vars(i32 %x) !dbg !5 {
+entry:
+  %a = alloca i32, align 4
+  store i32 %x, ptr %a
+  %r = call i32 @external_helper(i32 %x)
+  ret i32 %r, !dbg !8
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-function-definition-external-call.c", directory: "/src")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+
+!4 = !DISubroutineType(types: !6)
+!6 = !{!7, !7}
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!5 = distinct !DISubprogram(name: "caller_with_vars", linkageName: "caller_with_vars", scope: !1, file: !1, line: 1, type: !4, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!8 = !DILocation(line: 5, column: 3, scope: !5)

diff  --git a/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition.ll b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition.ll
new file mode 100644
index 0000000000000..57c246edff8f3
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/debug-info/debug-function-definition.ll
@@ -0,0 +1,55 @@
+; RUN: llc --verify-machineinstrs -O0 -mtriple=spirv64-unknown-unknown --spirv-ext=+SPV_KHR_non_semantic_info %s -o - | FileCheck %s
+; RUN: %if spirv-tools %{ llc --verify-machineinstrs --spirv-ext=+SPV_KHR_non_semantic_info -O0 -mtriple=spirv64-unknown-unknown %s -o - -filetype=obj | spirv-val %}
+
+; Exercise NonSemantic DebugFunctionDefinition for defined functions, including
+; per-function emitter state across multiple definitions in one module.
+
+; CHECK-DAG: [[EXT:%[0-9]+]] = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
+; CHECK-DAG: [[VOID:%[0-9]+]] = OpTypeVoid
+; CHECK-DAG: [[I32:%[0-9]+]] = OpTypeInt 32 0
+; CHECK-DAG: [[PATH:%[0-9]+]] = OpString "{{[/\\]}}src{{[/\\]}}debug-function-definition.c"
+; CHECK-DAG: [[NAME1:%[0-9]+]] = OpString "add_one"
+; CHECK-DAG: [[NAME2:%[0-9]+]] = OpString "add_two"
+; CHECK-DAG: [[DS:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugSource [[PATH]]
+; CHECK-DAG: [[CU:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugCompilationUnit {{.*}}
+; CHECK-DAG: [[TF:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugTypeFunction {{.*}}
+; CHECK-DAG: [[DF1:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction [[NAME1]] [[TF]] [[DS]] {{.*}}
+; CHECK-DAG: [[DF2:%[0-9]+]] = OpExtInst [[VOID]] [[EXT]] DebugFunction [[NAME2]] [[TF]] [[DS]] {{.*}}
+; CHECK: [[ADD_ONE:%[0-9]+]] = OpFunction
+; CHECK: OpLabel
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF1]] [[ADD_ONE]]
+; CHECK: [[ADD_TWO:%[0-9]+]] = OpFunction
+; CHECK: OpLabel
+; CHECK-NEXT: OpExtInst [[VOID]] [[EXT]] DebugFunctionDefinition [[DF2]] [[ADD_TWO]]
+
+target triple = "spirv64-unknown-unknown"
+
+define spir_func i32 @add_one(i32 %value) !dbg !5 {
+entry:
+  %result = add i32 %value, 1
+  ret i32 %result, !dbg !8
+}
+
+define spir_func i32 @add_two(i32 %value) !dbg !9 {
+entry:
+  %result = add i32 %value, 2
+  ret i32 %result, !dbg !10
+}
+
+!llvm.dbg.cu = !{!0}
+!llvm.module.flags = !{!2, !3}
+
+!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
+!1 = !DIFile(filename: "debug-function-definition.c", directory: "/src")
+!2 = !{i32 7, !"Dwarf Version", i32 5}
+!3 = !{i32 2, !"Debug Info Version", i32 3}
+
+!4 = !DISubroutineType(types: !6)
+!6 = !{!7, !7}
+!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
+
+!5 = distinct !DISubprogram(name: "add_one", linkageName: "add_one", scope: !1, file: !1, line: 1, type: !4, scopeLine: 1, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!8 = !DILocation(line: 3, column: 3, scope: !5)
+
+!9 = distinct !DISubprogram(name: "add_two", linkageName: "add_two", scope: !1, file: !1, line: 5, type: !4, scopeLine: 5, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0)
+!10 = !DILocation(line: 7, column: 3, scope: !9)


        


More information about the llvm-commits mailing list