[Mlir-commits] [flang] [mlir] [acc] Support for Optional arguments in firstprivate recipes (PR #190079)
llvmlistbot at llvm.org
llvmlistbot at llvm.org
Thu Apr 9 10:50:11 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-mlir
Author: nvptm
<details>
<summary>Changes</summary>
Add support for explicit of implicit firstprivates that are Fortran Optional arguments.
---
Patch is 21.77 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/190079.diff
6 Files Affected:
- (modified) flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp (+62-17)
- (modified) flang/test/Fir/OpenACC/recipe-populate-firstprivate.mlir (+7-7)
- (modified) flang/test/Transforms/OpenACC/acc-implicit-firstprivate.fir (+9-9)
- (added) flang/test/Transforms/OpenACC/optional-firstprivate-recipe.fir (+39)
- (added) flang/test/Transforms/OpenACC/optional-firstprivate.fir (+34)
- (modified) mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp (+21-16)
``````````diff
diff --git a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
index 25af35513d18d..c7e6f5b0bff23 100644
--- a/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
+++ b/flang/lib/Optimizer/OpenACC/Support/FIROpenACCTypeInterfaces.cpp
@@ -741,14 +741,31 @@ template <typename Ty>
mlir::Value OpenACCMappableModel<Ty>::generatePrivateInit(
mlir::Type type, mlir::OpBuilder &mlirBuilder, mlir::Location loc,
mlir::TypedValue<mlir::acc::MappableType> var, llvm::StringRef varName,
- mlir::ValueRange bounds, mlir::Value initVal, mlir::acc::VariableInfoAttr,
- bool &needsDestroy) const {
+ mlir::ValueRange bounds, mlir::Value initVal,
+ mlir::acc::VariableInfoAttr varInfo, bool &needsDestroy) const {
mlir::ModuleOp mod = mlirBuilder.getInsertionBlock()
->getParent()
->getParentOfType<mlir::ModuleOp>();
assert(mod && "failed to retrieve ModuleOp");
fir::FirOpBuilder builder(mlirBuilder, mod);
+ // When variable is optional: use fir.is_present to check. When non-optional,
+ // skip the conditional to avoid unnecessary branches.
+ std::optional<fir::IfOp> optIfOp;
+ bool mayBeOptional = false;
+ if (auto fortranVarInfo =
+ mlir::dyn_cast_if_present<fir::OpenACCFortranVariableInfoAttr>(
+ varInfo)) {
+ mayBeOptional = fortranVarInfo.getMayBeOptional();
+ if (mayBeOptional) {
+ mlir::Value cond =
+ fir::IsPresentOp::create(builder, loc, builder.getI1Type(), var);
+ optIfOp = fir::IfOp::create(builder, loc, mlir::TypeRange{type}, cond,
+ /*withElseRegion=*/true);
+ builder.setInsertionPointToStart(&optIfOp->getThenRegion().front());
+ }
+ }
+
hlfir::Entity inputVar = hlfir::Entity{var};
if (inputVar.isPolymorphic())
TODO(loc, "OpenACC: polymorphic variable privatization");
@@ -844,21 +861,22 @@ mlir::Value OpenACCMappableModel<Ty>::generatePrivateInit(
"dynamic allocation of the whole base in case of section is not "
"expected");
+ mlir::Value retVal;
if (inputVar.getType() == alloc.getType() && !allocateSection)
- return alloc;
+ retVal = alloc;
// Step4: reconstruct the input variable from the privatized part:
- // - get a mock base address if the privatized part is a section (so that any
- // addressing of the input variable can be replaced by the same addressing of
- // the privatized part even though the allocated part for the private does not
- // cover all the input variable storage. This is relying on OpenACC
- // constraint that any addressing of such privatized variable inside the
- // construct region can only address the variable inside the privatized
+ // - get a mock base address if the privatized part is a section (so that
+ // any addressing of the input variable can be replaced by the same
+ // addressing of the privatized part even though the allocated part for the
+ // private does not cover all the input variable storage. This is relying on
+ // OpenACC constraint that any addressing of such privatized variable inside
+ // the construct region can only address the variable inside the privatized
// section).
- // - reconstruct a descriptor with the same bounds and type parameters as the
- // input if needed.
- // - store this new descriptor in a temporary allocation if the input variable
- // is a POINTER/ALLOCATABLE.
+ // - reconstruct a descriptor with the same bounds and type parameters as
+ // the input if needed.
+ // - store this new descriptor in a temporary allocation if the input
+ // variable is a POINTER/ALLOCATABLE.
llvm::SmallVector<mlir::Value> inputVarLowerBounds, inputVarExtents;
if (dereferencedVar.isArray()) {
for (int dim = 0; dim < dereferencedVar.getRank(); ++dim) {
@@ -873,9 +891,9 @@ mlir::Value OpenACCMappableModel<Ty>::generatePrivateInit(
if (allocateSection) {
// To compute the mock base address without doing pointer arithmetic,
// compute: TYPE, TEMP(ZERO_BASED_SECTION_LB:) MOCK_BASE = TEMP(0)
- // This addresses the section "backwards" (0 <= ZERO_BASED_SECTION_LB). This
- // is currently OK, but care should be taken to avoid tripping bound checks
- // if added in the future.
+ // This addresses the section "backwards" (0 <= ZERO_BASED_SECTION_LB).
+ // This is currently OK, but care should be taken to avoid tripping bound
+ // checks if added in the future.
mlir::Type inputBaseAddrType =
dereferencedVar.getBoxType().getBaseAddressType();
mlir::Value tempBaseAddr =
@@ -900,7 +918,7 @@ mlir::Value OpenACCMappableModel<Ty>::generatePrivateInit(
builder.createConvert(loc, inputBaseAddrType, mockBase);
}
- mlir::Value retVal = privateVarBaseAddr;
+ retVal = privateVarBaseAddr;
if (inputVar.isBoxAddressOrValue()) {
// Recreate descriptor with same bounds as the input variable.
mlir::Value shape;
@@ -918,6 +936,15 @@ mlir::Value OpenACCMappableModel<Ty>::generatePrivateInit(
retVal = box;
}
}
+
+ if (mayBeOptional) {
+ fir::ResultOp::create(builder, loc, retVal);
+ builder.setInsertionPointToStart(&optIfOp->getElseRegion().front());
+ mlir::Value absent = fir::AbsentOp::create(builder, loc, type);
+ fir::ResultOp::create(builder, loc, absent);
+ retVal = optIfOp->getResult(0);
+ }
+
return retVal;
}
@@ -964,6 +991,24 @@ bool OpenACCMappableModel<Ty>::generateCopy(
source = hlfir::derefPointersAndAllocatables(loc, builder, source);
destination = hlfir::derefPointersAndAllocatables(loc, builder, destination);
+ // When optional: only copy when source is present (fir.is_present). When
+ // absent, destination is already null from init. When non-optional, copy
+ // directly without the conditional.
+ if (auto fortranVarInfo =
+ mlir::dyn_cast_if_present<fir::OpenACCFortranVariableInfoAttr>(
+ varInfo)) {
+ if (fortranVarInfo.getMayBeOptional()) {
+ // When variable is optional: use fir.is_present to check. When
+ // non-optional, skip the conditional to avoid unnecessary branches.
+ std::optional<fir::IfOp> optIfOp;
+ mlir::Value cond =
+ fir::IsPresentOp::create(builder, loc, builder.getI1Type(), src);
+ optIfOp = fir::IfOp::create(builder, loc, mlir::TypeRange{}, cond,
+ /*withElseRegion=*/false);
+ builder.setInsertionPointToStart(&optIfOp->getThenRegion().front());
+ }
+ }
+
if (!bounds.empty())
std::tie(source, destination) =
genArraySectionsInRecipe(builder, loc, bounds, source, destination);
diff --git a/flang/test/Fir/OpenACC/recipe-populate-firstprivate.mlir b/flang/test/Fir/OpenACC/recipe-populate-firstprivate.mlir
index a033734d2ff0e..3eb850d2f468e 100644
--- a/flang/test/Fir/OpenACC/recipe-populate-firstprivate.mlir
+++ b/flang/test/Fir/OpenACC/recipe-populate-firstprivate.mlir
@@ -12,7 +12,7 @@
// CHECK: } copy {
// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<f32>, %[[DST:.*]]: !fir.ref<f32>):
// CHECK: %[[LOAD:.*]] = fir.load %[[SRC]] : !fir.ref<f32>
-// CHECK: fir.store %[[LOAD]] to %[[DST]] : !fir.ref<f32>
+// CHECK: hlfir.assign %[[LOAD]] to %[[DST]] temporary_lhs : f32, !fir.ref<f32>
// CHECK: acc.terminator
// CHECK: }
// CHECK-NOT: destroy
@@ -34,7 +34,7 @@ func.func @test_scalar() {
// CHECK: } copy {
// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<i32>, %[[DST:.*]]: !fir.ref<i32>):
// CHECK: %[[LOAD:.*]] = fir.load %[[SRC]] : !fir.ref<i32>
-// CHECK: fir.store %[[LOAD]] to %[[DST]] : !fir.ref<i32>
+// CHECK: hlfir.assign %[[LOAD]] to %[[DST]] temporary_lhs : i32, !fir.ref<i32>
// CHECK: acc.terminator
// CHECK: }
// CHECK-NOT: destroy
@@ -56,7 +56,7 @@ func.func @test_int() {
// CHECK: } copy {
// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<!fir.logical<4>>, %[[DST:.*]]: !fir.ref<!fir.logical<4>>):
// CHECK: %[[LOAD:.*]] = fir.load %[[SRC]] : !fir.ref<!fir.logical<4>>
-// CHECK: fir.store %[[LOAD]] to %[[DST]] : !fir.ref<!fir.logical<4>>
+// CHECK: hlfir.assign %[[LOAD]] to %[[DST]] temporary_lhs : !fir.logical<4>, !fir.ref<!fir.logical<4>>
// CHECK: acc.terminator
// CHECK: }
// CHECK-NOT: destroy
@@ -78,7 +78,7 @@ func.func @test_logical() {
// CHECK: } copy {
// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<complex<f32>>, %[[DST:.*]]: !fir.ref<complex<f32>>):
// CHECK: %[[LOAD:.*]] = fir.load %[[SRC]] : !fir.ref<complex<f32>>
-// CHECK: fir.store %[[LOAD]] to %[[DST]] : !fir.ref<complex<f32>>
+// CHECK: hlfir.assign %[[LOAD]] to %[[DST]] temporary_lhs : complex<f32>, !fir.ref<complex<f32>>
// CHECK: acc.terminator
// CHECK: }
// CHECK-NOT: destroy
@@ -99,7 +99,7 @@ func.func @test_complex() {
// CHECK: acc.yield %[[ALLOC]] : !fir.ref<!fir.array<100xf32>>
// CHECK: } copy {
// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<!fir.array<100xf32>>, %[[DST:.*]]: !fir.ref<!fir.array<100xf32>>):
-// CHECK: hlfir.assign %[[SRC]] to %[[DST]] : !fir.ref<!fir.array<100xf32>>, !fir.ref<!fir.array<100xf32>>
+// CHECK: hlfir.assign %[[SRC]] to %[[DST]] temporary_lhs : !fir.ref<!fir.array<100xf32>>, !fir.ref<!fir.array<100xf32>>
// CHECK: acc.terminator
// CHECK: }
// CHECK-NOT: destroy
@@ -120,7 +120,7 @@ func.func @test_array_1d() {
// CHECK: acc.yield %[[ALLOC]] : !fir.ref<!fir.array<10x20xi32>>
// CHECK: } copy {
// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<!fir.array<10x20xi32>>, %[[DST:.*]]: !fir.ref<!fir.array<10x20xi32>>):
-// CHECK: hlfir.assign %[[SRC]] to %[[DST]] : !fir.ref<!fir.array<10x20xi32>>, !fir.ref<!fir.array<10x20xi32>>
+// CHECK: hlfir.assign %[[SRC]] to %[[DST]] temporary_lhs : !fir.ref<!fir.array<10x20xi32>>, !fir.ref<!fir.array<10x20xi32>>
// CHECK: acc.terminator
// CHECK: }
// CHECK-NOT: destroy
@@ -141,7 +141,7 @@ func.func @test_array_2d() {
// CHECK: acc.yield %[[ALLOC]] : !fir.ref<!fir.type<_QTpoint{x:f32,y:f32,z:f32}>>
// CHECK: } copy {
// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<!fir.type<_QTpoint{x:f32,y:f32,z:f32}>>, %[[DST:.*]]: !fir.ref<!fir.type<_QTpoint{x:f32,y:f32,z:f32}>>):
-// CHECK: hlfir.assign %[[SRC]] to %[[DST]] : !fir.ref<!fir.type<_QTpoint{x:f32,y:f32,z:f32}>>, !fir.ref<!fir.type<_QTpoint{x:f32,y:f32,z:f32}>>
+// CHECK: hlfir.assign %[[SRC]] to %[[DST]] temporary_lhs : !fir.ref<!fir.type<_QTpoint{x:f32,y:f32,z:f32}>>, !fir.ref<!fir.type<_QTpoint{x:f32,y:f32,z:f32}>>
// CHECK: acc.terminator
// CHECK: }
// CHECK-NOT: destroy
diff --git a/flang/test/Transforms/OpenACC/acc-implicit-firstprivate.fir b/flang/test/Transforms/OpenACC/acc-implicit-firstprivate.fir
index d1033d1b580ff..48f3ac2e7fb8f 100644
--- a/flang/test/Transforms/OpenACC/acc-implicit-firstprivate.fir
+++ b/flang/test/Transforms/OpenACC/acc-implicit-firstprivate.fir
@@ -12,7 +12,7 @@
// CHECK: } copy {
// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<i32>, %[[DST:.*]]: !fir.ref<i32>):
// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref<i32>
-// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref<i32>
+// CHECK: hlfir.assign %[[LOADED]] to %[[DST]] temporary_lhs : i32, !fir.ref<i32>
// CHECK: acc.terminator
// CHECK: }
@@ -38,7 +38,7 @@ func.func @test_i32_scalar_in_parallel() {
// CHECK: } copy {
// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<i64>, %[[DST:.*]]: !fir.ref<i64>):
// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref<i64>
-// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref<i64>
+// CHECK: hlfir.assign %[[LOADED]] to %[[DST]] temporary_lhs : i64, !fir.ref<i64>
// CHECK: acc.terminator
// CHECK: }
@@ -64,7 +64,7 @@ func.func @test_i64_scalar_in_parallel() {
// CHECK: } copy {
// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<f32>, %[[DST:.*]]: !fir.ref<f32>):
// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref<f32>
-// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref<f32>
+// CHECK: hlfir.assign %[[LOADED]] to %[[DST]] temporary_lhs : f32, !fir.ref<f32>
// CHECK: acc.terminator
// CHECK: }
@@ -90,7 +90,7 @@ func.func @test_f32_scalar_in_parallel() {
// CHECK: } copy {
// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<f64>, %[[DST:.*]]: !fir.ref<f64>):
// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref<f64>
-// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref<f64>
+// CHECK: hlfir.assign %[[LOADED]] to %[[DST]] temporary_lhs : f64, !fir.ref<f64>
// CHECK: acc.terminator
// CHECK: }
@@ -116,7 +116,7 @@ func.func @test_f64_scalar_in_parallel() {
// CHECK: } copy {
// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<!fir.logical<4>>, %[[DST:.*]]: !fir.ref<!fir.logical<4>>):
// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref<!fir.logical<4>>
-// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref<!fir.logical<4>>
+// CHECK: hlfir.assign %[[LOADED]] to %[[DST]] temporary_lhs : !fir.logical<4>, !fir.ref<!fir.logical<4>>
// CHECK: acc.terminator
// CHECK: }
@@ -142,7 +142,7 @@ func.func @test_logical_scalar_in_parallel() {
// CHECK: } copy {
// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<complex<f32>>, %[[DST:.*]]: !fir.ref<complex<f32>>):
// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref<complex<f32>>
-// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref<complex<f32>>
+// CHECK: hlfir.assign %[[LOADED]] to %[[DST]] temporary_lhs : complex<f32>, !fir.ref<complex<f32>>
// CHECK: acc.terminator
// CHECK: }
@@ -168,7 +168,7 @@ func.func @test_complex_scalar_in_parallel() {
// CHECK: } copy {
// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<complex<f64>>, %[[DST:.*]]: !fir.ref<complex<f64>>):
// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref<complex<f64>>
-// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref<complex<f64>>
+// CHECK: hlfir.assign %[[LOADED]] to %[[DST]] temporary_lhs : complex<f64>, !fir.ref<complex<f64>>
// CHECK: acc.terminator
// CHECK: }
@@ -230,7 +230,7 @@ func.func @test_f64_scalar_in_serial() {
// CHECK: } copy {
// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<i8>, %[[DST:.*]]: !fir.ref<i8>):
// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref<i8>
-// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref<i8>
+// CHECK: hlfir.assign %[[LOADED]] to %[[DST]] temporary_lhs : i8, !fir.ref<i8>
// CHECK: acc.terminator
// CHECK: }
@@ -256,7 +256,7 @@ func.func @test_i8_scalar_in_parallel() {
// CHECK: } copy {
// CHECK: ^bb0(%[[SRC:.*]]: !fir.ref<i16>, %[[DST:.*]]: !fir.ref<i16>):
// CHECK: %[[LOADED:.*]] = fir.load %[[SRC]] : !fir.ref<i16>
-// CHECK: fir.store %[[LOADED]] to %[[DST]] : !fir.ref<i16>
+// CHECK: hlfir.assign %[[LOADED]] to %[[DST]] temporary_lhs : i16, !fir.ref<i16>
// CHECK: acc.terminator
// CHECK: }
diff --git a/flang/test/Transforms/OpenACC/optional-firstprivate-recipe.fir b/flang/test/Transforms/OpenACC/optional-firstprivate-recipe.fir
new file mode 100644
index 0000000000000..5085105ae8a15
--- /dev/null
+++ b/flang/test/Transforms/OpenACC/optional-firstprivate-recipe.fir
@@ -0,0 +1,39 @@
+// RUN: fir-opt %s --pass-pipeline="builtin.module(acc-initialize-fir-analyses,acc-implicit-data)" -split-input-file | FileCheck %s
+
+// Optional REAL dummy referenced inside acc.parallel (Fortran OPTIONAL) must get
+// @firstprivatization_optional_ref_f32 from acc-implicit-data
+
+// -----
+
+func.func @test_optional_firstprivate_f32(%arg0: !fir.ref<f32> {fir.bindc_name = "copt", fir.optional}) {
+ acc.parallel {
+ %load = fir.load %arg0 : !fir.ref<f32>
+ acc.yield
+ }
+ return
+}
+
+// CHECK-LABEL: acc.firstprivate.recipe @firstprivatization_optional_ref_f32 : !fir.ref<f32> init {
+// CHECK: ^bb0(%{{.*}}: !fir.ref<f32>):
+// CHECK: %{{.*}} = fir.is_present %{{.*}} : (!fir.ref<f32>) -> i1
+// CHECK: fir.if %{{.*}} -> (!fir.ref<f32>) {
+// CHECK: %{{.*}} = fir.alloca f32
+// CHECK: fir.result %{{.*}} : !fir.ref<f32>
+// CHECK: } else {
+// CHECK: %{{.*}} = fir.absent !fir.ref<f32>
+// CHECK: fir.result %{{.*}} : !fir.ref<f32>
+// CHECK: }
+// CHECK: acc.yield %{{.*}} : !fir.ref<f32>
+// CHECK: } copy {
+// CHECK: ^bb0(%{{.*}}: !fir.ref<f32>, %{{.*}}: !fir.ref<f32>):
+// CHECK: %{{.*}} = fir.is_present %{{.*}} : (!fir.ref<f32>) -> i1
+// CHECK: fir.if %{{.*}} {
+// CHECK: %{{.*}} = fir.load %{{.*}} : !fir.ref<f32>
+// CHECK: hlfir.assign %{{.*}} to %{{.*}} temporary_lhs : f32, !fir.ref<f32>
+// CHECK: }
+// CHECK: acc.terminator
+// CHECK: }
+
+// bindc_name on the arg does not always flow into acc.firstprivate's name=; minimal FIR gets name = "".
+// CHECK: acc.firstprivate varPtr({{.*}} : !fir.ref<f32>) recipe(@firstprivatization_optional_ref_f32) -> !fir.ref<f32> {implicit = true, name = ""}
+// CHECK: acc.parallel firstprivate(
diff --git a/flang/test/Transforms/OpenACC/optional-firstprivate.fir b/flang/test/Transforms/OpenACC/optional-firstprivate.fir
new file mode 100644
index 0000000000000..44a59d33291f5
--- /dev/null
+++ b/flang/test/Transforms/OpenACC/optional-firstprivate.fir
@@ -0,0 +1,34 @@
+// RUN: fir-opt %s --pass-pipeline="builtin.module(acc-initialize-fir-analyses,acc-implicit-data,acc-recipe-materialization)" -split-input-file | FileCheck %s
+
+// Verify that optional scalar (Fortran OPTIONAL dummy) gets the optional
+// firstprivate recipe from acc-implicit-data and that acc-recipe-materialization
+// inlines the fir.is_present / fir.if / fir.load / hlfir.assign body.
+
+// -----
+
+func.func @test_optional_firstprivate(%arg0: !fir.ref<i32> {fir.bindc_name = "x", fir.optional}) {
+ acc.parallel {
+ %load = fir.load %arg0 : !fir.ref<i32>
+ acc.yield
+ }
+ return
+}
+
+// Recipe exists right after acc-implicit-data (before acc-recipe-materialization)
+// IMPLICIT: acc.firstprivate.recipe @firstprivatization_optional_ref_i32
+
+// After acc-recipe-materialization: recipe is materialized, check the inlined body
+// CHECK: acc.firstprivate_map varPtr
+// CHECK: acc.parallel {
+// CHECK: fir.is_present
+// CHECK: fir.if %{{.*}} -> (!fir.ref<i32>) {
+// CHECK: fir.alloca i32
+// CHECK: fir.result
+// CHECK: } else {
+// CHECK: fir.absent !fir.ref<i32>
+// CHECK: fir.result
+// CHECK: fir.is_present
+// CHECK: fir.if %{{.*}} {
+// CHECK: fir.load
+// CHECK: hlfir.assign
+// CHECK: acc.yield
diff --git a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
index 28138fd906d69..bd26c70eb1831 100644
--- a/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
+++ b/mlir/lib/Dialect/OpenACC/IR/OpenACC.cpp
@@ -1598,10 +1598,12 @@ static LogicalResult createInitRegion(OpBuilder &builder, Location loc,
/// Create and populate a copy region for firstprivate recipes.
/// Returns success if the region is populated, failure otherwise.
-/// TODO: Handle MappableType - it does not yet have a copy API.
+/// `varInfo` must be the attribute produced by `createInitRegion` for
+/// `MappableType` (it is unused for `PointerLikeType` copy paths).
static LogicalResult createCopyRegion(OpBuilder &builder, Location loc,
Region ©Region, Type varType,
- ValueRange bounds) {
+ ValueRange bounds,
+ acc::VariableInfoAttr varInfo) {
// Create copy block with arguments: original value + privatized value +
// bounds
SmallVector<Type> copyArgTypes{varType, varType};
@@ -1615,20 +1617,20 @@ static LogicalResult createCopyRegion(OpBuilder &builder, Location loc,
copyBlock->addArguments(copyArgTypes, copyArgLocs);
builder.setInsertionPointToStart(copyBlock);
- bool isMappable = isa<MappableType>(varType);
- bool isPointerLike = isa<PointerLikeType>(varType);
- // TODO: Handle MappableType - it does not yet have a copy API.
- // Otherwise, for now just fallback to pointer-like behavior.
- if (isMappable && !isPointerLike)
- return failure();
+ Value originalArg = copyBlock->getArgument(0);
+ Value privatizedArg = copyBlock->getArgument(1);
- // Generate copy region body based on variable type
- if (isPointerLike) {
+ if (isa<MappableType>(varType)) {
+ auto mappableTy = cast<MappableType>(varType);
+ // g...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/190079
More information about the Mlir-commits
mailing list