[flang-commits] [flang] [flang][PFT-to-MLIR] reset Evaluation blocks between entry-point passes (PR #210681)

Kareem Ergawy via flang-commits flang-commits at lists.llvm.org
Mon Jul 20 04:32:23 PDT 2026


https://github.com/ergawy updated https://github.com/llvm/llvm-project/pull/210681

>From e5e20031fbe83a53d86229b7fff97c24fcb6e5c0 Mon Sep 17 00:00:00 2001
From: ergawy <kareem.ergawy at gmail.com>
Date: Mon, 20 Jul 2026 03:19:16 -0700
Subject: [PATCH] [flang][PFT-to-MLIR] reset Evaluation blocks between
 entry-point passes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

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.
---
 flang/lib/Lower/Bridge.cpp                    | 22 ++++++++++-
 .../if_construct_execute_region_wrap.f90      | 37 +++++++++++++++++++
 2 files changed, 58 insertions(+), 1 deletion(-)

diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 3679eea9a0955..cc39cfffb5afc 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -3288,7 +3288,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 =
@@ -6486,6 +6490,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);
 
@@ -6512,6 +6520,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