| //===-- Allocatable.cpp -- Allocatable statements lowering ----------------===// | 
 | // | 
 | // 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 "flang/Lower/Allocatable.h" | 
 | #include "flang/Evaluate/tools.h" | 
 | #include "flang/Lower/AbstractConverter.h" | 
 | #include "flang/Lower/CUDA.h" | 
 | #include "flang/Lower/ConvertType.h" | 
 | #include "flang/Lower/ConvertVariable.h" | 
 | #include "flang/Lower/IterationSpace.h" | 
 | #include "flang/Lower/Mangler.h" | 
 | #include "flang/Lower/OpenACC.h" | 
 | #include "flang/Lower/PFTBuilder.h" | 
 | #include "flang/Lower/Runtime.h" | 
 | #include "flang/Lower/StatementContext.h" | 
 | #include "flang/Optimizer/Builder/CUFCommon.h" | 
 | #include "flang/Optimizer/Builder/FIRBuilder.h" | 
 | #include "flang/Optimizer/Builder/Runtime/RTBuilder.h" | 
 | #include "flang/Optimizer/Builder/Todo.h" | 
 | #include "flang/Optimizer/Dialect/CUF/CUFOps.h" | 
 | #include "flang/Optimizer/Dialect/FIROps.h" | 
 | #include "flang/Optimizer/Dialect/FIROpsSupport.h" | 
 | #include "flang/Optimizer/HLFIR/HLFIROps.h" | 
 | #include "flang/Optimizer/Support/FatalError.h" | 
 | #include "flang/Optimizer/Support/InternalNames.h" | 
 | #include "flang/Parser/parse-tree.h" | 
 | #include "flang/Runtime/allocatable.h" | 
 | #include "flang/Runtime/pointer.h" | 
 | #include "flang/Semantics/tools.h" | 
 | #include "flang/Semantics/type.h" | 
 | #include "llvm/Support/CommandLine.h" | 
 |  | 
 | /// By default fir memory operation fir::AllocMemOp/fir::FreeMemOp are used. | 
 | /// This switch allow forcing the use of runtime and descriptors for everything. | 
 | /// This is mainly intended as a debug switch. | 
 | static llvm::cl::opt<bool> useAllocateRuntime( | 
 |     "use-alloc-runtime", | 
 |     llvm::cl::desc("Lower allocations to fortran runtime calls"), | 
 |     llvm::cl::init(false)); | 
 | /// Switch to force lowering of allocatable and pointers to descriptors in all | 
 | /// cases. This is now turned on by default since that is what will happen with | 
 | /// HLFIR lowering, so this allows getting early feedback of the impact. | 
 | /// If this turns out to cause performance regressions, a dedicated fir.box | 
 | /// "discretization pass" would make more sense to cover all the fir.box usage | 
 | /// (taking advantage of any future inlining for instance). | 
 | static llvm::cl::opt<bool> useDescForMutableBox( | 
 |     "use-desc-for-alloc", | 
 |     llvm::cl::desc("Always use descriptors for POINTER and ALLOCATABLE"), | 
 |     llvm::cl::init(true)); | 
 |  | 
 | //===----------------------------------------------------------------------===// | 
 | // Error management | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | namespace { | 
 | // Manage STAT and ERRMSG specifier information across a sequence of runtime | 
 | // calls for an ALLOCATE/DEALLOCATE stmt. | 
 | struct ErrorManager { | 
 |   void init(Fortran::lower::AbstractConverter &converter, mlir::Location loc, | 
 |             const Fortran::lower::SomeExpr *statExpr, | 
 |             const Fortran::lower::SomeExpr *errMsgExpr) { | 
 |     Fortran::lower::StatementContext stmtCtx; | 
 |     fir::FirOpBuilder &builder = converter.getFirOpBuilder(); | 
 |     hasStat = builder.createBool(loc, statExpr != nullptr); | 
 |     statAddr = statExpr | 
 |                    ? fir::getBase(converter.genExprAddr(loc, statExpr, stmtCtx)) | 
 |                    : mlir::Value{}; | 
 |     errMsgAddr = | 
 |         statExpr && errMsgExpr | 
 |             ? builder.createBox(loc, | 
 |                                 converter.genExprAddr(loc, errMsgExpr, stmtCtx)) | 
 |             : fir::AbsentOp::create( | 
 |                   builder, loc, | 
 |                   fir::BoxType::get(mlir::NoneType::get(builder.getContext()))); | 
 |     sourceFile = fir::factory::locationToFilename(builder, loc); | 
 |     sourceLine = fir::factory::locationToLineNo(builder, loc, | 
 |                                                 builder.getIntegerType(32)); | 
 |   } | 
 |  | 
 |   bool hasStatSpec() const { return static_cast<bool>(statAddr); } | 
 |  | 
 |   void genStatCheck(fir::FirOpBuilder &builder, mlir::Location loc) { | 
 |     if (statValue) { | 
 |       mlir::Value zero = | 
 |           builder.createIntegerConstant(loc, statValue.getType(), 0); | 
 |       auto cmp = mlir::arith::CmpIOp::create( | 
 |           builder, loc, mlir::arith::CmpIPredicate::eq, statValue, zero); | 
 |       auto ifOp = fir::IfOp::create(builder, loc, cmp, | 
 |                                     /*withElseRegion=*/false); | 
 |       builder.setInsertionPointToStart(&ifOp.getThenRegion().front()); | 
 |     } | 
 |   } | 
 |  | 
 |   void assignStat(fir::FirOpBuilder &builder, mlir::Location loc, | 
 |                   mlir::Value stat) { | 
 |     if (hasStatSpec()) { | 
 |       assert(stat && "missing stat value"); | 
 |       mlir::Value castStat = builder.createConvert( | 
 |           loc, fir::dyn_cast_ptrEleTy(statAddr.getType()), stat); | 
 |       fir::StoreOp::create(builder, loc, castStat, statAddr); | 
 |       statValue = stat; | 
 |     } | 
 |   } | 
 |  | 
 |   mlir::Value hasStat; | 
 |   mlir::Value errMsgAddr; | 
 |   mlir::Value sourceFile; | 
 |   mlir::Value sourceLine; | 
 |  | 
 | private: | 
 |   mlir::Value statAddr;  // STAT variable address | 
 |   mlir::Value statValue; // current runtime STAT value | 
 | }; | 
 |  | 
 | //===----------------------------------------------------------------------===// | 
 | // Allocatables runtime call generators | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | using namespace Fortran::runtime; | 
 | /// Generate a runtime call to set the bounds of an allocatable or pointer | 
 | /// descriptor. | 
 | static void genRuntimeSetBounds(fir::FirOpBuilder &builder, mlir::Location loc, | 
 |                                 const fir::MutableBoxValue &box, | 
 |                                 mlir::Value dimIndex, mlir::Value lowerBound, | 
 |                                 mlir::Value upperBound) { | 
 |   mlir::func::FuncOp callee = | 
 |       box.isPointer() | 
 |           ? fir::runtime::getRuntimeFunc<mkRTKey(PointerSetBounds)>(loc, | 
 |                                                                     builder) | 
 |           : fir::runtime::getRuntimeFunc<mkRTKey(AllocatableSetBounds)>( | 
 |                 loc, builder); | 
 |   const auto args = fir::runtime::createArguments( | 
 |       builder, loc, callee.getFunctionType(), box.getAddr(), dimIndex, | 
 |       lowerBound, upperBound); | 
 |   fir::CallOp::create(builder, loc, callee, args); | 
 | } | 
 |  | 
 | /// Generate runtime call to set the lengths of a character allocatable or | 
 | /// pointer descriptor. | 
 | static void genRuntimeInitCharacter(fir::FirOpBuilder &builder, | 
 |                                     mlir::Location loc, | 
 |                                     const fir::MutableBoxValue &box, | 
 |                                     mlir::Value len, int64_t kind = 0) { | 
 |   mlir::func::FuncOp callee = | 
 |       box.isPointer() | 
 |           ? fir::runtime::getRuntimeFunc<mkRTKey(PointerNullifyCharacter)>( | 
 |                 loc, builder) | 
 |           : fir::runtime::getRuntimeFunc<mkRTKey( | 
 |                 AllocatableInitCharacterForAllocate)>(loc, builder); | 
 |   llvm::ArrayRef<mlir::Type> inputTypes = callee.getFunctionType().getInputs(); | 
 |   if (inputTypes.size() != 5) | 
 |     fir::emitFatalError( | 
 |         loc, "AllocatableInitCharacter runtime interface not as expected"); | 
 |   llvm::SmallVector<mlir::Value> args = {box.getAddr(), len}; | 
 |   if (kind == 0) | 
 |     kind = mlir::cast<fir::CharacterType>(box.getEleTy()).getFKind(); | 
 |   args.push_back(builder.createIntegerConstant(loc, inputTypes[2], kind)); | 
 |   int rank = box.rank(); | 
 |   args.push_back(builder.createIntegerConstant(loc, inputTypes[3], rank)); | 
 |   // TODO: coarrays | 
 |   int corank = 0; | 
 |   args.push_back(builder.createIntegerConstant(loc, inputTypes[4], corank)); | 
 |   const auto convertedArgs = fir::runtime::createArguments( | 
 |       builder, loc, callee.getFunctionType(), args); | 
 |   fir::CallOp::create(builder, loc, callee, convertedArgs); | 
 | } | 
 |  | 
 | /// Generate a sequence of runtime calls to allocate memory. | 
 | static mlir::Value genRuntimeAllocate(fir::FirOpBuilder &builder, | 
 |                                       mlir::Location loc, | 
 |                                       const fir::MutableBoxValue &box, | 
 |                                       ErrorManager &errorManager) { | 
 |   mlir::func::FuncOp callee = | 
 |       box.isPointer() | 
 |           ? fir::runtime::getRuntimeFunc<mkRTKey(PointerAllocate)>(loc, builder) | 
 |           : fir::runtime::getRuntimeFunc<mkRTKey(AllocatableAllocate)>(loc, | 
 |                                                                        builder); | 
 |   llvm::SmallVector<mlir::Value> args{box.getAddr()}; | 
 |   if (!box.isPointer()) | 
 |     args.push_back( | 
 |         builder.createIntegerConstant(loc, builder.getI64Type(), -1)); | 
 |   args.push_back(errorManager.hasStat); | 
 |   args.push_back(errorManager.errMsgAddr); | 
 |   args.push_back(errorManager.sourceFile); | 
 |   args.push_back(errorManager.sourceLine); | 
 |   const auto convertedArgs = fir::runtime::createArguments( | 
 |       builder, loc, callee.getFunctionType(), args); | 
 |   return fir::CallOp::create(builder, loc, callee, convertedArgs).getResult(0); | 
 | } | 
 |  | 
 | /// Generate a sequence of runtime calls to allocate memory and assign with the | 
 | /// \p source. | 
 | static mlir::Value genRuntimeAllocateSource(fir::FirOpBuilder &builder, | 
 |                                             mlir::Location loc, | 
 |                                             const fir::MutableBoxValue &box, | 
 |                                             fir::ExtendedValue source, | 
 |                                             ErrorManager &errorManager) { | 
 |   mlir::func::FuncOp callee = | 
 |       box.isPointer() | 
 |           ? fir::runtime::getRuntimeFunc<mkRTKey(PointerAllocateSource)>( | 
 |                 loc, builder) | 
 |           : fir::runtime::getRuntimeFunc<mkRTKey(AllocatableAllocateSource)>( | 
 |                 loc, builder); | 
 |   const auto args = fir::runtime::createArguments( | 
 |       builder, loc, callee.getFunctionType(), box.getAddr(), | 
 |       fir::getBase(source), errorManager.hasStat, errorManager.errMsgAddr, | 
 |       errorManager.sourceFile, errorManager.sourceLine); | 
 |   return fir::CallOp::create(builder, loc, callee, args).getResult(0); | 
 | } | 
 |  | 
 | /// Generate runtime call to apply mold to the descriptor. | 
 | static void genRuntimeAllocateApplyMold(fir::FirOpBuilder &builder, | 
 |                                         mlir::Location loc, | 
 |                                         const fir::MutableBoxValue &box, | 
 |                                         fir::ExtendedValue mold, int rank) { | 
 |   mlir::func::FuncOp callee = | 
 |       box.isPointer() | 
 |           ? fir::runtime::getRuntimeFunc<mkRTKey(PointerApplyMold)>(loc, | 
 |                                                                     builder) | 
 |           : fir::runtime::getRuntimeFunc<mkRTKey(AllocatableApplyMold)>( | 
 |                 loc, builder); | 
 |   const auto args = fir::runtime::createArguments( | 
 |       builder, loc, callee.getFunctionType(), | 
 |       fir::factory::getMutableIRBox(builder, loc, box), fir::getBase(mold), | 
 |       builder.createIntegerConstant( | 
 |           loc, callee.getFunctionType().getInputs()[2], rank)); | 
 |   fir::CallOp::create(builder, loc, callee, args); | 
 | } | 
 |  | 
 | /// Generate a runtime call to deallocate memory. | 
 | static mlir::Value genRuntimeDeallocate(fir::FirOpBuilder &builder, | 
 |                                         mlir::Location loc, | 
 |                                         const fir::MutableBoxValue &box, | 
 |                                         ErrorManager &errorManager, | 
 |                                         mlir::Value declaredTypeDesc = {}) { | 
 |   // Ensure fir.box is up-to-date before passing it to deallocate runtime. | 
 |   mlir::Value boxAddress = fir::factory::getMutableIRBox(builder, loc, box); | 
 |   mlir::func::FuncOp callee; | 
 |   llvm::SmallVector<mlir::Value> args; | 
 |   llvm::SmallVector<mlir::Value> operands; | 
 |   if (box.isPolymorphic() || box.isUnlimitedPolymorphic()) { | 
 |     callee = box.isPointer() | 
 |                  ? fir::runtime::getRuntimeFunc<mkRTKey( | 
 |                        PointerDeallocatePolymorphic)>(loc, builder) | 
 |                  : fir::runtime::getRuntimeFunc<mkRTKey( | 
 |                        AllocatableDeallocatePolymorphic)>(loc, builder); | 
 |     if (!declaredTypeDesc) | 
 |       declaredTypeDesc = builder.createNullConstant(loc); | 
 |     operands = fir::runtime::createArguments( | 
 |         builder, loc, callee.getFunctionType(), boxAddress, declaredTypeDesc, | 
 |         errorManager.hasStat, errorManager.errMsgAddr, errorManager.sourceFile, | 
 |         errorManager.sourceLine); | 
 |   } else { | 
 |     callee = box.isPointer() | 
 |                  ? fir::runtime::getRuntimeFunc<mkRTKey(PointerDeallocate)>( | 
 |                        loc, builder) | 
 |                  : fir::runtime::getRuntimeFunc<mkRTKey(AllocatableDeallocate)>( | 
 |                        loc, builder); | 
 |     operands = fir::runtime::createArguments( | 
 |         builder, loc, callee.getFunctionType(), boxAddress, | 
 |         errorManager.hasStat, errorManager.errMsgAddr, errorManager.sourceFile, | 
 |         errorManager.sourceLine); | 
 |   } | 
 |   return fir::CallOp::create(builder, loc, callee, operands).getResult(0); | 
 | } | 
 |  | 
 | //===----------------------------------------------------------------------===// | 
 | // Allocate statement implementation | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | /// Helper to get symbol from AllocateObject. | 
 | static const Fortran::semantics::Symbol & | 
 | unwrapSymbol(const Fortran::parser::AllocateObject &allocObj) { | 
 |   const Fortran::parser::Name &lastName = | 
 |       Fortran::parser::GetLastName(allocObj); | 
 |   assert(lastName.symbol); | 
 |   return *lastName.symbol; | 
 | } | 
 |  | 
 | static fir::MutableBoxValue | 
 | genMutableBoxValue(Fortran::lower::AbstractConverter &converter, | 
 |                    mlir::Location loc, | 
 |                    const Fortran::parser::AllocateObject &allocObj) { | 
 |   const Fortran::lower::SomeExpr *expr = Fortran::semantics::GetExpr(allocObj); | 
 |   assert(expr && "semantic analysis failure"); | 
 |   return converter.genExprMutableBox(loc, *expr); | 
 | } | 
 |  | 
 | /// Implement Allocate statement lowering. | 
 | class AllocateStmtHelper { | 
 | public: | 
 |   AllocateStmtHelper(Fortran::lower::AbstractConverter &converter, | 
 |                      const Fortran::parser::AllocateStmt &stmt, | 
 |                      mlir::Location loc) | 
 |       : converter{converter}, builder{converter.getFirOpBuilder()}, stmt{stmt}, | 
 |         loc{loc} {} | 
 |  | 
 |   void lower() { | 
 |     visitAllocateOptions(); | 
 |     lowerAllocateLengthParameters(); | 
 |     errorManager.init(converter, loc, statExpr, errMsgExpr); | 
 |     Fortran::lower::StatementContext stmtCtx; | 
 |     if (sourceExpr) | 
 |       sourceExv = converter.genExprBox(loc, *sourceExpr, stmtCtx); | 
 |     if (moldExpr) | 
 |       moldExv = converter.genExprBox(loc, *moldExpr, stmtCtx); | 
 |     mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint(); | 
 |     for (const auto &allocation : | 
 |          std::get<std::list<Fortran::parser::Allocation>>(stmt.t)) | 
 |       lowerAllocation(unwrapAllocation(allocation)); | 
 |     builder.restoreInsertionPoint(insertPt); | 
 |   } | 
 |  | 
 | private: | 
 |   struct Allocation { | 
 |     const Fortran::parser::Allocation &alloc; | 
 |     const Fortran::semantics::DeclTypeSpec &type; | 
 |     bool hasCoarraySpec() const { | 
 |       return std::get<std::optional<Fortran::parser::AllocateCoarraySpec>>( | 
 |                  alloc.t) | 
 |           .has_value(); | 
 |     } | 
 |     const Fortran::parser::AllocateObject &getAllocObj() const { | 
 |       return std::get<Fortran::parser::AllocateObject>(alloc.t); | 
 |     } | 
 |     const Fortran::semantics::Symbol &getSymbol() const { | 
 |       return unwrapSymbol(getAllocObj()); | 
 |     } | 
 |     const std::list<Fortran::parser::AllocateShapeSpec> &getShapeSpecs() const { | 
 |       return std::get<std::list<Fortran::parser::AllocateShapeSpec>>(alloc.t); | 
 |     } | 
 |   }; | 
 |  | 
 |   Allocation unwrapAllocation(const Fortran::parser::Allocation &alloc) { | 
 |     const auto &allocObj = std::get<Fortran::parser::AllocateObject>(alloc.t); | 
 |     const Fortran::semantics::Symbol &symbol = unwrapSymbol(allocObj); | 
 |     assert(symbol.GetType()); | 
 |     return Allocation{alloc, *symbol.GetType()}; | 
 |   } | 
 |  | 
 |   void visitAllocateOptions() { | 
 |     for (const auto &allocOption : | 
 |          std::get<std::list<Fortran::parser::AllocOpt>>(stmt.t)) | 
 |       Fortran::common::visit( | 
 |           Fortran::common::visitors{ | 
 |               [&](const Fortran::parser::StatOrErrmsg &statOrErr) { | 
 |                 Fortran::common::visit( | 
 |                     Fortran::common::visitors{ | 
 |                         [&](const Fortran::parser::StatVariable &statVar) { | 
 |                           statExpr = Fortran::semantics::GetExpr(statVar); | 
 |                         }, | 
 |                         [&](const Fortran::parser::MsgVariable &errMsgVar) { | 
 |                           errMsgExpr = Fortran::semantics::GetExpr(errMsgVar); | 
 |                         }, | 
 |                     }, | 
 |                     statOrErr.u); | 
 |               }, | 
 |               [&](const Fortran::parser::AllocOpt::Source &source) { | 
 |                 sourceExpr = Fortran::semantics::GetExpr(source.v.value()); | 
 |               }, | 
 |               [&](const Fortran::parser::AllocOpt::Mold &mold) { | 
 |                 moldExpr = Fortran::semantics::GetExpr(mold.v.value()); | 
 |               }, | 
 |               [&](const Fortran::parser::AllocOpt::Stream &stream) { | 
 |                 streamExpr = Fortran::semantics::GetExpr(stream.v.value()); | 
 |               }, | 
 |               [&](const Fortran::parser::AllocOpt::Pinned &pinned) { | 
 |                 pinnedExpr = Fortran::semantics::GetExpr(pinned.v.value()); | 
 |               }, | 
 |           }, | 
 |           allocOption.u); | 
 |   } | 
 |  | 
 |   void lowerAllocation(const Allocation &alloc) { | 
 |     fir::MutableBoxValue boxAddr = | 
 |         genMutableBoxValue(converter, loc, alloc.getAllocObj()); | 
 |  | 
 |     if (sourceExpr) | 
 |       genSourceMoldAllocation(alloc, boxAddr, /*isSource=*/true); | 
 |     else if (moldExpr) | 
 |       genSourceMoldAllocation(alloc, boxAddr, /*isSource=*/false); | 
 |     else | 
 |       genSimpleAllocation(alloc, boxAddr); | 
 |   } | 
 |  | 
 |   static bool lowerBoundsAreOnes(const Allocation &alloc) { | 
 |     for (const Fortran::parser::AllocateShapeSpec &shapeSpec : | 
 |          alloc.getShapeSpecs()) | 
 |       if (std::get<0>(shapeSpec.t)) | 
 |         return false; | 
 |     return true; | 
 |   } | 
 |  | 
 |   /// Build name for the fir::allocmem generated for alloc. | 
 |   std::string mangleAlloc(const Allocation &alloc) { | 
 |     return converter.mangleName(alloc.getSymbol()) + ".alloc"; | 
 |   } | 
 |  | 
 |   /// Generate allocation without runtime calls. | 
 |   /// Only for intrinsic types. No coarrays, no polymorphism. No error recovery. | 
 |   void genInlinedAllocation(const Allocation &alloc, | 
 |                             const fir::MutableBoxValue &box) { | 
 |     llvm::SmallVector<mlir::Value> lbounds; | 
 |     llvm::SmallVector<mlir::Value> extents; | 
 |     Fortran::lower::StatementContext stmtCtx; | 
 |     mlir::Type idxTy = builder.getIndexType(); | 
 |     bool lBoundsAreOnes = lowerBoundsAreOnes(alloc); | 
 |     mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1); | 
 |     for (const Fortran::parser::AllocateShapeSpec &shapeSpec : | 
 |          alloc.getShapeSpecs()) { | 
 |       mlir::Value lb; | 
 |       if (!lBoundsAreOnes) { | 
 |         if (const std::optional<Fortran::parser::BoundExpr> &lbExpr = | 
 |                 std::get<0>(shapeSpec.t)) { | 
 |           lb = fir::getBase(converter.genExprValue( | 
 |               loc, Fortran::semantics::GetExpr(*lbExpr), stmtCtx)); | 
 |           lb = builder.createConvert(loc, idxTy, lb); | 
 |         } else { | 
 |           lb = one; | 
 |         } | 
 |         lbounds.emplace_back(lb); | 
 |       } | 
 |       mlir::Value ub = fir::getBase(converter.genExprValue( | 
 |           loc, Fortran::semantics::GetExpr(std::get<1>(shapeSpec.t)), stmtCtx)); | 
 |       ub = builder.createConvert(loc, idxTy, ub); | 
 |       if (lb) { | 
 |         mlir::Value diff = mlir::arith::SubIOp::create(builder, loc, ub, lb); | 
 |         extents.emplace_back( | 
 |             mlir::arith::AddIOp::create(builder, loc, diff, one)); | 
 |       } else { | 
 |         extents.emplace_back(ub); | 
 |       } | 
 |     } | 
 |     fir::factory::genInlinedAllocation(builder, loc, box, lbounds, extents, | 
 |                                        lenParams, mangleAlloc(alloc), | 
 |                                        /*mustBeHeap=*/true); | 
 |   } | 
 |  | 
 |   void postAllocationAction(const Allocation &alloc, | 
 |                             const fir::MutableBoxValue &box) { | 
 |     if (alloc.getSymbol().test(Fortran::semantics::Symbol::Flag::AccDeclare)) | 
 |       Fortran::lower::attachDeclarePostAllocAction(converter, builder, | 
 |                                                    alloc.getSymbol()); | 
 |   } | 
 |  | 
 |   void setPinnedToFalse() { | 
 |     if (!pinnedExpr) | 
 |       return; | 
 |     Fortran::lower::StatementContext stmtCtx; | 
 |     mlir::Value pinned = | 
 |         fir::getBase(converter.genExprAddr(loc, *pinnedExpr, stmtCtx)); | 
 |     mlir::Location loc = pinned.getLoc(); | 
 |     mlir::Value falseValue = builder.createBool(loc, false); | 
 |     mlir::Value falseConv = builder.createConvert( | 
 |         loc, fir::unwrapRefType(pinned.getType()), falseValue); | 
 |     fir::StoreOp::create(builder, loc, falseConv, pinned); | 
 |   } | 
 |  | 
 |   void genSimpleAllocation(const Allocation &alloc, | 
 |                            const fir::MutableBoxValue &box) { | 
 |     bool isCudaAllocate = | 
 |         Fortran::semantics::HasCUDAAttr(alloc.getSymbol()) || | 
 |         Fortran::semantics::HasCUDAComponent(alloc.getSymbol()); | 
 |     bool isCudaDeviceContext = cuf::isCUDADeviceContext(builder.getRegion()); | 
 |     bool inlineAllocation = !box.isDerived() && !errorManager.hasStatSpec() && | 
 |                             !alloc.type.IsPolymorphic() && | 
 |                             !alloc.hasCoarraySpec() && !useAllocateRuntime && | 
 |                             !box.isPointer(); | 
 |     unsigned allocatorIdx = Fortran::lower::getAllocatorIdx(alloc.getSymbol()); | 
 |  | 
 |     if (inlineAllocation && | 
 |         ((isCudaAllocate && isCudaDeviceContext) || !isCudaAllocate)) { | 
 |       // Pointers must use PointerAllocate so that their deallocations | 
 |       // can be validated. | 
 |       genInlinedAllocation(alloc, box); | 
 |       postAllocationAction(alloc, box); | 
 |       setPinnedToFalse(); | 
 |       return; | 
 |     } | 
 |  | 
 |     // Preserve characters' dynamic length. | 
 |     if (lenParams.empty() && box.isCharacter() && | 
 |         !box.hasNonDeferredLenParams()) { | 
 |       auto charTy = mlir::dyn_cast<fir::CharacterType>(box.getEleTy()); | 
 |       if (charTy && charTy.hasDynamicLen()) { | 
 |         fir::ExtendedValue exv{box}; | 
 |         lenParams.push_back(fir::factory::readCharLen(builder, loc, exv)); | 
 |       } | 
 |     } | 
 |  | 
 |     // Generate a sequence of runtime calls. | 
 |     errorManager.genStatCheck(builder, loc); | 
 |     genAllocateObjectInit(box, allocatorIdx); | 
 |     if (alloc.hasCoarraySpec()) | 
 |       TODO(loc, "coarray: allocation of a coarray object"); | 
 |     if (alloc.type.IsPolymorphic()) | 
 |       genSetType(alloc, box, loc); | 
 |     genSetDeferredLengthParameters(alloc, box); | 
 |     genAllocateObjectBounds(alloc, box); | 
 |     mlir::Value stat; | 
 |     if (!isCudaAllocate) { | 
 |       stat = genRuntimeAllocate(builder, loc, box, errorManager); | 
 |       setPinnedToFalse(); | 
 |     } else { | 
 |       stat = | 
 |           genCudaAllocate(builder, loc, box, errorManager, alloc.getSymbol()); | 
 |     } | 
 |     fir::factory::syncMutableBoxFromIRBox(builder, loc, box); | 
 |     postAllocationAction(alloc, box); | 
 |     errorManager.assignStat(builder, loc, stat); | 
 |   } | 
 |  | 
 |   /// Lower the length parameters that may be specified in the optional | 
 |   /// type specification. | 
 |   void lowerAllocateLengthParameters() { | 
 |     const Fortran::semantics::DeclTypeSpec *typeSpec = | 
 |         getIfAllocateStmtTypeSpec(); | 
 |     if (!typeSpec) | 
 |       return; | 
 |     if (const Fortran::semantics::DerivedTypeSpec *derived = | 
 |             typeSpec->AsDerived()) | 
 |       if (Fortran::semantics::CountLenParameters(*derived) > 0) | 
 |         TODO(loc, "setting derived type params in allocation"); | 
 |     if (typeSpec->category() == | 
 |         Fortran::semantics::DeclTypeSpec::Category::Character) { | 
 |       Fortran::semantics::ParamValue lenParam = | 
 |           typeSpec->characterTypeSpec().length(); | 
 |       if (Fortran::semantics::MaybeIntExpr intExpr = lenParam.GetExplicit()) { | 
 |         Fortran::lower::StatementContext stmtCtx; | 
 |         Fortran::lower::SomeExpr lenExpr{*intExpr}; | 
 |         lenParams.push_back( | 
 |             fir::getBase(converter.genExprValue(loc, lenExpr, stmtCtx))); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   // Set length parameters in the box stored in boxAddr. | 
 |   // This must be called before setting the bounds because it may use | 
 |   // Init runtime calls that may set the bounds to zero. | 
 |   void genSetDeferredLengthParameters(const Allocation &alloc, | 
 |                                       const fir::MutableBoxValue &box) { | 
 |     if (lenParams.empty()) | 
 |       return; | 
 |     // TODO: in case a length parameter was not deferred, insert a runtime check | 
 |     // that the length is the same (AllocatableCheckLengthParameter runtime | 
 |     // call). | 
 |     if (box.isCharacter()) | 
 |       genRuntimeInitCharacter(builder, loc, box, lenParams[0]); | 
 |  | 
 |     if (box.isDerived()) | 
 |       TODO(loc, "derived type length parameters in allocate"); | 
 |   } | 
 |  | 
 |   void genAllocateObjectInit(const fir::MutableBoxValue &box, | 
 |                              unsigned allocatorIdx) { | 
 |     if (box.isPointer()) { | 
 |       // For pointers, the descriptor may still be uninitialized (see Fortran | 
 |       // 2018 19.5.2.2). The allocation runtime needs to be given a descriptor | 
 |       // with initialized rank, types and attributes. Initialize the descriptor | 
 |       // here to ensure these constraints are fulfilled. | 
 |       mlir::Value nullPointer = fir::factory::createUnallocatedBox( | 
 |           builder, loc, box.getBoxTy(), box.nonDeferredLenParams(), | 
 |           /*typeSourceBox=*/{}, allocatorIdx); | 
 |       fir::StoreOp::create(builder, loc, nullPointer, box.getAddr()); | 
 |     } else { | 
 |       assert(box.isAllocatable() && "must be an allocatable"); | 
 |       // For allocatables, sync the MutableBoxValue and descriptor before the | 
 |       // calls in case it is tracked locally by a set of variables. | 
 |       fir::factory::getMutableIRBox(builder, loc, box); | 
 |     } | 
 |   } | 
 |  | 
 |   void genAllocateObjectBounds(const Allocation &alloc, | 
 |                                const fir::MutableBoxValue &box) { | 
 |     // Set bounds for arrays | 
 |     mlir::Type idxTy = builder.getIndexType(); | 
 |     mlir::Type i32Ty = builder.getIntegerType(32); | 
 |     Fortran::lower::StatementContext stmtCtx; | 
 |     for (const auto &iter : llvm::enumerate(alloc.getShapeSpecs())) { | 
 |       mlir::Value lb; | 
 |       const auto &bounds = iter.value().t; | 
 |       if (const std::optional<Fortran::parser::BoundExpr> &lbExpr = | 
 |               std::get<0>(bounds)) | 
 |         lb = fir::getBase(converter.genExprValue( | 
 |             loc, Fortran::semantics::GetExpr(*lbExpr), stmtCtx)); | 
 |       else | 
 |         lb = builder.createIntegerConstant(loc, idxTy, 1); | 
 |       mlir::Value ub = fir::getBase(converter.genExprValue( | 
 |           loc, Fortran::semantics::GetExpr(std::get<1>(bounds)), stmtCtx)); | 
 |       mlir::Value dimIndex = | 
 |           builder.createIntegerConstant(loc, i32Ty, iter.index()); | 
 |       // Runtime call | 
 |       genRuntimeSetBounds(builder, loc, box, dimIndex, lb, ub); | 
 |     } | 
 |     if (sourceExpr && sourceExpr->Rank() > 0 && | 
 |         alloc.getShapeSpecs().size() == 0) { | 
 |       // If the alloc object does not have shape list, get the bounds from the | 
 |       // source expression. | 
 |       mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1); | 
 |       const auto *sourceBox = sourceExv.getBoxOf<fir::BoxValue>(); | 
 |       assert(sourceBox && "source expression should be lowered to one box"); | 
 |       for (int i = 0; i < sourceExpr->Rank(); ++i) { | 
 |         auto dimVal = builder.createIntegerConstant(loc, idxTy, i); | 
 |         auto dimInfo = fir::BoxDimsOp::create(builder, loc, idxTy, idxTy, idxTy, | 
 |                                               sourceBox->getAddr(), dimVal); | 
 |         mlir::Value lb = | 
 |             fir::factory::readLowerBound(builder, loc, sourceExv, i, one); | 
 |         mlir::Value extent = dimInfo.getResult(1); | 
 |         mlir::Value ub = mlir::arith::SubIOp::create( | 
 |             builder, loc, mlir::arith::AddIOp::create(builder, loc, extent, lb), | 
 |             one); | 
 |         mlir::Value dimIndex = builder.createIntegerConstant(loc, i32Ty, i); | 
 |         genRuntimeSetBounds(builder, loc, box, dimIndex, lb, ub); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   void genSourceMoldAllocation(const Allocation &alloc, | 
 |                                const fir::MutableBoxValue &box, bool isSource) { | 
 |     unsigned allocatorIdx = Fortran::lower::getAllocatorIdx(alloc.getSymbol()); | 
 |     fir::ExtendedValue exv = isSource ? sourceExv : moldExv; | 
 |  | 
 |     // Generate a sequence of runtime calls. | 
 |     errorManager.genStatCheck(builder, loc); | 
 |     genAllocateObjectInit(box, allocatorIdx); | 
 |     if (alloc.hasCoarraySpec()) | 
 |       TODO(loc, "coarray: allocation of a coarray object"); | 
 |     // Set length of the allocate object if it has. Otherwise, get the length | 
 |     // from source for the deferred length parameter. | 
 |     const bool isDeferredLengthCharacter = | 
 |         box.isCharacter() && !box.hasNonDeferredLenParams(); | 
 |     if (lenParams.empty() && isDeferredLengthCharacter) | 
 |       lenParams.push_back(fir::factory::readCharLen(builder, loc, exv)); | 
 |     if (!isSource || alloc.type.IsPolymorphic()) | 
 |       genRuntimeAllocateApplyMold(builder, loc, box, exv, | 
 |                                   alloc.getSymbol().Rank()); | 
 |     if (isDeferredLengthCharacter) | 
 |       genSetDeferredLengthParameters(alloc, box); | 
 |     genAllocateObjectBounds(alloc, box); | 
 |     mlir::Value stat; | 
 |     if (Fortran::semantics::HasCUDAAttr(alloc.getSymbol())) { | 
 |       stat = | 
 |           genCudaAllocate(builder, loc, box, errorManager, alloc.getSymbol()); | 
 |     } else { | 
 |       if (isSource) | 
 |         stat = genRuntimeAllocateSource(builder, loc, box, exv, errorManager); | 
 |       else | 
 |         stat = genRuntimeAllocate(builder, loc, box, errorManager); | 
 |       setPinnedToFalse(); | 
 |     } | 
 |     fir::factory::syncMutableBoxFromIRBox(builder, loc, box); | 
 |     postAllocationAction(alloc, box); | 
 |     errorManager.assignStat(builder, loc, stat); | 
 |   } | 
 |  | 
 |   /// Generate call to PointerNullifyDerived or AllocatableInitDerived | 
 |   /// to set the dynamic type information. | 
 |   void genInitDerived(const fir::MutableBoxValue &box, mlir::Value typeDescAddr, | 
 |                       int rank, int corank = 0) { | 
 |     mlir::func::FuncOp callee = | 
 |         box.isPointer() | 
 |             ? fir::runtime::getRuntimeFunc<mkRTKey(PointerNullifyDerived)>( | 
 |                   loc, builder) | 
 |             : fir::runtime::getRuntimeFunc<mkRTKey( | 
 |                   AllocatableInitDerivedForAllocate)>(loc, builder); | 
 |  | 
 |     llvm::ArrayRef<mlir::Type> inputTypes = | 
 |         callee.getFunctionType().getInputs(); | 
 |     mlir::Value rankValue = | 
 |         builder.createIntegerConstant(loc, inputTypes[2], rank); | 
 |     mlir::Value corankValue = | 
 |         builder.createIntegerConstant(loc, inputTypes[3], corank); | 
 |     const auto args = fir::runtime::createArguments( | 
 |         builder, loc, callee.getFunctionType(), box.getAddr(), typeDescAddr, | 
 |         rankValue, corankValue); | 
 |     fir::CallOp::create(builder, loc, callee, args); | 
 |   } | 
 |  | 
 |   /// Generate call to PointerNullifyIntrinsic or AllocatableInitIntrinsic to | 
 |   /// set the dynamic type information for a polymorphic entity from an | 
 |   /// intrinsic type spec. | 
 |   void genInitIntrinsic(const fir::MutableBoxValue &box, | 
 |                         const TypeCategory category, int64_t kind, int rank, | 
 |                         int corank = 0) { | 
 |     mlir::func::FuncOp callee = | 
 |         box.isPointer() | 
 |             ? fir::runtime::getRuntimeFunc<mkRTKey(PointerNullifyIntrinsic)>( | 
 |                   loc, builder) | 
 |             : fir::runtime::getRuntimeFunc<mkRTKey( | 
 |                   AllocatableInitIntrinsicForAllocate)>(loc, builder); | 
 |  | 
 |     llvm::ArrayRef<mlir::Type> inputTypes = | 
 |         callee.getFunctionType().getInputs(); | 
 |     mlir::Value categoryValue = builder.createIntegerConstant( | 
 |         loc, inputTypes[1], static_cast<int32_t>(category)); | 
 |     mlir::Value kindValue = | 
 |         builder.createIntegerConstant(loc, inputTypes[2], kind); | 
 |     mlir::Value rankValue = | 
 |         builder.createIntegerConstant(loc, inputTypes[3], rank); | 
 |     mlir::Value corankValue = | 
 |         builder.createIntegerConstant(loc, inputTypes[4], corank); | 
 |     const auto args = fir::runtime::createArguments( | 
 |         builder, loc, callee.getFunctionType(), box.getAddr(), categoryValue, | 
 |         kindValue, rankValue, corankValue); | 
 |     fir::CallOp::create(builder, loc, callee, args); | 
 |   } | 
 |  | 
 |   /// Generate call to the AllocatableInitDerived to set up the type descriptor | 
 |   /// and other part of the descriptor for derived type. | 
 |   void genSetType(const Allocation &alloc, const fir::MutableBoxValue &box, | 
 |                   mlir::Location loc) { | 
 |     const Fortran::semantics::DeclTypeSpec *typeSpec = | 
 |         getIfAllocateStmtTypeSpec(); | 
 |  | 
 |     // No type spec provided in allocate statement so the declared type spec is | 
 |     // used. | 
 |     if (!typeSpec) | 
 |       typeSpec = &alloc.type; | 
 |     assert(typeSpec && "type spec missing for polymorphic allocation"); | 
 |  | 
 |     // Set up the descriptor for allocation for intrinsic type spec on | 
 |     // unlimited polymorphic entity. | 
 |     if (typeSpec->AsIntrinsic() && | 
 |         fir::isUnlimitedPolymorphicType(fir::getBase(box).getType())) { | 
 |       if (typeSpec->AsIntrinsic()->category() == TypeCategory::Character) { | 
 |         genRuntimeInitCharacter( | 
 |             builder, loc, box, lenParams[0], | 
 |             Fortran::evaluate::ToInt64(typeSpec->AsIntrinsic()->kind()) | 
 |                 .value()); | 
 |       } else { | 
 |         genInitIntrinsic( | 
 |             box, typeSpec->AsIntrinsic()->category(), | 
 |             Fortran::evaluate::ToInt64(typeSpec->AsIntrinsic()->kind()).value(), | 
 |             alloc.getSymbol().Rank()); | 
 |       } | 
 |       return; | 
 |     } | 
 |  | 
 |     // Do not generate calls for non derived-type type spec. | 
 |     if (!typeSpec->AsDerived()) | 
 |       return; | 
 |  | 
 |     auto typeDescAddr = Fortran::lower::getTypeDescAddr( | 
 |         converter, loc, typeSpec->derivedTypeSpec()); | 
 |     genInitDerived(box, typeDescAddr, alloc.getSymbol().Rank()); | 
 |   } | 
 |  | 
 |   /// Returns a pointer to the DeclTypeSpec if a type-spec is provided in the | 
 |   /// allocate statement. Returns a null pointer otherwise. | 
 |   const Fortran::semantics::DeclTypeSpec *getIfAllocateStmtTypeSpec() const { | 
 |     if (const auto &typeSpec = | 
 |             std::get<std::optional<Fortran::parser::TypeSpec>>(stmt.t)) | 
 |       return typeSpec->declTypeSpec; | 
 |     return nullptr; | 
 |   } | 
 |  | 
 |   mlir::Value genCudaAllocate(fir::FirOpBuilder &builder, mlir::Location loc, | 
 |                               const fir::MutableBoxValue &box, | 
 |                               ErrorManager &errorManager, | 
 |                               const Fortran::semantics::Symbol &sym) { | 
 |     Fortran::lower::StatementContext stmtCtx; | 
 |     cuf::DataAttributeAttr cudaAttr = | 
 |         Fortran::lower::translateSymbolCUFDataAttribute(builder.getContext(), | 
 |                                                         sym); | 
 |     mlir::Value errmsg = errMsgExpr ? errorManager.errMsgAddr : nullptr; | 
 |     mlir::Value stream = | 
 |         streamExpr | 
 |             ? fir::getBase(converter.genExprAddr(loc, *streamExpr, stmtCtx)) | 
 |             : nullptr; | 
 |     mlir::Value pinned = | 
 |         pinnedExpr | 
 |             ? fir::getBase(converter.genExprAddr(loc, *pinnedExpr, stmtCtx)) | 
 |             : nullptr; | 
 |     mlir::Value source = sourceExpr ? fir::getBase(sourceExv) : nullptr; | 
 |  | 
 |     // Keep return type the same as a standard AllocatableAllocate call. | 
 |     mlir::Type retTy = fir::runtime::getModel<int>()(builder.getContext()); | 
 |  | 
 |     return cuf::AllocateOp::create( | 
 |                builder, loc, retTy, box.getAddr(), errmsg, stream, pinned, | 
 |                source, cudaAttr, | 
 |                errorManager.hasStatSpec() ? builder.getUnitAttr() : nullptr) | 
 |         .getResult(); | 
 |   } | 
 |  | 
 |   Fortran::lower::AbstractConverter &converter; | 
 |   fir::FirOpBuilder &builder; | 
 |   const Fortran::parser::AllocateStmt &stmt; | 
 |   const Fortran::lower::SomeExpr *sourceExpr{nullptr}; | 
 |   const Fortran::lower::SomeExpr *moldExpr{nullptr}; | 
 |   const Fortran::lower::SomeExpr *statExpr{nullptr}; | 
 |   const Fortran::lower::SomeExpr *errMsgExpr{nullptr}; | 
 |   const Fortran::lower::SomeExpr *pinnedExpr{nullptr}; | 
 |   const Fortran::lower::SomeExpr *streamExpr{nullptr}; | 
 |   // If the allocate has a type spec, lenParams contains the | 
 |   // value of the length parameters that were specified inside. | 
 |   llvm::SmallVector<mlir::Value> lenParams; | 
 |   ErrorManager errorManager; | 
 |   // 9.7.1.2(7) The source-expr is evaluated exactly once for each AllocateStmt. | 
 |   fir::ExtendedValue sourceExv; | 
 |   fir::ExtendedValue moldExv; | 
 |  | 
 |   mlir::Location loc; | 
 | }; | 
 | } // namespace | 
 |  | 
 | void Fortran::lower::genAllocateStmt( | 
 |     Fortran::lower::AbstractConverter &converter, | 
 |     const Fortran::parser::AllocateStmt &stmt, mlir::Location loc) { | 
 |   AllocateStmtHelper{converter, stmt, loc}.lower(); | 
 | } | 
 |  | 
 | //===----------------------------------------------------------------------===// | 
 | // Deallocate statement implementation | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | static void preDeallocationAction(Fortran::lower::AbstractConverter &converter, | 
 |                                   fir::FirOpBuilder &builder, | 
 |                                   mlir::Value beginOpValue, | 
 |                                   const Fortran::semantics::Symbol &sym) { | 
 |   if (sym.test(Fortran::semantics::Symbol::Flag::AccDeclare)) | 
 |     Fortran::lower::attachDeclarePreDeallocAction(converter, builder, | 
 |                                                   beginOpValue, sym); | 
 | } | 
 |  | 
 | static void postDeallocationAction(Fortran::lower::AbstractConverter &converter, | 
 |                                    fir::FirOpBuilder &builder, | 
 |                                    const Fortran::semantics::Symbol &sym) { | 
 |   if (sym.test(Fortran::semantics::Symbol::Flag::AccDeclare)) | 
 |     Fortran::lower::attachDeclarePostDeallocAction(converter, builder, sym); | 
 | } | 
 |  | 
 | static mlir::Value genCudaDeallocate(fir::FirOpBuilder &builder, | 
 |                                      mlir::Location loc, | 
 |                                      const fir::MutableBoxValue &box, | 
 |                                      ErrorManager &errorManager, | 
 |                                      const Fortran::semantics::Symbol &sym) { | 
 |   cuf::DataAttributeAttr cudaAttr = | 
 |       Fortran::lower::translateSymbolCUFDataAttribute(builder.getContext(), | 
 |                                                       sym); | 
 |   mlir::Value errmsg = | 
 |       mlir::isa<fir::AbsentOp>(errorManager.errMsgAddr.getDefiningOp()) | 
 |           ? nullptr | 
 |           : errorManager.errMsgAddr; | 
 |  | 
 |   // Keep return type the same as a standard AllocatableAllocate call. | 
 |   mlir::Type retTy = fir::runtime::getModel<int>()(builder.getContext()); | 
 |   return cuf::DeallocateOp::create( | 
 |              builder, loc, retTy, box.getAddr(), errmsg, cudaAttr, | 
 |              errorManager.hasStatSpec() ? builder.getUnitAttr() : nullptr) | 
 |       .getResult(); | 
 | } | 
 |  | 
 | // Generate deallocation of a pointer/allocatable. | 
 | static mlir::Value | 
 | genDeallocate(fir::FirOpBuilder &builder, | 
 |               Fortran::lower::AbstractConverter &converter, mlir::Location loc, | 
 |               const fir::MutableBoxValue &box, ErrorManager &errorManager, | 
 |               mlir::Value declaredTypeDesc = {}, | 
 |               const Fortran::semantics::Symbol *symbol = nullptr) { | 
 |   bool isCudaSymbol = symbol && Fortran::semantics::HasCUDAAttr(*symbol); | 
 |   bool isCudaDeviceContext = cuf::isCUDADeviceContext(builder.getRegion()); | 
 |   bool inlineDeallocation = | 
 |       !box.isDerived() && !box.isPolymorphic() && !box.hasAssumedRank() && | 
 |       !box.isUnlimitedPolymorphic() && !errorManager.hasStatSpec() && | 
 |       !useAllocateRuntime && !box.isPointer(); | 
 |   // Deallocate intrinsic types inline. | 
 |   if (inlineDeallocation && | 
 |       ((isCudaSymbol && isCudaDeviceContext) || !isCudaSymbol)) { | 
 |     // Pointers must use PointerDeallocate so that their deallocations | 
 |     // can be validated. | 
 |     mlir::Value ret = fir::factory::genFreemem(builder, loc, box); | 
 |     if (symbol) | 
 |       postDeallocationAction(converter, builder, *symbol); | 
 |     return ret; | 
 |   } | 
 |   // Use runtime calls to deallocate descriptor cases. Sync MutableBoxValue | 
 |   // with its descriptor before and after calls if needed. | 
 |   errorManager.genStatCheck(builder, loc); | 
 |   mlir::Value stat; | 
 |   if (!isCudaSymbol) | 
 |     stat = | 
 |         genRuntimeDeallocate(builder, loc, box, errorManager, declaredTypeDesc); | 
 |   else | 
 |     stat = genCudaDeallocate(builder, loc, box, errorManager, *symbol); | 
 |   fir::factory::syncMutableBoxFromIRBox(builder, loc, box); | 
 |   if (symbol) | 
 |     postDeallocationAction(converter, builder, *symbol); | 
 |   errorManager.assignStat(builder, loc, stat); | 
 |   return stat; | 
 | } | 
 |  | 
 | void Fortran::lower::genDeallocateBox( | 
 |     Fortran::lower::AbstractConverter &converter, | 
 |     const fir::MutableBoxValue &box, mlir::Location loc, | 
 |     const Fortran::semantics::Symbol *sym, mlir::Value declaredTypeDesc) { | 
 |   const Fortran::lower::SomeExpr *statExpr = nullptr; | 
 |   const Fortran::lower::SomeExpr *errMsgExpr = nullptr; | 
 |   ErrorManager errorManager; | 
 |   errorManager.init(converter, loc, statExpr, errMsgExpr); | 
 |   fir::FirOpBuilder &builder = converter.getFirOpBuilder(); | 
 |   genDeallocate(builder, converter, loc, box, errorManager, declaredTypeDesc, | 
 |                 sym); | 
 | } | 
 |  | 
 | void Fortran::lower::genDeallocateIfAllocated( | 
 |     Fortran::lower::AbstractConverter &converter, | 
 |     const fir::MutableBoxValue &box, mlir::Location loc, | 
 |     const Fortran::semantics::Symbol *sym) { | 
 |   fir::FirOpBuilder &builder = converter.getFirOpBuilder(); | 
 |   mlir::Value isAllocated = | 
 |       fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, box); | 
 |   builder.genIfThen(loc, isAllocated) | 
 |       .genThen([&]() { | 
 |         if (mlir::Type eleType = box.getEleTy(); | 
 |             mlir::isa<fir::RecordType>(eleType) && box.isPolymorphic()) { | 
 |           mlir::Value declaredTypeDesc = fir::TypeDescOp::create( | 
 |               builder, loc, mlir::TypeAttr::get(eleType)); | 
 |           genDeallocateBox(converter, box, loc, sym, declaredTypeDesc); | 
 |         } else { | 
 |           genDeallocateBox(converter, box, loc, sym); | 
 |         } | 
 |       }) | 
 |       .end(); | 
 | } | 
 |  | 
 | void Fortran::lower::genDeallocateStmt( | 
 |     Fortran::lower::AbstractConverter &converter, | 
 |     const Fortran::parser::DeallocateStmt &stmt, mlir::Location loc) { | 
 |   const Fortran::lower::SomeExpr *statExpr = nullptr; | 
 |   const Fortran::lower::SomeExpr *errMsgExpr = nullptr; | 
 |   for (const Fortran::parser::StatOrErrmsg &statOrErr : | 
 |        std::get<std::list<Fortran::parser::StatOrErrmsg>>(stmt.t)) | 
 |     Fortran::common::visit( | 
 |         Fortran::common::visitors{ | 
 |             [&](const Fortran::parser::StatVariable &statVar) { | 
 |               statExpr = Fortran::semantics::GetExpr(statVar); | 
 |             }, | 
 |             [&](const Fortran::parser::MsgVariable &errMsgVar) { | 
 |               errMsgExpr = Fortran::semantics::GetExpr(errMsgVar); | 
 |             }, | 
 |         }, | 
 |         statOrErr.u); | 
 |   ErrorManager errorManager; | 
 |   errorManager.init(converter, loc, statExpr, errMsgExpr); | 
 |   fir::FirOpBuilder &builder = converter.getFirOpBuilder(); | 
 |   mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint(); | 
 |   for (const Fortran::parser::AllocateObject &allocateObject : | 
 |        std::get<std::list<Fortran::parser::AllocateObject>>(stmt.t)) { | 
 |     const Fortran::semantics::Symbol &symbol = unwrapSymbol(allocateObject); | 
 |     fir::MutableBoxValue box = | 
 |         genMutableBoxValue(converter, loc, allocateObject); | 
 |     mlir::Value declaredTypeDesc = {}; | 
 |     if (box.isPolymorphic()) { | 
 |       mlir::Type eleType = box.getEleTy(); | 
 |       if (mlir::isa<fir::RecordType>(eleType)) | 
 |         if (const Fortran::semantics::DerivedTypeSpec *derivedTypeSpec = | 
 |                 symbol.GetType()->AsDerived()) { | 
 |           declaredTypeDesc = | 
 |               Fortran::lower::getTypeDescAddr(converter, loc, *derivedTypeSpec); | 
 |         } | 
 |     } | 
 |     mlir::Value beginOpValue = genDeallocate( | 
 |         builder, converter, loc, box, errorManager, declaredTypeDesc, &symbol); | 
 |     preDeallocationAction(converter, builder, beginOpValue, symbol); | 
 |   } | 
 |   builder.restoreInsertionPoint(insertPt); | 
 | } | 
 |  | 
 | //===----------------------------------------------------------------------===// | 
 | // MutableBoxValue creation implementation | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | /// Is this symbol a pointer to a pointer array that does not have the | 
 | /// CONTIGUOUS attribute ? | 
 | static inline bool | 
 | isNonContiguousArrayPointer(const Fortran::semantics::Symbol &sym) { | 
 |   return Fortran::semantics::IsPointer(sym) && sym.Rank() != 0 && | 
 |          !sym.attrs().test(Fortran::semantics::Attr::CONTIGUOUS); | 
 | } | 
 |  | 
 | /// Is this symbol a polymorphic pointer? | 
 | static inline bool isPolymorphicPointer(const Fortran::semantics::Symbol &sym) { | 
 |   return Fortran::semantics::IsPointer(sym) && | 
 |          Fortran::semantics::IsPolymorphic(sym); | 
 | } | 
 |  | 
 | /// Is this symbol a polymorphic allocatable? | 
 | static inline bool | 
 | isPolymorphicAllocatable(const Fortran::semantics::Symbol &sym) { | 
 |   return Fortran::semantics::IsAllocatable(sym) && | 
 |          Fortran::semantics::IsPolymorphic(sym); | 
 | } | 
 |  | 
 | /// Is this a local procedure symbol in a procedure that contains internal | 
 | /// procedures ? | 
 | static bool mayBeCapturedInInternalProc(const Fortran::semantics::Symbol &sym) { | 
 |   const Fortran::semantics::Scope &owner = sym.owner(); | 
 |   Fortran::semantics::Scope::Kind kind = owner.kind(); | 
 |   // Test if this is a procedure scope that contains a subprogram scope that is | 
 |   // not an interface. | 
 |   if (kind == Fortran::semantics::Scope::Kind::Subprogram || | 
 |       kind == Fortran::semantics::Scope::Kind::MainProgram) | 
 |     for (const Fortran::semantics::Scope &childScope : owner.children()) | 
 |       if (childScope.kind() == Fortran::semantics::Scope::Kind::Subprogram) | 
 |         if (const Fortran::semantics::Symbol *childSym = childScope.symbol()) | 
 |           if (const auto *details = | 
 |                   childSym->detailsIf<Fortran::semantics::SubprogramDetails>()) | 
 |             if (!details->isInterface()) | 
 |               return true; | 
 |   return false; | 
 | } | 
 |  | 
 | /// In case it is safe to track the properties in variables outside a | 
 | /// descriptor, create the variables to hold the mutable properties of the | 
 | /// entity var. The variables are not initialized here. | 
 | static fir::MutableProperties | 
 | createMutableProperties(Fortran::lower::AbstractConverter &converter, | 
 |                         mlir::Location loc, | 
 |                         const Fortran::lower::pft::Variable &var, | 
 |                         mlir::ValueRange nonDeferredParams, bool alwaysUseBox) { | 
 |   fir::FirOpBuilder &builder = converter.getFirOpBuilder(); | 
 |   const Fortran::semantics::Symbol &sym = var.getSymbol(); | 
 |   // Globals and dummies may be associated, creating local variables would | 
 |   // require keeping the values and descriptor before and after every single | 
 |   // impure calls in the current scope (not only the ones taking the variable as | 
 |   // arguments. All.) Volatile means the variable may change in ways not defined | 
 |   // per Fortran, so lowering can most likely not keep the descriptor and values | 
 |   // in sync as needed. | 
 |   // Pointers to non contiguous arrays need to be represented with a fir.box to | 
 |   // account for the discontiguity. | 
 |   // Pointer/Allocatable in internal procedure are descriptors in the host link, | 
 |   // and it would increase complexity to sync this descriptor with the local | 
 |   // values every time the host link is escaping. | 
 |   if (alwaysUseBox || var.isGlobal() || Fortran::semantics::IsDummy(sym) || | 
 |       Fortran::semantics::IsFunctionResult(sym) || | 
 |       sym.attrs().test(Fortran::semantics::Attr::VOLATILE) || | 
 |       isNonContiguousArrayPointer(sym) || useAllocateRuntime || | 
 |       useDescForMutableBox || mayBeCapturedInInternalProc(sym) || | 
 |       isPolymorphicPointer(sym) || isPolymorphicAllocatable(sym)) | 
 |     return {}; | 
 |   fir::MutableProperties mutableProperties; | 
 |   std::string name = converter.mangleName(sym); | 
 |   mlir::Type baseAddrTy = converter.genType(sym); | 
 |   if (auto boxType = mlir::dyn_cast<fir::BaseBoxType>(baseAddrTy)) | 
 |     baseAddrTy = boxType.getEleTy(); | 
 |   // Allocate and set a variable to hold the address. | 
 |   // It will be set to null in setUnallocatedStatus. | 
 |   mutableProperties.addr = | 
 |       builder.allocateLocal(loc, baseAddrTy, name + ".addr", "", | 
 |                             /*shape=*/{}, /*typeparams=*/{}); | 
 |   // Allocate variables to hold lower bounds and extents. | 
 |   int rank = sym.Rank(); | 
 |   mlir::Type idxTy = builder.getIndexType(); | 
 |   for (decltype(rank) i = 0; i < rank; ++i) { | 
 |     mlir::Value lboundVar = | 
 |         builder.allocateLocal(loc, idxTy, name + ".lb" + std::to_string(i), "", | 
 |                               /*shape=*/{}, /*typeparams=*/{}); | 
 |     mlir::Value extentVar = | 
 |         builder.allocateLocal(loc, idxTy, name + ".ext" + std::to_string(i), "", | 
 |                               /*shape=*/{}, /*typeparams=*/{}); | 
 |     mutableProperties.lbounds.emplace_back(lboundVar); | 
 |     mutableProperties.extents.emplace_back(extentVar); | 
 |   } | 
 |  | 
 |   // Allocate variable to hold deferred length parameters. | 
 |   mlir::Type eleTy = baseAddrTy; | 
 |   if (auto newTy = fir::dyn_cast_ptrEleTy(eleTy)) | 
 |     eleTy = newTy; | 
 |   if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(eleTy)) | 
 |     eleTy = seqTy.getEleTy(); | 
 |   if (auto record = mlir::dyn_cast<fir::RecordType>(eleTy)) | 
 |     if (record.getNumLenParams() != 0) | 
 |       TODO(loc, "deferred length type parameters."); | 
 |   if (fir::isa_char(eleTy) && nonDeferredParams.empty()) { | 
 |     mlir::Value lenVar = builder.allocateLocal( | 
 |         loc, builder.getCharacterLengthType(), name + ".len", "", /*shape=*/{}, | 
 |         /*typeparams=*/{}); | 
 |     mutableProperties.deferredParams.emplace_back(lenVar); | 
 |   } | 
 |   return mutableProperties; | 
 | } | 
 |  | 
 | fir::MutableBoxValue Fortran::lower::createMutableBox( | 
 |     Fortran::lower::AbstractConverter &converter, mlir::Location loc, | 
 |     const Fortran::lower::pft::Variable &var, mlir::Value boxAddr, | 
 |     mlir::ValueRange nonDeferredParams, bool alwaysUseBox, unsigned allocator) { | 
 |   fir::MutableProperties mutableProperties = createMutableProperties( | 
 |       converter, loc, var, nonDeferredParams, alwaysUseBox); | 
 |   fir::MutableBoxValue box(boxAddr, nonDeferredParams, mutableProperties); | 
 |   fir::FirOpBuilder &builder = converter.getFirOpBuilder(); | 
 |   if (!var.isGlobal() && !Fortran::semantics::IsDummy(var.getSymbol())) | 
 |     fir::factory::disassociateMutableBox(builder, loc, box, | 
 |                                          /*polymorphicSetType=*/false, | 
 |                                          allocator); | 
 |   return box; | 
 | } | 
 |  | 
 | //===----------------------------------------------------------------------===// | 
 | // MutableBoxValue reading interface implementation | 
 | //===----------------------------------------------------------------------===// | 
 |  | 
 | bool Fortran::lower::isArraySectionWithoutVectorSubscript( | 
 |     const Fortran::lower::SomeExpr &expr) { | 
 |   return expr.Rank() > 0 && Fortran::evaluate::IsVariable(expr) && | 
 |          !Fortran::evaluate::UnwrapWholeSymbolDataRef(expr) && | 
 |          !Fortran::evaluate::HasVectorSubscript(expr); | 
 | } | 
 |  | 
 | void Fortran::lower::associateMutableBox( | 
 |     Fortran::lower::AbstractConverter &converter, mlir::Location loc, | 
 |     const fir::MutableBoxValue &box, const Fortran::lower::SomeExpr &source, | 
 |     mlir::ValueRange lbounds, Fortran::lower::StatementContext &stmtCtx) { | 
 |   fir::FirOpBuilder &builder = converter.getFirOpBuilder(); | 
 |   if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(source)) { | 
 |     fir::factory::disassociateMutableBox(builder, loc, box); | 
 |     cuf::genPointerSync(box.getAddr(), builder); | 
 |     return; | 
 |   } | 
 |   if (converter.getLoweringOptions().getLowerToHighLevelFIR()) { | 
 |     fir::ExtendedValue rhs = converter.genExprAddr(loc, source, stmtCtx); | 
 |     fir::factory::associateMutableBox(builder, loc, box, rhs, lbounds); | 
 |     cuf::genPointerSync(box.getAddr(), builder); | 
 |     return; | 
 |   } | 
 |   // The right hand side is not be evaluated into a temp. Array sections can | 
 |   // typically be represented as a value of type `!fir.box`. However, an | 
 |   // expression that uses vector subscripts cannot be emboxed. In that case, | 
 |   // generate a reference to avoid having to later use a fir.rebox to implement | 
 |   // the pointer association. | 
 |   fir::ExtendedValue rhs = isArraySectionWithoutVectorSubscript(source) | 
 |                                ? converter.genExprBox(loc, source, stmtCtx) | 
 |                                : converter.genExprAddr(loc, source, stmtCtx); | 
 |  | 
 |   fir::factory::associateMutableBox(builder, loc, box, rhs, lbounds); | 
 | } | 
 |  | 
 | bool Fortran::lower::isWholeAllocatable(const Fortran::lower::SomeExpr &expr) { | 
 |   if (const Fortran::semantics::Symbol *sym = | 
 |           Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(expr)) | 
 |     return Fortran::semantics::IsAllocatable(sym->GetUltimate()); | 
 |   return false; | 
 | } | 
 |  | 
 | bool Fortran::lower::isWholePointer(const Fortran::lower::SomeExpr &expr) { | 
 |   if (const Fortran::semantics::Symbol *sym = | 
 |           Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(expr)) | 
 |     return Fortran::semantics::IsPointer(sym->GetUltimate()); | 
 |   return false; | 
 | } | 
 |  | 
 | mlir::Value Fortran::lower::getAssumedCharAllocatableOrPointerLen( | 
 |     fir::FirOpBuilder &builder, mlir::Location loc, | 
 |     const Fortran::semantics::Symbol &sym, mlir::Value box) { | 
 |   // Read length from fir.box (explicit expr cannot safely be re-evaluated | 
 |   // here). | 
 |   auto readLength = [&]() { | 
 |     fir::BoxValue boxLoad = | 
 |         fir::LoadOp::create(builder, loc, fir::getBase(box)).getResult(); | 
 |     return fir::factory::readCharLen(builder, loc, boxLoad); | 
 |   }; | 
 |   if (Fortran::semantics::IsOptional(sym)) { | 
 |     mlir::IndexType idxTy = builder.getIndexType(); | 
 |     // It is not safe to unconditionally read boxes of optionals in case | 
 |     // they are absents. According to 15.5.2.12 3 (9), it is illegal to | 
 |     // inquire the length of absent optional, even if non deferred, so | 
 |     // it's fine to use undefOp in this case. | 
 |     auto isPresent = fir::IsPresentOp::create(builder, loc, builder.getI1Type(), | 
 |                                               fir::getBase(box)); | 
 |     mlir::Value len = | 
 |         builder.genIfOp(loc, {idxTy}, isPresent, true) | 
 |             .genThen( | 
 |                 [&]() { fir::ResultOp::create(builder, loc, readLength()); }) | 
 |             .genElse([&]() { | 
 |               auto undef = fir::UndefOp::create(builder, loc, idxTy); | 
 |               fir::ResultOp::create(builder, loc, undef.getResult()); | 
 |             }) | 
 |             .getResults()[0]; | 
 |     return len; | 
 |   } | 
 |  | 
 |   return readLength(); | 
 | } | 
 |  | 
 | mlir::Value Fortran::lower::getTypeDescAddr( | 
 |     AbstractConverter &converter, mlir::Location loc, | 
 |     const Fortran::semantics::DerivedTypeSpec &typeSpec) { | 
 |   mlir::Type typeDesc = | 
 |       Fortran::lower::translateDerivedTypeToFIRType(converter, typeSpec); | 
 |   fir::FirOpBuilder &builder = converter.getFirOpBuilder(); | 
 |   return fir::TypeDescOp::create(builder, loc, mlir::TypeAttr::get(typeDesc)); | 
 | } |