[flang-commits] [flang] 8efc412 - [flang][debug] Place prologue_end on the first executable statement (#232025)

via flang-commits flang-commits at lists.llvm.org
Mon Oct 19 03:01:38 PDT 2026


Author: Abid Qadeer
Date: 2026-10-19T11:01:33+01:00
New Revision: 8efc412af6125bcbc13d589c8d2019f201eee8ca

URL: https://github.com/llvm/llvm-project/commit/8efc412af6125bcbc13d589c8d2019f201eee8ca
DIFF: https://github.com/llvm/llvm-project/commit/8efc412af6125bcbc13d589c8d2019f201eee8ca.diff

LOG: [flang][debug] Place prologue_end on the first executable statement (#232025)

Consider this program:

  subroutine sub(a, n)
    integer :: n
    integer :: a(n)
    a(1) = 1
  end subroutine

  program main
    integer :: a(5)
    a = 7
    call sub(a, 5)
  end program

A breakpoint on 'sub' stops on the declaration of 'a', before the code
that computes its bounds has run. The variable is not merely described
with the wrong extent there, it cannot be read at all:

  (gdb) break sub
  Breakpoint 1 at 0x21e534: file ex.f90, line 3.
  (gdb) run
  Breakpoint 1, sub (a=..., n=5) at ex.f90:3
  3         integer :: a(n)
  (gdb) print a
  $1 = <optimized out>
  (gdb) print size(a)
  failed to find array bounds

Nothing has been optimized away at -O0. gdb says '<optimized out>'
because no location entry for 'a' covers that address: the location only
opens once the hidden variable that holds the extent has been written,
and that store is the instruction right before the first executable
statement.

LLVM places prologue_end, where a breakpoint on a procedure resolves, on
the first instruction after the frame setup that has a non zero line.
Compiler generated code was taking the line of the declaration in two
places and so becoming that instruction:

1. The code lowering generates before the execution part, which computes
the bounds and lengths of the variables, allocates the automatic ones,
binds the host associated ones and holds the alternate return selector.

2. The code the conversion of hlfir.declare to FIR generates to
materialize the HLFIR base, such as the descriptor of an array with
declared lower bounds.

Both now get line 0, while the declarations keep their location so that
the debug info still gives the line of the variable. Line 0 and not an
unknown location, which would drop the dbg.declare that CodeGen locates
at the definition of the storage.

The breakpoint then lands on the first executable statement:

  (gdb) break sub
  Breakpoint 1 at 0x21e544: file ex.f90, line 4.
  (gdb) run
  Breakpoint 1, sub (a=..., n=5) at ex.f90:4
  4         a(1) = 1
  (gdb) print a
  $1 = (7, 7, 7, 7, 7)
  (gdb) print size(a)
  $2 = 5

Assissted By: Cursor

Co-authored-by: Cursor <cursoragent at cursor.com>

Added: 
    flang/test/HLFIR/declare-codegen-loc.fir
    flang/test/Integration/debug-prologue-end.f90
    flang/test/Lower/specification-part-loc.f90

Modified: 
    flang/lib/Lower/Bridge.cpp
    flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
    flang/test/Lower/if-loc.f90
    flang/test/Lower/main_location.f90

Removed: 
    


################################################################################
diff  --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 7d25d89d3b0ab8..18ce19eafae994 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -6680,6 +6680,21 @@ class FirConverter : public Fortran::lower::AbstractConverter {
     if (Fortran::lower::pft::Evaluation *alternateEntryEval =
             funit.getEntryEval())
       genBranch(alternateEntryEval->lexicalSuccessor->block);
+
+    setNonExecutionPartLocations(func.getBody());
+  }
+
+  /// Give line 0 to everything in \p body that is not execution part so that it
+  /// does not take part in line table calculation unless it has
+  /// FortranVariableOpInterface.
+  static void setNonExecutionPartLocations(mlir::Region &body) {
+    body.walk([](mlir::Operation *op) {
+      if (mlir::isa<fir::FortranVariableOpInterface>(op))
+        return;
+      if (auto fileLoc = op->getLoc()->findInstanceOf<mlir::FileLineColLoc>())
+        op->setLoc(mlir::FileLineColLoc::get(fileLoc.getFilename(),
+                                             /*line=*/0, /*column=*/0));
+    });
   }
 
   /// Recursively null every Evaluation::block in \p evaluationList.  Called

diff  --git a/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp b/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
index 6a97d91533001c..c223b07c4bf583 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/ConvertToFIR.cpp
@@ -443,6 +443,11 @@ class DeclareOpConversion : public mlir::OpRewritePattern<hlfir::DeclareOp> {
     firDeclareOp->setDiscardableAttrs(
         declareOp->getDiscardableAttrDictionary());
 
+    // Give line 0 to code that materializes the HLFIR base.
+    if (auto fileLoc = loc->findInstanceOf<mlir::FileLineColLoc>())
+      loc = mlir::FileLineColLoc::get(fileLoc.getFilename(), /*line=*/0,
+                                      /*column=*/0);
+
     auto firBase = firDeclareOp.getResult();
     mlir::Value hlfirBase;
     mlir::Type hlfirBaseType = declareOp.getBase().getType();

diff  --git a/flang/test/HLFIR/declare-codegen-loc.fir b/flang/test/HLFIR/declare-codegen-loc.fir
new file mode 100644
index 00000000000000..f217caaa4fdcd2
--- /dev/null
+++ b/flang/test/HLFIR/declare-codegen-loc.fir
@@ -0,0 +1,30 @@
+// RUN: fir-opt %s --convert-hlfir-to-fir --mlir-print-debuginfo --mlir-print-local-scope | FileCheck %s
+
+// Test that the code materializing the HLFIR base gets line 0 while the
+// fir.declare keeps the line of the variable.
+
+func.func @test_embox(%arg0: !fir.ref<!fir.array<?xi32>>, %arg1: index, %arg2: index) {
+  %0 = fir.shape_shift %arg1, %arg2 : (index, index) -> !fir.shapeshift<1>
+  %1:2 = hlfir.declare %arg0(%0) uniq_name("_QFtestEa") : (!fir.ref<!fir.array<?xi32>>, !fir.shapeshift<1>) -> (!fir.box<!fir.array<?xi32>>, !fir.ref<!fir.array<?xi32>>) loc("test.f90":3:14)
+  return
+}
+// CHECK-LABEL: func.func @test_embox(
+// CHECK: fir.declare %{{.*}} loc("test.f90":3:14)
+// CHECK: fir.embox %{{.*}} loc("test.f90":0:0)
+
+func.func @test_rebox(%arg0: !fir.box<!fir.array<?xf32>>, %arg1: index) {
+  %0 = fir.shift %arg1 : (index) -> !fir.shift<1>
+  %1:2 = hlfir.declare %arg0(%0) uniq_name("_QFtestEx") : (!fir.box<!fir.array<?xf32>>, !fir.shift<1>) -> (!fir.box<!fir.array<?xf32>>, !fir.box<!fir.array<?xf32>>) loc("test.f90":4:11)
+  return
+}
+// CHECK-LABEL: func.func @test_rebox(
+// CHECK: fir.declare %{{.*}} loc("test.f90":4:11)
+// CHECK: fir.rebox %{{.*}} loc("test.f90":0:0)
+
+func.func @test_emboxchar(%arg0: !fir.ref<!fir.char<1,?>>, %arg1: index) {
+  %1:2 = hlfir.declare %arg0 typeparams %arg1 uniq_name("_QFtestEc") : (!fir.ref<!fir.char<1,?>>, index) -> (!fir.boxchar<1>, !fir.ref<!fir.char<1,?>>) loc("test.f90":5:23)
+  return
+}
+// CHECK-LABEL: func.func @test_emboxchar(
+// CHECK: fir.declare %{{.*}} loc("test.f90":5:23)
+// CHECK: fir.emboxchar %{{.*}} loc("test.f90":0:0)

diff  --git a/flang/test/Integration/debug-prologue-end.f90 b/flang/test/Integration/debug-prologue-end.f90
new file mode 100644
index 00000000000000..73f5d07cc2e512
--- /dev/null
+++ b/flang/test/Integration/debug-prologue-end.f90
@@ -0,0 +1,46 @@
+! Test that prologue_end is placed on the first executable statement.
+
+! REQUIRES: x86-registered-target
+! RUN: %flang_fc1 -triple x86_64-unknown-linux-gnu -debug-info-kind=standalone -S %s -o - | FileCheck %s
+
+subroutine adjustable(a, n)
+  integer :: n
+  integer :: a(4, n+1)
+  a(1,1) = 1
+! CHECK-LABEL: adjustable_:
+! CHECK: .loc {{[0-9]+}} [[# @LINE-2]] {{[0-9]+}} prologue_end
+end subroutine
+
+subroutine automatic(n)
+  integer :: n
+  real :: work(n)
+  work(1) = 1.0
+! CHECK-LABEL: automatic_:
+! CHECK: .loc {{[0-9]+}} [[# @LINE-2]] {{[0-9]+}} prologue_end
+end subroutine
+
+subroutine assumed_len(s)
+  character(*) :: s
+  s(1:1) = 'x'
+! CHECK-LABEL: assumed_len_:
+! CHECK: .loc {{[0-9]+}} [[# @LINE-2]] {{[0-9]+}} prologue_end
+end subroutine
+
+subroutine target_array(a, n)
+  integer :: n
+  integer, target :: a(n)
+  integer, pointer :: q(:)
+  q => a
+  q(1) = 1
+! CHECK-LABEL: target_array_:
+! CHECK: .loc {{[0-9]+}} [[# @LINE-3]] {{[0-9]+}} prologue_end
+end subroutine
+
+subroutine alt_return(a, n, *)
+  integer :: n
+  integer :: a(n)
+  a(1) = 1
+  return 1
+! CHECK-LABEL: alt_return_:
+! CHECK: .loc {{[0-9]+}} [[# @LINE-3]] {{[0-9]+}} prologue_end
+end subroutine

diff  --git a/flang/test/Lower/if-loc.f90 b/flang/test/Lower/if-loc.f90
index 87e4538412e7ca..2d8f1cda15d0e0 100644
--- a/flang/test/Lower/if-loc.f90
+++ b/flang/test/Lower/if-loc.f90
@@ -9,9 +9,9 @@
 end
 
 ! CHECK-LABEL: c.func @_QQmain
-  ! CHECK:  %[[V_0:[0-9]+]] = fir.address_of(@_QFEn) : !fir.ref<i32> loc("{{.*}}if-loc.f90":3:
+  ! CHECK:  %[[V_0:[0-9]+]] = fir.address_of(@_QFEn) : !fir.ref<i32> loc("{{.*}}if-loc.f90":0:0)
   ! CHECK:  %[[V_1:[0-9]+]]:2 = hlfir.declare %[[V_0]] uniq_name("_QFEn") : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>) loc("{{.*}}if-loc.f90":3:
-  ! CHECK:  %[[V_2:[0-9]+]] = fir.address_of(@_QFEx) : !fir.ref<i32> loc("{{.*}}if-loc.f90":3:
+  ! CHECK:  %[[V_2:[0-9]+]] = fir.address_of(@_QFEx) : !fir.ref<i32> loc("{{.*}}if-loc.f90":0:0)
   ! CHECK:  %[[V_3:[0-9]+]]:2 = hlfir.declare %[[V_2]] uniq_name("_QFEx") : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>) loc("{{.*}}if-loc.f90":3:
   ! CHECK:  %[[V_4:[0-9]+]] = fir.load %[[V_3]]#0 : !fir.ref<i32> loc("{{.*}}if-loc.f90":4:
   ! CHECK:  %[[V_5:[0-9]+]] = arith.cmpi ne, %[[V_4]], %c1{{.*}} : i32 loc("{{.*}}if-loc.f90":4:

diff  --git a/flang/test/Lower/main_location.f90 b/flang/test/Lower/main_location.f90
index a8917520d7774d..6e19d52019ec1a 100644
--- a/flang/test/Lower/main_location.f90
+++ b/flang/test/Lower/main_location.f90
@@ -16,7 +16,7 @@
 end
 
 ! TEST1: func.func @_QQmain() {
-! TEST1-NEXT: fir.dummy_scope : !fir.dscope loc("{{.*}}test1.f90":1:1)
+! TEST1-NEXT: fir.dummy_scope : !fir.dscope loc("{{.*}}test1.f90":0:0)
 ! TEST1-NEXT: return loc("{{.*}}test1.f90":3:1)
 ! TEST1-NEXT: } loc("{{.*}}test1.f90":1:1)
 
@@ -30,7 +30,7 @@
 end program
 
 ! TEST2: func.func @_QQmain() {
-! TEST2-NEXT: fir.dummy_scope : !fir.dscope loc("{{.*}}test2.f90":2:1)
+! TEST2-NEXT: fir.dummy_scope : !fir.dscope loc("{{.*}}test2.f90":0:0)
 ! TEST2-NEXT: return loc("{{.*}}test2.f90":4:1)
 ! TEST2-NEXT: } loc("{{.*}}test2.f90":2:1)
 

diff  --git a/flang/test/Lower/specification-part-loc.f90 b/flang/test/Lower/specification-part-loc.f90
new file mode 100644
index 00000000000000..11bcd04931af47
--- /dev/null
+++ b/flang/test/Lower/specification-part-loc.f90
@@ -0,0 +1,65 @@
+! RUN: bbc -emit-hlfir -mlir-print-debuginfo -o - %s | FileCheck %s
+
+! Test that the code of the specification part gets line 0 while the
+! declarations keep the line of the variable.
+
+subroutine adjustable(a, n)
+! CHECK-LABEL: func.func @_QPadjustable(
+  integer :: n
+! CHECK: hlfir.declare %arg1 {{.*}}uniq_name("_QFadjustableEn"){{.*}} loc("{{.*}}specification-part-loc.f90":[[# @LINE-1]]:14)
+! CHECK: fir.load %{{.*}} loc("{{.*}}specification-part-loc.f90":0:0)
+! CHECK: arith.muli %{{.*}} loc("{{.*}}specification-part-loc.f90":0:0)
+! CHECK: arith.select %{{.*}} loc("{{.*}}specification-part-loc.f90":0:0)
+! CHECK: fir.shape_shift %{{.*}} loc("{{.*}}specification-part-loc.f90":0:0)
+  integer :: a(n:2*n)
+! CHECK: hlfir.declare %arg0({{.*}}uniq_name("_QFadjustableEa"){{.*}} loc("{{.*}}specification-part-loc.f90":[[# @LINE-1]]:14)
+  a = 1
+! CHECK: hlfir.assign %{{.*}} loc("{{.*}}specification-part-loc.f90":[[# @LINE-1]]:3)
+end subroutine
+
+subroutine automatic(n)
+! CHECK-LABEL: func.func @_QPautomatic(
+  integer :: n
+! CHECK: hlfir.declare %arg0 {{.*}}uniq_name("_QFautomaticEn"){{.*}} loc("{{.*}}specification-part-loc.f90":[[# @LINE-1]]:14)
+! CHECK: fir.alloca !fir.array<?xf32>{{.*}} loc("{{.*}}specification-part-loc.f90":0:0)
+  real :: work(n)
+! CHECK: hlfir.declare %{{.*}}uniq_name("_QFautomaticEwork"){{.*}} loc("{{.*}}specification-part-loc.f90":[[# @LINE-1]]:11)
+  work = 1.0
+! CHECK: hlfir.assign %{{.*}} loc("{{.*}}specification-part-loc.f90":[[# @LINE-1]]:3)
+end subroutine
+
+subroutine assumed_len(s)
+! CHECK-LABEL: func.func @_QPassumed_len(
+! CHECK: fir.unboxchar %{{.*}} loc("{{.*}}specification-part-loc.f90":0:0)
+  character(*) :: s
+! CHECK: hlfir.declare %{{.*}}uniq_name("_QFassumed_lenEs"){{.*}} loc("{{.*}}specification-part-loc.f90":[[# @LINE-1]]:19)
+  s = 'x'
+! CHECK: hlfir.assign %{{.*}} loc("{{.*}}specification-part-loc.f90":[[# @LINE-1]]:3)
+end subroutine
+
+subroutine automatic_char(n)
+! CHECK-LABEL: func.func @_QPautomatic_char(
+  integer :: n
+! CHECK: fir.alloca !fir.char<1,?>{{.*}} loc("{{.*}}specification-part-loc.f90":0:0)
+  character(len=n) :: buf
+! CHECK: hlfir.declare %{{.*}}uniq_name("_QFautomatic_charEbuf"){{.*}} loc("{{.*}}specification-part-loc.f90":[[# @LINE-1]]:23)
+  buf = 'y'
+! CHECK: hlfir.assign %{{.*}} loc("{{.*}}specification-part-loc.f90":[[# @LINE-1]]:3)
+end subroutine
+
+! Host association tuple.
+subroutine host(n)
+! CHECK-LABEL: func.func @_QPhost(
+  integer :: n
+! CHECK: fir.alloca !fir.array<?xi32>{{.*}} loc("{{.*}}specification-part-loc.f90":0:0)
+  integer :: a(n)
+! CHECK: hlfir.declare %{{.*}}uniq_name("_QFhostEa"){{.*}} loc("{{.*}}specification-part-loc.f90":[[# @LINE-1]]:14)
+! CHECK: fir.alloca tuple<{{.*}}> loc("{{.*}}specification-part-loc.f90":0:0)
+! CHECK: fir.coordinate_of %{{.*}} loc("{{.*}}specification-part-loc.f90":0:0)
+  call inner
+! CHECK: fir.call @_QFhostPinner{{.*}} loc("{{.*}}specification-part-loc.f90":[[# @LINE-1]]:3)
+contains
+  subroutine inner
+    a(1) = 1
+  end subroutine
+end subroutine


        


More information about the flang-commits mailing list