From ea28d8871dcd1d674d38244c8fd37c34f6e9c52b Mon Sep 17 00:00:00 2001 From: Abid Qadeer Date: Thu, 6 Nov 2025 11:54:30 +0000 Subject: [PATCH 1/2] [flang][debug] Add debug type support for procedure pointers Fixes #161223 Procedure pointers in Fortran were generating incorrect debug type information, showing as 'integer' in GDB instead of the actual procedure signature. --- .../Transforms/DebugTypeGenerator.cpp | 24 +++++++++++ flang/test/Transforms/debug-proc-ptr-e2e.f90 | 26 ++++++++++++ flang/test/Transforms/debug-proc-ptr.fir | 41 +++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 flang/test/Transforms/debug-proc-ptr-e2e.f90 create mode 100644 flang/test/Transforms/debug-proc-ptr.fir diff --git a/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp b/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp index e1e6125fc348b..ed31746bf8dea 100644 --- a/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp +++ b/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp @@ -718,6 +718,30 @@ DebugTypeGenerator::convertType(mlir::Type Ty, mlir::LLVM::DIFileAttr fileAttr, return convertRecordType(recTy, fileAttr, scope, declOp); } else if (auto tupleTy = mlir::dyn_cast_if_present(Ty)) { return convertTupleType(tupleTy, fileAttr, scope, declOp); + } else if (mlir::isa(Ty)) { + // Handle function types - these represent procedure pointers after the + // BoxedProcedure pass has run and unwrapped the fir.boxproc type + llvm::SmallVector types; + + auto funcTy = mlir::cast(Ty); + // Add return type (or void if no return type) + if (funcTy.getNumResults() == 0) + types.push_back(mlir::LLVM::DINullTypeAttr::get(context)); + else + types.push_back( + convertType(funcTy.getResult(0), fileAttr, scope, declOp)); + + for (mlir::Type paramTy : funcTy.getInputs()) + types.push_back(convertType(paramTy, fileAttr, scope, declOp)); + + auto subroutineTy = mlir::LLVM::DISubroutineTypeAttr::get( + context, /*callingConvention=*/0, types); + + return mlir::LLVM::DIDerivedTypeAttr::get( + context, llvm::dwarf::DW_TAG_pointer_type, + mlir::StringAttr::get(context, ""), subroutineTy, + /*sizeInBits=*/ptrSize * 8, /*alignInBits=*/0, /*offset=*/0, + /*optional
=*/std::nullopt, /*extra data=*/nullptr); } else if (auto refTy = mlir::dyn_cast_if_present(Ty)) { auto elTy = refTy.getEleTy(); return convertPointerLikeType(elTy, fileAttr, scope, declOp, diff --git a/flang/test/Transforms/debug-proc-ptr-e2e.f90 b/flang/test/Transforms/debug-proc-ptr-e2e.f90 new file mode 100644 index 0000000000000..aa89160b7c8f9 --- /dev/null +++ b/flang/test/Transforms/debug-proc-ptr-e2e.f90 @@ -0,0 +1,26 @@ +! RUN: %flang_fc1 -emit-llvm -debug-info-kind=standalone %s -o - | FileCheck %s + +program test_proc_ptr + implicit none + procedure(fun1), pointer :: fun_ptr + + fun_ptr => fun1 + print *, fun_ptr(3) + +contains + integer function fun1(x) + integer :: x + fun1 = x + 1 + end function fun1 +end program test_proc_ptr + +! Check that fun_ptr is declared with correct type +! CHECK-DAG: ![[INT:.*]] = !DIBasicType(name: "integer", size: 32, encoding: DW_ATE_signed) +! CHECK-DAG: ![[PTR_INT:.*]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[INT]], size: 64) + +! Check that fun_ptr variable is a pointer to a subroutine type +! The order is: DILocalVariable -> pointer type -> subroutine type -> {return, params} +! CHECK-DAG: ![[FUN_PTR_VAR:.*]] = !DILocalVariable(name: "fun_ptr", {{.*}}type: ![[PROC_PTR:[0-9]+]] +! CHECK-DAG: ![[PROC_PTR]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[SUBR_TYPE:[0-9]+]], size: 64) +! CHECK-DAG: ![[SUBR_TYPE]] = !DISubroutineType(types: ![[SUBR_TYPES:[0-9]+]]) +! CHECK-DAG: ![[SUBR_TYPES]] = !{![[INT]], ![[PTR_INT]]} diff --git a/flang/test/Transforms/debug-proc-ptr.fir b/flang/test/Transforms/debug-proc-ptr.fir new file mode 100644 index 0000000000000..2963557786907 --- /dev/null +++ b/flang/test/Transforms/debug-proc-ptr.fir @@ -0,0 +1,41 @@ +// RUN: fir-opt --add-debug-info --mlir-print-debuginfo %s | FileCheck %s + +module { + func.func @_QQmain() attributes {fir.bindc_name = "test"} { + %0 = fir.alloca (!fir.ref) -> i32 {bindc_name = "fun_ptr", uniq_name = "_QFEfun_ptr"} + %1 = fircg.ext_declare %0 {uniq_name = "_QFEfun_ptr"} : (!fir.ref<(!fir.ref) -> i32>) -> !fir.ref<(!fir.ref) -> i32> loc(#loc1) + + // Procedure pointer with no return: procedure(sub1), pointer :: sub_ptr + %2 = fir.alloca () -> () {bindc_name = "sub_ptr", uniq_name = "_QFEsub_ptr"} + %3 = fircg.ext_declare %2 {uniq_name = "_QFEsub_ptr"} : (!fir.ref<() -> ()>) -> !fir.ref<() -> ()> loc(#loc2) + + // Procedure pointer with multiple args: procedure(func2), pointer :: func_ptr + %4 = fir.alloca (!fir.ref, !fir.ref) -> f32 {bindc_name = "func_ptr", uniq_name = "_QFEfunc_ptr"} + %5 = fircg.ext_declare %4 {uniq_name = "_QFEfunc_ptr"} : (!fir.ref<(!fir.ref, !fir.ref) -> f32>) -> !fir.ref<(!fir.ref, !fir.ref) -> f32> loc(#loc3) + + return + } loc(#loc) +} +#loc = loc("test.f90":1:1) +#loc1 = loc("test.f90":2:30) +#loc2 = loc("test.f90":3:30) +#loc3 = loc("test.f90":4:30) + +// CHECK-DAG: #[[INT:.*]] = #llvm.di_basic_type +// CHECK-DAG: #[[REAL32:.*]] = #llvm.di_basic_type +// CHECK-DAG: #[[REAL:.*]] = #llvm.di_basic_type + +// CHECK-DAG: #[[PTR_INT:.*]] = #llvm.di_derived_type +// CHECK-DAG: #[[PTR_REAL:.*]] = #llvm.di_derived_type + +// CHECK-DAG: #[[SUB1:.*]] = #llvm.di_subroutine_type +// CHECK-DAG: #[[PTR_SUB1:.*]] = #llvm.di_derived_type +// CHECK-DAG: #llvm.di_local_variable<{{.*}}name = "fun_ptr"{{.*}}type = #[[PTR_SUB1]]{{.*}}> + +// CHECK-DAG: #di_subroutine_type{{.*}} = #llvm.di_subroutine_type +// CHECK-DAG: #di_local_variable{{.*}} = #llvm.di_local_variable<{{.*}}name = "sub_ptr"{{.*}}type = #di_derived_type{{.*}}> +// CHECK-DAG: #di_derived_type{{.*}} = #llvm.di_derived_type + +// CHECK-DAG: #[[SUB3:.*]] = #llvm.di_subroutine_type +// CHECK-DAG: #[[PTR_SUB3:.*]] = #llvm.di_derived_type +// CHECK-DAG: #llvm.di_local_variable<{{.*}}name = "func_ptr"{{.*}}type = #[[PTR_SUB3]]{{.*}}> From 7d757d752ac17f62796b5c0d8f3ac381b59d507a Mon Sep 17 00:00:00 2001 From: Abid Qadeer Date: Thu, 6 Nov 2025 15:46:12 +0000 Subject: [PATCH 2/2] Handle review comments. --- flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp | 3 ++- flang/test/{Transforms => Integration}/debug-proc-ptr-e2e.f90 | 0 2 files changed, 2 insertions(+), 1 deletion(-) rename flang/test/{Transforms => Integration}/debug-proc-ptr-e2e.f90 (100%) diff --git a/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp b/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp index ed31746bf8dea..8019c399f3779 100644 --- a/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp +++ b/flang/lib/Optimizer/Transforms/DebugTypeGenerator.cpp @@ -720,7 +720,8 @@ DebugTypeGenerator::convertType(mlir::Type Ty, mlir::LLVM::DIFileAttr fileAttr, return convertTupleType(tupleTy, fileAttr, scope, declOp); } else if (mlir::isa(Ty)) { // Handle function types - these represent procedure pointers after the - // BoxedProcedure pass has run and unwrapped the fir.boxproc type + // BoxedProcedure pass has run and unwrapped the fir.boxproc type, as well + // as dummy procedures (which are represented as function types in FIR) llvm::SmallVector types; auto funcTy = mlir::cast(Ty); diff --git a/flang/test/Transforms/debug-proc-ptr-e2e.f90 b/flang/test/Integration/debug-proc-ptr-e2e.f90 similarity index 100% rename from flang/test/Transforms/debug-proc-ptr-e2e.f90 rename to flang/test/Integration/debug-proc-ptr-e2e.f90