https://github.com/blazie2004 updated https://github.com/llvm/llvm-project/pull/208387
>From 6158565249089bc039d9290d8403f0f6872f4197 Mon Sep 17 00:00:00 2001 From: Jay Satish Kumar Patel <[email protected]> Date: Wed, 8 Jul 2026 04:53:19 -0500 Subject: [PATCH 1/4] [Flang][OpenMP] Lower DECLARE TARGET INDIRECT clause --- flang/include/flang/Lower/OpenMP.h | 1 + flang/lib/Lower/OpenMP/ClauseProcessor.cpp | 21 +++++ flang/lib/Lower/OpenMP/ClauseProcessor.h | 1 + flang/lib/Lower/OpenMP/OpenMP.cpp | 40 ++++++--- flang/lib/Lower/OpenMP/Utils.h | 1 + flang/lib/Semantics/check-omp-structure.cpp | 15 ++++ .../Lower/OpenMP/Todo/omp-clause-indirect.f90 | 34 -------- .../OpenMP/declare-target-indirect-clause.f90 | 30 +++++++ .../OpenMP/declare-target-indirect-merge.f90 | 47 +++++++++++ .../Lower/OpenMP/declare-target-indirect.f90 | 39 +++++++++ flang/test/Semantics/indirect02.f90 | 7 +- flang/test/Semantics/indirect03.f90 | 31 +++++++ .../mlir/Dialect/OpenMP/OpenMPAttrDefs.td | 3 +- .../Dialect/OpenMP/OpenMPClauseOperands.h | 10 ++- .../Dialect/OpenMP/OpenMPOpsInterfaces.td | 18 +++- .../OpenMP/Transforms/MarkDeclareTarget.cpp | 3 +- .../OpenMP/OpenMPToLLVMIRTranslation.cpp | 83 ++++++++++++++++++- .../OpenMP/mark-declare-target-indirect.mlir | 39 +++++++++ ...target-declare-target-indirect-device.mlir | 19 +++++ ...mptarget-declare-target-indirect-host.mlir | 20 +++++ 20 files changed, 405 insertions(+), 57 deletions(-) delete mode 100644 flang/test/Lower/OpenMP/Todo/omp-clause-indirect.f90 create mode 100644 flang/test/Lower/OpenMP/declare-target-indirect-clause.f90 create mode 100644 flang/test/Lower/OpenMP/declare-target-indirect-merge.f90 create mode 100644 flang/test/Lower/OpenMP/declare-target-indirect.f90 create mode 100644 flang/test/Semantics/indirect03.f90 create mode 100644 mlir/test/Dialect/OpenMP/mark-declare-target-indirect.mlir create mode 100644 mlir/test/Target/LLVMIR/omptarget-declare-target-indirect-device.mlir create mode 100644 mlir/test/Target/LLVMIR/omptarget-declare-target-indirect-host.mlir diff --git a/flang/include/flang/Lower/OpenMP.h b/flang/include/flang/Lower/OpenMP.h index a3f35498b9180..975bbf6a15bb4 100644 --- a/flang/include/flang/Lower/OpenMP.h +++ b/flang/include/flang/Lower/OpenMP.h @@ -59,6 +59,7 @@ struct OMPDeferredDeclareTargetInfo { mlir::omp::DeclareTargetCaptureClause declareTargetCaptureClause; mlir::omp::DeclareTargetDeviceType declareTargetDeviceType; bool automap = false; + bool indirect = false; const Fortran::semantics::Symbol &sym; }; diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp index b2f511c2c9cd1..fd1af2f812ea5 100644 --- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp +++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp @@ -13,6 +13,8 @@ #include "ClauseProcessor.h" #include "Utils.h" +#include "flang/Evaluate/fold.h" +#include "flang/Evaluate/tools.h" #include "flang/Lower/ConvertCall.h" #include "flang/Lower/ConvertExprToHLFIR.h" #include "flang/Lower/OpenMP/Clauses.h" @@ -478,6 +480,25 @@ bool ClauseProcessor::processDeviceType( return false; } +bool ClauseProcessor::processIndirect( + mlir::omp::IndirectClauseOps &result) const { + if (auto *clause = findUniqueClause<omp::clause::Indirect>()) { + // Case: declare target ... indirect[(scalar-logical-constant)] + // An `indirect` clause with no argument defaults to `.true.`. + bool isIndirect = true; + if (clause->v) { + auto foldedExpr = Fortran::evaluate::Fold( + semaCtx.foldingContext(), Fortran::common::Clone(*clause->v)); + if (auto logicalVal = Fortran::evaluate::GetScalarConstantValue< + Fortran::evaluate::LogicalResult>(foldedExpr)) + isIndirect = logicalVal->IsTrue(); + } + result.indirect = isIndirect; + return true; + } + return false; +} + bool ClauseProcessor::processDistSchedule( lower::StatementContext &stmtCtx, mlir::omp::DistScheduleClauseOps &result) const { diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.h b/flang/lib/Lower/OpenMP/ClauseProcessor.h index 10d52bcdf89de..05622e5e3ede3 100644 --- a/flang/lib/Lower/OpenMP/ClauseProcessor.h +++ b/flang/lib/Lower/OpenMP/ClauseProcessor.h @@ -146,6 +146,7 @@ class ClauseProcessor { processEnter(llvm::SmallVectorImpl<DeclareTargetCaptureInfo> &result) const; bool processIf(omp::clause::If::DirectiveNameModifier directiveName, mlir::omp::IfClauseOps &result) const; + bool processIndirect(mlir::omp::IndirectClauseOps &result) const; bool processInReduction(mlir::Location currentLocation, mlir::omp::InReductionClauseOps &result, diff --git a/flang/lib/Lower/OpenMP/OpenMP.cpp b/flang/lib/Lower/OpenMP/OpenMP.cpp index 7503d33c8df38..5ea2703ea571c 100644 --- a/flang/lib/Lower/OpenMP/OpenMP.cpp +++ b/flang/lib/Lower/OpenMP/OpenMP.cpp @@ -1554,9 +1554,13 @@ static void getDeclareTargetInfo( cp.processEnter(symbolAndClause); cp.processLink(symbolAndClause); cp.processTo(symbolAndClause); + cp.processIndirect(clauseOps); - cp.processTODO<clause::Indirect>(converter.getCurrentLocation(), - llvm::omp::Directive::OMPD_declare_target); + // The `indirect` clause applies to the functions named by the directive + // (it requires an `enter` or `to` clause). Propagate the directive-level + // value to each captured symbol so it reaches the declare target attribute. + for (DeclareTargetCaptureInfo &sym : symbolAndClause) + sym.indirect = clauseOps.indirect; } } @@ -1580,7 +1584,8 @@ static void collectDeferredDeclareTargets( if (!op) { deferredDeclareTarget.push_back({symClause.clause, clauseOps.deviceType, - symClause.automap, symClause.symbol}); + symClause.automap, symClause.indirect, + symClause.symbol}); } } } @@ -1828,7 +1833,8 @@ getImplicitMapTypeAndKind(fir::FirOpBuilder &firOpBuilder, static void markDeclareTarget(mlir::Operation *op, lower::AbstractConverter &converter, mlir::omp::DeclareTargetCaptureClause captureClause, - mlir::omp::DeclareTargetDeviceType deviceType, bool automap) { + mlir::omp::DeclareTargetDeviceType deviceType, bool automap, + bool indirect) { // TODO: Add support for program local variables with declare target applied auto declareTargetOp = llvm::dyn_cast<mlir::omp::DeclareTargetInterface>(op); if (!declareTargetOp) @@ -1838,18 +1844,32 @@ markDeclareTarget(mlir::Operation *op, lower::AbstractConverter &converter, // The function or global already has a declare target applied to it, very // likely through implicit capture (usage in another declare target - // function/subroutine). It should be marked as any if it has been assigned - // both host and nohost, else we skip, as there is no change + // function/subroutine), or because it is named by more than one declare + // target directive. It should be marked as any if it has been assigned both + // host and nohost. The `indirect` modifier is a capability: once any + // declaration requests it, it must stay set, so it is merged with logical OR + // rather than overwritten (which could drop a previous `indirect = true`). if (declareTargetOp.isDeclareTarget()) { + bool mergedIndirect = + declareTargetOp.getDeclareTargetIndirect() || indirect; + if (declareTargetOp.getDeclareTargetDeviceType() != deviceType) declareTargetOp.setDeclareTarget(mlir::omp::DeclareTargetDeviceType::any, captureClause, automap, - /*implicit=*/false); + /*implicit=*/false, mergedIndirect); + else if (mergedIndirect != declareTargetOp.getDeclareTargetIndirect()) + // Same device type, but a later declaration added `indirect`; update it + // while preserving the already-established capture clause and automap. + declareTargetOp.setDeclareTarget( + declareTargetOp.getDeclareTargetDeviceType(), + declareTargetOp.getDeclareTargetCaptureClause(), + declareTargetOp.getDeclareTargetAutomap(), /*implicit=*/false, + mergedIndirect); return; } declareTargetOp.setDeclareTarget(deviceType, captureClause, automap, - /*implicit=*/false); + /*implicit=*/false, indirect); } //===----------------------------------------------------------------------===// @@ -6791,7 +6811,7 @@ genOMP(lower::AbstractConverter &converter, lower::SymMap &symTable, continue; markDeclareTarget(op, converter, symClause.clause, clauseOps.deviceType, - symClause.automap); + symClause.automap, symClause.indirect); } } @@ -8017,7 +8037,7 @@ bool Fortran::lower::markOpenMPDeferredDeclareTargetFunctions( deviceCodeFound = true; markDeclareTarget(op, converter, declTar.declareTargetCaptureClause, - devType, declTar.automap); + devType, declTar.automap, declTar.indirect); } return deviceCodeFound; diff --git a/flang/lib/Lower/OpenMP/Utils.h b/flang/lib/Lower/OpenMP/Utils.h index 94f85c43f7033..db38c2b755be7 100644 --- a/flang/lib/Lower/OpenMP/Utils.h +++ b/flang/lib/Lower/OpenMP/Utils.h @@ -53,6 +53,7 @@ namespace omp { struct DeclareTargetCaptureInfo { mlir::omp::DeclareTargetCaptureClause clause; bool automap = false; + bool indirect = false; const semantics::Symbol &symbol; DeclareTargetCaptureInfo(mlir::omp::DeclareTargetCaptureClause c, diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp index 1cc5a5d885a0c..5c0c6f4dd4424 100644 --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -2711,6 +2711,21 @@ void OmpStructureChecker::Leave(const parser::OmpDeclareTargetDirective &x) { context_.Warn(common::UsageWarning::OpenMPUsage, toClause->source, "The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead."_warn_en_US); } + if (indirectClause) { + // The INDIRECT clause is only allowed together with DEVICE_TYPE(ANY) (an + // absent DEVICE_TYPE clause also implies ANY). A host- or device-only + // procedure cannot be the target of an indirect device invocation. + if (const parser::OmpClause *deviceTypeClause{ + FindClause(llvm::omp::Clause::OMPC_device_type)}) { + const auto &deviceType{ + std::get<parser::OmpClause::DeviceType>(deviceTypeClause->u)}; + if (deviceType.v.v != + parser::OmpDeviceTypeClause::DeviceTypeDescription::Any) { + context_.Say(x.source, + "Only the DEVICE_TYPE(ANY) clause is allowed with the INDIRECT clause on the DECLARE TARGET directive"_err_en_US); + } + } + } } bool toClauseFound{false}; diff --git a/flang/test/Lower/OpenMP/Todo/omp-clause-indirect.f90 b/flang/test/Lower/OpenMP/Todo/omp-clause-indirect.f90 deleted file mode 100644 index 82efa8818a83c..0000000000000 --- a/flang/test/Lower/OpenMP/Todo/omp-clause-indirect.f90 +++ /dev/null @@ -1,34 +0,0 @@ -! This test checks the lowering of OpenMP Indirect Clause when used with the Declare Target directive - -! RUN: not %flang_fc1 -emit-fir -fopenmp -fopenmp-version=52 %s 2>&1 | FileCheck %s - -module functions - implicit none - - interface - function func() result(i) - character(1) :: i - end function - end interface - -contains - function func1() result(i) - !CHECK: not yet implemented: Unhandled clause INDIRECT in DECLARE TARGET construct - !$omp declare target enter(func1) indirect(.true.) - character(1) :: i - i = 'a' - return - end function -end module - -program main - use functions - implicit none - procedure (func), pointer :: ptr1=>func1 - character(1) :: val1 - - !$omp target map(from: val1) - val1 = ptr1() - !$omp end target - -end program diff --git a/flang/test/Lower/OpenMP/declare-target-indirect-clause.f90 b/flang/test/Lower/OpenMP/declare-target-indirect-clause.f90 new file mode 100644 index 0000000000000..ced2535925888 --- /dev/null +++ b/flang/test/Lower/OpenMP/declare-target-indirect-clause.f90 @@ -0,0 +1,30 @@ +!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 %s -o - | FileCheck %s +!RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -fopenmp-is-target-device %s -o - | FileCheck %s + +! Check that the INDIRECT clause on a DECLARE TARGET directive is lowered to the +! `indirect` field of the omp.declare_target attribute. + +module functions + implicit none +contains + ! CHECK: func.func @_QMfunctionsPfunc_true({{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} + function func_true() result(i) + !$omp declare target enter(func_true) indirect(.true.) + character(1) :: i + i = 'a' + end function + + ! CHECK: func.func @_QMfunctionsPfunc_implicit({{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} + function func_implicit() result(i) + !$omp declare target enter(func_implicit) indirect + character(1) :: i + i = 'b' + end function + + ! CHECK: func.func @_QMfunctionsPfunc_false({{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = false>} + function func_false() result(i) + !$omp declare target enter(func_false) indirect(.false.) + character(1) :: i + i = 'c' + end function +end module diff --git a/flang/test/Lower/OpenMP/declare-target-indirect-merge.f90 b/flang/test/Lower/OpenMP/declare-target-indirect-merge.f90 new file mode 100644 index 0000000000000..cee898cb84bee --- /dev/null +++ b/flang/test/Lower/OpenMP/declare-target-indirect-merge.f90 @@ -0,0 +1,47 @@ +! Check that the INDIRECT modifier is preserved (merged with logical OR) when a +! procedure is named by more than one DECLARE TARGET directive. A prior +! `indirect = true` must not be dropped, either by the device_type merge to +! `any` or by an early return when the device_type already matches. + +! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 %s -o - | FileCheck %s + +module m1 + implicit none +contains + ! A later directive adds `indirect` with the same (default) device_type. + ! CHECK: func.func @_QMm1Pfoo1() -> i32 attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} + function foo1() result(i) + !$omp declare target enter(foo1) + !$omp declare target enter(foo1) indirect(.true.) + integer :: i + i = 1 + end function +end module + +module m2 + implicit none +contains + ! `indirect` first, plain second: it must stay set. + ! CHECK: func.func @_QMm2Pfoo2() -> i32 attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} + function foo2() result(i) + !$omp declare target enter(foo2) indirect(.true.) + !$omp declare target enter(foo2) + integer :: i + i = 1 + end function +end module + +module m3 + implicit none +contains + ! `indirect` (device_type any) followed by a device_type(nohost) declaration: + ! the device type merges to `any` and the `indirect = true` must be carried + ! over rather than overwritten. + ! CHECK: func.func @_QMm3Pfoo3() -> i32 attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} + function foo3() result(i) + !$omp declare target enter(foo3) indirect(.true.) + !$omp declare target enter(foo3) device_type(nohost) + integer :: i + i = 1 + end function +end module diff --git a/flang/test/Lower/OpenMP/declare-target-indirect.f90 b/flang/test/Lower/OpenMP/declare-target-indirect.f90 new file mode 100644 index 0000000000000..3cd87d9b82575 --- /dev/null +++ b/flang/test/Lower/OpenMP/declare-target-indirect.f90 @@ -0,0 +1,39 @@ +! This test checks the lowering of the OpenMP INDIRECT clause when used with the +! DECLARE TARGET directive, together with an indirect call (through a procedure +! pointer) from within a target region. + +! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 %s -o - | FileCheck %s +! RUN: %flang_fc1 -emit-hlfir -fopenmp -fopenmp-version=52 -fopenmp-is-target-device %s -o - | FileCheck %s + +module functions + implicit none + + interface + function func() result(i) + character(1) :: i + end function + end interface + +contains + ! CHECK: func.func @_QMfunctionsPfunc1({{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} + function func1() result(i) + !$omp declare target enter(func1) indirect(.true.) + character(1) :: i + i = 'a' + return + end function +end module + +program main + use functions + implicit none + procedure (func), pointer :: ptr1=>func1 + character(1) :: val1 + + ! CHECK-LABEL: func.func @_QQmain() + ! CHECK: omp.target + !$omp target map(from: val1) + val1 = ptr1() + !$omp end target + +end program diff --git a/flang/test/Semantics/indirect02.f90 b/flang/test/Semantics/indirect02.f90 index 3fae39f1c2281..8835919341a9b 100644 --- a/flang/test/Semantics/indirect02.f90 +++ b/flang/test/Semantics/indirect02.f90 @@ -1,7 +1,8 @@ -! This test checks the lowering of OpenMP Indirect Clause when used with the Declare Target directive +! This test checks that the OpenMP INDIRECT clause on the DECLARE TARGET +! directive is rejected before OpenMP 5.1 and accepted from 5.1 onwards. ! RUN: not %flang -fopenmp -fopenmp-version=50 %s 2>&1 | FileCheck %s --check-prefix="CHECK-50" -! RUN: not %flang -fopenmp -fopenmp-version=52 %s 2>&1 | FileCheck %s --check-prefix="CHECK-52" +! RUN: %flang_fc1 -fopenmp -fopenmp-version=52 -fdebug-unparse %s 2>&1 | FileCheck %s --check-prefix="CHECK-52" module functions implicit none @@ -15,7 +16,7 @@ function func() result(i) contains function func1() result(i) !CHECK-50: INDIRECT clause is not allowed on DECLARE TARGET directive in OpenMP v5.0, try -fopenmp-version=51 - !CHECK-52: not yet implemented: Unhandled clause INDIRECT in DECLARE TARGET construct + !CHECK-52: !$OMP DECLARE TARGET ENTER(func1) INDIRECT(.true._4) !$omp declare target enter(func1) indirect(.true.) character(1) :: i i = 'a' diff --git a/flang/test/Semantics/indirect03.f90 b/flang/test/Semantics/indirect03.f90 new file mode 100644 index 0000000000000..0bab188a9ff51 --- /dev/null +++ b/flang/test/Semantics/indirect03.f90 @@ -0,0 +1,31 @@ +! This test checks the OpenMP restriction that the INDIRECT clause on a DECLARE +! TARGET directive is only allowed with DEVICE_TYPE(ANY) (an absent DEVICE_TYPE +! clause also implies ANY). A host- or device-only procedure cannot be the +! target of an indirect device invocation. + +! RUN: not %flang -fopenmp -fopenmp-version=52 %s 2>&1 | FileCheck %s + +module functions + implicit none +contains + !CHECK: Only the DEVICE_TYPE(ANY) clause is allowed with the INDIRECT clause on the DECLARE TARGET directive + function func_host() result(i) + !$omp declare target enter(func_host) device_type(host) indirect(.true.) + character(1) :: i + i = 'a' + end function + + !CHECK: Only the DEVICE_TYPE(ANY) clause is allowed with the INDIRECT clause on the DECLARE TARGET directive + function func_nohost() result(i) + !$omp declare target enter(func_nohost) device_type(nohost) indirect(.true.) + character(1) :: i + i = 'b' + end function + + ! DEVICE_TYPE(ANY) with INDIRECT is allowed, so no error is expected here. + function func_any() result(i) + !$omp declare target enter(func_any) device_type(any) indirect(.true.) + character(1) :: i + i = 'c' + end function +end module diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPAttrDefs.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPAttrDefs.td index 55895add86dc4..9f0b776870265 100644 --- a/mlir/include/mlir/Dialect/OpenMP/OpenMPAttrDefs.td +++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPAttrDefs.td @@ -46,7 +46,8 @@ def DeclareTargetAttr : OpenMP_Attr<"DeclareTarget", "declaretarget"> { (ins OptionalParameter<"DeclareTargetDeviceTypeAttr">:$device_type, OptionalParameter<"DeclareTargetCaptureClauseAttr">:$capture_clause, DefaultValuedParameter<"bool", "false">:$automap, - DefaultValuedParameter<"bool", "false">:$implicit); + DefaultValuedParameter<"bool", "false">:$implicit, + DefaultValuedParameter<"bool", "false">:$indirect); let assemblyFormat = "`<` struct(params) `>`"; } diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPClauseOperands.h b/mlir/include/mlir/Dialect/OpenMP/OpenMPClauseOperands.h index 35b58375698f6..fbe5ee576228b 100644 --- a/mlir/include/mlir/Dialect/OpenMP/OpenMPClauseOperands.h +++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPClauseOperands.h @@ -33,6 +33,12 @@ struct DeviceTypeClauseOps { DeclareTargetDeviceType deviceType = DeclareTargetDeviceType::any; }; +struct IndirectClauseOps { + /// Whether the declare target entities may be invoked indirectly (through a + /// function pointer) from within a target region. + bool indirect = false; +}; + //===----------------------------------------------------------------------===// // Extra operation operand structures. //===----------------------------------------------------------------------===// @@ -43,8 +49,8 @@ using HostEvaluatedOperands = detail::Clauses<CollapseClauseOps, LoopRelatedClauseOps, NumTeamsClauseOps, NumThreadsClauseOps, ThreadLimitClauseOps>; -// TODO: Add `indirect` clause. -using DeclareTargetOperands = detail::Clauses<DeviceTypeClauseOps>; +using DeclareTargetOperands = + detail::Clauses<DeviceTypeClauseOps, IndirectClauseOps>; /// omp.target_enter_data, omp.target_exit_data and omp.target_update take the /// same clauses, so we give the structure to be shared by all of them a diff --git a/mlir/include/mlir/Dialect/OpenMP/OpenMPOpsInterfaces.td b/mlir/include/mlir/Dialect/OpenMP/OpenMPOpsInterfaces.td index 66730ae52d8ee..4e637753c7132 100644 --- a/mlir/include/mlir/Dialect/OpenMP/OpenMPOpsInterfaces.td +++ b/mlir/include/mlir/Dialect/OpenMP/OpenMPOpsInterfaces.td @@ -376,7 +376,7 @@ def DeclareTargetInterface : OpInterface<"DeclareTargetInterface"> { /*methodName=*/"setDeclareTarget", (ins "mlir::omp::DeclareTargetDeviceType":$deviceType, "mlir::omp::DeclareTargetCaptureClause":$captureClause, - "bool":$automap, "bool":$implicit), [{}], [{ + "bool":$automap, "bool":$implicit, "bool":$indirect), [{}], [{ $_op->setAttr("omp.declare_target", mlir::omp::DeclareTargetAttr::get( $_op->getContext(), @@ -384,7 +384,7 @@ def DeclareTargetInterface : OpInterface<"DeclareTargetInterface"> { $_op->getContext(), deviceType), mlir::omp::DeclareTargetCaptureClauseAttr::get( $_op->getContext(), captureClause), - automap, implicit)); + automap, implicit, indirect)); }]>, InterfaceMethod< /*description=*/[{ @@ -446,6 +446,20 @@ def DeclareTargetInterface : OpInterface<"DeclareTargetInterface"> { if (auto dAttr = llvm::dyn_cast_or_null<mlir::omp::DeclareTargetAttr>(dTar)) return dAttr.getImplicit(); return false; + }]>, + InterfaceMethod< + /*description=*/[{ + Return true if the DeclareTarget attribute has the INDIRECT modifier, + indicating that the entity may be invoked through a function pointer + from within a target region. + }], + /*retTy=*/"bool", + /*methodName=*/"getDeclareTargetIndirect", + (ins), [{}], [{ + if (mlir::Attribute dTar = $_op->getAttr("omp.declare_target")) + if (auto dAttr = llvm::dyn_cast_or_null<mlir::omp::DeclareTargetAttr>(dTar)) + return dAttr.getIndirect(); + return false; }]> ]; } diff --git a/mlir/lib/Dialect/OpenMP/Transforms/MarkDeclareTarget.cpp b/mlir/lib/Dialect/OpenMP/Transforms/MarkDeclareTarget.cpp index d988752da84b9..519a51e6a18f6 100644 --- a/mlir/lib/Dialect/OpenMP/Transforms/MarkDeclareTarget.cpp +++ b/mlir/lib/Dialect/OpenMP/Transforms/MarkDeclareTarget.cpp @@ -282,7 +282,8 @@ class MarkDeclareTargetPass // Update the operation and add callees to the worklist to propagate it. declareTargetOp.setDeclareTarget(changedDeviceType, omp::DeclareTargetCaptureClause::to, - /*automap=*/false, /*implicit=*/true); + /*automap=*/false, /*implicit=*/true, + /*indirect=*/false); for (auto &callee : calls[workItem.first]) worklist.push_back({callee.getKey(), changedDeviceType}); diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp index 47a8493708d0f..45623a61a42a1 100644 --- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp @@ -9708,6 +9708,69 @@ convertOmpTarget(Operation &opInst, llvm::IRBuilderBase &builder, return success(); } +/// Register a `declare target ... indirect` function so that the OpenMP runtime +/// can resolve indirect calls (through a function pointer) to it from within a +/// target region. This mirrors clang's +/// `CGOpenMPRuntime::emitDeclareTargetFunction`. +/// +/// On the host the function itself is registered as an indirect offload entry, +/// which causes an offloading entry (with the indirect flag) to be emitted at +/// module finalization. On the target device a new, externally visible global +/// holding the address of the function is generated (so the runtime can read +/// the device address while leaving the function's own linkage and visibility +/// unchanged) and that global is registered instead. +static void registerIndirectDeclareTargetFunction( + FunctionOpInterface funcOp, llvm::OpenMPIRBuilder *ompBuilder, + LLVM::ModuleTranslation &moduleTranslation) { + llvm::Function *llvmFunc = moduleTranslation.lookupFunction(funcOp.getName()); + if (!llvmFunc) + return; + + // Build the unique offload entry name using the function as the parent name, + // e.g. `__omp_offloading_<device>_<file>_<func>_l<line>`. + auto loc = funcOp->getLoc()->findInstanceOf<FileLineColLoc>(); + auto fileInfoCallBack = [&loc]() { + std::string filename = ""; + std::uint64_t lineNo = 0; + if (loc) { + filename = loc.getFilename().str(); + lineNo = loc.getLine(); + } + return std::pair<std::string, std::uint64_t>(llvm::StringRef(filename), + lineNo); + }; + + llvm::vfs::FileSystem &vfs = moduleTranslation.getFileSystem(); + llvm::TargetRegionEntryInfo entryInfo = ompBuilder->getTargetEntryUniqueInfo( + fileInfoCallBack, vfs, funcOp.getName()); + llvm::SmallString<128> name; + ompBuilder->OffloadInfoManager.getTargetRegionEntryFnName(name, entryInfo); + + llvm::Module *llvmModule = moduleTranslation.getLLVMModule(); + const llvm::DataLayout &dl = llvmModule->getDataLayout(); + // The entry tracks a pointer to the function, so its size is the store size + // of a pointer. + int64_t varSize = dl.getTypeStoreSize( + llvm::PointerType::getUnqual(llvmModule->getContext())); + + llvm::Constant *addr = llvmFunc; + if (ompBuilder->Config.isTargetDevice()) { + llvm::PointerType *fnPtrTy = llvm::PointerType::get( + llvmModule->getContext(), dl.getProgramAddressSpace()); + auto *addrGlobal = new llvm::GlobalVariable( + *llvmModule, fnPtrTy, /*isConstant=*/true, + llvm::GlobalValue::ExternalLinkage, llvmFunc, name, nullptr, + llvm::GlobalValue::NotThreadLocal, dl.getDefaultGlobalsAddressSpace()); + addrGlobal->setVisibility(llvm::GlobalValue::ProtectedVisibility); + addr = addrGlobal; + } + + ompBuilder->OffloadInfoManager.registerDeviceGlobalVarEntryInfo( + name, addr, varSize, + llvm::OffloadEntriesInfoManager::OMPTargetGlobalVarEntryIndirect, + llvm::GlobalValue::WeakODRLinkage); +} + static LogicalResult convertDeclareTargetAttr(Operation *op, mlir::omp::DeclareTargetAttr attribute, llvm::OpenMPIRBuilder *ompBuilder, @@ -9722,13 +9785,25 @@ convertDeclareTargetAttr(Operation *op, mlir::omp::DeclareTargetAttr attribute, if (FunctionOpInterface funcOp = dyn_cast<FunctionOpInterface>(op)) { if (auto offloadMod = dyn_cast<omp::OffloadModuleInterface>( op->getParentOfType<ModuleOp>().getOperation())) { - if (!offloadMod.getIsTargetDevice()) - return success(); - + bool isTargetDevice = offloadMod.getIsTargetDevice(); omp::DeclareTargetDeviceType declareType = attribute.getDeviceType().getValue(); + bool isHostFunc = declareType == omp::DeclareTargetDeviceType::host; + + // A `declare target ... indirect(.true.)` function must be registered so + // that indirect calls to it from within a target region can be resolved + // by the runtime. This applies to both host and device compilation, but + // not to host-only functions that are about to be deleted on the device. + mlir::BoolAttr indirectAttr = attribute.getIndirect(); + if (indirectAttr && indirectAttr.getValue() && + !(isTargetDevice && isHostFunc)) + registerIndirectDeclareTargetFunction(funcOp, ompBuilder, + moduleTranslation); + + if (!isTargetDevice) + return success(); - if (declareType == omp::DeclareTargetDeviceType::host) { + if (isHostFunc) { llvm::Function *llvmFunc = moduleTranslation.lookupFunction(funcOp.getName()); llvmFunc->dropAllReferences(); diff --git a/mlir/test/Dialect/OpenMP/mark-declare-target-indirect.mlir b/mlir/test/Dialect/OpenMP/mark-declare-target-indirect.mlir new file mode 100644 index 0000000000000..3ebdcb66cd017 --- /dev/null +++ b/mlir/test/Dialect/OpenMP/mark-declare-target-indirect.mlir @@ -0,0 +1,39 @@ +// RUN: mlir-opt -omp-mark-declare-target -split-input-file %s | FileCheck %s + +// The `omp-mark-declare-target` pass marks functions that are reachable from +// explicit target code as implicitly declare target. The `indirect` modifier +// however is a property of the specific declare target declaration and must NOT +// be propagated to functions that are only reached through (direct) calls. + +// A function explicitly declared `indirect` that directly calls another +// function: the callee is implicitly captured and must be marked declare target +// with `indirect = false`, not inherit the parent's `indirect = true`. +module { + // CHECK: func.func @indirect_parent() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} + func.func @indirect_parent() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} { + func.call @direct_callee() : () -> () + return + } + + // CHECK: func.func @direct_callee() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = false>} + func.func @direct_callee() { + return + } +} + +// ----- + +// A callee that is itself explicitly declared `indirect` keeps its own value +// (the pass must not clobber it). +module { + // CHECK: func.func @indirect_parent2() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} + func.func @indirect_parent2() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} { + func.call @explicitly_indirect_callee() : () -> () + return + } + + // CHECK: func.func @explicitly_indirect_callee() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} + func.func @explicitly_indirect_callee() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} { + return + } +} diff --git a/mlir/test/Target/LLVMIR/omptarget-declare-target-indirect-device.mlir b/mlir/test/Target/LLVMIR/omptarget-declare-target-indirect-device.mlir new file mode 100644 index 0000000000000..ad86bed2770fa --- /dev/null +++ b/mlir/test/Target/LLVMIR/omptarget-declare-target-indirect-device.mlir @@ -0,0 +1,19 @@ +// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s + +// Test the device-side lowering of `declare target ... indirect` functions. A +// new global holding the address of the function is generated with protected +// visibility so the runtime can access it. A function marked `indirect = false` +// must not generate such a global. + +module attributes {llvm.target_triple = "amdgcn-amd-amdhsa", omp.is_target_device = true} { + // CHECK: @[[ENTRY:__omp_offloading_[0-9a-z]+_[0-9a-z]+_indirect_fn_l[0-9]+]] = protected constant ptr @indirect_fn + llvm.func @indirect_fn() attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false, indirect = true>} { + llvm.return + } + + // A function marked `indirect = false` must not produce an indirect global. + // CHECK-NOT: plain_fn_l + llvm.func @plain_fn() attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false, indirect = false>} { + llvm.return + } +} diff --git a/mlir/test/Target/LLVMIR/omptarget-declare-target-indirect-host.mlir b/mlir/test/Target/LLVMIR/omptarget-declare-target-indirect-host.mlir new file mode 100644 index 0000000000000..f3b098018d196 --- /dev/null +++ b/mlir/test/Target/LLVMIR/omptarget-declare-target-indirect-host.mlir @@ -0,0 +1,20 @@ +// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s + +// Test the host-side lowering of `declare target ... indirect` functions. The +// runtime needs an offload entry (flag 8 == OMPTargetGlobalVarEntryIndirect) +// so that indirect calls within a target region can be resolved. A function +// marked `indirect = false` must not register an offload entry. + +// CHECK-DAG: %struct.__tgt_offload_entry = type { i64, i16, i16, i32, ptr, ptr, i64, i64, ptr } +module attributes {llvm.target_triple = "x86_64-unknown-linux-gnu", omp.is_target_device = false} { + // CHECK: @.offloading.entry.[[ENTRY:__omp_offloading_[0-9a-z]+_[0-9a-z]+_indirect_fn_l[0-9]+]] = weak constant %struct.__tgt_offload_entry { i64 0, i16 1, i16 1, i32 8, ptr @indirect_fn, ptr @{{.*}}, i64 8, i64 0, ptr null } + llvm.func @indirect_fn() attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false, indirect = true>} { + llvm.return + } + + // A function marked `indirect = false` must not produce an offload entry. + // CHECK-NOT: plain_fn_l + llvm.func @plain_fn() attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false, indirect = false>} { + llvm.return + } +} >From 9daf6f4715adc53634d4a4a6bff21618e24524fc Mon Sep 17 00:00:00 2001 From: Jay Satish Kumar Patel <[email protected]> Date: Wed, 29 Jul 2026 02:19:58 -0500 Subject: [PATCH 2/4] Rewrite device indirect calls via indirect-call lookup; fix CIR build and TO+INDIRECT --- clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.cpp | 3 +- .../include/flang/Optimizer/OpenMP/Passes.td | 11 +++ flang/lib/Optimizer/OpenMP/CMakeLists.txt | 1 + .../Optimizer/OpenMP/IndirectCallLookup.cpp | 93 +++++++++++++++++++ flang/lib/Optimizer/Passes/Pipelines.cpp | 2 + flang/lib/Semantics/check-omp-structure.cpp | 2 +- .../Transforms/omp-indirect-call-lookup.fir | 63 +++++++++++++ 7 files changed, 173 insertions(+), 2 deletions(-) create mode 100644 flang/lib/Optimizer/OpenMP/IndirectCallLookup.cpp create mode 100644 flang/test/Transforms/omp-indirect-call-lookup.fir diff --git a/clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.cpp b/clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.cpp index 93ec2248f76eb..88615aac9057b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenOpenMPRuntime.cpp @@ -190,5 +190,6 @@ void CIRGenOpenMPRuntime::emitDeclareTargetFunction(const FunctionDecl *fd, llvm::cast<mlir::omp::DeclareTargetInterface>(funcOp.getOperation()); declTargetIface.setDeclareTarget(convertDeviceType(attr->getDevType()), convertCaptureClause(attr->getMapType()), - /*automap=*/false, /*implicit=*/false); + /*automap=*/false, /*implicit=*/false, + /*indirect=*/false); } diff --git a/flang/include/flang/Optimizer/OpenMP/Passes.td b/flang/include/flang/Optimizer/OpenMP/Passes.td index 08272d24252ee..d917913e8fd92 100644 --- a/flang/include/flang/Optimizer/OpenMP/Passes.td +++ b/flang/include/flang/Optimizer/OpenMP/Passes.td @@ -117,6 +117,17 @@ def AutomapToTargetDataPass let dependentDialects = ["mlir::omp::OpenMPDialect"]; } +def IndirectCallLookupPass + : Pass<"omp-indirect-call-lookup", "mlir::ModuleOp"> { + let summary = "Route device indirect calls through the OpenMP indirect-call " + "lookup runtime function."; + let dependentDialects = [ + "mlir::func::FuncDialect", + "fir::FIROpsDialect", + "mlir::omp::OpenMPDialect" + ]; +} + def UnimplementedDeviceCheckPass : Pass<"omp-unimplemented-device-check", "::mlir::ModuleOp"> { let summary = "Report not-yet-implemented situations on target device code"; diff --git a/flang/lib/Optimizer/OpenMP/CMakeLists.txt b/flang/lib/Optimizer/OpenMP/CMakeLists.txt index f29ba86a8a28c..31b19f8e18bc4 100644 --- a/flang/lib/Optimizer/OpenMP/CMakeLists.txt +++ b/flang/lib/Optimizer/OpenMP/CMakeLists.txt @@ -4,6 +4,7 @@ add_flang_library(FlangOpenMPTransforms AutomapToTargetData.cpp DoConcurrentConversion.cpp GenericLoopConversion.cpp + IndirectCallLookup.cpp MapsForPrivatizedSymbols.cpp MapInfoFinalization.cpp LowerWorkdistribute.cpp diff --git a/flang/lib/Optimizer/OpenMP/IndirectCallLookup.cpp b/flang/lib/Optimizer/OpenMP/IndirectCallLookup.cpp new file mode 100644 index 0000000000000..c530b823ef703 --- /dev/null +++ b/flang/lib/Optimizer/OpenMP/IndirectCallLookup.cpp @@ -0,0 +1,93 @@ +//===- IndirectCallLookup.cpp ---------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// On a GPU target device, rewrites indirect fir.call ops so the callee (a host +// function address held in a procedure pointer) is resolved to the device +// address via the `__llvm_omp_indirect_call_lookup` runtime function. +// +//===----------------------------------------------------------------------===// + +#include "flang/Optimizer/Dialect/FIRDialect.h" +#include "flang/Optimizer/Dialect/FIROps.h" +#include "flang/Optimizer/OpenMP/Passes.h" + +#include "mlir/Dialect/Func/IR/FuncOps.h" +#include "mlir/Dialect/OpenMP/OpenMPDialect.h" +#include "mlir/Dialect/OpenMP/OpenMPInterfaces.h" +#include "mlir/IR/Builders.h" +#include "llvm/ADT/SmallVector.h" + +namespace flangomp { +#define GEN_PASS_DEF_INDIRECTCALLLOOKUPPASS +#include "flang/Optimizer/OpenMP/Passes.h.inc" +} // namespace flangomp + +using namespace mlir; + +/// Runtime function that maps a host function address to the device address. +static constexpr llvm::StringRef indirectCallLookupName = + "__llvm_omp_indirect_call_lookup"; + +namespace { +class IndirectCallLookupPass + : public flangomp::impl::IndirectCallLookupPassBase<IndirectCallLookupPass> { +public: + void runOnOperation() override { + mlir::ModuleOp module = getOperation(); + auto offloadMod = mlir::dyn_cast<mlir::omp::OffloadModuleInterface>( + module.getOperation()); + + // Only a GPU target device needs host-to-device address translation. + if (!offloadMod || !offloadMod.getIsTargetDevice() || + !offloadMod.getIsGPU()) + return; + + // An indirect fir.call has no callee symbol; operand 0 is the callee value. + llvm::SmallVector<fir::CallOp> indirectCalls; + module.walk([&](fir::CallOp call) { + if (!call.getCallee()) + indirectCalls.push_back(call); + }); + if (indirectCalls.empty()) + return; + + mlir::MLIRContext *ctx = &getContext(); + mlir::OpBuilder builder(ctx); + + // A function value lowers to a pointer, so an opaque `() -> ()` type matches + // the runtime function's ptr argument and result. + auto opaqueFnTy = mlir::FunctionType::get(ctx, {}, {}); + + // Declare the runtime lookup function once. + auto lookupFn = + module.lookupSymbol<mlir::func::FuncOp>(indirectCallLookupName); + if (!lookupFn) { + builder.setInsertionPointToStart(module.getBody()); + lookupFn = mlir::func::FuncOp::create( + builder, module.getLoc(), indirectCallLookupName, + mlir::FunctionType::get(ctx, {opaqueFnTy}, {opaqueFnTy})); + lookupFn.setPrivate(); + } + + for (fir::CallOp call : indirectCalls) { + builder.setInsertionPoint(call); + mlir::Location loc = call.getLoc(); + mlir::Value callee = call.getOperand(0); + + // Resolve the host callee to the device address, then call through it. + mlir::Value hostAddr = + fir::ConvertOp::create(builder, loc, opaqueFnTy, callee); + auto lookup = fir::CallOp::create(builder, loc, lookupFn, + mlir::ValueRange{hostAddr}); + mlir::Value deviceCallee = fir::ConvertOp::create( + builder, loc, callee.getType(), lookup.getResult(0)); + call.setOperand(0, deviceCallee); + } + } +}; +} // namespace diff --git a/flang/lib/Optimizer/Passes/Pipelines.cpp b/flang/lib/Optimizer/Passes/Pipelines.cpp index 232b27a148ab2..4de405745ba0a 100644 --- a/flang/lib/Optimizer/Passes/Pipelines.cpp +++ b/flang/lib/Optimizer/Passes/Pipelines.cpp @@ -381,6 +381,8 @@ void createOpenMPFIRPassPipeline(mlir::PassManager &pm, pm.addPass(flangomp::createMapInfoFinalizationPass()); pm.addPass(flangomp::createGenericLoopConversionPass()); + if (opts.isTargetDevice) + pm.addPass(flangomp::createIndirectCallLookupPass()); } void createDebugPasses(mlir::PassManager &pm, diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp index 5c0c6f4dd4424..31954fc510f79 100644 --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -2702,7 +2702,7 @@ void OmpStructureChecker::Leave(const parser::OmpDeclareTargetDirective &x) { context_.Say(x.source, "If the DECLARE TARGET directive has a clause, it must contain at least one ENTER clause or LINK clause"_err_en_US); } - if (indirectClause && !enterClause) { + if (indirectClause && !enterClause && !toClause) { context_.Say(x.source, "The INDIRECT clause cannot be used without the ENTER clause with the DECLARE TARGET directive."_err_en_US); } diff --git a/flang/test/Transforms/omp-indirect-call-lookup.fir b/flang/test/Transforms/omp-indirect-call-lookup.fir new file mode 100644 index 0000000000000..3b2af2a43f0d1 --- /dev/null +++ b/flang/test/Transforms/omp-indirect-call-lookup.fir @@ -0,0 +1,63 @@ +// RUN: fir-opt --split-input-file --omp-indirect-call-lookup %s | FileCheck %s + +// On a GPU target device, indirect calls are routed through the lookup so the +// host callee address is resolved to the device address. + +// CHECK: func.func private @__llvm_omp_indirect_call_lookup(() -> ()) -> (() -> ()) +module attributes {omp.is_target_device = true, omp.is_gpu = true} { + // CHECK-LABEL: func.func @gpu_device_caller + func.func @gpu_device_caller(%arg0: !fir.ref<!fir.boxproc<() -> i32>>) -> i32 { + %0 = fir.load %arg0 : !fir.ref<!fir.boxproc<() -> i32>> + %1 = fir.box_addr %0 : (!fir.boxproc<() -> i32>) -> (() -> i32) + // CHECK: %[[BOXADDR:.*]] = fir.box_addr + // CHECK: %[[HOST:.*]] = fir.convert %[[BOXADDR]] : (() -> i32) -> (() -> ()) + // CHECK: %[[DEV:.*]] = fir.call @__llvm_omp_indirect_call_lookup(%[[HOST]]) : (() -> ()) -> (() -> ()) + // CHECK: %[[CALLEE:.*]] = fir.convert %[[DEV]] : (() -> ()) -> (() -> i32) + // CHECK: fir.call %[[CALLEE]]() : () -> i32 + %2 = fir.call %1() : () -> i32 + return %2 : i32 + } +} + +// ----- + +// A non-GPU target device shares the host address space, so no lookup is added. +module attributes {omp.is_target_device = true, omp.is_gpu = false} { + // CHECK-LABEL: func.func @nongpu_device_caller + // CHECK-NOT: __llvm_omp_indirect_call_lookup + func.func @nongpu_device_caller(%arg0: !fir.ref<!fir.boxproc<() -> i32>>) -> i32 { + %0 = fir.load %arg0 : !fir.ref<!fir.boxproc<() -> i32>> + %1 = fir.box_addr %0 : (!fir.boxproc<() -> i32>) -> (() -> i32) + // CHECK: fir.call %{{.*}}() : () -> i32 + %2 = fir.call %1() : () -> i32 + return %2 : i32 + } +} + +// ----- + +// Host compilation: no lookup is added. +module attributes {omp.is_target_device = false, omp.is_gpu = false} { + // CHECK-LABEL: func.func @host_caller + // CHECK-NOT: __llvm_omp_indirect_call_lookup + func.func @host_caller(%arg0: !fir.ref<!fir.boxproc<() -> i32>>) -> i32 { + %0 = fir.load %arg0 : !fir.ref<!fir.boxproc<() -> i32>> + %1 = fir.box_addr %0 : (!fir.boxproc<() -> i32>) -> (() -> i32) + %2 = fir.call %1() : () -> i32 + return %2 : i32 + } +} + +// ----- + +// Direct calls are left untouched on a GPU target device. +module attributes {omp.is_target_device = true, omp.is_gpu = true} { + func.func private @callee() -> i32 + // CHECK-LABEL: func.func @direct_caller + // CHECK-NOT: __llvm_omp_indirect_call_lookup + func.func @direct_caller() -> i32 { + // CHECK: fir.call @callee() : () -> i32 + %0 = fir.call @callee() : () -> i32 + return %0 : i32 + } +} >From 380e6f6c7c1dc68f9dd4802f54853f10af1f49bb Mon Sep 17 00:00:00 2001 From: Jay Satish Kumar Patel <[email protected]> Date: Wed, 29 Jul 2026 02:33:04 -0500 Subject: [PATCH 3/4] [Flang][OpenMP] Test INDIRECT with the TO clause on DECLARE TARGET --- flang/lib/Optimizer/OpenMP/IndirectCallLookup.cpp | 7 ++++--- flang/test/Semantics/indirect02.f90 | 9 +++++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/flang/lib/Optimizer/OpenMP/IndirectCallLookup.cpp b/flang/lib/Optimizer/OpenMP/IndirectCallLookup.cpp index c530b823ef703..042229ba08690 100644 --- a/flang/lib/Optimizer/OpenMP/IndirectCallLookup.cpp +++ b/flang/lib/Optimizer/OpenMP/IndirectCallLookup.cpp @@ -35,7 +35,8 @@ static constexpr llvm::StringRef indirectCallLookupName = namespace { class IndirectCallLookupPass - : public flangomp::impl::IndirectCallLookupPassBase<IndirectCallLookupPass> { + : public flangomp::impl::IndirectCallLookupPassBase< + IndirectCallLookupPass> { public: void runOnOperation() override { mlir::ModuleOp module = getOperation(); @@ -59,8 +60,8 @@ class IndirectCallLookupPass mlir::MLIRContext *ctx = &getContext(); mlir::OpBuilder builder(ctx); - // A function value lowers to a pointer, so an opaque `() -> ()` type matches - // the runtime function's ptr argument and result. + // A function value lowers to a pointer, so an opaque `() -> ()` type + // matches the runtime function's ptr argument and result. auto opaqueFnTy = mlir::FunctionType::get(ctx, {}, {}); // Declare the runtime lookup function once. diff --git a/flang/test/Semantics/indirect02.f90 b/flang/test/Semantics/indirect02.f90 index 8835919341a9b..2f04d2efe9d6a 100644 --- a/flang/test/Semantics/indirect02.f90 +++ b/flang/test/Semantics/indirect02.f90 @@ -22,6 +22,15 @@ function func1() result(i) i = 'a' return end function + + ! TO is the pre-5.2 spelling of ENTER, so INDIRECT is accepted with it too. + function func2() result(i) + !CHECK-52: !$OMP DECLARE TARGET TO(func2) INDIRECT(.true._4) + !$omp declare target to(func2) indirect(.true.) + character(1) :: i + i = 'b' + return + end function end module program main >From b28df8185db4ede7ae0518979df9d1104fd9f12a Mon Sep 17 00:00:00 2001 From: Jay Satish Kumar Patel <[email protected]> Date: Fri, 14 Aug 2026 04:17:10 -0500 Subject: [PATCH 4/4] [Flang][OpenMP] Fix INDIRECT clause argument handling in semantics and lowering --- flang/lib/Lower/OpenMP/ClauseProcessor.cpp | 6 ++--- flang/lib/Lower/OpenMP/ClauseProcessor.h | 2 +- flang/lib/Semantics/check-omp-structure.cpp | 16 ++++++++++--- .../OpenMP/declare-target-indirect-clause.f90 | 7 +++--- .../OpenMP/declare-target-indirect-merge.f90 | 6 ++--- .../Lower/OpenMP/declare-target-indirect.f90 | 23 ++++++++++++++++++- flang/test/Semantics/indirect01.f90 | 2 +- flang/test/Semantics/indirect03.f90 | 21 +++++++++++++++++ .../OpenMP/Transforms/FunctionFiltering.cpp | 3 ++- .../OpenMP/OpenMPToLLVMIRTranslation.cpp | 4 +--- .../OpenMP/mark-declare-target-indirect.mlir | 18 +++++++-------- ...target-declare-target-indirect-device.mlir | 4 ++-- ...mptarget-declare-target-indirect-host.mlir | 4 ++-- 13 files changed, 84 insertions(+), 32 deletions(-) diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp index fd1af2f812ea5..3376ec4634ac7 100644 --- a/flang/lib/Lower/OpenMP/ClauseProcessor.cpp +++ b/flang/lib/Lower/OpenMP/ClauseProcessor.cpp @@ -23,6 +23,7 @@ #include "flang/Optimizer/Builder/HLFIRTools.h" #include "flang/Optimizer/Dialect/FIRType.h" #include "flang/Optimizer/Support/InternalNames.h" +#include "flang/Semantics/openmp-utils.h" #include "flang/Semantics/tools.h" #include "flang/Utils/OpenMP.h" #include "llvm/Frontend/OpenMP/OMP.h.inc" @@ -489,9 +490,8 @@ bool ClauseProcessor::processIndirect( if (clause->v) { auto foldedExpr = Fortran::evaluate::Fold( semaCtx.foldingContext(), Fortran::common::Clone(*clause->v)); - if (auto logicalVal = Fortran::evaluate::GetScalarConstantValue< - Fortran::evaluate::LogicalResult>(foldedExpr)) - isIndirect = logicalVal->IsTrue(); + // The argument may have any logical kind, not just logical(4). + isIndirect = semantics::omp::GetLogicalValue(foldedExpr).value_or(true); } result.indirect = isIndirect; return true; diff --git a/flang/lib/Lower/OpenMP/ClauseProcessor.h b/flang/lib/Lower/OpenMP/ClauseProcessor.h index 05622e5e3ede3..9e84881907d1c 100644 --- a/flang/lib/Lower/OpenMP/ClauseProcessor.h +++ b/flang/lib/Lower/OpenMP/ClauseProcessor.h @@ -94,6 +94,7 @@ class ClauseProcessor { bool processInbranch(mlir::omp::InbranchClauseOps &result) const; bool processInclusive(mlir::Location currentLocation, mlir::omp::InclusiveClauseOps &result) const; + bool processIndirect(mlir::omp::IndirectClauseOps &result) const; bool processInitializer( lower::SymMap &symMap, ReductionProcessor::GenInitValueCBTy &genInitValueCB, @@ -146,7 +147,6 @@ class ClauseProcessor { processEnter(llvm::SmallVectorImpl<DeclareTargetCaptureInfo> &result) const; bool processIf(omp::clause::If::DirectiveNameModifier directiveName, mlir::omp::IfClauseOps &result) const; - bool processIndirect(mlir::omp::IndirectClauseOps &result) const; bool processInReduction(mlir::Location currentLocation, mlir::omp::InReductionClauseOps &result, diff --git a/flang/lib/Semantics/check-omp-structure.cpp b/flang/lib/Semantics/check-omp-structure.cpp index 31954fc510f79..6f3f12185dcf9 100644 --- a/flang/lib/Semantics/check-omp-structure.cpp +++ b/flang/lib/Semantics/check-omp-structure.cpp @@ -2704,7 +2704,7 @@ void OmpStructureChecker::Leave(const parser::OmpDeclareTargetDirective &x) { } if (indirectClause && !enterClause && !toClause) { context_.Say(x.source, - "The INDIRECT clause cannot be used without the ENTER clause with the DECLARE TARGET directive."_err_en_US); + "The INDIRECT clause cannot be used without the ENTER or TO clause with the DECLARE TARGET directive."_err_en_US); } unsigned version{context_.langOptions().OpenMPVersion}; if (toClause && version >= 52) { @@ -2712,11 +2712,21 @@ void OmpStructureChecker::Leave(const parser::OmpDeclareTargetDirective &x) { "The usage of TO clause on DECLARE TARGET directive has been deprecated. Use ENTER clause instead."_warn_en_US); } if (indirectClause) { + // The restriction applies only when the argument evaluates to true; an + // absent argument defaults to true. + bool isIndirect{true}; + if (const auto &indirectExpr{ + std::get<parser::OmpClause::Indirect>(indirectClause->u).v.v}) { + const auto &parserExpr{parser::UnwrapRef<parser::Expr>(*indirectExpr)}; + if (auto &&expr{GetEvaluateExpr(parserExpr)}) + isIndirect = GetLogicalValue(*expr).value_or(true); + } // The INDIRECT clause is only allowed together with DEVICE_TYPE(ANY) (an // absent DEVICE_TYPE clause also implies ANY). A host- or device-only // procedure cannot be the target of an indirect device invocation. - if (const parser::OmpClause *deviceTypeClause{ - FindClause(llvm::omp::Clause::OMPC_device_type)}) { + const parser::OmpClause *deviceTypeClause{ + FindClause(llvm::omp::Clause::OMPC_device_type)}; + if (isIndirect && deviceTypeClause) { const auto &deviceType{ std::get<parser::OmpClause::DeviceType>(deviceTypeClause->u)}; if (deviceType.v.v != diff --git a/flang/test/Lower/OpenMP/declare-target-indirect-clause.f90 b/flang/test/Lower/OpenMP/declare-target-indirect-clause.f90 index ced2535925888..e4c0a071afd47 100644 --- a/flang/test/Lower/OpenMP/declare-target-indirect-clause.f90 +++ b/flang/test/Lower/OpenMP/declare-target-indirect-clause.f90 @@ -7,21 +7,22 @@ module functions implicit none contains - ! CHECK: func.func @_QMfunctionsPfunc_true({{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} + ! CHECK: func.func @_QMfunctionsPfunc_true({{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), indirect = true>} function func_true() result(i) !$omp declare target enter(func_true) indirect(.true.) character(1) :: i i = 'a' end function - ! CHECK: func.func @_QMfunctionsPfunc_implicit({{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} + ! CHECK: func.func @_QMfunctionsPfunc_implicit({{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), indirect = true>} function func_implicit() result(i) !$omp declare target enter(func_implicit) indirect character(1) :: i i = 'b' end function - ! CHECK: func.func @_QMfunctionsPfunc_false({{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = false>} + ! A false value equals the attribute default, so no `indirect` field prints. + ! CHECK: func.func @_QMfunctionsPfunc_false({{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter)>} function func_false() result(i) !$omp declare target enter(func_false) indirect(.false.) character(1) :: i diff --git a/flang/test/Lower/OpenMP/declare-target-indirect-merge.f90 b/flang/test/Lower/OpenMP/declare-target-indirect-merge.f90 index cee898cb84bee..c417c88c54ec7 100644 --- a/flang/test/Lower/OpenMP/declare-target-indirect-merge.f90 +++ b/flang/test/Lower/OpenMP/declare-target-indirect-merge.f90 @@ -9,7 +9,7 @@ module m1 implicit none contains ! A later directive adds `indirect` with the same (default) device_type. - ! CHECK: func.func @_QMm1Pfoo1() -> i32 attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} + ! CHECK: func.func @_QMm1Pfoo1() -> i32 attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), indirect = true>} function foo1() result(i) !$omp declare target enter(foo1) !$omp declare target enter(foo1) indirect(.true.) @@ -22,7 +22,7 @@ module m2 implicit none contains ! `indirect` first, plain second: it must stay set. - ! CHECK: func.func @_QMm2Pfoo2() -> i32 attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} + ! CHECK: func.func @_QMm2Pfoo2() -> i32 attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), indirect = true>} function foo2() result(i) !$omp declare target enter(foo2) indirect(.true.) !$omp declare target enter(foo2) @@ -37,7 +37,7 @@ module m3 ! `indirect` (device_type any) followed by a device_type(nohost) declaration: ! the device type merges to `any` and the `indirect = true` must be carried ! over rather than overwritten. - ! CHECK: func.func @_QMm3Pfoo3() -> i32 attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} + ! CHECK: func.func @_QMm3Pfoo3() -> i32 attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), indirect = true>} function foo3() result(i) !$omp declare target enter(foo3) indirect(.true.) !$omp declare target enter(foo3) device_type(nohost) diff --git a/flang/test/Lower/OpenMP/declare-target-indirect.f90 b/flang/test/Lower/OpenMP/declare-target-indirect.f90 index 3cd87d9b82575..ce287b513ab38 100644 --- a/flang/test/Lower/OpenMP/declare-target-indirect.f90 +++ b/flang/test/Lower/OpenMP/declare-target-indirect.f90 @@ -15,13 +15,29 @@ function func() result(i) end interface contains - ! CHECK: func.func @_QMfunctionsPfunc1({{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} + ! CHECK: func.func @_QMfunctionsPfunc1({{.*}}attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), indirect = true>} function func1() result(i) !$omp declare target enter(func1) indirect(.true.) character(1) :: i i = 'a' return end function + + ! The argument may have any logical kind, not just the default one. A false + ! value matches the attribute default, so no `indirect` field is printed. + ! CHECK: func.func @_QMfunctionsPfunc2({{.*}}capture_clause = (enter)>} + function func2() result(i) + !$omp declare target enter(func2) indirect(.false._1) + character(1) :: i + i = 'b' + end function + + ! CHECK: func.func @_QMfunctionsPfunc3({{.*}}capture_clause = (enter)>} + function func3() result(i) + !$omp declare target enter(func3) indirect(.false._8) + character(1) :: i + i = 'c' + end function end module program main @@ -32,6 +48,11 @@ program main ! CHECK-LABEL: func.func @_QQmain() ! CHECK: omp.target + ! The procedure pointer is resolved to a callable address that is then used + ! as the callee of an indirect fir.call. + ! CHECK: %[[PROC:.*]] = fir.load %{{.*}} : !fir.ref<!fir.boxproc<{{.*}}>> + ! CHECK: %[[CALLEE:.*]] = fir.box_addr %[[PROC]] : (!fir.boxproc<{{.*}}>) -> {{.*}} + ! CHECK: fir.call %[[CALLEE]]( !$omp target map(from: val1) val1 = ptr1() !$omp end target diff --git a/flang/test/Semantics/indirect01.f90 b/flang/test/Semantics/indirect01.f90 index 81fcfbc94aa3e..67537cc5703d0 100644 --- a/flang/test/Semantics/indirect01.f90 +++ b/flang/test/Semantics/indirect01.f90 @@ -13,7 +13,7 @@ function func() result(i) contains function func1() result(i) - !CHECK: The INDIRECT clause cannot be used without the ENTER clause with the DECLARE TARGET directive. + !CHECK: The INDIRECT clause cannot be used without the ENTER or TO clause with the DECLARE TARGET directive. !$omp declare target indirect(.true.) character(1) :: i i = 'a' diff --git a/flang/test/Semantics/indirect03.f90 b/flang/test/Semantics/indirect03.f90 index 0bab188a9ff51..3d47d77a5bef7 100644 --- a/flang/test/Semantics/indirect03.f90 +++ b/flang/test/Semantics/indirect03.f90 @@ -28,4 +28,25 @@ function func_any() result(i) character(1) :: i i = 'c' end function + + ! The restriction only applies when INDIRECT evaluates to true, so a + ! device-only procedure is allowed here. + function func_false() result(i) + !$omp declare target enter(func_false) device_type(nohost) indirect(.false.) + character(1) :: i + i = 'd' + end function + + ! The argument may have any logical kind, not just the default one. + function func_false_kind1() result(i) + !$omp declare target enter(func_false_kind1) device_type(nohost) indirect(.false._1) + character(1) :: i + i = 'e' + end function + + function func_false_kind8() result(i) + !$omp declare target enter(func_false_kind8) device_type(nohost) indirect(.false._8) + character(1) :: i + i = 'f' + end function end module diff --git a/mlir/lib/Dialect/OpenMP/Transforms/FunctionFiltering.cpp b/mlir/lib/Dialect/OpenMP/Transforms/FunctionFiltering.cpp index 3b236dd69f425..48d571a4c442a 100644 --- a/mlir/lib/Dialect/OpenMP/Transforms/FunctionFiltering.cpp +++ b/mlir/lib/Dialect/OpenMP/Transforms/FunctionFiltering.cpp @@ -101,7 +101,8 @@ class FunctionFilteringPass if (declareTargetOp && !declareTargetOp.isDeclareTarget()) declareTargetOp.setDeclareTarget(omp::DeclareTargetDeviceType::host, omp::DeclareTargetCaptureClause::to, - /*automap=*/false, /*implicit=*/true); + /*automap=*/false, /*implicit=*/true, + /*indirect=*/false); return WalkResult::advance(); }); } diff --git a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp index 45623a61a42a1..f001385920398 100644 --- a/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp +++ b/mlir/lib/Target/LLVMIR/Dialect/OpenMP/OpenMPToLLVMIRTranslation.cpp @@ -9794,9 +9794,7 @@ convertDeclareTargetAttr(Operation *op, mlir::omp::DeclareTargetAttr attribute, // that indirect calls to it from within a target region can be resolved // by the runtime. This applies to both host and device compilation, but // not to host-only functions that are about to be deleted on the device. - mlir::BoolAttr indirectAttr = attribute.getIndirect(); - if (indirectAttr && indirectAttr.getValue() && - !(isTargetDevice && isHostFunc)) + if (attribute.getIndirect() && !(isTargetDevice && isHostFunc)) registerIndirectDeclareTargetFunction(funcOp, ompBuilder, moduleTranslation); diff --git a/mlir/test/Dialect/OpenMP/mark-declare-target-indirect.mlir b/mlir/test/Dialect/OpenMP/mark-declare-target-indirect.mlir index 3ebdcb66cd017..97714f81a060d 100644 --- a/mlir/test/Dialect/OpenMP/mark-declare-target-indirect.mlir +++ b/mlir/test/Dialect/OpenMP/mark-declare-target-indirect.mlir @@ -6,16 +6,16 @@ // be propagated to functions that are only reached through (direct) calls. // A function explicitly declared `indirect` that directly calls another -// function: the callee is implicitly captured and must be marked declare target -// with `indirect = false`, not inherit the parent's `indirect = true`. +// function: the callee is implicitly captured (marked with `implicit = true`) +// and must not inherit the parent's `indirect = true`. module { - // CHECK: func.func @indirect_parent() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} - func.func @indirect_parent() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} { + // CHECK: func.func @indirect_parent() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), indirect = true>} + func.func @indirect_parent() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), indirect = true>} { func.call @direct_callee() : () -> () return } - // CHECK: func.func @direct_callee() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = false>} + // CHECK: func.func @direct_callee() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (to), implicit = true>} func.func @direct_callee() { return } @@ -26,14 +26,14 @@ module { // A callee that is itself explicitly declared `indirect` keeps its own value // (the pass must not clobber it). module { - // CHECK: func.func @indirect_parent2() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} - func.func @indirect_parent2() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} { + // CHECK: func.func @indirect_parent2() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), indirect = true>} + func.func @indirect_parent2() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), indirect = true>} { func.call @explicitly_indirect_callee() : () -> () return } - // CHECK: func.func @explicitly_indirect_callee() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} - func.func @explicitly_indirect_callee() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), automap = false, indirect = true>} { + // CHECK: func.func @explicitly_indirect_callee() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), indirect = true>} + func.func @explicitly_indirect_callee() attributes {omp.declare_target = #omp.declaretarget<device_type = (any), capture_clause = (enter), indirect = true>} { return } } diff --git a/mlir/test/Target/LLVMIR/omptarget-declare-target-indirect-device.mlir b/mlir/test/Target/LLVMIR/omptarget-declare-target-indirect-device.mlir index ad86bed2770fa..07242b603613e 100644 --- a/mlir/test/Target/LLVMIR/omptarget-declare-target-indirect-device.mlir +++ b/mlir/test/Target/LLVMIR/omptarget-declare-target-indirect-device.mlir @@ -7,13 +7,13 @@ module attributes {llvm.target_triple = "amdgcn-amd-amdhsa", omp.is_target_device = true} { // CHECK: @[[ENTRY:__omp_offloading_[0-9a-z]+_[0-9a-z]+_indirect_fn_l[0-9]+]] = protected constant ptr @indirect_fn - llvm.func @indirect_fn() attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false, indirect = true>} { + llvm.func @indirect_fn() attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), indirect = true>} { llvm.return } // A function marked `indirect = false` must not produce an indirect global. // CHECK-NOT: plain_fn_l - llvm.func @plain_fn() attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false, indirect = false>} { + llvm.func @plain_fn() attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter)>} { llvm.return } } diff --git a/mlir/test/Target/LLVMIR/omptarget-declare-target-indirect-host.mlir b/mlir/test/Target/LLVMIR/omptarget-declare-target-indirect-host.mlir index f3b098018d196..aa9d88b3f0f64 100644 --- a/mlir/test/Target/LLVMIR/omptarget-declare-target-indirect-host.mlir +++ b/mlir/test/Target/LLVMIR/omptarget-declare-target-indirect-host.mlir @@ -8,13 +8,13 @@ // CHECK-DAG: %struct.__tgt_offload_entry = type { i64, i16, i16, i32, ptr, ptr, i64, i64, ptr } module attributes {llvm.target_triple = "x86_64-unknown-linux-gnu", omp.is_target_device = false} { // CHECK: @.offloading.entry.[[ENTRY:__omp_offloading_[0-9a-z]+_[0-9a-z]+_indirect_fn_l[0-9]+]] = weak constant %struct.__tgt_offload_entry { i64 0, i16 1, i16 1, i32 8, ptr @indirect_fn, ptr @{{.*}}, i64 8, i64 0, ptr null } - llvm.func @indirect_fn() attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false, indirect = true>} { + llvm.func @indirect_fn() attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), indirect = true>} { llvm.return } // A function marked `indirect = false` must not produce an offload entry. // CHECK-NOT: plain_fn_l - llvm.func @plain_fn() attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter), automap = false, indirect = false>} { + llvm.func @plain_fn() attributes {omp.declare_target = #omp.declaretarget<device_type = (nohost), capture_clause = (enter)>} { llvm.return } } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
