[flang-commits] [flang] [mlir] [mlir][OpenACC] Error on external calls without acc routine info (PR #223815)
Delaram Talaashrafi via flang-commits
flang-commits at lists.llvm.org
Thu Sep 17 13:55:42 PDT 2026
https://github.com/delaram-talaashrafi updated https://github.com/llvm/llvm-project/pull/223815
>From 5885d747bfb3ba71a77cf2532ae14a6decd8e41c Mon Sep 17 00:00:00 2001
From: Delaram Talaashrafi <dtalaashrafi at rome5.pgi.net>
Date: Tue, 15 Sep 2026 13:10:05 -0700
Subject: [PATCH 1/2] [mlir][OpenACC] Error on external calls without acc
routine info
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.
---
.../OpenACC/Support/FIROpenACCUtils.cpp | 7 +++
.../OpenACC/Transforms/ACCImplicitRoutine.cpp | 56 +++++++++++++++----
.../Dialect/OpenACC/acc-implicit-routine.mlir | 46 ++++++++++++++-
3 files changed, 98 insertions(+), 11 deletions(-)
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/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp b/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp
index bd2ab4df75606..ab8b08715dfdf 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,42 @@ 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()
+ << "Procedures called in a compute region must have acc routine "
+ "information - "
+ << 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 different 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 +213,27 @@ 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()
+ << "Procedures called in a compute region must have acc routine "
+ "information - "
+ << calleeSymbolRef.getLeafReference();
+ result = failure();
+ return;
+ }
builder.setInsertionPoint(callee);
auto newRoutineOp = createRoutineOp(builder, callee.getLoc(), callee);
routineQueue.push(newRoutineOp);
});
}
+ return result;
}
public:
@@ -218,11 +247,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 +262,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..a2b1a5114eb23 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 {{Procedures called in a compute region must have acc routine information - "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 {{Procedures called in a compute region must have acc routine information - "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 {{Procedures called in a compute region must have acc routine information - "ext_kernels"}}
+ func.call @ext_kernels() : () -> ()
+ acc.terminator
+ }
+ return
+ }
+}
>From 427f9ed8e9c2f1a308092a296be7da544c5377b2 Mon Sep 17 00:00:00 2001
From: Delaram Talaashrafi <dtalaashrafi at rome5.pgi.net>
Date: Thu, 17 Sep 2026 07:37:16 -0700
Subject: [PATCH 2/2] Reviews
---
.../OpenACC/acc-implicit-routine.fir | 149 ++++++++++++++++++
.../OpenACC/Transforms/ACCImplicitRoutine.cpp | 10 +-
.../Dialect/OpenACC/acc-implicit-routine.mlir | 6 +-
3 files changed, 156 insertions(+), 9 deletions(-)
create mode 100644 flang/test/Transforms/OpenACC/acc-implicit-routine.fir
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 ab8b08715dfdf..16b71189a4f94 100644
--- a/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp
+++ b/mlir/lib/Dialect/OpenACC/Transforms/ACCImplicitRoutine.cpp
@@ -163,10 +163,9 @@ class ACCImplicitRoutine
// cannot be applied, so the call target needs explicit routine
// information.
if (callee.isExternal()) {
- callOp->emitError()
- << "Procedures called in a compute region must have acc routine "
- "information - "
- << calleeSymbolRef.getLeafReference();
+ callOp->emitError() << "Calls in an acc compute region must be marked "
+ "with acc routine: "
+ << calleeSymbolRef.getLeafReference();
result = failure();
return;
}
@@ -222,8 +221,7 @@ class ACCImplicitRoutine
// information.
if (callee.isExternal()) {
callOp->emitError()
- << "Procedures called in a compute region must have acc routine "
- "information - "
+ << "Calls in acc routine must also be marked with acc routine: "
<< calleeSymbolRef.getLeafReference();
result = failure();
return;
diff --git a/mlir/test/Dialect/OpenACC/acc-implicit-routine.mlir b/mlir/test/Dialect/OpenACC/acc-implicit-routine.mlir
index a2b1a5114eb23..88853dfb307ee 100644
--- a/mlir/test/Dialect/OpenACC/acc-implicit-routine.mlir
+++ b/mlir/test/Dialect/OpenACC/acc-implicit-routine.mlir
@@ -264,7 +264,7 @@ module {
func.func private @ext_callee()
func.func @test_external_in_serial() {
acc.serial {
- // expected-error @below {{Procedures called in a compute region must have acc routine information - "ext_callee"}}
+ // expected-error @below {{Calls in an acc compute region must be marked with acc routine: "ext_callee"}}
func.call @ext_callee() : () -> ()
acc.yield
}
@@ -279,7 +279,7 @@ 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 {{Procedures called in a compute region must have acc routine information - "ext_from_routine"}}
+ // expected-error @below {{Calls in acc routine must also be marked with acc routine: "ext_from_routine"}}
func.call @ext_from_routine() : () -> ()
return
}
@@ -292,7 +292,7 @@ module {
func.func private @ext_kernels()
func.func @test_external_in_kernels() {
acc.kernels {
- // expected-error @below {{Procedures called in a compute region must have acc routine information - "ext_kernels"}}
+ // expected-error @below {{Calls in an acc compute region must be marked with acc routine: "ext_kernels"}}
func.call @ext_kernels() : () -> ()
acc.terminator
}
More information about the flang-commits
mailing list