[flang-commits] [flang] 65d1a5c - [mlir][OpenACC] Error on external calls without acc routine info (#223815)

via flang-commits flang-commits at lists.llvm.org
Thu Sep 17 14:48:52 PDT 2026


Author: Delaram Talaashrafi
Date: 2026-09-17T17:48:48-04:00
New Revision: 65d1a5cadd5b6b2b2f0904fce85ce14c17d3023b

URL: https://github.com/llvm/llvm-project/commit/65d1a5cadd5b6b2b2f0904fce85ce14c17d3023b
DIFF: https://github.com/llvm/llvm-project/commit/65d1a5cadd5b6b2b2f0904fce85ce14c17d3023b.diff

LOG: [mlir][OpenACC] Error on external calls without acc routine info (#223815)

Implicit routine can only be applied to procedures defined in the
current compilation unit. Diagnose external callees in compute regions
and existing routines, and treat Fortran intrinsics and `BIND(C)`
procedures as valid device symbols.

Added: 
    flang/test/Transforms/OpenACC/acc-implicit-routine.fir

Modified: 
    flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
    mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp
    mlir/test/Dialect/OpenACC/acc-implicit-routine.mlir

Removed: 
    


################################################################################
diff  --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
index ac931ff94485d..0bbf43220e185 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCUtils.cpp
@@ -762,6 +762,13 @@ bool fir::acc::isValidSymbolUse(mlir::Operation *user,
           fir::FIROpsDialect::getFirRuntimeAttrName()))
     return true;
 
+  if (fir::hasProcedureAttr<fir::FortranProcedureFlagsEnum::intrinsic>(
+          definingOp))
+    return true;
+
+  if (fir::hasBindcAttr(definingOp))
+    return true;
+
   if (auto cufProcAttr = definingOp->getAttrOfType<cuf::ProcAttributeAttr>(
           cuf::getProcAttrName())) {
     if (cufProcAttr.getValue() != cuf::ProcAttribute::Host)

diff  --git a/flang/test/Transforms/OpenACC/acc-implicit-routine.fir b/flang/test/Transforms/OpenACC/acc-implicit-routine.fir
new file mode 100644
index 0000000000000..44ba96e3d6b04
--- /dev/null
+++ b/flang/test/Transforms/OpenACC/acc-implicit-routine.fir
@@ -0,0 +1,149 @@
+// RUN: fir-opt %s --pass-pipeline="builtin.module(acc-initialize-fir-analyses,acc-implicit-routine)" -split-input-file -verify-diagnostics | FileCheck %s
+
+// -----
+
+// Defined procedure called from a compute region gets an implicit acc routine.
+module {
+  func.func @_QPcallee() {
+    return
+  }
+  func.func @_QPtest_defined_call() {
+    acc.serial {
+      fir.call @_QPcallee() : () -> ()
+      acc.yield
+    }
+    return
+  }
+}
+
+// CHECK: acc.routine @acc_routine_0 func(@_QPcallee) implicit
+// CHECK: func.func @_QPcallee() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_0]>}
+// CHECK: func.func @_QPtest_defined_call
+
+// -----
+
+// Nested fir.call from a compute region: both callees get implicit routines.
+module {
+  func.func @_QPleaf() {
+    return
+  }
+  func.func @_QPmiddle() {
+    fir.call @_QPleaf() : () -> ()
+    return
+  }
+  func.func @_QPtest_nested_calls() {
+    acc.serial {
+      fir.call @_QPmiddle() : () -> ()
+      acc.yield
+    }
+    return
+  }
+}
+
+// CHECK: acc.routine @acc_routine_1 func(@_QPleaf) implicit
+// CHECK: func.func @_QPleaf() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_1]>}
+// CHECK: acc.routine @acc_routine_0 func(@_QPmiddle) implicit
+// CHECK: func.func @_QPmiddle() attributes {acc.routine_info = #acc.routine_info<[@acc_routine_0]>}
+// CHECK: func.func @_QPtest_nested_calls
+
+// -----
+
+// Fortran intrinsic procedure declarations do not need an implicit routine.
+module {
+  func.func private @_QPsin(!fir.ref<f32>) -> f32 attributes {fir.proc_attrs = #fir.proc_attrs<intrinsic>}
+  func.func @_QPtest_intrinsic(%arg0: !fir.ref<f32>) {
+    acc.serial {
+      %0 = fir.call @_QPsin(%arg0) : (!fir.ref<f32>) -> f32
+      acc.yield
+    }
+    return
+  }
+}
+
+// CHECK: func.func private @_QPsin
+// CHECK: fir.call @_QPsin
+// CHECK-NOT: acc.routine @{{.*}} func(@_QPsin)
+
+// -----
+
+// BIND(C) procedure declarations do not need an implicit routine.
+module {
+  func.func private @c_func() attributes {fir.bindc_name = "c_func", fir.proc_attrs = #fir.proc_attrs<bind_c>}
+  func.func @_QPtest_bindc() {
+    acc.serial {
+      fir.call @c_func() : () -> ()
+      acc.yield
+    }
+    return
+  }
+}
+
+// CHECK: func.func private @c_func
+// CHECK: fir.call @c_func
+// CHECK-NOT: acc.routine @{{.*}} func(@c_func)
+
+// -----
+
+// Fortran runtime helpers are already valid device symbols.
+module {
+  func.func private @_FortranASumInteger4(!fir.box<none>) -> i32 attributes {fir.runtime}
+  func.func @_QPtest_runtime(%arg0: !fir.box<none>) {
+    acc.serial {
+      %0 = fir.call @_FortranASumInteger4(%arg0) : (!fir.box<none>) -> i32
+      acc.yield
+    }
+    return
+  }
+}
+
+// CHECK: func.func private @_FortranASumInteger4
+// CHECK: fir.call @_FortranASumInteger4
+// CHECK-NOT: acc.routine @{{.*}} func(@_FortranASumInteger4)
+
+// -----
+
+// An external procedure that already has an explicit acc routine is valid.
+module {
+  acc.routine @r_ext func(@_QPext_with_routine) seq
+  func.func private @_QPext_with_routine() attributes {acc.routine_info = #acc.routine_info<[@r_ext]>}
+  func.func @_QPtest_external_with_explicit_routine() {
+    acc.serial {
+      fir.call @_QPext_with_routine() : () -> ()
+      acc.yield
+    }
+    return
+  }
+}
+
+// CHECK: acc.routine @r_ext func(@_QPext_with_routine) seq
+// CHECK: func.func private @_QPext_with_routine
+// CHECK-NOT: acc.routine @{{.*}} func(@_QPext_with_routine) implicit
+
+// -----
+
+// An external Fortran procedure called from a compute region cannot receive an
+// implicit routine; it must already have acc routine information.
+module {
+  func.func private @_QPext_callee()
+  func.func @_QPtest_external_in_serial() {
+    acc.serial {
+      // expected-error @below {{Calls in an acc compute region must be marked with acc routine: "_QPext_callee"}}
+      fir.call @_QPext_callee() : () -> ()
+      acc.yield
+    }
+    return
+  }
+}
+
+// -----
+
+// Same requirement for an external procedure called from an existing routine.
+module {
+  acc.routine @r_caller func(@_QProutine_caller) seq
+  func.func private @_QPext_from_routine()
+  func.func @_QProutine_caller() attributes {acc.routine_info = #acc.routine_info<[@r_caller]>} {
+    // expected-error @below {{Calls in acc routine must also be marked with acc routine: "_QPext_from_routine"}}
+    fir.call @_QPext_from_routine() : () -> ()
+    return
+  }
+}

diff  --git a/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp b/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp
index bd2ab4df75606..16b71189a4f94 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp
@@ -133,10 +133,11 @@ class ACCImplicitRoutine
   }
 
   // Used to walk through a compute region looking for function calls.
-  void
+  LogicalResult
   implicitRoutineForCallsInComputeRegions(Operation *op, SymbolTable &symTab,
                                           mlir::OpBuilder &builder,
                                           acc::OpenACCSupport &accSupport) {
+    LogicalResult result = success();
     op->walk([&](CallOpInterface callOp) {
       if (!callOp.getCallableForCallee())
         return;
@@ -154,28 +155,41 @@ class ACCImplicitRoutine
       // regions, skip it
       if (!callee)
         return;
+      // Already a valid symbol for GPU regions (e.g. an existing routine or an
+      // LLVM intrinsic), skip it.
       if (accSupport.isValidSymbolUse(callOp.getOperation(), calleeSymbolRef))
         return;
+      // Without a definition in this compilation unit an implicit routine
+      // cannot be applied, so the call target needs explicit routine
+      // information.
+      if (callee.isExternal()) {
+        callOp->emitError() << "Calls in an acc compute region must be marked "
+                               "with acc routine: "
+                            << calleeSymbolRef.getLeafReference();
+        result = failure();
+        return;
+      }
       builder.setInsertionPoint(callee);
       createRoutineOp(builder, callee.getLoc(), callee);
     });
+    return result;
   }
 
   // Recursively handle calls within a routine operation
-  void implicitRoutineForCallsInRoutine(acc::RoutineOp routineOp,
-                                        mlir::OpBuilder &builder,
-                                        acc::OpenACCSupport &accSupport,
-                                        acc::DeviceType targetDeviceType) {
+  LogicalResult implicitRoutineForCallsInRoutine(
+      acc::RoutineOp routineOp, mlir::OpBuilder &builder,
+      acc::OpenACCSupport &accSupport, acc::DeviceType targetDeviceType) {
     // When bind clause is used, it means that the target is 
diff erent than the
     // function to which the `acc routine` is used with. Skip this case to
     // avoid implicitly recursively marking calls that would not end up on
     // device.
     if (isACCRoutineBindDefaultOrDeviceType(routineOp, targetDeviceType))
-      return;
+      return success();
 
     SymbolTable symTab(routineOp->getParentOfType<ModuleOp>());
     std::queue<acc::RoutineOp> routineQueue;
     routineQueue.push(routineOp);
+    LogicalResult result = success();
     while (!routineQueue.empty()) {
       auto currentRoutine = routineQueue.front();
       routineQueue.pop();
@@ -198,13 +212,26 @@ class ACCImplicitRoutine
         // regions, skip it
         if (!callee)
           return;
+        // Already a valid symbol for GPU regions (e.g. an existing routine or
+        // an LLVM intrinsic), skip it.
         if (accSupport.isValidSymbolUse(callOp.getOperation(), calleeSymbolRef))
           return;
+        // Without a definition in this compilation unit an implicit routine
+        // cannot be applied, so the call target needs explicit routine
+        // information.
+        if (callee.isExternal()) {
+          callOp->emitError()
+              << "Calls in acc routine must also be marked with acc routine: "
+              << calleeSymbolRef.getLeafReference();
+          result = failure();
+          return;
+        }
         builder.setInsertionPoint(callee);
         auto newRoutineOp = createRoutineOp(builder, callee.getLoc(), callee);
         routineQueue.push(newRoutineOp);
       });
     }
+    return result;
   }
 
 public:
@@ -218,11 +245,14 @@ class ACCImplicitRoutine
 
     acc::OpenACCSupport &accSupport = getAnalysis<acc::OpenACCSupport>();
 
+    LogicalResult result = success();
+
     // Handle compute regions
     module.walk([&](Operation *op) {
       if (isa<ACC_COMPUTE_CONSTRUCT_OPS>(op))
-        implicitRoutineForCallsInComputeRegions(op, symTab, builder,
-                                                accSupport);
+        if (failed(implicitRoutineForCallsInComputeRegions(op, symTab, builder,
+                                                           accSupport)))
+          result = failure();
     });
 
     // Use the device type option from the pass options.
@@ -230,9 +260,13 @@ class ACCImplicitRoutine
 
     // Handle existing routines
     module.walk([&](acc::RoutineOp routineOp) {
-      implicitRoutineForCallsInRoutine(routineOp, builder, accSupport,
-                                       targetDeviceType);
+      if (failed(implicitRoutineForCallsInRoutine(
+              routineOp, builder, accSupport, targetDeviceType)))
+        result = failure();
     });
+
+    if (failed(result))
+      return signalPassFailure();
   }
 };
 

diff  --git a/mlir/test/Dialect/OpenACC/acc-implicit-routine.mlir b/mlir/test/Dialect/OpenACC/acc-implicit-routine.mlir
index 20122dc0170c8..88853dfb307ee 100644
--- a/mlir/test/Dialect/OpenACC/acc-implicit-routine.mlir
+++ b/mlir/test/Dialect/OpenACC/acc-implicit-routine.mlir
@@ -1,4 +1,4 @@
-// RUN: mlir-opt %s -acc-implicit-routine -split-input-file | FileCheck %s
+// RUN: mlir-opt %s -acc-implicit-routine -split-input-file -verify-diagnostics | FileCheck %s
 
 // -----
 
@@ -255,3 +255,47 @@ module {
 // CHECK: test.call_and_store @undefined_callee
 // CHECK-NOT: acc.routine @acc_routine_
 // CHECK-NOT: acc.routine_info
+
+// -----
+
+// An external procedure called from a compute region cannot receive an
+// implicit routine; it must already have acc routine information.
+module {
+  func.func private @ext_callee()
+  func.func @test_external_in_serial() {
+    acc.serial {
+      // expected-error @below {{Calls in an acc compute region must be marked with acc routine: "ext_callee"}}
+      func.call @ext_callee() : () -> ()
+      acc.yield
+    }
+    return
+  }
+}
+
+// -----
+
+// Same requirement for an external procedure called from an existing routine.
+module {
+  acc.routine @r_caller func(@routine_caller_ext) seq
+  func.func private @ext_from_routine()
+  func.func @routine_caller_ext() attributes {acc.routine_info = #acc.routine_info<[@r_caller]>} {
+    // expected-error @below {{Calls in acc routine must also be marked with acc routine: "ext_from_routine"}}
+    func.call @ext_from_routine() : () -> ()
+    return
+  }
+}
+
+// -----
+
+// External callee from acc.kernels.
+module {
+  func.func private @ext_kernels()
+  func.func @test_external_in_kernels() {
+    acc.kernels {
+      // expected-error @below {{Calls in an acc compute region must be marked with acc routine: "ext_kernels"}}
+      func.call @ext_kernels() : () -> ()
+      acc.terminator
+    }
+    return
+  }
+}


        


More information about the flang-commits mailing list