[flang-commits] [flang] [Flang][OpenMP] Add declare simd to all subprogram entries (PR #215777)

Sergio Afonso via flang-commits flang-commits at lists.llvm.org
Wed Aug 12 04:23:31 PDT 2026


https://github.com/skatrak created https://github.com/llvm/llvm-project/pull/215777

Currently, lowering of OpenMP `declare simd` directives handles alternative ENTRY points to a subroutine or function the same way as an INTERFACE inside of a main program unit. This means they don't get the `omp.declare_simd` operation added to the resulting function.

This patch changes the existing check to prevent both cases from being detected as the same situation, enabling `omp.declare_simd` to be created for alternative ENTRY points.

However, this change uncovered an existing bug related to where operations associated to OpenMP declarative constructs are added when lowering alternative ENTRY points. The issue being that they get grouped in the same basic block as initialization code for the specification part of the function, which is only intended to run for the default entry point, so they get skipped. Directives such as `allocate` and `declare simd` are impacted by this, since operations for them are created inline when lowered.

The insertion point in these cases is updated to point to the end of the entry block to avoid being skipped.

Assisted-by: Claude Opus 4.6.

>From bcadc0b3c06c105a33127267265a021473af4b0a Mon Sep 17 00:00:00 2001
From: Sergio Afonso <Sergio.AfonsoFumero at amd.com>
Date: Wed, 12 Aug 2026 12:07:04 +0100
Subject: [PATCH] [Flang][OpenMP] Add declare simd to all subprogram entries

Currently, lowering of OpenMP `declare simd` directives handles
alternative ENTRY points to a subroutine or function the same way as an
INTERFACE inside of a main program unit. This means they don't get the
`omp.declare_simd` operation added to the resulting function.

This patch changes the existing check to prevent both cases from being
detected as the same situation, enabling `omp.declare_simd` to be created
for alternative ENTRY points.

However, this change uncovered an existing bug related to where
operations associated to OpenMP declarative constructs are added when
lowering alternative ENTRY points. The issue being that they get grouped
in the same basic block as initialization code for the specification
part of the function, which is only intended to run for the default
entry point, so they get skipped. Directives such as `allocate` and
`declare simd` are impacted by this, since operations for them are
created inline when lowered.

The insertion point in these cases is updated to point to the end of the
entry block to avoid being skipped.

Assisted-by: Claude Opus 4.6.
---
 flang/lib/Lower/Bridge.cpp                    | 20 +++++++
 flang/lib/Lower/OpenMP/OpenMP.cpp             | 23 +++----
 .../OpenMP/declare-simd-multiple-entry.f90    | 60 +++++++++++++++++++
 3 files changed, 92 insertions(+), 11 deletions(-)
 create mode 100644 flang/test/Lower/OpenMP/declare-simd-multiple-entry.f90

diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index a8e3e4a0aea1a..b304f59d9e0c4 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -4134,6 +4134,26 @@ class FirConverter : public Fortran::lower::AbstractConverter {
     Fortran::lower::gatherOpenMPDeferredDeclareTargets(
         *this, bridge.getSemanticsContext(), getEval(), ompDecl,
         ompDeferredDeclareTarget);
+
+    // A declarative directive describes a property of that procedure or its
+    // symbols; it is not an executable statement. Any operation it creates must
+    // therefore be reachable from every ENTRY. However, emitting at the running
+    // insertion point would drop the op into the shared specification-part
+    // block, which an alternate ENTRY skips over. Use the end of the entry
+    // block in that case instead.
+    //
+    // If this is reached through the specification part of an executable BLOCK
+    // construct (activeConstructStack is non-empty) or if this is a
+    // MODULE-scope declarative (currentFunctionUnit == nullptr), then we don't
+    // have to handle the multiple ENTRY case.
+    if (currentFunctionUnit && activeConstructStack.empty()) {
+      mlir::Block *entryBlock = builder->getEntryBlock();
+      if (entryBlock->mightHaveTerminator())
+        builder->setInsertionPoint(entryBlock->getTerminator());
+      else
+        builder->setInsertionPointToEnd(entryBlock);
+    }
+
     genOpenMPDeclarativeConstruct(
         *this, localSymbols, bridge.getSemanticsContext(), getEval(), ompDecl);
     builder->restoreInsertionPoint(insertPt);
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 1a6819cf10ee7..9f6b0fd850026 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -5790,23 +5790,24 @@ genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
   // A `declare simd` directive may appear in the specification part of an
   // interface body. In that case the PFT records the directive as an
   // evaluation of the enclosing program unit rather than of the interface
-  // body's subprogram, and the clause operands (linear/aligned/uniform)
-  // reference dummy arguments that are local to the interface body and
-  // therefore have no address in the enclosing scope. Detect this by
-  // comparing the program unit lexically containing the directive with the
-  // procedure currently being lowered; if they differ, this evaluation is
-  // for a different procedure (the interface-body subprogram) and emitting
-  // an `omp.declare_simd` op here would create it with null operands. Skip
-  // emission: lowering for `declare simd` on an external procedure declared
-  // only via an interface body is not handled by this op-based form.
+  // body's subprogram. Detect this by comparing the program unit lexically
+  // containing the directive with the procedure currently being lowered; if
+  // they differ, the two options are:
+  //   1. It represents the described INTERFACE case, and emitting an
+  // `omp.declare_simd` op would be incorrect. These are lowered as external
+  // function declarations.
+  //   2. It represents an alternative ENTRY point to a subprogram, in which
+  // case we do need to emit the proper `omp.declare_simd` op.
   const semantics::Scope &progUnitScope =
       semantics::GetProgramUnitContaining(semaCtx.FindScope(beginSpec.source));
   lower::pft::FunctionLikeUnit *owningProc = eval.getOwningProcedure();
+  bool owningProcNotMainProgram = owningProc && !owningProc->isMainProgram();
   const semantics::Symbol *owningSym =
-      (owningProc && !owningProc->isMainProgram())
+      owningProcNotMainProgram
           ? &owningProc->getSubprogramSymbol()
           : (owningProc ? owningProc->getMainProgramSymbol() : nullptr);
-  if (progUnitScope.symbol() != owningSym)
+
+  if (!owningProcNotMainProgram && progUnitScope.symbol() != owningSym)
     return;
 
   List<Clause> clauses = makeClauses(beginSpec.Clauses(), semaCtx);
diff --git a/flang/test/Lower/OpenMP/declare-simd-multiple-entry.f90 b/flang/test/Lower/OpenMP/declare-simd-multiple-entry.f90
new file mode 100644
index 0000000000000..f20426bb77e40
--- /dev/null
+++ b/flang/test/Lower/OpenMP/declare-simd-multiple-entry.f90
@@ -0,0 +1,60 @@
+! DECLARE SIMD applies to all entries of a subprogram, not just to the main one.
+
+! RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s
+
+subroutine main_entry(x)
+  !$omp declare simd linear(x:1)
+  integer :: x, y
+  call foo()
+
+entry alt_entry_1(x, y)
+  call bar(x, y)
+  return
+
+entry alt_entry_2(x)
+  call baz(x)
+  return
+end subroutine
+
+! CHECK-LABEL: func.func @_QPmain_entry(
+! CHECK-SAME:  %[[X_ARG:.*]]: !fir.ref<i32>{{.*}})
+! CHECK:       %[[X:.*]]:2 = hlfir.declare %[[X_ARG]]
+! CHECK:       omp.declare_simd linear(%[[X]]
+! CHECK:       return
+
+! CHECK-LABEL: func.func @_QPalt_entry_1(
+! CHECK-SAME:  %[[X_ARG:.*]]: !fir.ref<i32>{{.*}},{{.*}})
+! CHECK:       %[[X:.*]]:2 = hlfir.declare %[[X_ARG]]
+! CHECK:       omp.declare_simd linear(%[[X]]
+! CHECK:       return
+
+! CHECK-LABEL: func.func @_QPalt_entry_2(
+! CHECK-SAME:  %[[X_ARG:.*]]: !fir.ref<i32>{{.*}})
+! CHECK:       %[[X:.*]]:2 = hlfir.declare %[[X_ARG]]
+! CHECK:       omp.declare_simd linear(%[[X]]
+! CHECK:       return
+
+module mymod
+  contains
+  subroutine f(x)
+    !$omp declare simd linear(x:1)
+    integer :: x, y
+    call foo()
+
+  entry g(x, y)
+    call bar(x, y)
+    return
+  end subroutine
+end module mymod
+
+! CHECK-LABEL: func.func @_QMmymodPf(
+! CHECK-SAME:  %[[X_ARG:.*]]: !fir.ref<i32>{{.*}})
+! CHECK:       %[[X:.*]]:2 = hlfir.declare %[[X_ARG]]
+! CHECK:       omp.declare_simd linear(%[[X]]
+! CHECK:       return
+
+! CHECK-LABEL: func.func @_QMmymodPg(
+! CHECK-SAME:  %[[X_ARG:.*]]: !fir.ref<i32>{{.*}},{{.*}})
+! CHECK:       %[[X:.*]]:2 = hlfir.declare %[[X_ARG]]
+! CHECK:       omp.declare_simd linear(%[[X]]
+! CHECK:       return



More information about the flang-commits mailing list