[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