diff --git a/flang/lib/Lower/OpenMP/PrivateReductionUtils.cpp b/flang/lib/Lower/OpenMP/PrivateReductionUtils.cpp index 61706d0824101..d41564da20711 100644 --- a/flang/lib/Lower/OpenMP/PrivateReductionUtils.cpp +++ b/flang/lib/Lower/OpenMP/PrivateReductionUtils.cpp @@ -238,91 +238,327 @@ static mlir::Value generateZeroShapeForRank(fir::FirOpBuilder &builder, return builder.create(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(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 lenParams; + + void createYield(mlir::Value ret) { + builder.create(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.box>, + /// !fir.box> + 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(loc, boxTy, null, shape, + /*slice=*/mlir::Value{}, lenParams); + builder.create(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> +/// %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(loc, loadedMoldArg); + mlir::Value isNotAllocated = builder.genIsNullAddr(loc, addr); + fir::IfOp ifOp = builder.create(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(loc, valType, addr, shape, + /*slice=*/mlir::Value{}, lenParams); + builder.create(loc, nullBox, allocatedPrivVarArg); + return ifOp; +} + +void PopulateInitAndCleanupRegionsHelper::initAndCleanupBoxedScalar( + fir::BaseBoxType boxTy, bool needsInitialization) { + bool isAllocatableOrPointer = + mlir::isa(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( + 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(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(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>>> + if (source.isPolymorphic()) { + fir::ShapeShiftOp shape = getShapeShift(builder, loc, source); + mlir::Type arrayType = source.getElementOrSequenceType(); + mlir::Value allocatedArray = builder.create( + loc, arrayType, /*typeparams=*/mlir::ValueRange{}, shape.getExtents()); + mlir::Value firClass = builder.create(loc, source.getType(), + allocatedArray, shape); + initializeIfDerivedTypeBox( + builder, loc, firClass, source, needsInitialization, + /*isFirstprivate=*/kind == DeclOperationKind::FirstPrivate); + builder.create(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> - // %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 lenParams; - auto handleNullAllocatable = [&](mlir::Value boxAlloca, - mlir::Value loadedMold) -> fir::IfOp { - mlir::Value addr = builder.create(loc, loadedMold); - mlir::Value isNotAllocated = builder.genIsNullAddr(loc, addr); - fir::IfOp ifOp = builder.create(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(loc, ty, addr, shape, - /*slice=*/mlir::Value{}, lenParams); - builder.create(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 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(temp.getType())) + // the box created by the declare form createTempFromMold is missing + // lower bounds info + box = builder.create(loc, boxType, temp, shapeShift, + /*shift=*/mlir::Value{}); + else + box = builder.create( + loc, boxType, temp, shapeShift, + /*slice=*/mlir::Value{}, + /*typeParams=*/llvm::ArrayRef{}); + + if (scalarInitValue) + builder.create(loc, scalarInitValue, box); + + initializeIfDerivedTypeBox( + builder, loc, box, loadedMoldArg, needsInitialization, + /*isFirstPrivate=*/kind == DeclOperationKind::FirstPrivate); + + builder.create(loc, box, allocatedPrivVarArg); + if (ifUnallocated) + builder.setInsertionPointAfter(ifUnallocated); + createYield(allocatedPrivVarArg); +} - // all arrays are boxed - if (auto boxTy = mlir::dyn_cast_or_null(ty)) { - bool isAllocatableOrPointer = - mlir::isa(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(loc, boxedTy, allocatedPrivVarArg); + mlir::Value moldBox = builder.create(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(valTy)) { builder.setInsertionPointToEnd(initBlock); - mlir::Value 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(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( - loc, boxTy, null, shape, /*slice=*/mlir::Value{}, lenParams); - builder.create(loc, nullBox, boxAlloca); - yield(boxAlloca); + initBoxedPrivatePointer(boxTy); return; } @@ -331,177 +567,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> - 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( - 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(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(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>>> - if (source.isPolymorphic()) { - fir::ShapeShiftOp shape = getShapeShift(builder, loc, source); - mlir::Type arrayType = source.getElementOrSequenceType(); - mlir::Value allocatedArray = builder.create( - loc, arrayType, /*typeparams=*/mlir::ValueRange{}, - shape.getExtents()); - mlir::Value firClass = builder.create( - loc, source.getType(), allocatedArray, shape); - initializeIfDerivedTypeBox( - builder, loc, firClass, source, needsInitialization, - /*isFirstprivate=*/kind == DeclOperationKind::FirstPrivate); - builder.create(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 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(temp.getType())) - // the box created by the declare form createTempFromMold is missing lower - // bounds info - box = builder.create(loc, boxType, temp, shapeShift, - /*shift=*/mlir::Value{}); - else - box = builder.create( - loc, boxType, temp, shapeShift, - /*slice=*/mlir::Value{}, - /*typeParams=*/llvm::ArrayRef{}); - - if (scalarInitValue) - builder.create(loc, scalarInitValue, box); - - initializeIfDerivedTypeBox(builder, loc, box, moldArg, needsInitialization, - /*isFirstPrivate=*/kind == - DeclOperationKind::FirstPrivate); - - builder.create(loc, box, boxAlloca); - if (ifUnallocated) - builder.setInsertionPointAfter(ifUnallocated); - yield(boxAlloca); + initAndCleanupBoxedArray(boxTy, needsInitialization); return; } + // Unboxed types: if (auto boxCharTy = mlir::dyn_cast(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(loc, boxedTy, allocatedPrivVarArg); - mlir::Value moldBox = builder.create(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(); }