[flang-commits] [flang] 32353d4 - [flang][debug] Clear the source line of operations hoisted by LICM (#233584)
via flang-commits
flang-commits at lists.llvm.org
Mon Oct 19 16:13:02 PDT 2026
Author: jiel-nv
Date: 2026-10-19T16:12:58-07:00
New Revision: 32353d4924f6c0e498537f935f5569203e0396f2
URL: https://github.com/llvm/llvm-project/commit/32353d4924f6c0e498537f935f5569203e0396f2
DIFF: https://github.com/llvm/llvm-project/commit/32353d4924f6c0e498537f935f5569203e0396f2.diff
LOG: [flang][debug] Clear the source line of operations hoisted by LICM (#233584)
Fixes #233484.
When `flang-licm` hoists a loop-invariant operation, the operation keeps
the location of the statement it was written in. Because the hoist point
precedes the loop, the line table then claims that statement begins in
the loop setup code, and a breakpoint placed on the statement binds
there rather than in the loop body. At that address the variables the
statement reads are not live yet, so they print as `<optimized out>` in
gdb.
This patch clears the line and column of a hoisted operation, keeping
the file. LLVM does the same to instructions it hoists or sinks, so that
one does not keep reporting a line it no longer belongs to.
Added:
flang/test/Transforms/licm-hoisted-location.fir
Modified:
flang/lib/Optimizer/Transforms/LoopInvariantCodeMotion.cpp
Removed:
################################################################################
diff --git a/flang/lib/Optimizer/Transforms/LoopInvariantCodeMotion.cpp b/flang/lib/Optimizer/Transforms/LoopInvariantCodeMotion.cpp
index f76c8deba5204..9a7125770c337 100644
--- a/flang/lib/Optimizer/Transforms/LoopInvariantCodeMotion.cpp
+++ b/flang/lib/Optimizer/Transforms/LoopInvariantCodeMotion.cpp
@@ -59,6 +59,27 @@ struct LoopInvariantCodeMotion
} // namespace
+/// Returns 'loc' with its line and column cleared but the file and surrounding
+/// structure kept. Line zero marks the location as compiler-generated; the
+/// file is kept for consumers that require a file:line.
+static Location dropLineInfo(Location loc) {
+ if (auto fileLoc = dyn_cast<FileLineColRange>(loc)) {
+ return FileLineColLoc::get(fileLoc.getFilename(), /*line=*/0, /*column=*/0);
+ }
+ if (auto fusedLoc = dyn_cast<FusedLoc>(loc)) {
+ llvm::SmallVector<Location> locs;
+ for (Location nested : fusedLoc.getLocations()) {
+ locs.push_back(dropLineInfo(nested));
+ }
+ return FusedLoc::get(locs, fusedLoc.getMetadata(), loc.getContext());
+ }
+ if (auto callSiteLoc = dyn_cast<CallSiteLoc>(loc)) {
+ return CallSiteLoc::get(dropLineInfo(callSiteLoc.getCallee()),
+ callSiteLoc.getCaller());
+ }
+ return loc;
+}
+
/// 'location' is a memory reference used by a memory access.
/// The type of 'location' defines the data type of the access
/// (e.g. it is considered to be invalid to access 'i64'
@@ -454,6 +475,9 @@ void LoopInvariantCodeMotion::runOnOperation() {
};
auto moveOutOfRegion = [&](Operation *op, Region *) {
loopLike.moveOutOfLoop(op);
+ // A hoisted operation no longer belongs to the statement it was written
+ // in. Clear its debug location to avoid reporting that line early.
+ op->setLoc(dropLineInfo(op->getLoc()));
};
moveLoopInvariantCode(
diff --git a/flang/test/Transforms/licm-hoisted-location.fir b/flang/test/Transforms/licm-hoisted-location.fir
new file mode 100644
index 0000000000000..2179a4ba7fe1b
--- /dev/null
+++ b/flang/test/Transforms/licm-hoisted-location.fir
@@ -0,0 +1,87 @@
+// RUN: fir-opt -flang-licm --split-input-file --mlir-print-debuginfo --mlir-print-local-scope %s | FileCheck %s
+
+// Tests that LICM clears the line of a hoisted operation, keeping the file so
+// consumers that require a file:line still have one. Operations it does not
+// move keep their locations.
+
+// A pure operation, hoisted because it is loop invariant.
+func.func @_QPhoisted_pure(%arg0: !fir.ref<i32>, %arg1: i32, %n: index) {
+ %c1 = arith.constant 1 : index
+ fir.do_loop %i = %c1 to %n step %c1 {
+ %0 = arith.muli %arg1, %arg1 : i32 loc("test.f90":14:5)
+ fir.store %0 to %arg0 : !fir.ref<i32> loc("test.f90":14:5)
+ } loc("test.f90":13:3)
+ return loc("test.f90":16:1)
+} loc("test.f90":12:1)
+
+// CHECK-LABEL: func.func @_QPhoisted_pure(
+// CHECK: %[[MUL:.*]] = arith.muli {{.*}} loc("test.f90":0:0)
+// CHECK: fir.do_loop
+// CHECK: fir.store %[[MUL]] to {{.*}} loc("test.f90":14:5)
+// CHECK: } loc("test.f90":13:3)
+// CHECK: return loc("test.f90":16:1)
+
+// -----
+
+// A load, hoisted because the loop does not modify what it reads and the
+// location it reads is a non-OPTIONAL scalar dummy.
+func.func @_QPhoisted_load(%arg0: !fir.ref<i32>, %n: index) {
+ %c1 = arith.constant 1 : index
+ %local = fir.alloca i32
+ fir.do_loop %i = %c1 to %n step %c1 {
+ %0 = fir.load %arg0 : !fir.ref<i32> loc("test.f90":24:5)
+ fir.store %0 to %local : !fir.ref<i32> loc("test.f90":24:5)
+ } loc("test.f90":23:3)
+ return loc("test.f90":26:1)
+} loc("test.f90":22:1)
+
+// CHECK-LABEL: func.func @_QPhoisted_load(
+// CHECK: %[[LOAD:.*]] = fir.load {{.*}} loc("test.f90":0:0)
+// CHECK: fir.do_loop
+// CHECK: fir.store %[[LOAD]] to {{.*}} loc("test.f90":24:5)
+// CHECK: } loc("test.f90":23:3)
+// CHECK: return loc("test.f90":26:1)
+
+// -----
+
+// Nothing is loop invariant here, so no location changes.
+func.func @_QPnot_hoisted(%arg0: !fir.ref<i32>, %n: index) {
+ %c1 = arith.constant 1 : index
+ fir.do_loop %i = %c1 to %n step %c1 {
+ %0 = fir.convert %i : (index) -> i32 loc("test.f90":34:5)
+ fir.store %0 to %arg0 : !fir.ref<i32> loc("test.f90":34:5)
+ } loc("test.f90":33:3)
+ return loc("test.f90":36:1)
+} loc("test.f90":32:1)
+
+// CHECK-LABEL: func.func @_QPnot_hoisted(
+// CHECK: fir.do_loop
+// CHECK: %[[CONV:.*]] = fir.convert {{.*}} loc("test.f90":34:5)
+// CHECK: fir.store %[[CONV]] to {{.*}} loc("test.f90":34:5)
+// CHECK: } loc("test.f90":33:3)
+// CHECK: return loc("test.f90":36:1)
+
+// -----
+
+// Composite locations are cleared in their leaves. A fused location keeps its
+// metadata, and a callsite location keeps its caller so that the inlining
+// context survives, if any.
+func.func @_QPcomposite_locs(%arg0: !fir.ref<i32>, %arg1: i32, %n: index) {
+ %c1 = arith.constant 1 : index
+ fir.do_loop %i = %c1 to %n step %c1 {
+ %0 = arith.muli %arg1, %arg1 : i32 loc(fused["test.f90":44:5, "test.f90":45:7])
+ %1 = arith.addi %0, %arg1 : i32 loc(fused<"scope">["test.f90":44:5, "test.f90":45:7])
+ %2 = arith.subi %1, %arg1 : i32 loc(callsite("test.f90":44:5 at "test.f90":48:3))
+ fir.store %2 to %arg0 : !fir.ref<i32> loc("test.f90":44:5)
+ } loc("test.f90":43:3)
+ return loc("test.f90":46:1)
+} loc("test.f90":42:1)
+
+// CHECK-LABEL: func.func @_QPcomposite_locs(
+// CHECK: %[[MUL:.*]] = arith.muli {{.*}} loc("test.f90":0:0)
+// CHECK: %[[ADD:.*]] = arith.addi {{.*}} loc(fused<"scope">["test.f90":0:0])
+// CHECK: %[[SUB:.*]] = arith.subi {{.*}} loc(callsite("test.f90":0:0 at "test.f90":48:3))
+// CHECK: fir.do_loop
+// CHECK: fir.store %[[SUB]] to {{.*}} loc("test.f90":44:5)
+// CHECK: } loc("test.f90":43:3)
+// CHECK: return loc("test.f90":46:1)
More information about the flang-commits
mailing list