[flang-commits] [flang] 44e3757 - [flang][PFT-to-MLIR] reset Evaluation blocks between entry-point passes (#210681)
via flang-commits
flang-commits at lists.llvm.org
Mon Jul 20 06:43:13 PDT 2026
Author: Kareem Ergawy
Date: 2026-07-20T15:43:08+02:00
New Revision: 44e37579d19c7d0965166808c77f9e7ebf407df9
URL: https://github.com/llvm/llvm-project/commit/44e37579d19c7d0965166808c77f9e7ebf407df9
DIFF: https://github.com/llvm/llvm-project/commit/44e37579d19c7d0965166808c77f9e7ebf407df9.diff
LOG: [flang][PFT-to-MLIR] reset Evaluation blocks between entry-point passes (#210681)
A subprogram with an alternate ENTRY is lowered by walking the shared
PFT once per entry. Evaluation::block is populated during each walk
(top-level createEmptyBlocks and inside wrapUnstructuredConstruct's own
createEmptyBlocks), but the second pass previously inherited stale
pointers into the first entry's function/wrap regions. For a wrappable
IfConstruct nested inside a structured container, the entry block that
genFIR(IfConstruct) starts before creating its scf.execute_region then
sent the builder into the previous entry's region, and the wrap plus its
inner load ended up in the wrong func:
subroutine foo(a)
integer a
entry bar(a)
if (a .eq. 1) then
if (a .ne. 3) stop
end if
end subroutine
produced both scf.execute_region ops in _QPfoo, with an inner fir.load
referencing bar's %arg0 — 'fir.load' op using value defined outside the
region.
Fix: null every Evaluation::block in the shared PFT at the start of each
entry-point pass, so createEmptyBlocks fills a clean tree and later
reads (in particular the landing-pad reposition in genFIR(IfConstruct))
never see a pointer from a prior pass.
Co-authored-by: Claude Opus 4.7 <noreply at anthropic.com>
Added:
Modified:
flang/lib/Lower/Bridge.cpp
flang/test/Lower/if_construct_execute_region_wrap.f90
Removed:
################################################################################
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 909ebf98db47c..217df2de27793 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -3313,7 +3313,11 @@ class FirConverter : public Fortran::lower::AbstractConverter {
// Start a new block before generating the scf.execute_region op if needed.
// This avoids generating the region directly after a terminator (e.g. a
- // conditional branch from a parent construct).
+ // conditional branch from a parent construct). A non-null firstStmt.block
+ // here is a landing pad the enclosing wrap's createEmptyBlocks pre-
+ // allocated for us — startNewFunction runs resetEvaluationBlocks before
+ // each entry-point pass, so we can trust the pointer isn't stale from a
+ // previous pass.
if (Fortran::lower::pft::isWrappableConstruct(eval) &&
eval.hasNestedEvaluations()) {
Fortran::lower::pft::Evaluation &firstStmt =
@@ -6512,6 +6516,10 @@ class FirConverter : public Fortran::lower::AbstractConverter {
// with dummy_scope operands.
resetRegisteredDummySymbols();
+ // Clear any Evaluation::block pointers left over from a previous
+ // entry-point pass over this shared PFT.
+ resetEvaluationBlocks(funit.evaluationList);
+
// Create most function blocks in advance.
createEmptyBlocks(funit.evaluationList);
@@ -6538,6 +6546,18 @@ class FirConverter : public Fortran::lower::AbstractConverter {
genBranch(alternateEntryEval->lexicalSuccessor->block);
}
+ /// Recursively null every Evaluation::block in \p evaluationList. Called
+ /// once per entry-point pass over a shared PFT so a later pass never sees
+ /// block pointers that belong to a prior pass's function or wrap region.
+ void resetEvaluationBlocks(
+ std::list<Fortran::lower::pft::Evaluation> &evaluationList) {
+ for (Fortran::lower::pft::Evaluation &eval : evaluationList) {
+ eval.block = nullptr;
+ if (eval.evaluationList)
+ resetEvaluationBlocks(*eval.evaluationList);
+ }
+ }
+
/// Create global blocks for the current function. This eliminates the
/// distinction between forward and backward targets when generating
/// branches. A block is "global" if it can be the target of a GOTO or
diff --git a/flang/test/Lower/if_construct_execute_region_wrap.f90 b/flang/test/Lower/if_construct_execute_region_wrap.f90
index d1474a3aef70e..6817966cb6c2d 100644
--- a/flang/test/Lower/if_construct_execute_region_wrap.f90
+++ b/flang/test/Lower/if_construct_execute_region_wrap.f90
@@ -31,3 +31,40 @@ subroutine wrapped_if_nested_with_stop(a, b)
! CHECK: fir.unreachable
! CHECK: scf.yield
! CHECK: }
+
+! Subroutine with an alternate ENTRY point followed by a structured outer
+! IF that nests a wrappable inner IF. The shared PFT is lowered once per
+! entry, and the inner IfConstruct's firstStmt.block field still points
+! at the previous entry's wrap-region block on the second pass. Reading
+! that stale pointer used to move the builder into the previous entry's
+! function, so both wraps ended up in _QPfoo and _QPbar was left empty
+! with a load referencing an SSA value from the other function. Fixed by
+! only starting firstStmt.block when it belongs to the current builder's
+! region.
+subroutine foo_with_entry(a)
+ integer a
+entry bar_with_entry(a)
+ if (a .eq. 1) then
+ if (a .ne. 3) stop
+ end if
+end subroutine
+
+! CHECK-LABEL: func.func @_QPfoo_with_entry
+! CHECK: %[[FOO_A:.*]]:2 = hlfir.declare {{.*}}uniq_name = "_QFfoo_with_entryEa"
+! CHECK: fir.if
+! CHECK: scf.execute_region
+! CHECK: fir.load %[[FOO_A]]#0
+! CHECK: cf.cond_br
+! CHECK: fir.call @_FortranAStopStatement
+! CHECK: scf.yield
+! CHECK: }
+
+! CHECK-LABEL: func.func @_QPbar_with_entry
+! CHECK: %[[BAR_A:.*]]:2 = hlfir.declare {{.*}}uniq_name = "_QFfoo_with_entryEa"
+! CHECK: fir.if
+! CHECK: scf.execute_region
+! CHECK: fir.load %[[BAR_A]]#0
+! CHECK: cf.cond_br
+! CHECK: fir.call @_FortranAStopStatement
+! CHECK: scf.yield
+! CHECK: }
More information about the flang-commits
mailing list