[flang-commits] [flang] [flang][Lower][OpenMP][NFC] tidy up PrivateReductionUtils (PR #125867)
Tom Eccles via flang-commits
flang-commits at lists.llvm.org
Wed Feb 5 08:14:44 PST 2025
https://github.com/tblah updated https://github.com/llvm/llvm-project/pull/125867
>From 46da32a5df8aed05435567c56365fa7dfb1d41b0 Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Wed, 5 Feb 2025 13:54:43 +0000
Subject: [PATCH 1/2] [flang][Lower][OpenMP][NFC] tidy up PrivateReductionUtils
First part of a series of patches to improve private/reduction init and
cleanup region generation.
This commit is NFC. I factored out processing for each datatype into its
own method so that it is easier to keep track of what is being handled
where (I found the old gigantic init region generation function
difficult to navigate). The methods all share context in a helper class
to avoid having to pass a very large number of arguments.
I also removed the conflation between the mold argument and the mold
argument after loading. This should make it easier to avoid generating
dead uses of the mold argument in a later non-nfc patch.
---
.../Lower/OpenMP/PrivateReductionUtils.cpp | 549 +++++++++++-------
1 file changed, 325 insertions(+), 224 deletions(-)
diff --git a/flang/lib/Lower/OpenMP/PrivateReductionUtils.cpp b/flang/lib/Lower/OpenMP/PrivateReductionUtils.cpp
index 61706d0824101b6..11dcc053f14115d 100644
--- a/flang/lib/Lower/OpenMP/PrivateReductionUtils.cpp
+++ b/flang/lib/Lower/OpenMP/PrivateReductionUtils.cpp
@@ -238,91 +238,328 @@ static mlir::Value generateZeroShapeForRank(fir::FirOpBuilder &builder,
return builder.create<fir::ShapeOp>(loc, shapeTy, dims);
}
-void Fortran::lower::omp::populateByRefInitAndCleanupRegions(
- Fortran::lower::AbstractConverter &converter, mlir::Location loc,
- mlir::Type argType, mlir::Value scalarInitValue, mlir::Block *initBlock,
- mlir::Value allocatedPrivVarArg, mlir::Value moldArg,
- mlir::Region &cleanupRegion, DeclOperationKind kind,
- const Fortran::semantics::Symbol *sym) {
- fir::FirOpBuilder &builder = converter.getFirOpBuilder();
- mlir::Type ty = fir::unwrapRefType(argType);
- builder.setInsertionPointToEnd(initBlock);
- auto yield = [&](mlir::Value ret) {
- builder.create<mlir::omp::YieldOp>(loc, ret);
- };
+namespace {
+using namespace Fortran::lower::omp;
+/// Class to store shared data so we don't have to maintain so many function
+/// arguments
+class PopulateInitAndCleanupRegionsHelper {
+public:
+ PopulateInitAndCleanupRegionsHelper(
+ Fortran::lower::AbstractConverter &converter, mlir::Location loc,
+ mlir::Type argType, mlir::Value scalarInitValue,
+ mlir::Value allocatedPrivVarArg, mlir::Value moldArg,
+ mlir::Block *initBlock, mlir::Region &cleanupRegion,
+ DeclOperationKind kind, const Fortran::semantics::Symbol *sym)
+ : converter{converter}, builder{converter.getFirOpBuilder()}, loc{loc},
+ argType{argType}, scalarInitValue{scalarInitValue},
+ allocatedPrivVarArg{allocatedPrivVarArg}, moldArg{moldArg},
+ initBlock{initBlock}, cleanupRegion{cleanupRegion}, kind{kind},
+ sym{sym} {
+ valType = fir::unwrapRefType(argType);
+ }
- if (isPrivatization(kind))
- assert(sym && "Symbol information is needed to privatize derived types");
- bool needsInitialization =
- sym ? isDerivedTypeNeedingInitialization(sym->GetUltimate()) : false;
+ void populateByRefInitAndCleanupRegions();
- if (fir::isa_trivial(ty)) {
- builder.setInsertionPointToEnd(initBlock);
+private:
+ Fortran::lower::AbstractConverter &converter;
+ fir::FirOpBuilder &builder;
+
+ mlir::Location loc;
+
+ /// The type of the block arguments passed into the init and cleanup regions
+ mlir::Type argType;
+
+ /// argType stripped of any references
+ mlir::Type valType;
+
+ /// sclarInitValue: The value scalars should be initialized to (only
+ /// valid for reductions).
+ /// allocatedPrivVarArg: The allocation for the private
+ /// variable.
+ /// moldArg: The original variable.
+ /// loadedMoldArg: The original variable, loaded.
+ mlir::Value scalarInitValue, allocatedPrivVarArg, moldArg, loadedMoldArg;
+
+ /// The first block in the init region.
+ mlir::Block *initBlock;
+
+ /// The region to insert clanup code into.
+ mlir::Region &cleanupRegion;
+
+ /// The kind of operation we are generating init/cleanup regions for.
+ DeclOperationKind kind;
+
+ /// (optional) The symbol being privatized.
+ const Fortran::semantics::Symbol *sym;
+
+ /// Any length parameters which have been fetched for the type
+ mlir::SmallVector<mlir::Value> lenParams;
+
+ void createYield(mlir::Value ret) {
+ builder.create<mlir::omp::YieldOp>(loc, ret);
+ }
+ void initTrivialType() {
+ builder.setInsertionPointToEnd(initBlock);
if (scalarInitValue)
builder.createStoreWithConvert(loc, scalarInitValue, allocatedPrivVarArg);
- yield(allocatedPrivVarArg);
+ createYield(allocatedPrivVarArg);
+ }
+
+ void initBoxedPrivatePointer(fir::BaseBoxType boxTy);
+
+ /// e.g. !fir.box<!fir.heap<i32>>, !fir.box<!fir.type<....>>,
+ /// !fir.box<!fir.char<...>>
+ void initAndCleanupBoxedScalar(fir::BaseBoxType boxTy,
+ bool needsInitialization);
+
+ void initAndCleanupBoxedArray(fir::BaseBoxType boxTy,
+ bool needsInitialization);
+
+ void initAndCleanupBoxchar(fir::BoxCharType boxCharTy);
+
+ void initAndCleanupUnboxedDerivedType(bool needsInitialization);
+
+ fir::IfOp handleNullAllocatable();
+};
+
+} // namespace
+
+/// The initial state of a private pointer is undefined so we don't need to
+/// match the mold argument (OpenMP 5.2 end of page 106).
+void PopulateInitAndCleanupRegionsHelper::initBoxedPrivatePointer(
+ fir::BaseBoxType boxTy) {
+ assert(isPrivatization(kind));
+ // we need a shape with the right rank so that the embox op is lowered
+ // to an llvm struct of the right type. This returns nullptr if the types
+ // aren't right.
+ mlir::Value shape = generateZeroShapeForRank(builder, loc, loadedMoldArg);
+ // Just incase, do initialize the box with a null value
+ mlir::Value null = builder.createNullConstant(loc, boxTy.getEleTy());
+ mlir::Value nullBox;
+ nullBox = builder.create<fir::EmboxOp>(loc, boxTy, null, shape,
+ /*slice=*/mlir::Value{}, lenParams);
+ builder.create<fir::StoreOp>(loc, nullBox, allocatedPrivVarArg);
+ createYield(allocatedPrivVarArg);
+}
+/// Check if an allocatable box is unallocated. If so, initialize the boxAlloca
+/// to be unallocated e.g.
+/// %box_alloca = fir.alloca !fir.box<!fir.heap<...>>
+/// %addr = fir.box_addr %box
+/// if (%addr == 0) {
+/// %nullbox = fir.embox %addr
+/// fir.store %nullbox to %box_alloca
+/// } else {
+/// // ...
+/// fir.store %something to %box_alloca
+/// }
+/// omp.yield %box_alloca
+fir::IfOp PopulateInitAndCleanupRegionsHelper::handleNullAllocatable() {
+ mlir::Value addr = builder.create<fir::BoxAddrOp>(loc, loadedMoldArg);
+ mlir::Value isNotAllocated = builder.genIsNullAddr(loc, addr);
+ fir::IfOp ifOp = builder.create<fir::IfOp>(loc, isNotAllocated,
+ /*withElseRegion=*/true);
+ builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
+ // Just embox the null address and return.
+ // We have to give the embox a shape so that the LLVM box structure has the
+ // right rank. This returns an empty value if the types don't match.
+ mlir::Value shape = generateZeroShapeForRank(builder, loc, moldArg);
+
+ mlir::Value nullBox =
+ builder.create<fir::EmboxOp>(loc, valType, addr, shape,
+ /*slice=*/mlir::Value{}, lenParams);
+ builder.create<fir::StoreOp>(loc, nullBox, allocatedPrivVarArg);
+ return ifOp;
+}
+
+void PopulateInitAndCleanupRegionsHelper::initAndCleanupBoxedScalar(
+ fir::BaseBoxType boxTy, bool needsInitialization) {
+ bool isAllocatableOrPointer =
+ mlir::isa<fir::HeapType, fir::PointerType>(boxTy.getEleTy());
+ mlir::Type innerTy = fir::unwrapRefType(boxTy.getEleTy());
+ fir::IfOp ifUnallocated{nullptr};
+ if (isAllocatableOrPointer) {
+ ifUnallocated = handleNullAllocatable();
+ builder.setInsertionPointToStart(&ifUnallocated.getElseRegion().front());
+ }
+
+ mlir::Value valAlloc = builder.createHeapTemporary(loc, innerTy, /*name=*/{},
+ /*shape=*/{}, lenParams);
+ if (scalarInitValue)
+ builder.createStoreWithConvert(loc, scalarInitValue, valAlloc);
+ mlir::Value box = builder.create<fir::EmboxOp>(
+ loc, valType, valAlloc, /*shape=*/mlir::Value{},
+ /*slice=*/mlir::Value{}, lenParams);
+ initializeIfDerivedTypeBox(
+ builder, loc, box, loadedMoldArg, needsInitialization,
+ /*isFirstPrivate=*/kind == DeclOperationKind::FirstPrivate);
+ fir::StoreOp lastOp =
+ builder.create<fir::StoreOp>(loc, box, allocatedPrivVarArg);
+
+ createCleanupRegion(converter, loc, argType, cleanupRegion, sym);
+
+ if (ifUnallocated)
+ builder.setInsertionPointAfter(ifUnallocated);
+ else
+ builder.setInsertionPointAfter(lastOp);
+
+ createYield(allocatedPrivVarArg);
+}
+
+void PopulateInitAndCleanupRegionsHelper::initAndCleanupBoxedArray(
+ fir::BaseBoxType boxTy, bool needsInitialization) {
+ bool isAllocatableOrPointer =
+ mlir::isa<fir::HeapType, fir::PointerType>(boxTy.getEleTy());
+ getLengthParameters(builder, loc, loadedMoldArg, lenParams);
+
+ fir::IfOp ifUnallocated{nullptr};
+ if (isAllocatableOrPointer) {
+ ifUnallocated = handleNullAllocatable();
+ builder.setInsertionPointToStart(&ifUnallocated.getElseRegion().front());
+ }
+
+ // Create the private copy from the initial fir.box:
+ hlfir::Entity source = hlfir::Entity{loadedMoldArg};
+
+ // Special case for (possibly allocatable) arrays of polymorphic types
+ // e.g. !fir.class<!fir.heap<!fir.array<?x!fir.type<>>>>
+ if (source.isPolymorphic()) {
+ fir::ShapeShiftOp shape = getShapeShift(builder, loc, source);
+ mlir::Type arrayType = source.getElementOrSequenceType();
+ mlir::Value allocatedArray = builder.create<fir::AllocMemOp>(
+ loc, arrayType, /*typeparams=*/mlir::ValueRange{}, shape.getExtents());
+ mlir::Value firClass = builder.create<fir::EmboxOp>(loc, source.getType(),
+ allocatedArray, shape);
+ initializeIfDerivedTypeBox(
+ builder, loc, firClass, source, needsInitialization,
+ /*isFirstprivate=*/kind == DeclOperationKind::FirstPrivate);
+ builder.create<fir::StoreOp>(loc, firClass, allocatedPrivVarArg);
+ if (ifUnallocated)
+ builder.setInsertionPointAfter(ifUnallocated);
+ createYield(allocatedPrivVarArg);
+ mlir::OpBuilder::InsertionGuard guard(builder);
+ createCleanupRegion(converter, loc, argType, cleanupRegion, sym);
return;
}
- // check if an allocatable box is unallocated. If so, initialize the boxAlloca
- // to be unallocated e.g.
- // %box_alloca = fir.alloca !fir.box<!fir.heap<...>>
- // %addr = fir.box_addr %box
- // if (%addr == 0) {
- // %nullbox = fir.embox %addr
- // fir.store %nullbox to %box_alloca
- // } else {
- // // ...
- // fir.store %something to %box_alloca
- // }
- // omp.yield %box_alloca
- mlir::SmallVector<mlir::Value> lenParams;
- auto handleNullAllocatable = [&](mlir::Value boxAlloca,
- mlir::Value loadedMold) -> fir::IfOp {
- mlir::Value addr = builder.create<fir::BoxAddrOp>(loc, loadedMold);
- mlir::Value isNotAllocated = builder.genIsNullAddr(loc, addr);
- fir::IfOp ifOp = builder.create<fir::IfOp>(loc, isNotAllocated,
- /*withElseRegion=*/true);
- builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
- // Just embox the null address and return.
- // We have to give the embox a shape so that the LLVM box structure has the
- // right rank. This returns an empty value if the types don't match.
- mlir::Value shape = generateZeroShapeForRank(builder, loc, moldArg);
-
- mlir::Value nullBox =
- builder.create<fir::EmboxOp>(loc, ty, addr, shape,
- /*slice=*/mlir::Value{}, lenParams);
- builder.create<fir::StoreOp>(loc, nullBox, boxAlloca);
- return ifOp;
- };
+ // Allocating on the heap in case the whole reduction/privatization is nested
+ // inside of a loop
+ auto [temp, needsDealloc] = createTempFromMold(loc, builder, source);
+ // if needsDealloc isn't statically false, add cleanup region. Always
+ // do this for allocatable boxes because they might have been re-allocated
+ // in the body of the loop/parallel region
+
+ std::optional<int64_t> cstNeedsDealloc = fir::getIntIfConstant(needsDealloc);
+ assert(cstNeedsDealloc.has_value() &&
+ "createTempFromMold decides this statically");
+ if (cstNeedsDealloc.has_value() && *cstNeedsDealloc != false) {
+ mlir::OpBuilder::InsertionGuard guard(builder);
+ createCleanupRegion(converter, loc, argType, cleanupRegion, sym);
+ } else {
+ assert(!isAllocatableOrPointer &&
+ "Pointer-like arrays must be heap allocated");
+ }
+
+ // Put the temporary inside of a box:
+ // hlfir::genVariableBox doesn't handle non-default lower bounds
+ mlir::Value box;
+ fir::ShapeShiftOp shapeShift = getShapeShift(builder, loc, loadedMoldArg);
+ mlir::Type boxType = loadedMoldArg.getType();
+ if (mlir::isa<fir::BaseBoxType>(temp.getType()))
+ // the box created by the declare form createTempFromMold is missing
+ // lower bounds info
+ box = builder.create<fir::ReboxOp>(loc, boxType, temp, shapeShift,
+ /*shift=*/mlir::Value{});
+ else
+ box = builder.create<fir::EmboxOp>(
+ loc, boxType, temp, shapeShift,
+ /*slice=*/mlir::Value{},
+ /*typeParams=*/llvm::ArrayRef<mlir::Value>{});
+
+ if (scalarInitValue)
+ builder.create<hlfir::AssignOp>(loc, scalarInitValue, box);
+
+ initializeIfDerivedTypeBox(
+ builder, loc, box, loadedMoldArg, needsInitialization,
+ /*isFirstPrivate=*/kind == DeclOperationKind::FirstPrivate);
+
+ builder.create<fir::StoreOp>(loc, box, allocatedPrivVarArg);
+ if (ifUnallocated)
+ builder.setInsertionPointAfter(ifUnallocated);
+ createYield(allocatedPrivVarArg);
+}
- // all arrays are boxed
- if (auto boxTy = mlir::dyn_cast_or_null<fir::BaseBoxType>(ty)) {
- bool isAllocatableOrPointer =
- mlir::isa<fir::HeapType, fir::PointerType>(boxTy.getEleTy());
+void PopulateInitAndCleanupRegionsHelper::initAndCleanupBoxchar(
+ fir::BoxCharType boxCharTy) {
+ mlir::Type eleTy = boxCharTy.getEleTy();
+ builder.setInsertionPointToStart(initBlock);
+ fir::factory::CharacterExprHelper charExprHelper{builder, loc};
+ auto [addr, len] = charExprHelper.createUnboxChar(moldArg);
+
+ // Using heap temporary so that
+ // 1) It is safe to use privatization inside of big loops.
+ // 2) The lifetime can outlive the current stack frame for delayed task
+ // execution.
+ // We can't always allocate a boxchar implicitly as the type of the
+ // omp.private because the allocation potentially needs the length
+ // parameters fetched above.
+ // TODO: this deviates from the intended design for delayed task
+ // execution.
+ mlir::Value privateAddr = builder.createHeapTemporary(
+ loc, eleTy, /*name=*/{}, /*shape=*/{}, /*lenParams=*/len);
+ mlir::Value boxChar = charExprHelper.createEmboxChar(privateAddr, len);
+
+ createCleanupRegion(converter, loc, argType, cleanupRegion, sym);
+ builder.setInsertionPointToEnd(initBlock);
+ createYield(boxChar);
+}
+
+void PopulateInitAndCleanupRegionsHelper::initAndCleanupUnboxedDerivedType(
+ bool needsInitialization) {
+ builder.setInsertionPointToStart(initBlock);
+ mlir::Type boxedTy = fir::BoxType::get(valType);
+ mlir::Value newBox =
+ builder.create<fir::EmboxOp>(loc, boxedTy, allocatedPrivVarArg);
+ mlir::Value moldBox = builder.create<fir::EmboxOp>(loc, boxedTy, moldArg);
+ initializeIfDerivedTypeBox(builder, loc, newBox, moldBox, needsInitialization,
+ /*isFirstPrivate=*/kind ==
+ DeclOperationKind::FirstPrivate);
+
+ if (sym && hasFinalization(*sym))
+ createCleanupRegion(converter, loc, argType, cleanupRegion, sym);
+
+ builder.setInsertionPointToEnd(initBlock);
+ createYield(allocatedPrivVarArg);
+}
+
+/// This is the main driver deciding how to initialize the private variable.
+void PopulateInitAndCleanupRegionsHelper::populateByRefInitAndCleanupRegions() {
+ if (isPrivatization(kind)) {
+ assert(sym && "Symbol information is required to privatize derived types");
+ assert(!scalarInitValue && "ScalarInitvalue is unused for privatization");
+ }
+ mlir::Type valTy = fir::unwrapRefType(argType);
+
+ if (fir::isa_trivial(valTy)) {
+ initTrivialType();
+ return;
+ }
+
+ bool needsInitialization =
+ sym ? isDerivedTypeNeedingInitialization(sym->GetUltimate()) : false;
+
+ if (auto boxTy = mlir::dyn_cast_or_null<fir::BaseBoxType>(valTy)) {
builder.setInsertionPointToEnd(initBlock);
- mlir::Value boxAlloca = allocatedPrivVarArg;
+ // TODO: remove: boxAlloca = allocatedPrivVarArg;
- moldArg = builder.loadIfRef(loc, moldArg);
- getLengthParameters(builder, loc, moldArg, lenParams);
+ // TODO: don't do this unless it is needed
+ loadedMoldArg = builder.loadIfRef(loc, moldArg);
+ getLengthParameters(builder, loc, loadedMoldArg, lenParams);
- // The initial state of a private pointer is undefined so we don't need to
- // match the mold argument (OpenMP 5.2 end of page 106).
if (isPrivatization(kind) &&
mlir::isa<fir::PointerType>(boxTy.getEleTy())) {
- // we need a shape with the right rank so that the embox op is lowered
- // to an llvm struct of the right type. This returns nullptr if the types
- // aren't right.
- mlir::Value shape = generateZeroShapeForRank(builder, loc, moldArg);
- // Just incase, do initialize the box with a null value
- mlir::Value null = builder.createNullConstant(loc, boxTy.getEleTy());
- mlir::Value nullBox;
- nullBox = builder.create<fir::EmboxOp>(
- loc, boxTy, null, shape, /*slice=*/mlir::Value{}, lenParams);
- builder.create<fir::StoreOp>(loc, nullBox, boxAlloca);
- yield(boxAlloca);
+ initBoxedPrivatePointer(boxTy);
return;
}
@@ -331,177 +568,41 @@ void Fortran::lower::omp::populateByRefInitAndCleanupRegions(
bool isChar = fir::isa_char(innerTy);
if (fir::isa_trivial(innerTy) || isDerived || isChar) {
// boxed non-sequence value e.g. !fir.box<!fir.heap<i32>>
- if (!isAllocatableOrPointer && !isDerived)
- TODO(loc, "Reduction/Privatization of non-allocatable trivial or "
- "character typed box");
-
if ((isDerived || isChar) && (isReduction(kind) || scalarInitValue))
TODO(loc, "Reduction of an unsupported boxed type");
-
- fir::IfOp ifUnallocated{nullptr};
- if (isAllocatableOrPointer) {
- ifUnallocated = handleNullAllocatable(boxAlloca, moldArg);
- builder.setInsertionPointToStart(
- &ifUnallocated.getElseRegion().front());
- }
-
- mlir::Value valAlloc = builder.createHeapTemporary(
- loc, innerTy, /*name=*/{}, /*shape=*/{}, lenParams);
- if (scalarInitValue)
- builder.createStoreWithConvert(loc, scalarInitValue, valAlloc);
- mlir::Value box = builder.create<fir::EmboxOp>(
- loc, ty, valAlloc, /*shape=*/mlir::Value{}, /*slice=*/mlir::Value{},
- lenParams);
- initializeIfDerivedTypeBox(
- builder, loc, box, moldArg, needsInitialization,
- /*isFirstPrivate=*/kind == DeclOperationKind::FirstPrivate);
- fir::StoreOp lastOp = builder.create<fir::StoreOp>(loc, box, boxAlloca);
-
- createCleanupRegion(converter, loc, argType, cleanupRegion, sym);
-
- if (ifUnallocated)
- builder.setInsertionPointAfter(ifUnallocated);
- else
- builder.setInsertionPointAfter(lastOp);
- yield(boxAlloca);
+ initAndCleanupBoxedScalar(boxTy, needsInitialization);
return;
}
innerTy = fir::extractSequenceType(boxTy);
if (!innerTy || !mlir::isa<fir::SequenceType>(innerTy))
TODO(loc, "Unsupported boxed type for reduction/privatization");
-
- moldArg = builder.loadIfRef(loc, moldArg);
- getLengthParameters(builder, loc, moldArg, lenParams);
-
- fir::IfOp ifUnallocated{nullptr};
- if (isAllocatableOrPointer) {
- ifUnallocated = handleNullAllocatable(boxAlloca, moldArg);
- builder.setInsertionPointToStart(&ifUnallocated.getElseRegion().front());
- }
-
- // Create the private copy from the initial fir.box:
- mlir::Value loadedBox = builder.loadIfRef(loc, moldArg);
- hlfir::Entity source = hlfir::Entity{loadedBox};
-
- // Special case for (possibly allocatable) arrays of polymorphic types
- // e.g. !fir.class<!fir.heap<!fir.array<?x!fir.type<>>>>
- if (source.isPolymorphic()) {
- fir::ShapeShiftOp shape = getShapeShift(builder, loc, source);
- mlir::Type arrayType = source.getElementOrSequenceType();
- mlir::Value allocatedArray = builder.create<fir::AllocMemOp>(
- loc, arrayType, /*typeparams=*/mlir::ValueRange{},
- shape.getExtents());
- mlir::Value firClass = builder.create<fir::EmboxOp>(
- loc, source.getType(), allocatedArray, shape);
- initializeIfDerivedTypeBox(
- builder, loc, firClass, source, needsInitialization,
- /*isFirstprivate=*/kind == DeclOperationKind::FirstPrivate);
- builder.create<fir::StoreOp>(loc, firClass, allocatedPrivVarArg);
- if (ifUnallocated)
- builder.setInsertionPointAfter(ifUnallocated);
- yield(allocatedPrivVarArg);
- mlir::OpBuilder::InsertionGuard guard(builder);
- createCleanupRegion(converter, loc, argType, cleanupRegion, sym);
- return;
- }
-
- // Allocating on the heap in case the whole reduction is nested inside of a
- // loop
- // TODO: compare performance here to using allocas - this could be made to
- // work by inserting stacksave/stackrestore around the reduction in
- // openmpirbuilder
- auto [temp, needsDealloc] = createTempFromMold(loc, builder, source);
- // if needsDealloc isn't statically false, add cleanup region. Always
- // do this for allocatable boxes because they might have been re-allocated
- // in the body of the loop/parallel region
-
- std::optional<int64_t> cstNeedsDealloc =
- fir::getIntIfConstant(needsDealloc);
- assert(cstNeedsDealloc.has_value() &&
- "createTempFromMold decides this statically");
- if (cstNeedsDealloc.has_value() && *cstNeedsDealloc != false) {
- mlir::OpBuilder::InsertionGuard guard(builder);
- createCleanupRegion(converter, loc, argType, cleanupRegion, sym);
- } else {
- assert(!isAllocatableOrPointer &&
- "Pointer-like arrays must be heap allocated");
- }
-
- // Put the temporary inside of a box:
- // hlfir::genVariableBox doesn't handle non-default lower bounds
- mlir::Value box;
- fir::ShapeShiftOp shapeShift = getShapeShift(builder, loc, loadedBox);
- mlir::Type boxType = loadedBox.getType();
- if (mlir::isa<fir::BaseBoxType>(temp.getType()))
- // the box created by the declare form createTempFromMold is missing lower
- // bounds info
- box = builder.create<fir::ReboxOp>(loc, boxType, temp, shapeShift,
- /*shift=*/mlir::Value{});
- else
- box = builder.create<fir::EmboxOp>(
- loc, boxType, temp, shapeShift,
- /*slice=*/mlir::Value{},
- /*typeParams=*/llvm::ArrayRef<mlir::Value>{});
-
- if (scalarInitValue)
- builder.create<hlfir::AssignOp>(loc, scalarInitValue, box);
-
- initializeIfDerivedTypeBox(builder, loc, box, moldArg, needsInitialization,
- /*isFirstPrivate=*/kind ==
- DeclOperationKind::FirstPrivate);
-
- builder.create<fir::StoreOp>(loc, box, boxAlloca);
- if (ifUnallocated)
- builder.setInsertionPointAfter(ifUnallocated);
- yield(boxAlloca);
+ initAndCleanupBoxedArray(boxTy, needsInitialization);
return;
}
+ // Unboxed types:
if (auto boxCharTy = mlir::dyn_cast<fir::BoxCharType>(argType)) {
- mlir::Type eleTy = boxCharTy.getEleTy();
- builder.setInsertionPointToStart(initBlock);
- fir::factory::CharacterExprHelper charExprHelper{builder, loc};
- auto [addr, len] = charExprHelper.createUnboxChar(moldArg);
-
- // Using heap temporary so that
- // 1) It is safe to use privatization inside of big loops.
- // 2) The lifetime can outlive the current stack frame for delayed task
- // execution.
- // We can't always allocate a boxchar implicitly as the type of the
- // omp.private because the allocation potentially needs the length
- // parameters fetched above.
- // TODO: this deviates from the intended design for delayed task execution.
- mlir::Value privateAddr = builder.createHeapTemporary(
- loc, eleTy, /*name=*/{}, /*shape=*/{}, /*lenParams=*/len);
- mlir::Value boxChar = charExprHelper.createEmboxChar(privateAddr, len);
-
- createCleanupRegion(converter, loc, argType, cleanupRegion, sym);
-
- builder.setInsertionPointToEnd(initBlock);
- yield(boxChar);
+ initAndCleanupBoxchar(boxCharTy);
return;
}
-
- if (fir::isa_derived(ty)) {
- builder.setInsertionPointToStart(initBlock);
- mlir::Type boxedTy = fir::BoxType::get(ty);
- mlir::Value newBox =
- builder.create<fir::EmboxOp>(loc, boxedTy, allocatedPrivVarArg);
- mlir::Value moldBox = builder.create<fir::EmboxOp>(loc, boxedTy, moldArg);
- initializeIfDerivedTypeBox(
- builder, loc, newBox, moldBox, needsInitialization,
- /*isFirstPrivate=*/kind == DeclOperationKind::FirstPrivate);
-
- if (sym && hasFinalization(*sym))
- createCleanupRegion(converter, loc, argType, cleanupRegion, sym);
-
- builder.setInsertionPointToEnd(initBlock);
- yield(allocatedPrivVarArg);
+ if (fir::isa_derived(valType)) {
+ initAndCleanupUnboxedDerivedType(needsInitialization);
return;
}
TODO(loc,
"creating reduction/privatization init region for unsupported type");
- return;
+}
+
+void Fortran::lower::omp::populateByRefInitAndCleanupRegions(
+ Fortran::lower::AbstractConverter &converter, mlir::Location loc,
+ mlir::Type argType, mlir::Value scalarInitValue, mlir::Block *initBlock,
+ mlir::Value allocatedPrivVarArg, mlir::Value moldArg,
+ mlir::Region &cleanupRegion, DeclOperationKind kind,
+ const Fortran::semantics::Symbol *sym) {
+ PopulateInitAndCleanupRegionsHelper helper(
+ converter, loc, argType, scalarInitValue, allocatedPrivVarArg, moldArg,
+ initBlock, cleanupRegion, kind, sym);
+ helper.populateByRefInitAndCleanupRegions();
}
>From 8fb8c64a4570b357f1b2096ef08e5f877f3b61da Mon Sep 17 00:00:00 2001
From: Tom Eccles <tom.eccles at arm.com>
Date: Wed, 5 Feb 2025 16:14:06 +0000
Subject: [PATCH 2/2] Remove debugging comment
---
flang/lib/Lower/OpenMP/PrivateReductionUtils.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/flang/lib/Lower/OpenMP/PrivateReductionUtils.cpp b/flang/lib/Lower/OpenMP/PrivateReductionUtils.cpp
index 11dcc053f14115d..d41564da207112d 100644
--- a/flang/lib/Lower/OpenMP/PrivateReductionUtils.cpp
+++ b/flang/lib/Lower/OpenMP/PrivateReductionUtils.cpp
@@ -551,7 +551,6 @@ void PopulateInitAndCleanupRegionsHelper::populateByRefInitAndCleanupRegions() {
if (auto boxTy = mlir::dyn_cast_or_null<fir::BaseBoxType>(valTy)) {
builder.setInsertionPointToEnd(initBlock);
- // TODO: remove: boxAlloca = allocatedPrivVarArg;
// TODO: don't do this unless it is needed
loadedMoldArg = builder.loadIfRef(loc, moldArg);
More information about the flang-commits
mailing list