[flang-commits] [flang] [flang][openacc] add acc.routine op for external names added in bind clauses. (PR #205591)
via flang-commits
flang-commits at lists.llvm.org
Wed Jun 24 09:22:10 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-openacc
Author: Andre Kuhlenschmidt (akuhlens)
<details>
<summary>Changes</summary>
This adds acc.routine ops for the func.func ops that declare external functions bound for device specific. This is needed to get the ACCRoutineToGPUFunc pass to move the function declaration into the correct region.
---
Full diff: https://github.com/llvm/llvm-project/pull/205591.diff
2 Files Affected:
- (modified) flang/lib/Lower/OpenACC.cpp (+135-10)
- (added) flang/test/Lower/OpenACC/acc-routine-bind-devtype-filter.f90 (+53)
``````````diff
diff --git a/flang/lib/Lower/OpenACC.cpp b/flang/lib/Lower/OpenACC.cpp
index 5fbc678e6d0ae..5809fd5f87743 100644
--- a/flang/lib/Lower/OpenACC.cpp
+++ b/flang/lib/Lower/OpenACC.cpp
@@ -4687,7 +4687,61 @@ void Fortran::lower::materializeOpenACCRoutineBindTargets(
? converter.getMLIRSymbolTable()
: nullptr;
- for (mlir::acc::RoutineOp routineOp : module.getOps<mlir::acc::RoutineOp>()) {
+ mlir::Attribute defaultDeviceTypeAttr = mlir::acc::DeviceTypeAttr::get(
+ builder.getContext(), mlir::acc::DeviceType::None);
+
+ auto getDeviceType = [](mlir::Attribute attr) -> mlir::acc::DeviceType {
+ return mlir::cast<mlir::acc::DeviceTypeAttr>(attr).getValue();
+ };
+
+ auto hasDeviceType = [&](llvm::ArrayRef<mlir::Attribute> deviceTypes,
+ mlir::Attribute deviceType) {
+ mlir::acc::DeviceType value = getDeviceType(deviceType);
+ return llvm::any_of(deviceTypes, [&](mlir::Attribute attr) {
+ return getDeviceType(attr) == value;
+ });
+ };
+
+ auto deviceTypeAppliesToBindTarget =
+ [&](mlir::Attribute deviceType,
+ llvm::ArrayRef<mlir::Attribute> targetDeviceTypes) {
+ // Clauses without an explicit device_type are the default routine
+ // clauses used as a fallback by later routine lowering.
+ return getDeviceType(deviceType) == mlir::acc::DeviceType::None ||
+ hasDeviceType(targetDeviceTypes, deviceType);
+ };
+
+ auto appendMatchingDeviceTypes =
+ [&](mlir::ArrayAttr attrs, llvm::ArrayRef<mlir::Attribute> deviceTypes,
+ llvm::SmallVector<mlir::Attribute> &out) {
+ if (!attrs)
+ return;
+ for (mlir::Attribute attr : attrs)
+ if (deviceTypeAppliesToBindTarget(attr, deviceTypes))
+ out.push_back(attr);
+ };
+
+ auto appendMatchingAttrs =
+ [&](mlir::ArrayAttr attrs, mlir::ArrayAttr attrDeviceTypes,
+ llvm::ArrayRef<mlir::Attribute> deviceTypes,
+ llvm::SmallVector<mlir::Attribute> &outAttrs,
+ llvm::SmallVector<mlir::Attribute> &outDeviceTypes) {
+ if (!attrs || !attrDeviceTypes)
+ return;
+ assert(attrs.size() == attrDeviceTypes.size() &&
+ "expect same number of attributes");
+ for (auto it : llvm::enumerate(attrDeviceTypes)) {
+ mlir::Attribute deviceType = it.value();
+ if (!deviceTypeAppliesToBindTarget(deviceType, deviceTypes))
+ continue;
+ outAttrs.push_back(attrs[it.index()]);
+ outDeviceTypes.push_back(deviceType);
+ }
+ };
+
+ llvm::SmallVector<mlir::acc::RoutineOp> routineOps(
+ module.getOps<mlir::acc::RoutineOp>());
+ for (mlir::acc::RoutineOp routineOp : routineOps) {
// bind renames the same callable, so clone the decorated routine's type.
mlir::func::FuncOp decorated = fir::FirOpBuilder::getNamedFunction(
module, symbolTable, routineOp.getFuncName());
@@ -4696,20 +4750,91 @@ void Fortran::lower::materializeOpenACCRoutineBindTargets(
: mlir::FunctionType::get(builder.getContext(), {}, {});
auto declare = [&](llvm::StringRef name) {
- if (!fir::FirOpBuilder::getNamedFunction(module, symbolTable, name))
- fir::FirOpBuilder::createFunction(builder.getUnknownLoc(), module, name,
- type, symbolTable);
+ if (mlir::func::FuncOp func =
+ fir::FirOpBuilder::getNamedFunction(module, symbolTable, name))
+ return func;
+ return fir::FirOpBuilder::createFunction(builder.getUnknownLoc(), module,
+ name, type, symbolTable);
+ };
+
+ auto createRoutineForBindTarget =
+ [&](mlir::func::FuncOp target,
+ llvm::ArrayRef<mlir::Attribute> bindTargetDeviceTypes) {
+ if (target->hasAttr(mlir::acc::getRoutineInfoAttrName()))
+ return;
+
+ llvm::SmallVector<mlir::Attribute> emptyBindIdNames,
+ emptyBindStrNames, emptyBindIdNameDeviceTypes,
+ emptyBindStrNameDeviceTypes, gangDeviceTypes, gangDimValues,
+ gangDimDeviceTypes, seqDeviceTypes, workerDeviceTypes,
+ vectorDeviceTypes;
+ appendMatchingDeviceTypes(routineOp.getGangAttr(),
+ bindTargetDeviceTypes, gangDeviceTypes);
+ appendMatchingAttrs(
+ routineOp.getGangDimAttr(), routineOp.getGangDimDeviceTypeAttr(),
+ bindTargetDeviceTypes, gangDimValues, gangDimDeviceTypes);
+ appendMatchingDeviceTypes(routineOp.getSeqAttr(),
+ bindTargetDeviceTypes, seqDeviceTypes);
+ appendMatchingDeviceTypes(routineOp.getWorkerAttr(),
+ bindTargetDeviceTypes, workerDeviceTypes);
+ appendMatchingDeviceTypes(routineOp.getVectorAttr(),
+ bindTargetDeviceTypes, vectorDeviceTypes);
+
+ createOpenACCRoutineConstruct(
+ converter, routineOp.getLoc(), module, target,
+ target.getName().str(), routineOp.getNohost(), emptyBindIdNames,
+ emptyBindStrNames, emptyBindIdNameDeviceTypes,
+ emptyBindStrNameDeviceTypes, gangDeviceTypes, gangDimValues,
+ gangDimDeviceTypes, seqDeviceTypes, workerDeviceTypes,
+ vectorDeviceTypes);
+ };
+
+ struct BindTarget {
+ mlir::func::FuncOp target;
+ llvm::SmallVector<mlir::Attribute> deviceTypes;
+ };
+ llvm::SmallVector<BindTarget> bindTargets;
+
+ auto addBindTarget = [&](mlir::func::FuncOp target,
+ mlir::Attribute deviceType) {
+ for (BindTarget &bindTarget : bindTargets) {
+ if (bindTarget.target.getOperation() != target.getOperation())
+ continue;
+ if (!hasDeviceType(bindTarget.deviceTypes, deviceType))
+ bindTarget.deviceTypes.push_back(deviceType);
+ return;
+ }
+ bindTargets.push_back({target, {deviceType}});
+ };
+
+ auto getBindDeviceType = [&](mlir::ArrayAttr deviceTypes,
+ unsigned index) -> mlir::Attribute {
+ if (deviceTypes) {
+ assert(index < deviceTypes.size() &&
+ "expect bind name and device_type arrays to match");
+ return deviceTypes[index];
+ }
+ return defaultDeviceTypeAttr;
};
// bind(identifier) is mangled; bind("string") is a verbatim asm name.
if (mlir::ArrayAttr binds = routineOp.getBindIdNameAttr())
- for (mlir::Attribute bind : binds)
- if (auto symRef = mlir::dyn_cast<mlir::SymbolRefAttr>(bind))
- declare(symRef.getLeafReference());
+ for (auto bind : llvm::enumerate(binds))
+ if (auto symRef = mlir::dyn_cast<mlir::SymbolRefAttr>(bind.value()))
+ addBindTarget(
+ declare(symRef.getLeafReference()),
+ getBindDeviceType(routineOp.getBindIdNameDeviceTypeAttr(),
+ bind.index()));
if (mlir::ArrayAttr binds = routineOp.getBindStrNameAttr())
- for (mlir::Attribute bind : binds)
- if (auto strAttr = mlir::dyn_cast<mlir::StringAttr>(bind))
- declare(strAttr.getValue());
+ for (auto bind : llvm::enumerate(binds))
+ if (auto strAttr = mlir::dyn_cast<mlir::StringAttr>(bind.value()))
+ addBindTarget(
+ declare(strAttr.getValue()),
+ getBindDeviceType(routineOp.getBindStrNameDeviceTypeAttr(),
+ bind.index()));
+
+ for (BindTarget &bindTarget : bindTargets)
+ createRoutineForBindTarget(bindTarget.target, bindTarget.deviceTypes);
}
}
diff --git a/flang/test/Lower/OpenACC/acc-routine-bind-devtype-filter.f90 b/flang/test/Lower/OpenACC/acc-routine-bind-devtype-filter.f90
new file mode 100644
index 0000000000000..725dec744b01b
--- /dev/null
+++ b/flang/test/Lower/OpenACC/acc-routine-bind-devtype-filter.f90
@@ -0,0 +1,53 @@
+! Device_type-specific acc routine bind targets inherit only the clauses for
+! the bind target's own device type.
+
+! RUN: bbc -fopenacc -emit-hlfir %s -o - | FileCheck %s
+
+subroutine s_bind_devtype_filter(n, x)
+ integer :: n, i
+ real :: x(n)
+ !$acc routine(foo) device_type(nvidia) vector bind(foo_n) device_type(multicore) worker bind(foo_m)
+ external :: foo
+ !$acc parallel loop
+ do i = 1, n
+ call foo(x(i))
+ end do
+end subroutine
+
+! CHECK-DAG: acc.routine @{{.*}} func(@_QPfoo) bind(@_QPfoo_n [#acc.device_type<nvidia>], @_QPfoo_m [#acc.device_type<multicore>]) worker ([#acc.device_type<multicore>]) vector ([#acc.device_type<nvidia>])
+! CHECK-DAG: acc.routine @[[FOO_N_ROUTINE:.*]] func(@_QPfoo_n) vector ([#acc.device_type<nvidia>]){{$}}
+! CHECK-DAG: acc.routine @[[FOO_M_ROUTINE:.*]] func(@_QPfoo_m) worker ([#acc.device_type<multicore>]){{$}}
+! CHECK-DAG: func.func private @_QPfoo_n({{.*}}) attributes {acc.routine_info = #acc.routine_info<[@[[FOO_N_ROUTINE]]]>}
+! CHECK-DAG: func.func private @_QPfoo_m({{.*}}) attributes {acc.routine_info = #acc.routine_info<[@[[FOO_M_ROUTINE]]]>}
+
+subroutine s_bind_devtype_merged_target(n, x)
+ integer :: n, i
+ real :: x(n)
+ !$acc routine(foo_merge) device_type(nvidia) vector bind(foo_dev) device_type(multicore) worker bind(foo_dev)
+ external :: foo_merge
+ !$acc parallel loop
+ do i = 1, n
+ call foo_merge(x(i))
+ end do
+end subroutine
+
+! CHECK-DAG: acc.routine @{{.*}} func(@_QPfoo_merge) bind(@_QPfoo_dev [#acc.device_type<nvidia>], @_QPfoo_dev [#acc.device_type<multicore>]) worker ([#acc.device_type<multicore>]) vector ([#acc.device_type<nvidia>])
+! CHECK-DAG: acc.routine @[[FOO_DEV_ROUTINE:.*]] func(@_QPfoo_dev) worker ([#acc.device_type<multicore>]) vector ([#acc.device_type<nvidia>]){{$}}
+! CHECK-DAG: func.func private @_QPfoo_dev({{.*}}) attributes {acc.routine_info = #acc.routine_info<[@[[FOO_DEV_ROUTINE]]]>}
+
+subroutine s_bind_before_modality(n, x)
+ integer :: n, i
+ real :: x(n)
+ !$acc routine(bar) device_type(nvidia) bind(bar_n) vector device_type(multicore) bind(bar_m) seq
+ external :: bar
+ !$acc parallel loop
+ do i = 1, n
+ call bar(x(i))
+ end do
+end subroutine
+
+! CHECK-DAG: acc.routine @{{.*}} func(@_QPbar) bind(@_QPbar_n [#acc.device_type<nvidia>], @_QPbar_m [#acc.device_type<multicore>]) vector ([#acc.device_type<nvidia>]) seq ([#acc.device_type<multicore>])
+! CHECK-DAG: acc.routine @[[BAR_N_ROUTINE:.*]] func(@_QPbar_n) vector ([#acc.device_type<nvidia>]){{$}}
+! CHECK-DAG: acc.routine @[[BAR_M_ROUTINE:.*]] func(@_QPbar_m) seq ([#acc.device_type<multicore>]){{$}}
+! CHECK-DAG: func.func private @_QPbar_n({{.*}}) attributes {acc.routine_info = #acc.routine_info<[@[[BAR_N_ROUTINE]]]>}
+! CHECK-DAG: func.func private @_QPbar_m({{.*}}) attributes {acc.routine_info = #acc.routine_info<[@[[BAR_M_ROUTINE]]]>}
``````````
</details>
https://github.com/llvm/llvm-project/pull/205591
More information about the flang-commits
mailing list