blob: 268c7828ab56fd21c96b50dc466d008f73efba17 [file] [log] [blame] [edit]
//===-- PrivateReductionUtils.cpp -------------------------------*- C++ -*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
//
//===----------------------------------------------------------------------===//
#include "PrivateReductionUtils.h"
#include "flang/Lower/AbstractConverter.h"
#include "flang/Lower/Allocatable.h"
#include "flang/Lower/ConvertVariable.h"
#include "flang/Optimizer/Builder/BoxValue.h"
#include "flang/Optimizer/Builder/Character.h"
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Builder/HLFIRTools.h"
#include "flang/Optimizer/Builder/Runtime/Derived.h"
#include "flang/Optimizer/Builder/Todo.h"
#include "flang/Optimizer/Dialect/FIROps.h"
#include "flang/Optimizer/Dialect/FIRType.h"
#include "flang/Optimizer/HLFIR/HLFIRDialect.h"
#include "flang/Optimizer/HLFIR/HLFIROps.h"
#include "flang/Optimizer/Support/FatalError.h"
#include "flang/Semantics/symbol.h"
#include "mlir/Dialect/OpenMP/OpenMPDialect.h"
#include "mlir/IR/Location.h"
static bool hasFinalization(const Fortran::semantics::Symbol &sym) {
if (sym.has<Fortran::semantics::ObjectEntityDetails>())
if (const Fortran::semantics::DeclTypeSpec *declTypeSpec = sym.GetType())
if (const Fortran::semantics::DerivedTypeSpec *derivedTypeSpec =
declTypeSpec->AsDerived())
return Fortran::semantics::IsFinalizable(*derivedTypeSpec);
return false;
}
static void createCleanupRegion(Fortran::lower::AbstractConverter &converter,
mlir::Location loc, mlir::Type argType,
mlir::Region &cleanupRegion,
const Fortran::semantics::Symbol *sym) {
fir::FirOpBuilder &builder = converter.getFirOpBuilder();
assert(cleanupRegion.empty());
mlir::Block *block = builder.createBlock(&cleanupRegion, cleanupRegion.end(),
{argType}, {loc});
builder.setInsertionPointToEnd(block);
auto typeError = [loc]() {
fir::emitFatalError(loc,
"Attempt to create an omp cleanup region "
"for a type that wasn't allocated",
/*genCrashDiag=*/true);
};
mlir::Type valTy = fir::unwrapRefType(argType);
if (auto boxTy = mlir::dyn_cast_or_null<fir::BaseBoxType>(valTy)) {
// TODO: what about undoing init of unboxed derived types?
if (auto recTy = mlir::dyn_cast<fir::RecordType>(
fir::unwrapSequenceType(fir::dyn_cast_ptrOrBoxEleTy(boxTy)))) {
mlir::Type eleTy = boxTy.getEleTy();
if (mlir::isa<fir::PointerType, fir::HeapType>(eleTy)) {
mlir::Type mutableBoxTy =
fir::ReferenceType::get(fir::BoxType::get(eleTy));
mlir::Value converted =
builder.createConvert(loc, mutableBoxTy, block->getArgument(0));
if (recTy.getNumLenParams() > 0)
TODO(loc, "Deallocate box with length parameters");
fir::MutableBoxValue mutableBox{converted, /*lenParameters=*/{},
/*mutableProperties=*/{}};
Fortran::lower::genDeallocateIfAllocated(converter, mutableBox, loc);
builder.create<mlir::omp::YieldOp>(loc);
return;
}
}
// TODO: just replace this whole body with
// Fortran::lower::genDeallocateIfAllocated (not done now to avoid test
// churn)
mlir::Value arg = builder.loadIfRef(loc, block->getArgument(0));
assert(mlir::isa<fir::BaseBoxType>(arg.getType()));
// Deallocate box
// The FIR type system doesn't nesecarrily know that this is a mutable box
// if we allocated the thread local array on the heap to avoid looped stack
// allocations.
mlir::Value addr =
hlfir::genVariableRawAddress(loc, builder, hlfir::Entity{arg});
mlir::Value isAllocated = builder.genIsNotNullAddr(loc, addr);
fir::IfOp ifOp =
builder.create<fir::IfOp>(loc, isAllocated, /*withElseRegion=*/false);
builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
mlir::Value cast = builder.createConvert(
loc, fir::HeapType::get(fir::dyn_cast_ptrEleTy(addr.getType())), addr);
builder.create<fir::FreeMemOp>(loc, cast);
builder.setInsertionPointAfter(ifOp);
builder.create<mlir::omp::YieldOp>(loc);
return;
}
if (auto boxCharTy = mlir::dyn_cast<fir::BoxCharType>(argType)) {
auto [addr, len] =
fir::factory::CharacterExprHelper{builder, loc}.createUnboxChar(
block->getArgument(0));
// convert addr to a heap type so it can be used with fir::FreeMemOp
auto refTy = mlir::cast<fir::ReferenceType>(addr.getType());
auto heapTy = fir::HeapType::get(refTy.getEleTy());
addr = builder.createConvert(loc, heapTy, addr);
builder.create<fir::FreeMemOp>(loc, addr);
builder.create<mlir::omp::YieldOp>(loc);
return;
}
typeError();
}
fir::ShapeShiftOp Fortran::lower::omp::getShapeShift(
fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value box,
bool cannotHaveNonDefaultLowerBounds, bool useDefaultLowerBounds) {
fir::SequenceType sequenceType = mlir::cast<fir::SequenceType>(
hlfir::getFortranElementOrSequenceType(box.getType()));
const unsigned rank = sequenceType.getDimension();
llvm::SmallVector<mlir::Value> lbAndExtents;
lbAndExtents.reserve(rank * 2);
mlir::Type idxTy = builder.getIndexType();
mlir::Value oneVal;
auto one = [&] {
if (!oneVal)
oneVal = builder.createIntegerConstant(loc, idxTy, 1);
return oneVal;
};
if ((cannotHaveNonDefaultLowerBounds || useDefaultLowerBounds) &&
!sequenceType.hasDynamicExtents()) {
// We don't need fir::BoxDimsOp if all of the extents are statically known
// and we can assume default lower bounds. This helps avoids reads from the
// mold arg.
// We may also want to use default lower bounds to iterate through array
// elements without having to adjust each index.
for (int64_t extent : sequenceType.getShape()) {
assert(extent != sequenceType.getUnknownExtent());
lbAndExtents.push_back(one());
mlir::Value extentVal = builder.createIntegerConstant(loc, idxTy, extent);
lbAndExtents.push_back(extentVal);
}
} else {
for (unsigned i = 0; i < rank; ++i) {
// TODO: ideally we want to hoist box reads out of the critical section.
// We could do this by having box dimensions in block arguments like
// OpenACC does
mlir::Value dim = builder.createIntegerConstant(loc, idxTy, i);
auto dimInfo =
builder.create<fir::BoxDimsOp>(loc, idxTy, idxTy, idxTy, box, dim);
lbAndExtents.push_back(useDefaultLowerBounds ? one()
: dimInfo.getLowerBound());
lbAndExtents.push_back(dimInfo.getExtent());
}
}
auto shapeShiftTy = fir::ShapeShiftType::get(builder.getContext(), rank);
auto shapeShift =
builder.create<fir::ShapeShiftOp>(loc, shapeShiftTy, lbAndExtents);
return shapeShift;
}
// Initialize box newBox using moldBox. These should both have the same type and
// be boxes containing derived types e.g.
// fir.box<!fir.type<>>
// fir.box<!fir.heap<!fir.type<>>
// fir.box<!fir.heap<!fir.array<fir.type<>>>
// fir.class<...<!fir.type<>>>
// If the type doesn't match , this does nothing
static void initializeIfDerivedTypeBox(fir::FirOpBuilder &builder,
mlir::Location loc, mlir::Value newBox,
mlir::Value moldBox, bool hasInitializer,
bool isFirstPrivate) {
assert(moldBox.getType() == newBox.getType());
fir::BoxType boxTy = mlir::dyn_cast<fir::BoxType>(newBox.getType());
fir::ClassType classTy = mlir::dyn_cast<fir::ClassType>(newBox.getType());
if (!boxTy && !classTy)
return;
// remove pointer and array types in the middle
mlir::Type eleTy = boxTy ? boxTy.getElementType() : classTy.getEleTy();
mlir::Type derivedTy = fir::unwrapRefType(eleTy);
if (auto array = mlir::dyn_cast<fir::SequenceType>(derivedTy))
derivedTy = array.getElementType();
if (!fir::isa_derived(derivedTy))
return;
if (hasInitializer)
fir::runtime::genDerivedTypeInitialize(builder, loc, newBox);
if (hlfir::mayHaveAllocatableComponent(derivedTy) && !isFirstPrivate)
fir::runtime::genDerivedTypeInitializeClone(builder, loc, newBox, moldBox);
}
static void getLengthParameters(fir::FirOpBuilder &builder, mlir::Location loc,
mlir::Value moldArg,
llvm::SmallVectorImpl<mlir::Value> &lenParams) {
// We pass derived types unboxed and so are not self-contained entities.
// Assume that unboxed derived types won't need length paramters.
if (!hlfir::isFortranEntity(moldArg))
return;
hlfir::genLengthParameters(loc, builder, hlfir::Entity{moldArg}, lenParams);
if (lenParams.empty())
return;
// The verifier for EmboxOp doesn't allow length parameters when the the
// character already has static LEN. genLengthParameters may still return them
// in this case.
auto strTy = mlir::dyn_cast<fir::CharacterType>(
fir::getFortranElementType(moldArg.getType()));
if (strTy && strTy.hasConstantLen())
lenParams.resize(0);
}
static bool
isDerivedTypeNeedingInitialization(const Fortran::semantics::Symbol &sym) {
// Fortran::lower::hasDefaultInitialization returns false for ALLOCATABLE, so
// re-implement here.
// ignorePointer=true because either the pointer points to the same target as
// the original variable, or it is uninitialized.
if (const Fortran::semantics::DeclTypeSpec *declTypeSpec = sym.GetType())
if (const Fortran::semantics::DerivedTypeSpec *derivedTypeSpec =
declTypeSpec->AsDerived())
return derivedTypeSpec->HasDefaultInitialization(
/*ignoreAllocatable=*/false, /*ignorePointer=*/true);
return false;
}
static mlir::Value generateZeroShapeForRank(fir::FirOpBuilder &builder,
mlir::Location loc,
mlir::Value moldArg) {
mlir::Type moldType = fir::unwrapRefType(moldArg.getType());
mlir::Type eleType = fir::dyn_cast_ptrOrBoxEleTy(moldType);
fir::SequenceType seqTy =
mlir::dyn_cast_if_present<fir::SequenceType>(eleType);
if (!seqTy)
return mlir::Value{};
unsigned rank = seqTy.getShape().size();
mlir::Value zero =
builder.createIntegerConstant(loc, builder.getIndexType(), 0);
mlir::SmallVector<mlir::Value> dims;
dims.resize(rank, zero);
mlir::Type shapeTy = fir::ShapeType::get(builder.getContext(), rank);
return builder.create<fir::ShapeOp>(loc, shapeTy, dims);
}
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,
bool cannotHaveLowerBounds)
: converter{converter}, builder{converter.getFirOpBuilder()}, loc{loc},
argType{argType}, scalarInitValue{scalarInitValue},
allocatedPrivVarArg{allocatedPrivVarArg}, moldArg{moldArg},
initBlock{initBlock}, cleanupRegion{cleanupRegion}, kind{kind},
sym{sym}, cannotHaveNonDefaultLowerBounds{cannotHaveLowerBounds} {
valType = fir::unwrapRefType(argType);
}
void populateByRefInitAndCleanupRegions();
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. Access via
/// getLoadedMoldArg().
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;
/// If the source variable being privatized definitely can't have non-default
/// lower bounds then we don't need to generate code to read them.
bool cannotHaveNonDefaultLowerBounds;
void createYield(mlir::Value ret) {
builder.create<mlir::omp::YieldOp>(loc, ret);
}
void initTrivialType() {
builder.setInsertionPointToEnd(initBlock);
if (scalarInitValue)
builder.createStoreWithConvert(loc, scalarInitValue, 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();
// Do this lazily so that we don't load it when it is not used.
inline mlir::Value getLoadedMoldArg() {
if (loadedMoldArg)
return loadedMoldArg;
loadedMoldArg = builder.loadIfRef(loc, moldArg);
return loadedMoldArg;
}
};
} // 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, 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, 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, getLoadedMoldArg());
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, getLoadedMoldArg(), 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, getLoadedMoldArg(), 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{getLoadedMoldArg()};
// 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, cannotHaveNonDefaultLowerBounds);
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;
}
// 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, getLoadedMoldArg(),
cannotHaveNonDefaultLowerBounds);
mlir::Type boxType = getLoadedMoldArg().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, getLoadedMoldArg(), needsInitialization,
/*isFirstPrivate=*/kind == DeclOperationKind::FirstPrivate);
builder.create<fir::StoreOp>(loc, box, allocatedPrivVarArg);
if (ifUnallocated)
builder.setInsertionPointAfter(ifUnallocated);
createYield(allocatedPrivVarArg);
}
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);
// TODO: don't do this unless it is needed
getLengthParameters(builder, loc, getLoadedMoldArg(), lenParams);
if (isPrivatization(kind) &&
mlir::isa<fir::PointerType>(boxTy.getEleTy())) {
initBoxedPrivatePointer(boxTy);
return;
}
mlir::Type innerTy = fir::unwrapRefType(boxTy.getEleTy());
bool isDerived = fir::isa_derived(innerTy);
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 ((isDerived || isChar) && (isReduction(kind) || scalarInitValue))
TODO(loc, "Reduction of an unsupported boxed type");
initAndCleanupBoxedScalar(boxTy, needsInitialization);
return;
}
innerTy = fir::extractSequenceType(boxTy);
if (!innerTy || !mlir::isa<fir::SequenceType>(innerTy))
TODO(loc, "Unsupported boxed type for reduction/privatization");
initAndCleanupBoxedArray(boxTy, needsInitialization);
return;
}
// Unboxed types:
if (auto boxCharTy = mlir::dyn_cast<fir::BoxCharType>(argType)) {
initAndCleanupBoxchar(boxCharTy);
return;
}
if (fir::isa_derived(valType)) {
initAndCleanupUnboxedDerivedType(needsInitialization);
return;
}
TODO(loc,
"creating reduction/privatization init region for unsupported type");
}
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, bool cannotHaveLowerBounds) {
PopulateInitAndCleanupRegionsHelper helper(
converter, loc, argType, scalarInitValue, allocatedPrivVarArg, moldArg,
initBlock, cleanupRegion, kind, sym, cannotHaveLowerBounds);
helper.populateByRefInitAndCleanupRegions();
// Often we load moldArg to check something (e.g. length parameters, shape)
// but then those answers can be gotten statically without accessing the
// runtime value and so the only remaining use is a dead load. These loads can
// force us to insert additional barriers and so should be avoided where
// possible.
if (moldArg.hasOneUse()) {
mlir::Operation *user = *moldArg.getUsers().begin();
if (auto load = mlir::dyn_cast<fir::LoadOp>(user))
if (load.use_empty())
load.erase();
}
}