[flang-commits] [flang] [Flang][OpenMP] Support declare target on interfaces local to procedures (PR #218877)

via flang-commits flang-commits at lists.llvm.org
Wed Aug 26 03:27:03 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-fir-hlfir

Author: Sergio Afonso (skatrak)

<details>
<summary>Changes</summary>

Declarative directives implicitly affecting the surrounding procedures are handled differently for interface procedures in Flang due to the fact that the corresponding evaluation during lowering is attached to the parent program unit, rather than the interface.

However, the current implementation to handle this edge case for `declare_target` is based on the incorrect assumption that this can be detected by comparing the owning procedure's symbol to the symbol of the program unit lexically containing the directive. This fails whenever an interface is located anywhere except for the main program.

Instead, this patch queries the details of the program unit containing the directive to check directly whether it's an interface. Anything else, including alternative entries to a procedure, is handled in the regular way.

---
Full diff: https://github.com/llvm/llvm-project/pull/218877.diff


4 Files Affected:

- (modified) flang/lib/Lower/OpenMP/OpenMP.cpp (+13-22) 
- (added) flang/test/Lower/OpenMP/declare-target-entry-iface.f90 (+295) 
- (modified) flang/test/Lower/OpenMP/declare-target-interface.f90 (+17-1) 
- (removed) flang/test/Lower/OpenMP/declare-target-multiple-entry.f90 (-25) 


``````````diff
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index 7503d33c8df38..09dce815427f9 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -1512,13 +1512,6 @@ static void getDeclareTargetInfo(
       // Case: implicit capture of the enclosing function/subroutine.
       Fortran::lower::pft::FunctionLikeUnit *owningProc =
           eval.getOwningProcedure();
-      bool owningProcNotMainProgram =
-          owningProc && !owningProc->isMainProgram();
-
-      const semantics::Symbol *owningSym =
-          owningProcNotMainProgram
-              ? &owningProc->getSubprogramSymbol()
-              : (owningProc ? owningProc->getMainProgramSymbol() : nullptr);
 
       // A bare '!$omp declare target' may appear in the specification part of
       // an interface body. In that case, the PFT records the directive as an
@@ -1526,26 +1519,24 @@ static void getDeclareTargetInfo(
       // body's subprogram, so eval.getOwningProcedure() points at the main
       // program.
       //
-      // Detect this by comparing the program unit lexically containing
-      // the directive with the procedure currently being lowered; when they
-      // differ, it might be this case or it might be one of the entries of a
-      // multiple-entry subprogram. In the first case, the directive belongs to
-      // the interface-body subprogram; otherwise, the owning subprogram is the
-      // correct one.
+      // Detect this by looking at the program unit lexically containing the
+      // directive with the procedure currently being lowered. If it is an
+      // interface, then use its symbol instead.
       const semantics::Scope &progUnitScope =
           semantics::GetProgramUnitContaining(
               semaCtx.FindScope(construct.v.source));
-      const semantics::Symbol *lexicalSym = progUnitScope.symbol();
-
-      if (lexicalSym && lexicalSym != owningSym) {
-        // Interface subprogram capture or non-default subprogram entry.
+      const semantics::Symbol *progUnitSym = progUnitScope.symbol();
+      const auto *subpDetails =
+          progUnitSym ? progUnitSym->detailsIf<semantics::SubprogramDetails>()
+                      : nullptr;
+      if (progUnitSym && subpDetails && subpDetails->isInterface()) {
         symbolAndClause.emplace_back(mlir::omp::DeclareTargetCaptureClause::to,
-                                     owningProcNotMainProgram ? *owningSym
-                                                              : *lexicalSym);
-      } else if (owningProcNotMainProgram) {
-        // Main programs are never device routines, so skip those here.
+                                     *progUnitSym);
+      } else {
+        assert(owningProc && !owningProc->isMainProgram() &&
+               "unexpected missing owning procedure or main program");
         symbolAndClause.emplace_back(mlir::omp::DeclareTargetCaptureClause::to,
-                                     *owningSym);
+                                     owningProc->getSubprogramSymbol());
       }
     }
 
diff --git a/flang/test/Lower/OpenMP/declare-target-entry-iface.f90 b/flang/test/Lower/OpenMP/declare-target-entry-iface.f90
new file mode 100644
index 0000000000000..fe81cbece3ccd
--- /dev/null
+++ b/flang/test/Lower/OpenMP/declare-target-entry-iface.f90
@@ -0,0 +1,295 @@
+!RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s
+!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-is-target-device %s -o - | FileCheck %s
+
+! Test that a bare '!$omp declare target' inside multiple entry procedures and
+! interfaces marks all impacted functions as declare-target, including when
+! complex multi-level nests of subroutines, contained subroutines and
+! interfaces are present.
+
+! CHECK: func.func {{.*}}@{{.*}}top()
+! CHECK-NOT: omp.declare_target
+subroutine top()
+  interface
+    subroutine topiface()
+      interface
+        subroutine topifacenested()
+        end subroutine
+        subroutine topifacenesteddt()
+          !$omp declare target
+        end subroutine
+      end interface
+    end subroutine
+    subroutine topifacedt()
+      !$omp declare target
+      interface
+        subroutine topifacedtnested()
+        end subroutine
+        subroutine topifacedtnesteddt()
+          !$omp declare target
+        end subroutine
+      end interface
+    end subroutine
+  end interface
+
+  call topiface()
+  call topifacedt()
+  call topifacenested()
+  call topifacenesteddt()
+  call topifacedtnested()
+  call topifacedtnesteddt()
+  call topnested()
+  call topnesteddt()
+
+  contains
+    ! CHECK: func.func {{.*}}@{{.*}}topnested()
+    ! CHECK-NOT: omp.declare_target
+    subroutine topnested()
+      interface
+        subroutine topnestediface()
+        end subroutine
+        subroutine topnestedifacedt()
+          !$omp declare target
+        end subroutine
+      end interface
+
+      call topnestediface()
+      call topnestedifacedt()
+    end subroutine
+    ! CHECK: func.func {{.*}}@{{.*}}topnesteddt()
+    ! CHECK-SAME: omp.declare_target
+    subroutine topnesteddt()
+      !$omp declare target
+      interface
+        subroutine topnesteddtiface()
+        end subroutine
+        subroutine topnesteddtifacedt()
+          !$omp declare target
+        end subroutine
+      end interface
+
+      call topnesteddtiface()
+      call topnesteddtifacedt()
+    end subroutine
+end subroutine
+
+! CHECK: func.func {{.*}}@{{.*}}topdt()
+! CHECK-SAME: omp.declare_target
+subroutine topdt()
+  !$omp declare target
+  interface
+    subroutine topdtiface()
+      interface
+        subroutine topdtifacenested()
+        end subroutine
+        subroutine topdtifacenesteddt()
+          !$omp declare target
+        end subroutine
+      end interface
+    end subroutine
+    subroutine topdtifacedt()
+      !$omp declare target
+      interface
+        subroutine topdtifacedtnested()
+        end subroutine
+        subroutine topdtifacedtnesteddt()
+          !$omp declare target
+        end subroutine
+      end interface
+    end subroutine
+  end interface
+
+  call topdtiface()
+  call topdtifacedt()
+  call topdtifacenested()
+  call topdtifacenesteddt()
+  call topdtifacedtnested()
+  call topdtifacedtnesteddt()
+  call topdtnested()
+  call topdtnesteddt()
+
+  contains
+    ! CHECK: func.func {{.*}}@{{.*}}topdtnested()
+    ! CHECK-NOT: omp.declare_target
+    subroutine topdtnested()
+      interface
+        subroutine topdtnestediface()
+        end subroutine
+        subroutine topdtnestedifacedt()
+          !$omp declare target
+        end subroutine
+      end interface
+
+      call topdtnestediface()
+      call topdtnestedifacedt()
+    end subroutine
+    ! CHECK: func.func {{.*}}@{{.*}}topdtnesteddt()
+    ! CHECK-SAME: omp.declare_target
+    subroutine topdtnesteddt()
+      !$omp declare target
+      interface
+        subroutine topdtnesteddtiface()
+        end subroutine
+        subroutine topdtnesteddtifacedt()
+          !$omp declare target
+        end subroutine
+      end interface
+
+      call topdtnesteddtiface()
+      call topdtnesteddtifacedt()
+    end subroutine
+end subroutine
+
+! CHECK: func.func {{.*}}@{{.*}}split()
+! CHECK-NOT: omp.declare_target
+subroutine split()
+
+  interface
+    subroutine splitiface()
+    end subroutine
+    subroutine splitifacedt()
+      !$omp declare target
+    end subroutine
+  end interface
+
+  call splitiface()
+  call splitifacedt()
+  return
+
+! CHECK: func.func {{.*}}@{{.*}}splita()
+! CHECK-NOT: omp.declare_target
+entry splita()
+  return
+
+! CHECK: func.func {{.*}}@{{.*}}splitb()
+! CHECK-NOT: omp.declare_target
+entry splitb()
+  return
+
+  contains
+    ! CHECK: func.func {{.*}}@{{.*}}splitnested()
+    ! CHECK-NOT: omp.declare_target
+    subroutine splitnested()
+    end subroutine
+    ! CHECK: func.func {{.*}}@{{.*}}splitnesteddt()
+    ! CHECK-SAME: omp.declare_target
+    subroutine splitnesteddt()
+      !$omp declare target
+    end subroutine
+end subroutine
+
+! CHECK: func.func {{.*}}@{{.*}}splitdt()
+! CHECK-SAME: omp.declare_target
+subroutine splitdt()
+  !$omp declare target
+
+  interface
+    subroutine splitdtiface()
+    end subroutine
+    subroutine splitdtifacedt()
+      !$omp declare target
+    end subroutine
+  end interface
+
+  call splitdtiface()
+  call splitdtifacedt()
+  call splitdtnested()
+  call splitdtnesteddt()
+  return
+
+! CHECK: func.func {{.*}}@{{.*}}splitdta()
+! CHECK-SAME: omp.declare_target
+entry splitdta()
+  return
+
+! CHECK: func.func {{.*}}@{{.*}}splitdtb()
+! CHECK-SAME: omp.declare_target
+entry splitdtb()
+  return
+
+  contains
+    ! CHECK: func.func {{.*}}@{{.*}}splitdtnested()
+    ! CHECK-NOT: omp.declare_target
+    subroutine splitdtnested()
+    end subroutine
+    ! CHECK: func.func {{.*}}@{{.*}}splitdtnesteddt()
+    ! CHECK-SAME: omp.declare_target
+    subroutine splitdtnesteddt()
+      !$omp declare target
+    end subroutine
+end subroutine
+
+! CHECK: func.func {{.*}}@{{.*}}main()
+! CHECK-NOT: omp.declare_target
+program main
+  interface
+    subroutine progiface()
+    end subroutine
+    subroutine progifacedt()
+      !$omp declare target
+    end subroutine
+  end interface
+
+  call progiface()
+  call progifacedt()
+end program
+
+! CHECK: func.func {{.*}}@{{.*}}topiface()
+! CHECK-NOT: omp.declare_target
+! CHECK: func.func {{.*}}@{{.*}}topifacedt()
+! CHECK-SAME: omp.declare_target
+
+! CHECK: func.func {{.*}}@{{.*}}topifacenested()
+! CHECK-NOT: omp.declare_target
+! CHECK: func.func {{.*}}@{{.*}}topifacenesteddt()
+! CHECK-SAME: omp.declare_target
+! CHECK: func.func {{.*}}@{{.*}}topifacedtnested()
+! CHECK-NOT: omp.declare_target
+! CHECK: func.func {{.*}}@{{.*}}topifacedtnesteddt()
+! CHECK-SAME: omp.declare_target
+
+! CHECK: func.func {{.*}}@{{.*}}topnestediface()
+! CHECK-NOT: omp.declare_target
+! CHECK: func.func {{.*}}@{{.*}}topnestedifacedt()
+! CHECK-SAME: omp.declare_target
+! CHECK: func.func {{.*}}@{{.*}}topnesteddtiface()
+! CHECK-NOT: omp.declare_target
+! CHECK: func.func {{.*}}@{{.*}}topnesteddtifacedt()
+! CHECK-SAME: omp.declare_target
+
+! CHECK: func.func {{.*}}@{{.*}}topdtiface()
+! CHECK-NOT: omp.declare_target
+! CHECK: func.func {{.*}}@{{.*}}topdtifacedt()
+! CHECK-SAME: omp.declare_target
+
+! CHECK: func.func {{.*}}@{{.*}}topdtifacenested()
+! CHECK-NOT: omp.declare_target
+! CHECK: func.func {{.*}}@{{.*}}topdtifacenesteddt()
+! CHECK-SAME: omp.declare_target
+! CHECK: func.func {{.*}}@{{.*}}topdtifacedtnested()
+! CHECK-NOT: omp.declare_target
+! CHECK: func.func {{.*}}@{{.*}}topdtifacedtnesteddt()
+! CHECK-SAME: omp.declare_target
+
+! CHECK: func.func {{.*}}@{{.*}}topdtnestediface()
+! CHECK-NOT: omp.declare_target
+! CHECK: func.func {{.*}}@{{.*}}topdtnestedifacedt()
+! CHECK-SAME: omp.declare_target
+! CHECK: func.func {{.*}}@{{.*}}topdtnesteddtiface()
+! CHECK-NOT: omp.declare_target
+! CHECK: func.func {{.*}}@{{.*}}topdtnesteddtifacedt()
+! CHECK-SAME: omp.declare_target
+
+! CHECK: func.func {{.*}}@{{.*}}splitiface()
+! CHECK-NOT: omp.declare_target
+! CHECK: func.func {{.*}}@{{.*}}splitifacedt()
+! CHECK-SAME: omp.declare_target
+
+! CHECK: func.func {{.*}}@{{.*}}splitdtiface()
+! CHECK-NOT: omp.declare_target
+! CHECK: func.func {{.*}}@{{.*}}splitdtifacedt()
+! CHECK-SAME: omp.declare_target
+
+! CHECK: func.func {{.*}}@{{.*}}progiface()
+! CHECK-NOT: omp.declare_target
+! CHECK: func.func {{.*}}@{{.*}}progifacedt()
+! CHECK-SAME: omp.declare_target
diff --git a/flang/test/Lower/OpenMP/declare-target-interface.f90 b/flang/test/Lower/OpenMP/declare-target-interface.f90
index e92d1905c7ad0..feea033c46f8f 100644
--- a/flang/test/Lower/OpenMP/declare-target-interface.f90
+++ b/flang/test/Lower/OpenMP/declare-target-interface.f90
@@ -1,11 +1,27 @@
 !RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s
 !RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-is-device %s -o - | FileCheck %s
 
-!CHECK: module attributes
+!CHECK: func.func @{{.*}}b()
+!CHECK-NOT: omp.declare_target
+
+!CHECK: func.func @{{.*}}c()
+!CHECK-SAME: omp.declare_target
+
+!CHECK: func.func private @{{.*}}a()
+!CHECK-SAME: omp.declare_target
+
 module iface
 interface
   subroutine a()
   !$omp declare target
   end subroutine
 end interface
+contains
+  subroutine b()
+    call a()
+  end subroutine
+  subroutine c()
+    !$omp declare target
+    call a()
+  end subroutine
 end module
diff --git a/flang/test/Lower/OpenMP/declare-target-multiple-entry.f90 b/flang/test/Lower/OpenMP/declare-target-multiple-entry.f90
deleted file mode 100644
index ba6b6693b087e..0000000000000
--- a/flang/test/Lower/OpenMP/declare-target-multiple-entry.f90
+++ /dev/null
@@ -1,25 +0,0 @@
-!RUN: %flang_fc1 -emit-hlfir -fopenmp %s -o - | FileCheck %s
-!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-is-target-device %s -o - | FileCheck %s
-
-! Test that a bare '!$omp declare target' inside a multiple entry subprogram
-! marks all entries as declare-target functions.
-
-! CHECK: func.func @_QPfirst_entry{{.*}}device_type = (any)
-! CHECK: func.func @_QPsecond_entry{{.*}}device_type = (any)
-! CHECK: func.func @_QPthird_entry{{.*}}device_type = (any)
-
-subroutine first_entry()
-  implicit none
-  !$omp declare target
-
-  call foo()
-  return
-
-entry second_entry()
-  call bar()
-  return
-
-entry third_entry()
-  call baz()
-  return
-end subroutine

``````````

</details>


https://github.com/llvm/llvm-project/pull/218877


More information about the flang-commits mailing list