[llvm-branch-commits] [lldb] [lldb][Fortran] Added support for functions to DWARFASTParserFortran (PR #234312)

Iasonas Karaprodromidis via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Wed Oct 21 11:52:16 PDT 2026


https://github.com/Iasonaskrpr created https://github.com/llvm/llvm-project/pull/234312

Depends on #234285.

Adds handling for the `DW_TAG_subprogram` and `DW_TAG_subroutine_type` DWARF tags in ParseTypeFromDWARF as well as implementing the ParseFunctionFromDWARF function, which is necessary for the `frame variable` command to work.

**Changes:**
- Added handling for `DW_TAG_subprogram` and `DW_TAG_subroutine_type`, parsing the tag and collecting information about the functions they represent.
- Implemented `ParseFunctionFromDWARF` which is necessary for `frame variable`.
- Tested function parsing with unit tests in `DWARFASTParserFortranTests`.

Relates to #109119.

>From 1fa828c41440a0143ae30dadc48551963cbcf640 Mon Sep 17 00:00:00 2001
From: Iasonaskrpr <iaskarapro at gmail.com>
Date: Tue, 20 Oct 2026 20:39:31 +0300
Subject: [PATCH] [lldb][Fortran] Added support for functions to
 DWARFASTParserFortran plus tests

---
 .../DWARF/DWARFASTParserFortran.cpp           | 136 ++++++++-
 .../SymbolFile/DWARF/DWARFASTParserFortran.h  |   5 +
 .../DWARF/DWARFASTParserFortranTests.cpp      | 266 ++++++++++++++++++
 3 files changed, 404 insertions(+), 3 deletions(-)

diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserFortran.cpp b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserFortran.cpp
index d9caa194291183..df50a8482639a5 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserFortran.cpp
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserFortran.cpp
@@ -63,6 +63,37 @@ TypeSP DWARFASTParserFortran::UpdateSymbolContextScopeForType(
   return type_sp;
 }
 
+void DWARFASTParserFortran::ParseChildParameters(
+    const DWARFDIE &parent_die,
+    llvm::SmallVectorImpl<CompilerType> &function_param_types,
+    llvm::SmallVectorImpl<llvm::StringRef> &function_param_names) {
+  if (!parent_die)
+    return;
+
+  for (DWARFDIE die : parent_die.children()) {
+    const dw_tag_t tag = die.Tag();
+    switch (tag) {
+    case DW_TAG_formal_parameter: {
+      if (die.GetAttributeValueAsUnsigned(DW_AT_artificial, 0))
+        continue;
+
+      DWARFDIE param_type_die = die.GetAttributeValueAsReferenceDIE(DW_AT_type);
+
+      Type *type = die.ResolveTypeUID(param_type_die);
+      if (!type)
+        break;
+
+      function_param_names.emplace_back(die.GetName());
+      function_param_types.push_back(type->GetForwardCompilerType());
+    } break;
+    default:
+      break;
+    }
+  }
+
+  assert(function_param_names.size() == function_param_types.size());
+}
+
 lldb::TypeSP DWARFASTParserFortran::ParseTypeFromDWARF(
     const lldb_private::SymbolContext &sc,
     const lldb_private::plugin::dwarf::DWARFDIE &die, bool *type_is_new_ptr) {
@@ -146,13 +177,61 @@ lldb::TypeSP DWARFASTParserFortran::ParseTypeFromDWARF(
                         compiler_type, Type::ResolveState::Full);
     break;
   }
+  case DW_TAG_subprogram:
+  case DW_TAG_subroutine_type: {
+    CompilerType return_type;
+    dwarf->GetDIEToType()[die.GetDIE()] = DIE_IS_BEING_PARSED;
+    attributes = die.GetAttributes();
+    size_t num_attr = attributes.Size();
+
+    for (size_t i = 0; i < num_attr; ++i) {
+      if (attributes.ExtractFormValueAtIndex(i, form_value)) {
+        switch (attributes.AttributeAtIndex(i)) {
+        case DW_AT_name:
+          type_name_cstr = form_value.AsCString();
+          if (type_name_cstr &&
+              type_name_cstr[0]) { // Check for null AND empty string
+            type_name.SetString(llvm::StringRef(type_name_cstr));
+          } else {
+            type_name.SetCString("UNKNOWN_FORTRAN_FUNCTION");
+          }
+          break;
+
+        case DW_AT_type: {
+          DWARFDIE type_die = form_value.Reference();
+          if (!type_die)
+            break;
+
+          if (Type *resolved_type = die.GetDWARF()->ResolveType(type_die);)
+            return_type = resolved_type->GetForwardCompilerType();
+
+          break;
+        }
+
+        default:
+          break;
+        }
+      }
+    }
+
+    llvm::SmallVector<CompilerType, 4> function_params_types;
+    llvm::SmallVector<llvm::StringRef, 4> function_params_names;
+    ParseChildParameters(die, function_params_types, function_params_names);
+
+    compiler_type = m_ast.CreateFortranFunction(
+        type_name, function_params_types, function_params_names, return_type);
+
+    type_sp = dwarf->MakeType(die.GetID(), type_name, 0, nullptr,
+                              LLDB_INVALID_UID, Type::eEncodingIsUID, decl,
+                              compiler_type, Type::ResolveState::Full);
+    break;
+  }
 
   default:
-    if (log) {
+    if (log)
       dwarf->GetObjectFile()->GetModule()->LogMessage(
           log, "[{0:x16}]: unhandled type tag {1:x4} ({2})", die.GetOffset(),
           tag, DW_TAG_value_to_name(tag));
-    }
 
     break;
   }
@@ -168,7 +247,58 @@ lldb_private::Function *DWARFASTParserFortran::ParseFunctionFromDWARF(
     lldb_private::CompileUnit &comp_unit,
     const lldb_private::plugin::dwarf::DWARFDIE &die,
     lldb_private::AddressRanges ranges) {
-  return nullptr;
+  if (die.Tag() != DW_TAG_subprogram && die.Tag() != DW_TAG_subroutine_type)
+    return nullptr;
+
+  llvm::DWARFAddressRangesVector unused_func_ranges;
+  const char *name = nullptr;
+  const char *mangled = nullptr;
+  std::optional<int> decl_file = 0;
+  std::optional<int> decl_line = 0;
+  std::optional<int> decl_column = 0;
+  std::optional<int> call_file = 0;
+  std::optional<int> call_line = 0;
+  std::optional<int> call_column = 0;
+  DWARFExpressionList frame_base;
+  if (!die.GetDIENamesAndRanges(name, mangled, unused_func_ranges, decl_file,
+                                decl_line, decl_column, call_file, call_line,
+                                call_column, &frame_base))
+    return nullptr;
+
+  Mangled func_name;
+  // Mangled doesn't know how to demangle fortran names
+  if (mangled)
+    func_name.SetMangledName(ConstString(mangled));
+
+  if (name)
+    func_name.SetDemangledName(ConstString(name));
+
+  FunctionSP func_sp;
+
+  SymbolFileDWARF *dwarf = die.GetDWARF();
+
+  // Supply the type only if it has already been parsed
+  Type *func_type = dwarf->GetDIEToType().lookup(die.GetDIE());
+
+  assert(func_type == nullptr || func_type != DIE_IS_BEING_PARSED);
+
+  const user_id_t func_user_id = die.GetID();
+
+  Address func_addr = ranges[0].GetBaseAddress();
+
+  func_sp = std::make_shared<Function>(&comp_unit,
+                                       func_user_id, // UserID is the DIE offset
+                                       func_user_id, func_name, func_type,
+                                       std::move(func_addr), std::move(ranges));
+
+  if (func_sp.get() == nullptr)
+    return nullptr;
+
+  if (frame_base.IsValid())
+    func_sp->GetFrameBaseExpression() = frame_base;
+
+  comp_unit.AddFunction(func_sp);
+  return func_sp.get();
 }
 
 bool DWARFASTParserFortran::CompleteTypeFromDWARF(
diff --git a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserFortran.h b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserFortran.h
index 2afa12ae232edd..96988be8591d53 100644
--- a/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserFortran.h
+++ b/lldb/source/Plugins/SymbolFile/DWARF/DWARFASTParserFortran.h
@@ -43,6 +43,11 @@ class DWARFASTParserFortran
       const lldb_private::SymbolContext &sc,
       const lldb_private::plugin::dwarf::DWARFDIE &die, lldb::TypeSP type_sp);
 
+  void ParseChildParameters(
+      const lldb_private::plugin::dwarf::DWARFDIE &parent_die,
+      llvm::SmallVectorImpl<lldb_private::CompilerType> &function_param_types,
+      llvm::SmallVectorImpl<llvm::StringRef> &function_param_names);
+
   lldb::TypeSP
   ParseTypeFromDWARF(const lldb_private::SymbolContext &sc,
                      const lldb_private::plugin::dwarf::DWARFDIE &die,
diff --git a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserFortranTests.cpp b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserFortranTests.cpp
index 6e4b44b7105d18..e76951d8f516ce 100644
--- a/lldb/unittests/SymbolFile/DWARF/DWARFASTParserFortranTests.cpp
+++ b/lldb/unittests/SymbolFile/DWARF/DWARFASTParserFortranTests.cpp
@@ -207,3 +207,269 @@ TEST_F(DWARFASTParserFortranTests, EnsureBaseTypeParsingWorks) {
   // Ensure all expected DIEs were iterated
   EXPECT_EQ(type_idx, expected_types.size());
 }
+
+TEST_F(DWARFASTParserFortranTests, EnsureFunctionParsingWorks) {
+  const char *yamldata = R"(
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_X86_64
+DWARF:
+  debug_abbrev:
+    - ID:              0
+      Table:
+        - Code:            0x1
+          Tag:             DW_TAG_compile_unit
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_language
+              Form:            DW_FORM_data2
+        - Code:            0x2
+          Tag:             DW_TAG_subprogram
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_string
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref4
+        - Code:            0x3
+          Tag:             DW_TAG_formal_parameter
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_string
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref4
+        - Code:            0x4
+          Tag:             DW_TAG_base_type
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_string
+            - Attribute:       DW_AT_encoding
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_byte_size
+              Form:            DW_FORM_data1
+  debug_info:
+    - Version:         4
+      Entries:
+        - AbbrCode:        0x1  # DW_TAG_compile_unit
+          Values:
+            - Value:           0x22  # DW_LANG_Fortran95
+        
+        - AbbrCode:        0x4  # DW_TAG_base_type
+          Values:
+            - CStr:            'integer'
+            - Value:           0x5   # DW_ATE_signed
+            - Value:           0x4   # 4 bytes
+
+        - AbbrCode:        0x2  # DW_TAG_subprogram
+          Values:
+            - CStr:            'multiply_and_add'
+            - Value:           0x0E  # Return type points to 0x0E (integer)
+        
+        # Child 1: Parameter 'a'
+        - AbbrCode:        0x3  # DW_TAG_formal_parameter
+          Values:
+            - CStr:            'a'
+            - Value:           0x0E  # Type points to 0x0E (integer)
+        
+        # Child 2: Parameter 'b'
+        - AbbrCode:        0x3  # DW_TAG_formal_parameter
+          Values:
+            - CStr:            'b'
+            - Value:           0x0E  # Type points to 0x0E (integer)
+            
+        - AbbrCode:        0x0  # End of subprogram children
+        - AbbrCode:        0x0  # End of compile_unit children
+)";
+
+  DWARFASTParserFortranYAMLTester tester(yamldata);
+  DWARFDIE cu_die = tester.GetCUDIE();
+  DWARFDIE subprogram_die;
+  SymbolContext sc;
+  bool is_new_type;
+  std::string expected_name = "INTEGER multiply_and_add(INTEGER a, INTEGER b)";
+  for (DWARFDIE child_die : cu_die.children()) {
+    if (child_die.Tag() == DW_TAG_subprogram) {
+      subprogram_die = child_die;
+      break;
+    }
+  }
+  EXPECT_TRUE(subprogram_die.IsValid());
+  lldb::TypeSP func_type_sp =
+      tester.GetParser().ParseTypeFromDWARF(sc, subprogram_die, &is_new_type);
+  EXPECT_TRUE(is_new_type);
+  EXPECT_TRUE(func_type_sp->IsValidType());
+  CompilerType function_type = func_type_sp->GetForwardCompilerType();
+  EXPECT_TRUE(function_type.IsFunctionType());
+  EXPECT_EQ(function_type.GetTypeName().GetString(), expected_name);
+  EXPECT_EQ(function_type.GetFunctionArgumentCount(), 2);
+  CompilerType arg_1 = function_type.GetFunctionArgumentAtIndex(0);
+  EXPECT_TRUE(arg_1.IsValid());
+  CompilerType arg_2 = function_type.GetFunctionArgumentAtIndex(1);
+  EXPECT_TRUE(arg_2.IsValid());
+  CompilerType return_type = function_type.GetFunctionReturnType();
+  EXPECT_TRUE(return_type.IsValid());
+  EXPECT_EQ(arg_1.GetBasicTypeEnumeration(), eBasicTypeInt);
+  EXPECT_EQ(arg_2.GetBasicTypeEnumeration(), eBasicTypeInt);
+  EXPECT_EQ(return_type.GetBasicTypeEnumeration(), eBasicTypeInt);
+}
+
+TEST_F(DWARFASTParserFortranTests,
+       EnsureFunctionParsingWithNoParametersOrArgumentsWorks) {
+  const char *yamldata = R"(
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_X86_64
+DWARF:
+  debug_abbrev:
+    - ID:              0
+      Table:
+        - Code:            0x1
+          Tag:             DW_TAG_compile_unit
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_language
+              Form:            DW_FORM_data2
+        - Code:            0x2
+          Tag:             DW_TAG_base_type
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_string
+            - Attribute:       DW_AT_encoding
+              Form:            DW_FORM_data1
+            - Attribute:       DW_AT_byte_size
+              Form:            DW_FORM_data1
+        - Code:            0x3
+          Tag:             DW_TAG_subprogram
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_string
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref4
+        - Code:            0x4
+          Tag:             DW_TAG_subprogram
+          Children:        DW_CHILDREN_yes
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_string
+        - Code:            0x5
+          Tag:             DW_TAG_formal_parameter
+          Children:        DW_CHILDREN_no
+          Attributes:
+            - Attribute:       DW_AT_name
+              Form:            DW_FORM_string
+            - Attribute:       DW_AT_type
+              Form:            DW_FORM_ref4
+  debug_info:
+    - Version:         4
+      Entries:
+        - AbbrCode:        0x1  # DW_TAG_compile_unit
+          Values:
+            - Value:           0x22  # DW_LANG_Fortran95
+        
+        - AbbrCode:        0x2  # DW_TAG_base_type
+          Values:
+            - CStr:            'integer'
+            - Value:           0x5   # DW_ATE_signed
+            - Value:           0x4   # 4 bytes
+        
+        - AbbrCode:        0x4  # DW_TAG_subprogram (No DW_AT_type)
+          Values:
+            - CStr:            'my_subroutine'
+        - AbbrCode:        0x5  # DW_TAG_formal_parameter
+          Values:
+            - CStr:            'a'
+            - Value:           0x0E
+        - AbbrCode:        0x5  # DW_TAG_formal_parameter
+          Values:
+            - CStr:            'b'
+            - Value:           0x0E
+        - AbbrCode:        0x0  # End of my_subroutine
+        
+        - AbbrCode:        0x3  # DW_TAG_subprogram (Has DW_AT_type)
+          Values:
+            - CStr:            'no_args_func'
+            - Value:           0x0E
+        - AbbrCode:        0x0  # End of no_args_func (0 parameters)
+        
+        - AbbrCode:        0x4  # DW_TAG_subprogram (No DW_AT_type)
+          Values:
+            - CStr:            'empty_sub'
+        - AbbrCode:        0x0  # End of empty_sub
+            
+        - AbbrCode:        0x0  # End of compile_unit
+)";
+
+  DWARFASTParserFortranYAMLTester tester(yamldata);
+  DWARFDIE cu_die = tester.GetCUDIE();
+  SymbolContext sc;
+
+  int subprograms_found = 0;
+
+  for (DWARFDIE child_die : cu_die.children()) {
+    if (child_die.Tag() != DW_TAG_subprogram)
+      continue;
+    subprograms_found++;
+
+    bool is_new_type;
+    lldb::TypeSP func_type_sp =
+        tester.GetParser().ParseTypeFromDWARF(sc, child_die, &is_new_type);
+
+    EXPECT_TRUE(is_new_type);
+    EXPECT_TRUE(func_type_sp->IsValidType());
+
+    CompilerType function_type = func_type_sp->GetForwardCompilerType();
+    EXPECT_TRUE(function_type.IsFunctionType());
+
+    llvm::StringRef name = child_die.GetName();
+
+    if (name == "my_subroutine") {
+      // Subroutine: No return type, 2 arguments
+      EXPECT_EQ(function_type.GetTypeName().GetString(),
+                "my_subroutine(INTEGER a, INTEGER b)");
+      EXPECT_EQ(function_type.GetFunctionArgumentCount(), 2);
+
+      CompilerType arg_1 = function_type.GetFunctionArgumentAtIndex(0);
+      CompilerType arg_2 = function_type.GetFunctionArgumentAtIndex(1);
+      EXPECT_TRUE(arg_1.IsValid());
+      EXPECT_TRUE(arg_2.IsValid());
+      EXPECT_EQ(arg_1.GetBasicTypeEnumeration(), eBasicTypeInt);
+      EXPECT_EQ(arg_2.GetBasicTypeEnumeration(), eBasicTypeInt);
+
+      CompilerType return_type = function_type.GetFunctionReturnType();
+      EXPECT_FALSE(
+          return_type
+              .IsValid()); // Subroutines shouldn't have a valid return type
+
+    } else if (name == "no_args_func") {
+      // Function: Has return type, 0 arguments
+      EXPECT_EQ(function_type.GetTypeName().GetString(),
+                "INTEGER no_args_func()");
+      EXPECT_EQ(function_type.GetFunctionArgumentCount(), 0);
+
+      CompilerType return_type = function_type.GetFunctionReturnType();
+      EXPECT_TRUE(return_type.IsValid());
+      EXPECT_EQ(return_type.GetBasicTypeEnumeration(), eBasicTypeInt);
+
+    } else if (name == "empty_sub") {
+      // Subroutine: No return type, 0 arguments
+      EXPECT_EQ(function_type.GetTypeName().GetString(), "empty_sub()");
+      EXPECT_EQ(function_type.GetFunctionArgumentCount(), 0);
+
+      CompilerType return_type = function_type.GetFunctionReturnType();
+      EXPECT_FALSE(return_type.IsValid());
+    }
+  }
+
+  // Make sure we actually found and tested all 3!
+  EXPECT_EQ(subprograms_found, 3);
+}



More information about the llvm-branch-commits mailing list