[flang-commits] [flang] 22351c7 - [Flang][OpenMP] Add support for interop construct (#203959)
via flang-commits
flang-commits at lists.llvm.org
Wed Jul 22 09:03:40 PDT 2026
Author: Sunil Shrestha
Date: 2026-07-22T12:03:34-04:00
New Revision: 22351c74e21ff19633cc101c6d92c7c179e5a4a3
URL: https://github.com/llvm/llvm-project/commit/22351c74e21ff19633cc101c6d92c7c179e5a4a3
DIFF: https://github.com/llvm/llvm-project/commit/22351c74e21ff19633cc101c6d92c7c179e5a4a3.diff
LOG: [Flang][OpenMP] Add support for interop construct (#203959)
This adds support for the OpenMP interop construct, which enables
interoperability with foreign runtime environments (e.g., CUDA, HIP).
The init, use, and destroy action clauses are lowered from the Fortran
parse tree to MLIR interop ops and translated to the appropriate runtime
calls. The omp_lib module is extended with interop query API interfaces,
property constants, and return code constants. A semantics fix relaxes
the depend/targetsync constraint for use/destroy directives where the
interop type was established at a separate init site. Unsupported depend
clauses and prefer_type selector-list forms emit explicit diagnostics
instead of being silently dropped.
Assisted-by: Claude Opus 4.6
Added:
flang/test/Lower/OpenMP/Todo/interop-prefer-type-selector-list.f90
flang/test/Lower/OpenMP/Todo/interop-prefer-type-unknown-fr.f90
flang/test/Lower/OpenMP/interop.f90
flang/test/Semantics/OpenMP/interop-construct-v60.f90
flang/test/Semantics/OpenMP/interop-prefer-type.f90
mlir/test/Target/LLVMIR/openmp-interop-depend.mlir
mlir/test/Target/LLVMIR/openmp-interop.mlir
Modified:
flang/lib/Lower/OpenMP/Clauses.cpp
flang/lib/Lower/OpenMP/OpenMP.cpp
flang/lib/Semantics/check-omp-structure.cpp
llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
mlir/include/mlir/Dialect/OpenMP/OpenMPAttrDefs.td
mlir/include/mlir/Dialect/OpenMP/OpenMPEnums.td
mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
mlir/test/Dialect/OpenMP/ops.mlir
openmp/module/omp_lib.F90.var
openmp/module/omp_lib.h.var
Removed:
flang/test/Lower/OpenMP/Todo/interop-construct.f90
################################################################################
diff --git a/flang/lib/Lower/OpenMP/Clauses.cpp b/flang/lib/Lower/OpenMP/Clauses.cpp
index df0a129ff9c6f..96dcfa9ee9fbe 100644
--- a/flang/lib/Lower/OpenMP/Clauses.cpp
+++ b/flang/lib/Lower/OpenMP/Clauses.cpp
@@ -10,6 +10,7 @@
#include "flang/Common/idioms.h"
#include "flang/Evaluate/expression.h"
+#include "flang/Optimizer/Builder/Todo.h"
#include "flang/Parser/parse-tree.h"
#include "flang/Semantics/expression.h"
#include "flang/Semantics/openmp-modifiers.h"
@@ -1061,8 +1062,53 @@ Indirect make(const parser::OmpClause::Indirect &inp,
Init make(const parser::OmpClause::Init &inp,
semantics::SemanticsContext &semaCtx) {
- // inp -> empty
- llvm_unreachable("Empty: init");
+ // OmpInitClause has modifiers: OmpPreferType, OmpInteropType,
+ // OmpDepinfoModifier, and an OmpObject (the interop variable).
+ auto &mods = semantics::OmpGetModifiers(inp.v);
+
+ // Extract interop types (repeatable modifier).
+ Init::InteropTypes interopTypes;
+ for (auto *interopType :
+ semantics::OmpGetRepeatableModifier<parser::OmpInteropType>(mods)) {
+ switch (interopType->v) {
+ case parser::OmpInteropType::Value::Target:
+ interopTypes.push_back(Init::InteropType::Target);
+ break;
+ case parser::OmpInteropType::Value::Targetsync:
+ interopTypes.push_back(Init::InteropType::Targetsync);
+ break;
+ }
+ }
+
+ // Extract prefer_type (optional modifier).
+ std::optional<Init::InteropPreference> interopPreference;
+ if (auto *preferType =
+ semantics::OmpGetUniqueModifier<parser::OmpPreferType>(mods)) {
+ Init::InteropPreference prefs;
+ for (auto &prefSpec : preferType->v) {
+ common::visit(
+ common::visitors{
+ [&](const parser::OmpPreferenceSpecification::
+ ForeignRuntimeIdentifier &fri) {
+ prefs.push_back(makeExpr(fri.value(), semaCtx));
+ },
+ [&](const std::list<parser::OmpPreferenceSelector> &selectors) {
+ // TODO: Handle preference-selector lists.
+ if (!selectors.empty())
+ TODO_NOLOC("prefer_type with preference-selector lists in "
+ "interop init clause");
+ },
+ },
+ prefSpec.u);
+ }
+ if (!prefs.empty())
+ interopPreference = std::move(prefs);
+ }
+
+ auto &interopVar = std::get<parser::OmpObject>(inp.v.t);
+ return Init{{/*InteropPreference=*/std::move(interopPreference),
+ /*InteropTypes=*/std::move(interopTypes),
+ /*InteropVar=*/makeObject(interopVar, semaCtx)}};
}
Initializer make(const parser::OmpClause::Initializer &inp,
diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp
index a3acc2e991307..2d5ab995559af 100644
--- a/flang/lib/Lower/OpenMP/OpenMP.cpp
+++ b/flang/lib/Lower/OpenMP/OpenMP.cpp
@@ -19,6 +19,7 @@
#include "Utils.h"
#include "flang/Common/idioms.h"
#include "flang/Evaluate/expression.h"
+#include "flang/Evaluate/fold.h"
#include "flang/Evaluate/tools.h"
#include "flang/Evaluate/type.h"
#include "flang/Lower/Bridge.h"
@@ -56,6 +57,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/StringSwitch.h"
using namespace Fortran::lower::omp;
using namespace Fortran::common::openmp;
@@ -6279,8 +6281,149 @@ static void genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable,
semantics::SemanticsContext &semaCtx,
lower::pft::Evaluation &eval,
const parser::OpenMPInteropConstruct &interopConstruct) {
- if (!semaCtx.langOptions().OpenMPSimd)
- TODO(converter.getCurrentLocation(), "OpenMPInteropConstruct");
+ if (semaCtx.langOptions().OpenMPSimd)
+ return;
+
+ fir::FirOpBuilder &firOpBuilder = converter.getFirOpBuilder();
+ mlir::Location loc = converter.genLocation(interopConstruct.source);
+ mlir::MLIRContext *context = firOpBuilder.getContext();
+
+ List<Clause> clauses = makeClauses(interopConstruct.v.Clauses(), semaCtx);
+
+ // Process shared clauses (depend, nowait, device) that apply to all
+ // action clauses.
+ lower::StatementContext stmtCtx;
+ mlir::omp::DependClauseOps dependOps;
+ mlir::omp::NowaitClauseOps nowaitOps;
+ mlir::Value deviceValue;
+
+ ClauseProcessor cp(converter, semaCtx, clauses);
+ cp.processDepend(symTable, stmtCtx, dependOps);
+ cp.processNowait(nowaitOps);
+
+ // Process device clause manually.
+ for (auto &clause : clauses) {
+ if (auto *deviceClause = std::get_if<clause::Device>(&clause.u)) {
+ const auto &deviceExpr = std::get<SomeExpr>(deviceClause->t);
+ deviceValue = fir::getBase(converter.genExprValue(deviceExpr, stmtCtx));
+ }
+ }
+
+ // Helper to get the address of an interop variable from an Object.
+ auto getInteropVarAddr = [&](const Object &object) -> mlir::Value {
+ const semantics::Symbol *sym = object.sym();
+ assert(sym && "interop variable must have a symbol");
+ mlir::Value addr = converter.getSymbolAddress(*sym);
+ assert(addr && "interop variable must have a valid address");
+ return addr;
+ };
+
+ // Process action clauses: init, destroy, use.
+ for (auto &clause : clauses) {
+ if (auto *initClause = std::get_if<clause::Init>(&clause.u)) {
+ auto &interopVar = std::get<clause::Init::InteropVar>(initClause->t);
+ auto &interopTypes = std::get<clause::Init::InteropTypes>(initClause->t);
+ auto &interopPref =
+ std::get<std::optional<clause::Init::InteropPreference>>(
+ initClause->t);
+
+ mlir::Value interopVarAddr = getInteropVarAddr(interopVar);
+
+ // Convert interop types to MLIR attributes. A single init clause may
+ // list both `target` and `targetsync`; keep them on one op and let the
+ // LLVM-IR translation collapse them into a single runtime init call,
+ // matching Clang.
+ llvm::SmallVector<mlir::Attribute> interopTypeAttrs;
+ for (auto itype : interopTypes) {
+ switch (itype) {
+ case clause::Init::InteropType::Target:
+ interopTypeAttrs.push_back(mlir::omp::InteropTypeAttr::get(
+ context, mlir::omp::InteropType::target));
+ break;
+ case clause::Init::InteropType::Targetsync:
+ interopTypeAttrs.push_back(mlir::omp::InteropTypeAttr::get(
+ context, mlir::omp::InteropType::targetsync));
+ break;
+ }
+ }
+ auto interopTypesAttr = mlir::ArrayAttr::get(context, interopTypeAttrs);
+
+ // Convert prefer_type to I64ArrayAttr if present.
+ mlir::ArrayAttr preferTypeAttr;
+ if (interopPref) {
+ llvm::SmallVector<int64_t> prefValues;
+ for (auto &pref : *interopPref) {
+ // Try to evaluate as a constant integer (e.g., omp_ifr_cuda).
+ if (auto constVal = evaluate::ToInt64(pref)) {
+ prefValues.push_back(*constVal);
+ } else if (auto str =
+ evaluate::GetScalarConstantValue<evaluate::Ascii>(
+ pref)) {
+ // Map standard OpenMP foreign-runtime identifier strings to
+ // their well-known integer values (OpenMP 5.1, Table 22.2).
+ auto frId = llvm::StringSwitch<std::optional<int64_t>>(
+ llvm::StringRef(*str).lower())
+ .Case("cuda", 1)
+ .Case("cuda_driver", 2)
+ .Case("opencl", 3)
+ .Case("sycl", 4)
+ .Case("hip", 5)
+ .Case("level_zero", 6)
+ .Case("hsa", 7)
+ .Default(std::nullopt);
+ if (frId)
+ prefValues.push_back(*frId);
+ else
+ TODO(loc, "unknown foreign-runtime identifier in "
+ "prefer_type");
+ } else {
+ TODO(loc, "non-constant prefer_type value in interop init");
+ }
+ }
+ if (!prefValues.empty())
+ preferTypeAttr = firOpBuilder.getI64ArrayAttr(prefValues);
+ }
+
+ // Emit a single InteropInitOp carrying all interop-types from this init
+ // clause. The translation to LLVM IR collapses them into one runtime
+ // call, matching Clang (one init clause -> one __tgt_interop_init).
+ mlir::omp::InteropInitOp::create(
+ firOpBuilder, loc, interopVarAddr, interopTypesAttr, preferTypeAttr,
+ deviceValue,
+ dependOps.dependKinds.empty()
+ ? nullptr
+ : firOpBuilder.getArrayAttr(dependOps.dependKinds),
+ dependOps.dependVars,
+ /*depend_iterated_kinds=*/nullptr,
+ /*depend_iterated=*/mlir::ValueRange{}, nowaitOps.nowait);
+
+ } else if (auto *destroyClause = std::get_if<clause::Destroy>(&clause.u)) {
+ assert(destroyClause->v &&
+ "destroy clause must have an interop variable");
+ if (destroyClause->v) {
+ mlir::Value interopVarAddr = getInteropVarAddr(*destroyClause->v);
+ mlir::omp::InteropDestroyOp::create(
+ firOpBuilder, loc, interopVarAddr, deviceValue,
+ dependOps.dependKinds.empty()
+ ? nullptr
+ : firOpBuilder.getArrayAttr(dependOps.dependKinds),
+ dependOps.dependVars,
+ /*depend_iterated_kinds=*/nullptr,
+ /*depend_iterated=*/mlir::ValueRange{}, nowaitOps.nowait);
+ }
+
+ } else if (auto *useClause = std::get_if<clause::Use>(&clause.u)) {
+ mlir::Value interopVarAddr = getInteropVarAddr(useClause->v);
+ mlir::omp::InteropUseOp::create(
+ firOpBuilder, loc, interopVarAddr, deviceValue,
+ dependOps.dependKinds.empty()
+ ? nullptr
+ : firOpBuilder.getArrayAttr(dependOps.dependKinds),
+ dependOps.dependVars,
+ /*depend_iterated_kinds=*/nullptr,
+ /*depend_iterated=*/mlir::ValueRange{}, nowaitOps.nowait);
+ }
+ }
}
static void
diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp
index bf38dcb823c5c..4e29a1c387ec2 100644
--- a/flang/lib/Semantics/check-omp-structure.cpp
+++ b/flang/lib/Semantics/check-omp-structure.cpp
@@ -5975,6 +5975,7 @@ void OmpStructureChecker::Enter(const parser::OmpClause::ThreadLimit &x) {
void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
bool isDependClauseOccurred{false};
+ bool hasInitClause{false};
int targetCount{0}, targetSyncCount{0};
std::set<const Symbol *> objectSymbolList;
const auto &clauseList{std::get<std::optional<parser::OmpClauseList>>(x.v.t)};
@@ -5982,6 +5983,7 @@ void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
common::visit(
common::visitors{
[&](const parser::OmpClause::Init &initClause) {
+ hasInitClause = true;
if (OmpVerifyModifiers(initClause.v, llvm::omp::OMPC_init,
GetContext().directiveSource, context_)) {
@@ -6004,6 +6006,38 @@ void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
"The '%s' is not allowed on INTEROP construct"_err_en_US,
desc.name.str());
}
+ // A prefer_type foreign-runtime-identifier must be a
+ // constant expression of integer OpenMP type or a base
+ // language string literal. This is enforced for the flat form
+ // (prefer_type(fr-id, ...)); the OpenMP 6.0
+ // brace form (prefer_type({fr(...), attr(...)}, ...)) is not
+ // yet supported in lowering and is diagnosed there, so its
+ // operands are not validated here.
+ if (auto *preferType{
+ OmpGetUniqueModifier<parser::OmpPreferType>(
+ modifiers)}) {
+ for (const auto &prefSpec : preferType->v) {
+ const auto *fri{
+ std::get_if<parser::OmpPreferenceSpecification::
+ ForeignRuntimeIdentifier>(&prefSpec.u)};
+ if (!fri) {
+ continue;
+ }
+ if (const auto *expr{GetExpr(context_, fri->value())}) {
+ std::optional<evaluate::DynamicType> type{
+ expr->GetType()};
+ bool isIntOrChar{type &&
+ (type->category() ==
+ evaluate::TypeCategory::Integer ||
+ type->category() ==
+ evaluate::TypeCategory::Character)};
+ if (!evaluate::IsConstantExpr(*expr) || !isIntOrChar) {
+ context_.Say(fri->value().source,
+ "The foreign-runtime-identifier in a `prefer_type` modifier must be a constant expression of integer OpenMP type or a base language string literal"_err_en_US);
+ }
+ }
+ }
+ }
}
const auto *interopVar{parser::Unwrap<parser::OmpObject>(
std::get<parser::OmpObject>(initClause.v.t))};
@@ -6058,7 +6092,11 @@ void OmpStructureChecker::Enter(const parser::OpenMPInteropConstruct &x) {
context_.Say(GetContext().directiveSource,
"Each interop-type may be specified at most once."_err_en_US);
}
- if (isDependClauseOccurred && !targetSyncCount) {
+ // Only enforce the depend/targetsync constraint when an init clause is
+ // present on this directive. For use/destroy-only directives, the interop
+ // object's type was established at its init site, which may be in a
+ //
diff erent statement — we cannot verify it statically here.
+ if (isDependClauseOccurred && hasInitClause && !targetSyncCount) {
context_.Say(GetContext().directiveSource,
"A DEPEND clause can only appear on the directive if the interop-type includes TARGETSYNC"_err_en_US);
}
diff --git a/flang/test/Lower/OpenMP/Todo/interop-construct.f90 b/flang/test/Lower/OpenMP/Todo/interop-construct.f90
deleted file mode 100644
index 4f69667863990..0000000000000
--- a/flang/test/Lower/OpenMP/Todo/interop-construct.f90
+++ /dev/null
@@ -1,9 +0,0 @@
-! REQUIRES: openmp_runtime
-! RUN: %not_todo_cmd %flang_fc1 -emit-llvm %openmp_flags -fopenmp-version=52 -o - %s 2>&1 | FileCheck %s
-
-! CHECK: not yet implemented: OpenMPInteropConstruct
-program interop_test
- use omp_lib
- integer(omp_interop_kind) :: obj
- !$omp interop init(targetsync,target: obj)
-end program interop_test
diff --git a/flang/test/Lower/OpenMP/Todo/interop-prefer-type-selector-list.f90 b/flang/test/Lower/OpenMP/Todo/interop-prefer-type-selector-list.f90
new file mode 100644
index 0000000000000..be0f63eb64bc2
--- /dev/null
+++ b/flang/test/Lower/OpenMP/Todo/interop-prefer-type-selector-list.f90
@@ -0,0 +1,11 @@
+! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -fopenmp-version=60 -o - %s 2>&1 | FileCheck %s
+
+! A prefer_type preference-selector list (the OpenMP 6.0 brace form) is valid
+! syntax but not yet lowered.
+
+! CHECK: not yet implemented: prefer_type with preference-selector lists in interop init clause
+
+subroutine interop_prefer_type_selector_list(obj)
+ integer(8) :: obj
+ !$omp interop init(prefer_type({fr("cuda")}), target: obj)
+end subroutine
diff --git a/flang/test/Lower/OpenMP/Todo/interop-prefer-type-unknown-fr.f90 b/flang/test/Lower/OpenMP/Todo/interop-prefer-type-unknown-fr.f90
new file mode 100644
index 0000000000000..848fff122ddd8
--- /dev/null
+++ b/flang/test/Lower/OpenMP/Todo/interop-prefer-type-unknown-fr.f90
@@ -0,0 +1,11 @@
+! RUN: %not_todo_cmd %flang_fc1 -emit-fir -fopenmp -fopenmp-version=60 -o - %s 2>&1 | FileCheck %s
+
+! A prefer_type foreign-runtime identifier string that is not one of the
+! standard OpenMP names cannot be mapped to a runtime id.
+
+! CHECK: not yet implemented: unknown foreign-runtime identifier in prefer_type
+
+subroutine interop_prefer_type_unknown_fr(obj)
+ integer(8) :: obj
+ !$omp interop init(prefer_type("no_such_runtime"), target: obj)
+end subroutine
diff --git a/flang/test/Lower/OpenMP/interop.f90 b/flang/test/Lower/OpenMP/interop.f90
new file mode 100644
index 0000000000000..b20332bef6893
--- /dev/null
+++ b/flang/test/Lower/OpenMP/interop.f90
@@ -0,0 +1,175 @@
+! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=60 %s -o - | FileCheck %s
+
+!===============================================================================
+! Interop Init — target
+!===============================================================================
+
+!CHECK-LABEL: func.func @_QPtest_interop_init(
+!CHECK-SAME: %[[ARG:.*]]: !fir.ref<i64> {fir.bindc_name = "obj"})
+!CHECK: %[[DECL:.*]]:2 = hlfir.declare %[[ARG]]
+!CHECK: omp.interop.init %[[DECL]]#0 : !fir.ref<i64> interop_types([#omp<interop_type(target)>])
+subroutine test_interop_init(obj)
+ integer(8) :: obj
+ !$omp interop init(target: obj)
+end subroutine
+
+!===============================================================================
+! Interop Init — targetsync
+!===============================================================================
+
+!CHECK-LABEL: func.func @_QPtest_interop_init_targetsync(
+!CHECK: omp.interop.init %{{.*}} : !fir.ref<i64> interop_types([#omp<interop_type(targetsync)>])
+subroutine test_interop_init_targetsync(obj)
+ integer(8) :: obj
+ !$omp interop init(targetsync: obj)
+end subroutine
+
+!===============================================================================
+! Interop Init — targetsync, target (both)
+!===============================================================================
+
+!CHECK-LABEL: func.func @_QPtest_interop_init_both(
+!CHECK: omp.interop.init %{{.*}} : !fir.ref<i64> interop_types([#omp<interop_type(targetsync)>, #omp<interop_type(target)>])
+subroutine test_interop_init_both(obj)
+ integer(8) :: obj
+ !$omp interop init(targetsync, target: obj)
+end subroutine
+
+!===============================================================================
+! Interop Init — untyped (OpenMP 6.0: interop-type modifier is optional)
+!===============================================================================
+
+!CHECK-LABEL: func.func @_QPtest_interop_init_untyped(
+!CHECK: omp.interop.init %{{.*}} : !fir.ref<i64> interop_types([])
+subroutine test_interop_init_untyped(obj)
+ integer(8) :: obj
+ !$omp interop init(obj)
+end subroutine
+
+!===============================================================================
+! Interop Init — nowait
+!===============================================================================
+
+!CHECK-LABEL: func.func @_QPtest_interop_init_nowait(
+!CHECK: omp.interop.init %{{.*}} : !fir.ref<i64> interop_types([#omp<interop_type(target)>]) nowait
+subroutine test_interop_init_nowait(obj)
+ integer(8) :: obj
+ !$omp interop init(target: obj) nowait
+end subroutine
+
+!===============================================================================
+! Interop Init — device clause
+!===============================================================================
+
+!CHECK-LABEL: func.func @_QPtest_interop_device(
+!CHECK: %[[DEV:.*]] = fir.load %{{.*}} : !fir.ref<i32>
+!CHECK: omp.interop.init %{{.*}} : !fir.ref<i64> interop_types([#omp<interop_type(target)>]) device(%[[DEV]] : i32)
+subroutine test_interop_device(obj, dev)
+ integer(8) :: obj
+ integer :: dev
+ !$omp interop device(dev) init(target: obj)
+end subroutine
+
+!===============================================================================
+! Interop Init — prefer_type with string FR identifiers
+!===============================================================================
+
+!CHECK-LABEL: func.func @_QPtest_interop_prefer_str(
+!CHECK: omp.interop.init %{{.*}} : !fir.ref<i64> interop_types([#omp<interop_type(targetsync)>]) prefer_type([1, 6])
+subroutine test_interop_prefer_str(obj)
+ integer(8) :: obj
+ !$omp interop init(prefer_type("cuda", "level_zero"), targetsync: obj)
+end subroutine
+
+!===============================================================================
+! Interop Init — prefer_type with string FR identifier (hip)
+!===============================================================================
+
+!CHECK-LABEL: func.func @_QPtest_interop_prefer_hip(
+!CHECK: omp.interop.init %{{.*}} : !fir.ref<i64> interop_types([#omp<interop_type(target)>]) prefer_type([5])
+subroutine test_interop_prefer_hip(obj)
+ integer(8) :: obj
+ !$omp interop init(prefer_type("hip"), target: obj)
+end subroutine
+
+!===============================================================================
+! Interop Init — prefer_type with integer constant FR identifiers
+!===============================================================================
+
+!CHECK-LABEL: func.func @_QPtest_interop_prefer_int(
+!CHECK: omp.interop.init %{{.*}} : !fir.ref<i64> interop_types([#omp<interop_type(targetsync)>]) prefer_type([4])
+subroutine test_interop_prefer_int(obj)
+ integer(8) :: obj
+ integer, parameter :: omp_ifr_sycl = 4
+ !$omp interop init(prefer_type(omp_ifr_sycl), targetsync: obj)
+end subroutine
+
+!===============================================================================
+! Interop Use
+!===============================================================================
+
+!CHECK-LABEL: func.func @_QPtest_interop_use(
+!CHECK: omp.interop.use %{{.*}} : !fir.ref<i64>
+subroutine test_interop_use(obj)
+ integer(8) :: obj
+ !$omp interop use(obj)
+end subroutine
+
+!===============================================================================
+! Interop Use — nowait
+!===============================================================================
+
+!CHECK-LABEL: func.func @_QPtest_interop_use_nowait(
+!CHECK: omp.interop.use %{{.*}} : !fir.ref<i64> nowait
+subroutine test_interop_use_nowait(obj)
+ integer(8) :: obj
+ !$omp interop use(obj) nowait
+end subroutine
+
+!===============================================================================
+! Interop Use — device clause
+!===============================================================================
+
+!CHECK-LABEL: func.func @_QPtest_interop_use_device(
+!CHECK: %[[DEV:.*]] = fir.load %{{.*}} : !fir.ref<i32>
+!CHECK: omp.interop.use %{{.*}} : !fir.ref<i64> device(%[[DEV]] : i32)
+subroutine test_interop_use_device(obj, dev)
+ integer(8) :: obj
+ integer :: dev
+ !$omp interop use(obj) device(dev)
+end subroutine
+
+!===============================================================================
+! Interop Destroy
+!===============================================================================
+
+!CHECK-LABEL: func.func @_QPtest_interop_destroy(
+!CHECK: omp.interop.destroy %{{.*}} : !fir.ref<i64>
+subroutine test_interop_destroy(obj)
+ integer(8) :: obj
+ !$omp interop destroy(obj)
+end subroutine
+
+!===============================================================================
+! Interop Destroy — nowait
+!===============================================================================
+
+!CHECK-LABEL: func.func @_QPtest_interop_destroy_nowait(
+!CHECK: omp.interop.destroy %{{.*}} : !fir.ref<i64> nowait
+subroutine test_interop_destroy_nowait(obj)
+ integer(8) :: obj
+ !$omp interop destroy(obj) nowait
+end subroutine
+
+!===============================================================================
+! Interop Destroy — device clause
+!===============================================================================
+
+!CHECK-LABEL: func.func @_QPtest_interop_destroy_device(
+!CHECK: %[[DEV:.*]] = fir.load %{{.*}} : !fir.ref<i32>
+!CHECK: omp.interop.destroy %{{.*}} : !fir.ref<i64> device(%[[DEV]] : i32)
+subroutine test_interop_destroy_device(obj, dev)
+ integer(8) :: obj
+ integer :: dev
+ !$omp interop destroy(obj) device(dev)
+end subroutine
diff --git a/flang/test/Semantics/OpenMP/interop-construct-v60.f90 b/flang/test/Semantics/OpenMP/interop-construct-v60.f90
new file mode 100644
index 0000000000000..78a226f8a6b1f
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/interop-construct-v60.f90
@@ -0,0 +1,24 @@
+! REQUIRES: openmp_runtime
+
+! RUN: %python %S/../test_errors.py %s %flang %openmp_flags -fopenmp-version=60
+! OpenMP Version 6.0
+! 14.1 Interop construct
+! In OpenMP 6.0 the interop-type modifier on an INIT clause is optional, so
+! INIT(obj) alone is accepted. A DEPEND clause, however, requires the
+! interop-type to include TARGETSYNC, which an untyped INIT does not provide,
+! so INIT(obj) combined with DEPEND is diagnosed.
+
+SUBROUTINE test_interop_untyped()
+ USE omp_lib
+ INTEGER(OMP_INTEROP_KIND) :: obj
+ !$OMP INTEROP INIT(obj)
+ PRINT *, 'pass'
+END SUBROUTINE test_interop_untyped
+
+SUBROUTINE test_interop_untyped_depend()
+ USE omp_lib
+ INTEGER(OMP_INTEROP_KIND) :: obj
+ !ERROR: A DEPEND clause can only appear on the directive if the interop-type includes TARGETSYNC
+ !$OMP INTEROP INIT(obj) DEPEND(INOUT: obj)
+ PRINT *, 'pass'
+END SUBROUTINE test_interop_untyped_depend
diff --git a/flang/test/Semantics/OpenMP/interop-prefer-type.f90 b/flang/test/Semantics/OpenMP/interop-prefer-type.f90
new file mode 100644
index 0000000000000..3af9ae2b3f00c
--- /dev/null
+++ b/flang/test/Semantics/OpenMP/interop-prefer-type.f90
@@ -0,0 +1,30 @@
+! REQUIRES: openmp_runtime
+
+! RUN: %python %S/../test_errors.py %s %flang %openmp_flags -fopenmp-version=60
+! OpenMP Version 6.0
+! 14.1 Interop construct
+! The foreign-runtime-identifier in a `prefer_type` modifier must be a constant
+! expression of integer OpenMP type or a base language string literal.
+
+SUBROUTINE test_prefer_type_nonconstant()
+ USE omp_lib
+ INTEGER(OMP_INTEROP_KIND) :: obj
+ INTEGER :: n
+ !ERROR: The foreign-runtime-identifier in a `prefer_type` modifier must be a constant expression of integer OpenMP type or a base language string literal
+ !$OMP INTEROP INIT(PREFER_TYPE(n), TARGET: obj)
+ PRINT *, 'pass'
+END SUBROUTINE test_prefer_type_nonconstant
+
+SUBROUTINE test_prefer_type_constant_int()
+ USE omp_lib
+ INTEGER(OMP_INTEROP_KIND) :: obj
+ !$OMP INTEROP INIT(PREFER_TYPE(omp_ifr_cuda), TARGET: obj)
+ PRINT *, 'pass'
+END SUBROUTINE test_prefer_type_constant_int
+
+SUBROUTINE test_prefer_type_string()
+ USE omp_lib
+ INTEGER(OMP_INTEROP_KIND) :: obj
+ !$OMP INTEROP INIT(PREFER_TYPE("cuda"), TARGET: obj)
+ PRINT *, 'pass'
+END SUBROUTINE test_prefer_type_string
diff --git a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
index e19ed110adf1e..fb1cebf28b95b 100644
--- a/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
+++ b/llvm/lib/Frontend/OpenMP/OMPIRBuilder.cpp
@@ -8227,6 +8227,8 @@ CallInst *OpenMPIRBuilder::createOMPInteropInit(
Value *ThreadId = getOrCreateThreadID(Ident);
if (Device == nullptr)
Device = Constant::getAllOnesValue(Int32);
+ else if (Device->getType() != Int32)
+ Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
Constant *InteropTypeVal = ConstantInt::get(Int32, (int)InteropType);
if (NumDependences == nullptr) {
NumDependences = ConstantInt::get(Int32, 0);
@@ -8255,6 +8257,8 @@ CallInst *OpenMPIRBuilder::createOMPInteropDestroy(
Value *ThreadId = getOrCreateThreadID(Ident);
if (Device == nullptr)
Device = Constant::getAllOnesValue(Int32);
+ else if (Device->getType() != Int32)
+ Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
if (NumDependences == nullptr) {
NumDependences = ConstantInt::get(Int32, 0);
PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
@@ -8283,6 +8287,8 @@ CallInst *OpenMPIRBuilder::createOMPInteropUse(const LocationDescription &Loc,
Value *ThreadId = getOrCreateThreadID(Ident);
if (Device == nullptr)
Device = Constant::getAllOnesValue(Int32);
+ else if (Device->getType() != Int32)
+ Device = Builder.CreateIntCast(Device, Int32, /*isSigned=*/true);
if (NumDependences == nullptr) {
NumDependences = ConstantInt::get(Int32, 0);
PointerType *PointerTypeVar = PointerType::getUnqual(M.getContext());
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPAttrDefs.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPAttrDefs.td
index c9e6764e7d634..f6bc8f11ccccb 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPAttrDefs.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPAttrDefs.td
@@ -79,6 +79,16 @@ def TaskDependArrayAttr
let constBuilderCall = ?;
}
+//===----------------------------------------------------------------------===//
+// InteropTypeArrayAttr
+//===----------------------------------------------------------------------===//
+
+def InteropTypeArrayAttr
+ : TypedArrayAttrBase<InteropTypeAttr,
+ InteropTypeAttr.summary # " array"> {
+ let constBuilderCall = ?;
+}
+
//===----------------------------------------------------------------------===//
// VersionAttr
//===----------------------------------------------------------------------===//
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPEnums.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPEnums.td
index deda86b26db61..e373d1a52a3fc 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPEnums.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPEnums.td
@@ -377,4 +377,22 @@ def FallbackModifierAttr : OpenMP_EnumAttr<FallbackModifier,
let assemblyFormat = "`(` $value `)`";
}
+//===----------------------------------------------------------------------===//
+// interop_type for interop construct
+//===----------------------------------------------------------------------===//
+
+def InteropTypeTarget : I32EnumAttrCase<"target", 0>;
+def InteropTypeTargetSync : I32EnumAttrCase<"targetsync", 1>;
+
+def InteropType : OpenMP_I32EnumAttr<
+ "InteropType",
+ "interop type", [
+ InteropTypeTarget,
+ InteropTypeTargetSync
+ ]>;
+
+def InteropTypeAttr : OpenMP_EnumAttr<InteropType, "interop_type"> {
+ let assemblyFormat = "`(` $value `)`";
+}
+
#endif // OPENMP_ENUMS
diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
index 98242a66e411f..0a5736f46b088 100644
--- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
+++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOps.td
@@ -2655,4 +2655,104 @@ def GroupprivateOp : OpenMP_Op<"groupprivate",
}];
}
+//===----------------------------------------------------------------------===//
+// 14.1 interop construct
+//===----------------------------------------------------------------------===//
+
+def InteropInitOp
+ : OpenMP_Op<"interop.init", traits = [AttrSizedOperandSegments],
+ clauses = [OpenMP_DependClause, OpenMP_NowaitClause]> {
+ let summary = "interop init operation";
+ let description = [{
+ The interop init operation initializes an interop object for
+ foreign runtime interoperability. The `interop_type` attribute specifies
+ whether the interop object provides access to the target execution
+ environment (`target`), a synchronization object (`targetsync`), or both.
+
+ The optional `prefer_type` attribute provides a list of preferred foreign
+ runtime types as integer identifiers.
+
+ The optional `device` operand specifies the device number.
+ }] # clausesDescription;
+
+ let arguments = !con((ins
+ OpenMP_PointerLikeType:$interop_var,
+ InteropTypeArrayAttr:$interop_types,
+ OptionalAttr<I64ArrayAttr>:$prefer_type,
+ Optional<AnyInteger>:$device),
+ clausesArgs);
+
+ let assemblyFormat = [{
+ $interop_var `:` type($interop_var)
+ `interop_types` `(` $interop_types `)`
+ (`prefer_type` `(` $prefer_type^ `)`)?
+ (`device` `(` $device^ `:` type($device) `)`)?
+ oilist(
+ `depend` `(`
+ custom<DependVarList>($depend_vars, type($depend_vars), $depend_kinds,
+ $depend_iterated, type($depend_iterated),
+ $depend_iterated_kinds) `)`
+ | `nowait` $nowait
+ )
+ attr-dict
+ }];
+}
+
+def InteropUseOp
+ : OpenMP_Op<"interop.use", traits = [AttrSizedOperandSegments],
+ clauses = [OpenMP_DependClause, OpenMP_NowaitClause]> {
+ let summary = "interop use operation";
+ let description = [{
+ The interop use operation indicates use of an interop object. This is
+ used in combination with depend clauses to synchronize tasks with
+ foreign runtime operations.
+ }] # clausesDescription;
+
+ let arguments = !con((ins
+ OpenMP_PointerLikeType:$interop_var,
+ Optional<AnyInteger>:$device),
+ clausesArgs);
+
+ let assemblyFormat = [{
+ $interop_var `:` type($interop_var)
+ (`device` `(` $device^ `:` type($device) `)`)?
+ oilist(
+ `depend` `(`
+ custom<DependVarList>($depend_vars, type($depend_vars), $depend_kinds,
+ $depend_iterated, type($depend_iterated),
+ $depend_iterated_kinds) `)`
+ | `nowait` $nowait
+ )
+ attr-dict
+ }];
+}
+
+def InteropDestroyOp
+ : OpenMP_Op<"interop.destroy", traits = [AttrSizedOperandSegments],
+ clauses = [OpenMP_DependClause, OpenMP_NowaitClause]> {
+ let summary = "interop destroy operation";
+ let description = [{
+ The interop destroy operation releases an interop object and frees
+ any resources associated with it.
+ }] # clausesDescription;
+
+ let arguments = !con((ins
+ OpenMP_PointerLikeType:$interop_var,
+ Optional<AnyInteger>:$device),
+ clausesArgs);
+
+ let assemblyFormat = [{
+ $interop_var `:` type($interop_var)
+ (`device` `(` $device^ `:` type($device) `)`)?
+ oilist(
+ `depend` `(`
+ custom<DependVarList>($depend_vars, type($depend_vars), $depend_kinds,
+ $depend_iterated, type($depend_iterated),
+ $depend_iterated_kinds) `)`
+ | `nowait` $nowait
+ )
+ attr-dict
+ }];
+}
+
#endif // OPENMP_OPS
diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
index f2fefa1f5a53f..85963c402bc87 100644
--- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
+++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp
@@ -4364,6 +4364,104 @@ convertOmpTaskgroupOp(omp::TaskgroupOp tgOp, llvm::IRBuilderBase &builder,
return success();
}
+static LogicalResult
+convertOmpInteropInitOp(omp::InteropInitOp initOp, llvm::IRBuilderBase &builder,
+ LLVM::ModuleTranslation &moduleTranslation) {
+ if (!initOp.getDependVars().empty() || initOp.getDependKinds() ||
+ !initOp.getDependIterated().empty() || initOp.getDependIteratedKinds())
+ return initOp.emitError()
+ << "not yet implemented: Unhandled clause depend in "
+ << omp::InteropInitOp::getOperationName() << " operation";
+
+ llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();
+ llvm::Value *interopVar =
+ moduleTranslation.lookupValue(initOp.getInteropVar());
+ llvm::Value *device = initOp.getDevice()
+ ? moduleTranslation.lookupValue(initOp.getDevice())
+ : nullptr;
+
+ // TODO: Handle depend clauses when supported.
+ llvm::Value *numDeps = llvm::ConstantInt::get(builder.getInt32Ty(), 0);
+ llvm::Value *depArray = llvm::ConstantPointerNull::get(builder.getPtrTy());
+ bool hasNowait = initOp.getNowait();
+
+ // A single `init` clause may list both `target` and `targetsync`, but the
+ // runtime init call takes a single interop-type. Collapse the set to one
+ // value, matching Clang: if `target` is present use Target, otherwise
+ // TargetSync. The offload runtime object model supports only one type per
+ // object; representing both would require a runtime change.
+ bool hasTarget = false, hasTargetSync = false;
+ for (mlir::Attribute typeAttr : initOp.getInteropTypes()) {
+ switch (cast<omp::InteropTypeAttr>(typeAttr).getValue()) {
+ case omp::InteropType::target:
+ hasTarget = true;
+ break;
+ case omp::InteropType::targetsync:
+ hasTargetSync = true;
+ break;
+ }
+ }
+ llvm::omp::OMPInteropType interopType =
+ (!hasTarget && hasTargetSync) ? llvm::omp::OMPInteropType::TargetSync
+ : llvm::omp::OMPInteropType::Target;
+ ompBuilder->createOMPInteropInit(builder, interopVar, interopType, device,
+ numDeps, depArray, hasNowait);
+ return success();
+}
+
+static LogicalResult
+convertOmpInteropDestroyOp(omp::InteropDestroyOp destroyOp,
+ llvm::IRBuilderBase &builder,
+ LLVM::ModuleTranslation &moduleTranslation) {
+ if (!destroyOp.getDependVars().empty() || destroyOp.getDependKinds() ||
+ !destroyOp.getDependIterated().empty() ||
+ destroyOp.getDependIteratedKinds())
+ return destroyOp.emitError()
+ << "not yet implemented: Unhandled clause depend in "
+ << omp::InteropDestroyOp::getOperationName() << " operation";
+
+ llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();
+ llvm::Value *interopVar =
+ moduleTranslation.lookupValue(destroyOp.getInteropVar());
+ llvm::Value *device =
+ destroyOp.getDevice()
+ ? moduleTranslation.lookupValue(destroyOp.getDevice())
+ : nullptr;
+
+ llvm::Value *numDeps = llvm::ConstantInt::get(builder.getInt32Ty(), 0);
+ llvm::Value *depArray = llvm::ConstantPointerNull::get(builder.getPtrTy());
+ bool hasNowait = destroyOp.getNowait();
+
+ ompBuilder->createOMPInteropDestroy(builder, interopVar, device, numDeps,
+ depArray, hasNowait);
+ return success();
+}
+
+static LogicalResult
+convertOmpInteropUseOp(omp::InteropUseOp useOp, llvm::IRBuilderBase &builder,
+ LLVM::ModuleTranslation &moduleTranslation) {
+ if (!useOp.getDependVars().empty() || useOp.getDependKinds() ||
+ !useOp.getDependIterated().empty() || useOp.getDependIteratedKinds())
+ return useOp.emitError()
+ << "not yet implemented: Unhandled clause depend in "
+ << omp::InteropUseOp::getOperationName() << " operation";
+
+ llvm::OpenMPIRBuilder *ompBuilder = moduleTranslation.getOpenMPBuilder();
+ llvm::Value *interopVar =
+ moduleTranslation.lookupValue(useOp.getInteropVar());
+ llvm::Value *device = useOp.getDevice()
+ ? moduleTranslation.lookupValue(useOp.getDevice())
+ : nullptr;
+
+ llvm::Value *numDeps = llvm::ConstantInt::get(builder.getInt32Ty(), 0);
+ llvm::Value *depArray = llvm::ConstantPointerNull::get(builder.getPtrTy());
+ bool hasNowait = useOp.getNowait();
+
+ ompBuilder->createOMPInteropUse(builder, interopVar, device, numDeps,
+ depArray, hasNowait);
+ return success();
+}
+
static LogicalResult
convertOmpTaskwaitOp(omp::TaskwaitOp twOp, llvm::IRBuilderBase &builder,
LLVM::ModuleTranslation &moduleTranslation) {
@@ -9802,6 +9900,15 @@ LogicalResult OpenMPDialectLLVMIRTranslationInterface::convertOperation(
.Case([&](omp::TaskwaitOp op) {
return convertOmpTaskwaitOp(op, builder, moduleTranslation);
})
+ .Case([&](omp::InteropInitOp op) {
+ return convertOmpInteropInitOp(op, builder, moduleTranslation);
+ })
+ .Case([&](omp::InteropDestroyOp op) {
+ return convertOmpInteropDestroyOp(op, builder, moduleTranslation);
+ })
+ .Case([&](omp::InteropUseOp op) {
+ return convertOmpInteropUseOp(op, builder, moduleTranslation);
+ })
.Case<omp::YieldOp, omp::TerminatorOp, omp::DeclareMapperOp,
omp::DeclareMapperInfoOp, omp::DeclareReductionOp,
omp::CriticalDeclareOp>([](auto op) {
diff --git a/mlir/test/Dialect/OpenMP/ops.mlir b/mlir/test/Dialect/OpenMP/ops.mlir
index 9a1874379d2fb..d08ca5664efe4 100644
--- a/mlir/test/Dialect/OpenMP/ops.mlir
+++ b/mlir/test/Dialect/OpenMP/ops.mlir
@@ -4234,3 +4234,57 @@ func.func @omp_target_map_iterated(%lb : index, %ub : index, %step : index,
}
return
}
+
+// -----
+
+// CHECK-LABEL: func.func @omp_interop_init
+func.func @omp_interop_init(%obj : !llvm.ptr, %device : i32) -> () {
+ // CHECK: omp.interop.init %{{.*}} : !llvm.ptr interop_types([#omp<interop_type(target)>])
+ omp.interop.init %obj : !llvm.ptr interop_types([#omp<interop_type(target)>])
+
+ // CHECK: omp.interop.init %{{.*}} : !llvm.ptr interop_types([#omp<interop_type(targetsync)>])
+ omp.interop.init %obj : !llvm.ptr interop_types([#omp<interop_type(targetsync)>])
+
+ // CHECK: omp.interop.init %{{.*}} : !llvm.ptr interop_types([#omp<interop_type(targetsync)>, #omp<interop_type(target)>]) prefer_type([1, 6]) device(%{{.*}} : i32) nowait
+ omp.interop.init %obj : !llvm.ptr interop_types([#omp<interop_type(targetsync)>, #omp<interop_type(target)>]) prefer_type([1, 6]) device(%device : i32) nowait
+ return
+}
+
+// -----
+
+// CHECK-LABEL: func.func @omp_interop_use
+func.func @omp_interop_use(%obj : !llvm.ptr, %device : i32) -> () {
+ // CHECK: omp.interop.use %{{.*}} : !llvm.ptr
+ omp.interop.use %obj : !llvm.ptr
+
+ // CHECK: omp.interop.use %{{.*}} : !llvm.ptr device(%{{.*}} : i32) nowait
+ omp.interop.use %obj : !llvm.ptr device(%device : i32) nowait
+ return
+}
+
+// -----
+
+// CHECK-LABEL: func.func @omp_interop_destroy
+func.func @omp_interop_destroy(%obj : !llvm.ptr, %device : i32) -> () {
+ // CHECK: omp.interop.destroy %{{.*}} : !llvm.ptr
+ omp.interop.destroy %obj : !llvm.ptr
+
+ // CHECK: omp.interop.destroy %{{.*}} : !llvm.ptr device(%{{.*}} : i32) nowait
+ omp.interop.destroy %obj : !llvm.ptr device(%device : i32) nowait
+ return
+}
+
+// -----
+
+// CHECK-LABEL: func.func @omp_interop_depend
+func.func @omp_interop_depend(%obj : !llvm.ptr, %dep : !llvm.ptr) -> () {
+ // CHECK: omp.interop.init %{{.*}} : !llvm.ptr interop_types([#omp<interop_type(targetsync)>]) depend(taskdependinout -> %{{.*}} : !llvm.ptr)
+ omp.interop.init %obj : !llvm.ptr interop_types([#omp<interop_type(targetsync)>]) depend(taskdependinout -> %dep : !llvm.ptr)
+
+ // CHECK: omp.interop.use %{{.*}} : !llvm.ptr depend(taskdependin -> %{{.*}} : !llvm.ptr)
+ omp.interop.use %obj : !llvm.ptr depend(taskdependin -> %dep : !llvm.ptr)
+
+ // CHECK: omp.interop.destroy %{{.*}} : !llvm.ptr depend(taskdependout -> %{{.*}} : !llvm.ptr)
+ omp.interop.destroy %obj : !llvm.ptr depend(taskdependout -> %dep : !llvm.ptr)
+ return
+}
diff --git a/mlir/test/Target/LLVMIR/openmp-interop-depend.mlir b/mlir/test/Target/LLVMIR/openmp-interop-depend.mlir
new file mode 100644
index 0000000000000..d362714add856
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/openmp-interop-depend.mlir
@@ -0,0 +1,28 @@
+// RUN: rm -rf %t && split-file %s %t
+// RUN: not mlir-translate --mlir-to-llvmir %t/init.mlir 2>&1 | FileCheck %s --check-prefix=INIT
+// RUN: not mlir-translate --mlir-to-llvmir %t/use.mlir 2>&1 | FileCheck %s --check-prefix=USE
+// RUN: not mlir-translate --mlir-to-llvmir %t/destroy.mlir 2>&1 | FileCheck %s --check-prefix=DESTROY
+
+// The depend clause on interop operations is not yet handled by the
+// translation to LLVM IR; each action op must emit a clean diagnostic.
+
+//--- init.mlir
+// INIT: error: not yet implemented: Unhandled clause depend in omp.interop.init operation
+llvm.func @test_interop_init_depend(%interop: !llvm.ptr, %dep: !llvm.ptr) {
+ omp.interop.init %interop : !llvm.ptr interop_types([#omp<interop_type(targetsync)>]) depend(taskdependinout -> %dep : !llvm.ptr)
+ llvm.return
+}
+
+//--- use.mlir
+// USE: error: not yet implemented: Unhandled clause depend in omp.interop.use operation
+llvm.func @test_interop_use_depend(%interop: !llvm.ptr, %dep: !llvm.ptr) {
+ omp.interop.use %interop : !llvm.ptr depend(taskdependinout -> %dep : !llvm.ptr)
+ llvm.return
+}
+
+//--- destroy.mlir
+// DESTROY: error: not yet implemented: Unhandled clause depend in omp.interop.destroy operation
+llvm.func @test_interop_destroy_depend(%interop: !llvm.ptr, %dep: !llvm.ptr) {
+ omp.interop.destroy %interop : !llvm.ptr depend(taskdependinout -> %dep : !llvm.ptr)
+ llvm.return
+}
diff --git a/mlir/test/Target/LLVMIR/openmp-interop.mlir b/mlir/test/Target/LLVMIR/openmp-interop.mlir
new file mode 100644
index 0000000000000..85cadd241e23d
--- /dev/null
+++ b/mlir/test/Target/LLVMIR/openmp-interop.mlir
@@ -0,0 +1,95 @@
+// RUN: mlir-translate --mlir-to-llvmir %s | FileCheck %s
+
+// CHECK-LABEL: define void @test_interop_init(
+// CHECK-SAME: ptr %[[OBJ:.*]])
+// CHECK: %[[GTID:.*]] = call i32 @__kmpc_global_thread_num(ptr @{{.*}})
+// CHECK: call void @__tgt_interop_init(ptr @{{.*}}, i32 %[[GTID]], ptr %[[OBJ]], i32 1, i32 -1, i32 0, ptr null, i32 0)
+// CHECK: ret void
+llvm.func @test_interop_init(%arg0: !llvm.ptr) {
+ %0 = llvm.mlir.constant(1 : i64) : i64
+ omp.interop.init %arg0 : !llvm.ptr interop_types([#omp<interop_type(target)>])
+ llvm.return
+}
+
+// CHECK-LABEL: define void @test_interop_init_targetsync(
+// CHECK: call void @__tgt_interop_init(ptr @{{.*}}, i32 %{{.*}}, ptr %{{.*}}, i32 2, i32 -1, i32 0, ptr null, i32 0)
+llvm.func @test_interop_init_targetsync(%arg0: !llvm.ptr) {
+ omp.interop.init %arg0 : !llvm.ptr interop_types([#omp<interop_type(targetsync)>])
+ llvm.return
+}
+
+// CHECK-LABEL: define void @test_interop_init_both(
+// CHECK: call void @__tgt_interop_init(ptr @{{.*}}, i32 %{{.*}}, ptr %{{.*}}, i32 1, i32 -1, i32 0, ptr null, i32 0)
+// CHECK-NOT: call void @__tgt_interop_init
+llvm.func @test_interop_init_both(%arg0: !llvm.ptr) {
+ omp.interop.init %arg0 : !llvm.ptr interop_types([#omp<interop_type(targetsync)>, #omp<interop_type(target)>])
+ llvm.return
+}
+
+// CHECK-LABEL: define void @test_interop_use(
+// CHECK: %[[GTID:.*]] = call i32 @__kmpc_global_thread_num(ptr @{{.*}})
+// CHECK: call void @__tgt_interop_use(ptr @{{.*}}, i32 %[[GTID]], ptr %{{.*}}, i32 -1, i32 0, ptr null, i32 0)
+// CHECK: ret void
+llvm.func @test_interop_use(%arg0: !llvm.ptr) {
+ omp.interop.use %arg0 : !llvm.ptr
+ llvm.return
+}
+
+// CHECK-LABEL: define void @test_interop_destroy(
+// CHECK: %[[GTID:.*]] = call i32 @__kmpc_global_thread_num(ptr @{{.*}})
+// CHECK: call void @__tgt_interop_destroy(ptr @{{.*}}, i32 %[[GTID]], ptr %{{.*}}, i32 -1, i32 0, ptr null, i32 0)
+// CHECK: ret void
+llvm.func @test_interop_destroy(%arg0: !llvm.ptr) {
+ omp.interop.destroy %arg0 : !llvm.ptr
+ llvm.return
+}
+
+// CHECK-LABEL: define void @test_interop_init_nowait(
+// CHECK: call void @__tgt_interop_init(ptr @{{.*}}, i32 %{{.*}}, ptr %{{.*}}, i32 1, i32 -1, i32 0, ptr null, i32 1)
+llvm.func @test_interop_init_nowait(%arg0: !llvm.ptr) {
+ omp.interop.init %arg0 : !llvm.ptr interop_types([#omp<interop_type(target)>]) nowait
+ llvm.return
+}
+
+// CHECK-LABEL: define void @test_interop_destroy_nowait(
+// CHECK: call void @__tgt_interop_destroy(ptr @{{.*}}, i32 %{{.*}}, ptr %{{.*}}, i32 -1, i32 0, ptr null, i32 1)
+llvm.func @test_interop_destroy_nowait(%arg0: !llvm.ptr) {
+ omp.interop.destroy %arg0 : !llvm.ptr nowait
+ llvm.return
+}
+
+// CHECK-LABEL: define void @test_interop_init_device(
+// CHECK: call void @__tgt_interop_init(ptr @{{.*}}, i32 %{{.*}}, ptr %{{.*}}, i32 1, i32 %{{.*}}, i32 0, ptr null, i32 0)
+llvm.func @test_interop_init_device(%arg0: !llvm.ptr, %arg1: i32) {
+ omp.interop.init %arg0 : !llvm.ptr interop_types([#omp<interop_type(target)>]) device(%arg1 : i32)
+ llvm.return
+}
+
+// CHECK-LABEL: define void @test_interop_init_device_i64(
+// CHECK: %[[DEVICE:.*]] = trunc i64 %{{.*}} to i32
+// CHECK: call void @__tgt_interop_init(ptr @{{.*}}, i32 %{{.*}}, ptr %{{.*}}, i32 1, i32 %[[DEVICE]], i32 0, ptr null, i32 0)
+llvm.func @test_interop_init_device_i64(%arg0: !llvm.ptr, %arg1: i64) {
+ omp.interop.init %arg0 : !llvm.ptr interop_types([#omp<interop_type(target)>]) device(%arg1 : i64)
+ llvm.return
+}
+
+// CHECK-LABEL: define void @test_interop_destroy_device_i64(
+// CHECK: %[[DEVICE:.*]] = trunc i64 %{{.*}} to i32
+// CHECK: call void @__tgt_interop_destroy(ptr @{{.*}}, i32 %{{.*}}, ptr %{{.*}}, i32 %[[DEVICE]], i32 0, ptr null, i32 0)
+llvm.func @test_interop_destroy_device_i64(%arg0: !llvm.ptr, %arg1: i64) {
+ omp.interop.destroy %arg0 : !llvm.ptr device(%arg1 : i64)
+ llvm.return
+}
+
+// CHECK-LABEL: define void @test_interop_use_device_i64(
+// CHECK: %[[DEVICE:.*]] = trunc i64 %{{.*}} to i32
+// CHECK: call void @__tgt_interop_use(ptr @{{.*}}, i32 %{{.*}}, ptr %{{.*}}, i32 %[[DEVICE]], i32 0, ptr null, i32 0)
+llvm.func @test_interop_use_device_i64(%arg0: !llvm.ptr, %arg1: i64) {
+ omp.interop.use %arg0 : !llvm.ptr device(%arg1 : i64)
+ llvm.return
+}
+
+// CHECK: declare i32 @__kmpc_global_thread_num(ptr)
+// CHECK: declare void @__tgt_interop_init(ptr, i32, ptr, i32, i32, i32, ptr, i32)
+// CHECK: declare void @__tgt_interop_use(ptr, i32, ptr, i32, i32, ptr, i32)
+// CHECK: declare void @__tgt_interop_destroy(ptr, i32, ptr, i32, i32, ptr, i32)
diff --git a/openmp/module/omp_lib.F90.var b/openmp/module/omp_lib.F90.var
index 2db2a33e90127..a41b2faad9d3d 100644
--- a/openmp/module/omp_lib.F90.var
+++ b/openmp/module/omp_lib.F90.var
@@ -40,6 +40,8 @@
integer, parameter, public :: omp_alloctrait_val_kind = c_intptr_t
integer, parameter, public :: omp_interop_kind = c_intptr_t
integer, parameter, public :: omp_interop_fr_kind = omp_integer_kind
+ integer, parameter, public :: omp_interop_property_kind = omp_integer_kind
+ integer, parameter, public :: omp_interop_rc_kind = omp_integer_kind
type omp_alloctrait
integer(kind=omp_alloctrait_key_kind) key
@@ -84,6 +86,8 @@
public :: omp_alloctrait_val_kind
public :: omp_interop_kind
public :: omp_interop_fr_kind
+ public :: omp_interop_property_kind
+ public :: omp_interop_rc_kind
public :: omp_alloctrait
public :: omp_pause_resource_kind
public :: omp_depend_kind
@@ -218,6 +222,28 @@
integer (kind=omp_interop_kind), parameter, public :: omp_interop_none = 0
+! interop property constants
+ integer (kind=omp_interop_property_kind), parameter, public :: omp_ipr_fr_id = -1
+ integer (kind=omp_interop_property_kind), parameter, public :: omp_ipr_fr_name = -2
+ integer (kind=omp_interop_property_kind), parameter, public :: omp_ipr_vendor = -3
+ integer (kind=omp_interop_property_kind), parameter, public :: omp_ipr_vendor_name = -4
+ integer (kind=omp_interop_property_kind), parameter, public :: omp_ipr_device_num = -5
+ integer (kind=omp_interop_property_kind), parameter, public :: omp_ipr_platform = -6
+ integer (kind=omp_interop_property_kind), parameter, public :: omp_ipr_device = -7
+ integer (kind=omp_interop_property_kind), parameter, public :: omp_ipr_device_context = -8
+ integer (kind=omp_interop_property_kind), parameter, public :: omp_ipr_targetsync = -9
+ integer (kind=omp_interop_property_kind), parameter, public :: omp_ipr_first = -9
+
+! interop return code constants
+ integer (kind=omp_interop_rc_kind), parameter, public :: omp_irc_no_value = 1
+ integer (kind=omp_interop_rc_kind), parameter, public :: omp_irc_success = 0
+ integer (kind=omp_interop_rc_kind), parameter, public :: omp_irc_empty = -1
+ integer (kind=omp_interop_rc_kind), parameter, public :: omp_irc_out_of_range = -2
+ integer (kind=omp_interop_rc_kind), parameter, public :: omp_irc_type_int = -3
+ integer (kind=omp_interop_rc_kind), parameter, public :: omp_irc_type_ptr = -4
+ integer (kind=omp_interop_rc_kind), parameter, public :: omp_irc_type_str = -5
+ integer (kind=omp_interop_rc_kind), parameter, public :: omp_irc_other = -6
+
interface
! ***
@@ -1061,6 +1087,67 @@
logical (kind=omp_logical_kind) kmp_get_cancellation_status
end function kmp_get_cancellation_status
+! ***
+! *** interop query routines
+! ***
+
+ function omp_get_num_interop_properties(interop) bind(c)
+ use omp_lib_kinds
+ integer (kind=omp_interop_kind), value :: interop
+ integer (kind=omp_integer_kind) omp_get_num_interop_properties
+ end function omp_get_num_interop_properties
+
+ function omp_get_interop_int(interop, property_id, err) bind(c)
+ use omp_lib_kinds
+ use, intrinsic :: iso_c_binding, only: c_intptr_t, c_int
+ integer (kind=omp_interop_kind), value :: interop
+ integer (kind=omp_interop_property_kind), value :: property_id
+ integer (kind=c_int) :: err
+ integer (kind=c_intptr_t) omp_get_interop_int
+ end function omp_get_interop_int
+
+ function omp_get_interop_ptr(interop, property_id, err) bind(c)
+ use omp_lib_kinds
+ use, intrinsic :: iso_c_binding, only: c_ptr, c_int
+ integer (kind=omp_interop_kind), value :: interop
+ integer (kind=omp_interop_property_kind), value :: property_id
+ integer (kind=c_int) :: err
+ type(c_ptr) omp_get_interop_ptr
+ end function omp_get_interop_ptr
+
+ function omp_get_interop_str(interop, property_id, err) bind(c)
+ use omp_lib_kinds
+ use, intrinsic :: iso_c_binding, only: c_ptr, c_int
+ integer (kind=omp_interop_kind), value :: interop
+ integer (kind=omp_interop_property_kind), value :: property_id
+ integer (kind=c_int) :: err
+ type(c_ptr) omp_get_interop_str
+ end function omp_get_interop_str
+
+ function omp_get_interop_name(interop, property_id) bind(c)
+ use omp_lib_kinds
+ use, intrinsic :: iso_c_binding, only: c_ptr
+ integer (kind=omp_interop_kind), value :: interop
+ integer (kind=omp_interop_property_kind), value :: property_id
+ type(c_ptr) omp_get_interop_name
+ end function omp_get_interop_name
+
+ function omp_get_interop_type_desc(interop, property_id) bind(c)
+ use omp_lib_kinds
+ use, intrinsic :: iso_c_binding, only: c_ptr
+ integer (kind=omp_interop_kind), value :: interop
+ integer (kind=omp_interop_property_kind), value :: property_id
+ type(c_ptr) omp_get_interop_type_desc
+ end function omp_get_interop_type_desc
+
+ function omp_get_interop_rc_desc(interop, ret_code) bind(c)
+ use omp_lib_kinds
+ use, intrinsic :: iso_c_binding, only: c_ptr
+ integer (kind=omp_interop_kind), value :: interop
+ integer (kind=omp_interop_rc_kind), value :: ret_code
+ type(c_ptr) omp_get_interop_rc_desc
+ end function omp_get_interop_rc_desc
+
end interface
! make the above routine definitions public
@@ -1182,5 +1269,12 @@
public :: kmp_set_warnings_on
public :: kmp_set_warnings_off
public :: kmp_get_cancellation_status
+ public :: omp_get_num_interop_properties
+ public :: omp_get_interop_int
+ public :: omp_get_interop_ptr
+ public :: omp_get_interop_str
+ public :: omp_get_interop_name
+ public :: omp_get_interop_type_desc
+ public :: omp_get_interop_rc_desc
end module omp_lib
diff --git a/openmp/module/omp_lib.h.var b/openmp/module/omp_lib.h.var
index e515c9434f120..5e1996e31fe0a 100644
--- a/openmp/module/omp_lib.h.var
+++ b/openmp/module/omp_lib.h.var
@@ -56,6 +56,10 @@
parameter(omp_interop_kind=int_ptr_kind())
integer omp_interop_fr_kind
parameter(omp_interop_fr_kind=omp_integer_kind)
+ integer omp_interop_property_kind
+ parameter(omp_interop_property_kind=omp_integer_kind)
+ integer omp_interop_rc_kind
+ parameter(omp_interop_rc_kind=omp_integer_kind)
integer(kind=omp_integer_kind)openmp_version
parameter(openmp_version=@LIBOMP_OMP_YEAR_MONTH@)
@@ -296,6 +300,46 @@
integer(kind=omp_interop_kind)omp_interop_none
parameter(omp_interop_none=0)
+! interop property constants
+ integer(kind=omp_interop_property_kind)omp_ipr_fr_id
+ parameter(omp_ipr_fr_id=-1)
+ integer(kind=omp_interop_property_kind)omp_ipr_fr_name
+ parameter(omp_ipr_fr_name=-2)
+ integer(kind=omp_interop_property_kind)omp_ipr_vendor
+ parameter(omp_ipr_vendor=-3)
+ integer(kind=omp_interop_property_kind)omp_ipr_vendor_name
+ parameter(omp_ipr_vendor_name=-4)
+ integer(kind=omp_interop_property_kind)omp_ipr_device_num
+ parameter(omp_ipr_device_num=-5)
+ integer(kind=omp_interop_property_kind)omp_ipr_platform
+ parameter(omp_ipr_platform=-6)
+ integer(kind=omp_interop_property_kind)omp_ipr_device
+ parameter(omp_ipr_device=-7)
+ integer(kind=omp_interop_property_kind)omp_ipr_device_context
+ parameter(omp_ipr_device_context=-8)
+ integer(kind=omp_interop_property_kind)omp_ipr_targetsync
+ parameter(omp_ipr_targetsync=-9)
+ integer(kind=omp_interop_property_kind)omp_ipr_first
+ parameter(omp_ipr_first=-9)
+
+! interop return code constants
+ integer(kind=omp_interop_rc_kind)omp_irc_no_value
+ parameter(omp_irc_no_value=1)
+ integer(kind=omp_interop_rc_kind)omp_irc_success
+ parameter(omp_irc_success=0)
+ integer(kind=omp_interop_rc_kind)omp_irc_empty
+ parameter(omp_irc_empty=-1)
+ integer(kind=omp_interop_rc_kind)omp_irc_out_of_range
+ parameter(omp_irc_out_of_range=-2)
+ integer(kind=omp_interop_rc_kind)omp_irc_type_int
+ parameter(omp_irc_type_int=-3)
+ integer(kind=omp_interop_rc_kind)omp_irc_type_ptr
+ parameter(omp_irc_type_ptr=-4)
+ integer(kind=omp_interop_rc_kind)omp_irc_type_str
+ parameter(omp_irc_type_str=-5)
+ integer(kind=omp_interop_rc_kind)omp_irc_other
+ parameter(omp_irc_other=-6)
+
interface
! ***
@@ -1129,6 +1173,67 @@
subroutine kmp_set_warnings_off() bind(c)
end subroutine kmp_set_warnings_off
+
+! ***
+! *** interop query routines
+! ***
+
+ function omp_get_num_interop_properties(interop) bind(c)
+ import
+ integer (kind=omp_interop_kind), value :: interop
+ integer (kind=omp_integer_kind) omp_get_num_interop_properties
+ end function omp_get_num_interop_properties
+
+ function omp_get_interop_int(interop, property_id, err) bind(c)
+ use, intrinsic :: iso_c_binding, only: c_intptr_t, c_int
+ import
+ integer (kind=omp_interop_kind), value :: interop
+ integer (kind=omp_interop_property_kind), value :: property_id
+ integer (kind=c_int) :: err
+ integer (kind=c_intptr_t) omp_get_interop_int
+ end function omp_get_interop_int
+
+ function omp_get_interop_ptr(interop, property_id, err) bind(c)
+ use, intrinsic :: iso_c_binding, only: c_ptr, c_int
+ import
+ integer (kind=omp_interop_kind), value :: interop
+ integer (kind=omp_interop_property_kind), value :: property_id
+ integer (kind=c_int) :: err
+ type(c_ptr) omp_get_interop_ptr
+ end function omp_get_interop_ptr
+
+ function omp_get_interop_str(interop, property_id, err) bind(c)
+ use, intrinsic :: iso_c_binding, only: c_ptr, c_int
+ import
+ integer (kind=omp_interop_kind), value :: interop
+ integer (kind=omp_interop_property_kind), value :: property_id
+ integer (kind=c_int) :: err
+ type(c_ptr) omp_get_interop_str
+ end function omp_get_interop_str
+
+ function omp_get_interop_name(interop, property_id) bind(c)
+ use, intrinsic :: iso_c_binding, only: c_ptr
+ import
+ integer (kind=omp_interop_kind), value :: interop
+ integer (kind=omp_interop_property_kind), value :: property_id
+ type(c_ptr) omp_get_interop_name
+ end function omp_get_interop_name
+
+ function omp_get_interop_type_desc(interop, property_id) bind(c)
+ use, intrinsic :: iso_c_binding, only: c_ptr
+ import
+ integer (kind=omp_interop_kind), value :: interop
+ integer (kind=omp_interop_property_kind), value :: property_id
+ type(c_ptr) omp_get_interop_type_desc
+ end function omp_get_interop_type_desc
+
+ function omp_get_interop_rc_desc(interop, ret_code) bind(c)
+ use, intrinsic :: iso_c_binding, only: c_ptr
+ import
+ integer (kind=omp_interop_kind), value :: interop
+ integer (kind=omp_interop_rc_kind), value :: ret_code
+ type(c_ptr) omp_get_interop_rc_desc
+ end function omp_get_interop_rc_desc
end interface
#ifdef __INTEL_COMPILER
More information about the flang-commits
mailing list