[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