1 //===-- ConvertCall.cpp ---------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "flang/Lower/ConvertCall.h" 14 #include "flang/Lower/Allocatable.h" 15 #include "flang/Lower/ConvertExprToHLFIR.h" 16 #include "flang/Lower/ConvertProcedureDesignator.h" 17 #include "flang/Lower/ConvertVariable.h" 18 #include "flang/Lower/CustomIntrinsicCall.h" 19 #include "flang/Lower/HlfirIntrinsics.h" 20 #include "flang/Lower/StatementContext.h" 21 #include "flang/Lower/SymbolMap.h" 22 #include "flang/Optimizer/Builder/BoxValue.h" 23 #include "flang/Optimizer/Builder/Character.h" 24 #include "flang/Optimizer/Builder/FIRBuilder.h" 25 #include "flang/Optimizer/Builder/HLFIRTools.h" 26 #include "flang/Optimizer/Builder/IntrinsicCall.h" 27 #include "flang/Optimizer/Builder/LowLevelIntrinsics.h" 28 #include "flang/Optimizer/Builder/MutableBox.h" 29 #include "flang/Optimizer/Builder/Runtime/Derived.h" 30 #include "flang/Optimizer/Builder/Todo.h" 31 #include "flang/Optimizer/Dialect/CUF/CUFOps.h" 32 #include "flang/Optimizer/Dialect/FIROpsSupport.h" 33 #include "flang/Optimizer/HLFIR/HLFIROps.h" 34 #include "mlir/IR/IRMapping.h" 35 #include "llvm/Support/CommandLine.h" 36 #include "llvm/Support/Debug.h" 37 #include <optional> 38 39 #define DEBUG_TYPE "flang-lower-expr" 40 41 static llvm::cl::opt<bool> useHlfirIntrinsicOps( 42 "use-hlfir-intrinsic-ops", llvm::cl::init(true), 43 llvm::cl::desc("Lower via HLFIR transformational intrinsic operations such " 44 "as hlfir.sum")); 45 46 static constexpr char tempResultName[] = ".tmp.func_result"; 47 48 /// Helper to package a Value and its properties into an ExtendedValue. 49 static fir::ExtendedValue toExtendedValue(mlir::Location loc, mlir::Value base, 50 llvm::ArrayRef<mlir::Value> extents, 51 llvm::ArrayRef<mlir::Value> lengths) { 52 mlir::Type type = base.getType(); 53 if (mlir::isa<fir::BaseBoxType>(type)) 54 return fir::BoxValue(base, /*lbounds=*/{}, lengths, extents); 55 type = fir::unwrapRefType(type); 56 if (mlir::isa<fir::BaseBoxType>(type)) 57 return fir::MutableBoxValue(base, lengths, /*mutableProperties*/ {}); 58 if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(type)) { 59 if (seqTy.getDimension() != extents.size()) 60 fir::emitFatalError(loc, "incorrect number of extents for array"); 61 if (mlir::isa<fir::CharacterType>(seqTy.getEleTy())) { 62 if (lengths.empty()) 63 fir::emitFatalError(loc, "missing length for character"); 64 assert(lengths.size() == 1); 65 return fir::CharArrayBoxValue(base, lengths[0], extents); 66 } 67 return fir::ArrayBoxValue(base, extents); 68 } 69 if (mlir::isa<fir::CharacterType>(type)) { 70 if (lengths.empty()) 71 fir::emitFatalError(loc, "missing length for character"); 72 assert(lengths.size() == 1); 73 return fir::CharBoxValue(base, lengths[0]); 74 } 75 return base; 76 } 77 78 /// Lower a type(C_PTR/C_FUNPTR) argument with VALUE attribute into a 79 /// reference. A C pointer can correspond to a Fortran dummy argument of type 80 /// C_PTR with the VALUE attribute. (see 18.3.6 note 3). 81 static mlir::Value genRecordCPtrValueArg(fir::FirOpBuilder &builder, 82 mlir::Location loc, mlir::Value rec, 83 mlir::Type ty) { 84 mlir::Value cAddr = fir::factory::genCPtrOrCFunptrAddr(builder, loc, rec, ty); 85 mlir::Value cVal = builder.create<fir::LoadOp>(loc, cAddr); 86 return builder.createConvert(loc, cAddr.getType(), cVal); 87 } 88 89 // Find the argument that corresponds to the host associations. 90 // Verify some assumptions about how the signature was built here. 91 [[maybe_unused]] static unsigned findHostAssocTuplePos(mlir::func::FuncOp fn) { 92 // Scan the argument list from last to first as the host associations are 93 // appended for now. 94 for (unsigned i = fn.getNumArguments(); i > 0; --i) 95 if (fn.getArgAttr(i - 1, fir::getHostAssocAttrName())) { 96 // Host assoc tuple must be last argument (for now). 97 assert(i == fn.getNumArguments() && "tuple must be last"); 98 return i - 1; 99 } 100 llvm_unreachable("anyFuncArgsHaveAttr failed"); 101 } 102 103 mlir::Value 104 Fortran::lower::argumentHostAssocs(Fortran::lower::AbstractConverter &converter, 105 mlir::Value arg) { 106 if (auto addr = mlir::dyn_cast_or_null<fir::AddrOfOp>(arg.getDefiningOp())) { 107 auto &builder = converter.getFirOpBuilder(); 108 if (auto funcOp = builder.getNamedFunction(addr.getSymbol())) 109 if (fir::anyFuncArgsHaveAttr(funcOp, fir::getHostAssocAttrName())) 110 return converter.hostAssocTupleValue(); 111 } 112 return {}; 113 } 114 115 static bool mustCastFuncOpToCopeWithImplicitInterfaceMismatch( 116 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 117 mlir::FunctionType callSiteType, mlir::FunctionType funcOpType) { 118 // Deal with argument number mismatch by making a function pointer so 119 // that function type cast can be inserted. Do not emit a warning here 120 // because this can happen in legal program if the function is not 121 // defined here and it was first passed as an argument without any more 122 // information. 123 if (callSiteType.getNumResults() != funcOpType.getNumResults() || 124 callSiteType.getNumInputs() != funcOpType.getNumInputs()) 125 return true; 126 127 // Implicit interface result type mismatch are not standard Fortran, but 128 // some compilers are not complaining about it. The front end is not 129 // protecting lowering from this currently. Support this with a 130 // discouraging warning. 131 // Cast the actual function to the current caller implicit type because 132 // that is the behavior we would get if we could not see the definition. 133 if (callSiteType.getResults() != funcOpType.getResults()) { 134 LLVM_DEBUG(mlir::emitWarning( 135 loc, "a return type mismatch is not standard compliant and may " 136 "lead to undefined behavior.")); 137 return true; 138 } 139 140 // In HLFIR, there is little attempt to cope with implicit interface 141 // mismatch on the arguments. The argument are always prepared according 142 // to the implicit interface. Cast the actual function if any of the 143 // argument mismatch cannot be dealt with a simple fir.convert. 144 if (converter.getLoweringOptions().getLowerToHighLevelFIR()) 145 for (auto [actualType, dummyType] : 146 llvm::zip(callSiteType.getInputs(), funcOpType.getInputs())) 147 if (actualType != dummyType && 148 !fir::ConvertOp::canBeConverted(actualType, dummyType)) 149 return true; 150 return false; 151 } 152 153 static mlir::Value readDim3Value(fir::FirOpBuilder &builder, mlir::Location loc, 154 mlir::Value dim3Addr, llvm::StringRef comp) { 155 mlir::Type i32Ty = builder.getI32Type(); 156 mlir::Type refI32Ty = fir::ReferenceType::get(i32Ty); 157 llvm::SmallVector<mlir::Value> lenParams; 158 159 mlir::Value designate = builder.create<hlfir::DesignateOp>( 160 loc, refI32Ty, dim3Addr, /*component=*/comp, 161 /*componentShape=*/mlir::Value{}, hlfir::DesignateOp::Subscripts{}, 162 /*substring=*/mlir::ValueRange{}, /*complexPartAttr=*/std::nullopt, 163 mlir::Value{}, lenParams); 164 165 return hlfir::loadTrivialScalar(loc, builder, hlfir::Entity{designate}); 166 } 167 168 static mlir::Value remapActualToDummyDescriptor( 169 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 170 Fortran::lower::SymMap &symMap, 171 const Fortran::lower::CallerInterface::PassedEntity &arg, 172 Fortran::lower::CallerInterface &caller, bool isBindcCall) { 173 fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 174 mlir::IndexType idxTy = builder.getIndexType(); 175 mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0); 176 Fortran::lower::StatementContext localStmtCtx; 177 auto lowerSpecExpr = [&](const auto &expr, 178 bool isAssumedSizeExtent) -> mlir::Value { 179 mlir::Value convertExpr = builder.createConvert( 180 loc, idxTy, fir::getBase(converter.genExprValue(expr, localStmtCtx))); 181 if (isAssumedSizeExtent) 182 return convertExpr; 183 return fir::factory::genMaxWithZero(builder, loc, convertExpr); 184 }; 185 bool mapSymbols = caller.mustMapInterfaceSymbolsForDummyArgument(arg); 186 if (mapSymbols) { 187 symMap.pushScope(); 188 const Fortran::semantics::Symbol *sym = caller.getDummySymbol(arg); 189 assert(sym && "call must have explicit interface to map interface symbols"); 190 Fortran::lower::mapCallInterfaceSymbolsForDummyArgument(converter, caller, 191 symMap, *sym); 192 } 193 llvm::SmallVector<mlir::Value> extents; 194 llvm::SmallVector<mlir::Value> lengths; 195 mlir::Type dummyBoxType = caller.getDummyArgumentType(arg); 196 mlir::Type dummyBaseType = fir::unwrapPassByRefType(dummyBoxType); 197 if (mlir::isa<fir::SequenceType>(dummyBaseType)) 198 caller.walkDummyArgumentExtents( 199 arg, [&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) { 200 extents.emplace_back(lowerSpecExpr(e, isAssumedSizeExtent)); 201 }); 202 mlir::Value shape; 203 if (!extents.empty()) { 204 if (isBindcCall) { 205 // Preserve zero lower bounds (see F'2023 18.5.3). 206 llvm::SmallVector<mlir::Value> lowerBounds(extents.size(), zero); 207 shape = builder.genShape(loc, lowerBounds, extents); 208 } else { 209 shape = builder.genShape(loc, extents); 210 } 211 } 212 213 hlfir::Entity explicitArgument = hlfir::Entity{caller.getInput(arg)}; 214 mlir::Type dummyElementType = fir::unwrapSequenceType(dummyBaseType); 215 if (auto recType = llvm::dyn_cast<fir::RecordType>(dummyElementType)) 216 if (recType.getNumLenParams() > 0) 217 TODO(loc, "sequence association of length parameterized derived type " 218 "dummy arguments"); 219 if (fir::isa_char(dummyElementType)) 220 lengths.emplace_back(hlfir::genCharLength(loc, builder, explicitArgument)); 221 mlir::Value baseAddr = 222 hlfir::genVariableRawAddress(loc, builder, explicitArgument); 223 baseAddr = builder.createConvert(loc, fir::ReferenceType::get(dummyBaseType), 224 baseAddr); 225 mlir::Value mold; 226 if (fir::isPolymorphicType(dummyBoxType)) 227 mold = explicitArgument; 228 mlir::Value remapped = 229 builder.create<fir::EmboxOp>(loc, dummyBoxType, baseAddr, shape, 230 /*slice=*/mlir::Value{}, lengths, mold); 231 if (mapSymbols) 232 symMap.popScope(); 233 return remapped; 234 } 235 236 /// Create a descriptor for sequenced associated descriptor that are passed 237 /// by descriptor. Sequence association (F'2023 15.5.2.12) implies that the 238 /// dummy shape and rank need to not be the same as the actual argument. This 239 /// helper creates a descriptor based on the dummy shape and rank (sequence 240 /// association can only happen with explicit and assumed-size array) so that it 241 /// is safe to assume the rank of the incoming descriptor inside the callee. 242 /// This helper must be called once all the actual arguments have been lowered 243 /// and placed inside "caller". Copy-in/copy-out must already have been 244 /// generated if needed using the actual argument shape (the dummy shape may be 245 /// assumed-size). 246 static void remapActualToDummyDescriptors( 247 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 248 Fortran::lower::SymMap &symMap, 249 const Fortran::lower::PreparedActualArguments &loweredActuals, 250 Fortran::lower::CallerInterface &caller, bool isBindcCall) { 251 fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 252 for (auto [preparedActual, arg] : 253 llvm::zip(loweredActuals, caller.getPassedArguments())) { 254 if (arg.isSequenceAssociatedDescriptor()) { 255 if (!preparedActual.value().handleDynamicOptional()) { 256 mlir::Value remapped = remapActualToDummyDescriptor( 257 loc, converter, symMap, arg, caller, isBindcCall); 258 caller.placeInput(arg, remapped); 259 } else { 260 // Absent optional actual argument descriptor cannot be read and 261 // remapped unconditionally. 262 mlir::Type dummyType = caller.getDummyArgumentType(arg); 263 mlir::Value isPresent = preparedActual.value().getIsPresent(); 264 auto &argLambdaCapture = arg; 265 mlir::Value remapped = 266 builder 267 .genIfOp(loc, {dummyType}, isPresent, 268 /*withElseRegion=*/true) 269 .genThen([&]() { 270 mlir::Value newBox = remapActualToDummyDescriptor( 271 loc, converter, symMap, argLambdaCapture, caller, 272 isBindcCall); 273 builder.create<fir::ResultOp>(loc, newBox); 274 }) 275 .genElse([&]() { 276 mlir::Value absent = 277 builder.create<fir::AbsentOp>(loc, dummyType); 278 builder.create<fir::ResultOp>(loc, absent); 279 }) 280 .getResults()[0]; 281 caller.placeInput(arg, remapped); 282 } 283 } 284 } 285 } 286 287 std::pair<Fortran::lower::LoweredResult, bool> 288 Fortran::lower::genCallOpAndResult( 289 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 290 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx, 291 Fortran::lower::CallerInterface &caller, mlir::FunctionType callSiteType, 292 std::optional<mlir::Type> resultType, bool isElemental) { 293 fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 294 using PassBy = Fortran::lower::CallerInterface::PassEntityBy; 295 bool mustPopSymMap = false; 296 if (caller.mustMapInterfaceSymbolsForResult()) { 297 symMap.pushScope(); 298 mustPopSymMap = true; 299 Fortran::lower::mapCallInterfaceSymbolsForResult(converter, caller, symMap); 300 } 301 // If this is an indirect call, retrieve the function address. Also retrieve 302 // the result length if this is a character function (note that this length 303 // will be used only if there is no explicit length in the local interface). 304 mlir::Value funcPointer; 305 mlir::Value charFuncPointerLength; 306 if (const Fortran::evaluate::ProcedureDesignator *procDesignator = 307 caller.getIfIndirectCall()) { 308 if (mlir::Value passedArg = caller.getIfPassedArg()) { 309 // Procedure pointer component call with PASS argument. To avoid 310 // "double" lowering of the ComponentRef, semantics only place the 311 // ComponentRef in the ActualArguments, not in the ProcedureDesignator ( 312 // that is only the component symbol). 313 // Fetch the passed argument and addresses of its procedure pointer 314 // component. 315 funcPointer = Fortran::lower::derefPassProcPointerComponent( 316 loc, converter, *procDesignator, passedArg, symMap, stmtCtx); 317 } else { 318 Fortran::lower::SomeExpr expr{*procDesignator}; 319 fir::ExtendedValue loweredProc = 320 converter.genExprAddr(loc, expr, stmtCtx); 321 funcPointer = fir::getBase(loweredProc); 322 // Dummy procedure may have assumed length, in which case the result 323 // length was passed along the dummy procedure. 324 // This is not possible with procedure pointer components. 325 if (const fir::CharBoxValue *charBox = loweredProc.getCharBox()) 326 charFuncPointerLength = charBox->getLen(); 327 } 328 } 329 330 const bool isExprCall = 331 converter.getLoweringOptions().getLowerToHighLevelFIR() && 332 callSiteType.getNumResults() == 1 && 333 llvm::isa<fir::SequenceType>(callSiteType.getResult(0)); 334 335 mlir::IndexType idxTy = builder.getIndexType(); 336 auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value { 337 mlir::Value convertExpr = builder.createConvert( 338 loc, idxTy, fir::getBase(converter.genExprValue(expr, stmtCtx))); 339 return fir::factory::genMaxWithZero(builder, loc, convertExpr); 340 }; 341 llvm::SmallVector<mlir::Value> resultLengths; 342 mlir::Value arrayResultShape; 343 hlfir::EvaluateInMemoryOp evaluateInMemory; 344 auto allocatedResult = [&]() -> std::optional<fir::ExtendedValue> { 345 llvm::SmallVector<mlir::Value> extents; 346 llvm::SmallVector<mlir::Value> lengths; 347 if (!caller.callerAllocateResult()) 348 return {}; 349 mlir::Type type = caller.getResultStorageType(); 350 if (mlir::isa<fir::SequenceType>(type)) 351 caller.walkResultExtents( 352 [&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) { 353 assert(!isAssumedSizeExtent && "result cannot be assumed-size"); 354 extents.emplace_back(lowerSpecExpr(e)); 355 }); 356 caller.walkResultLengths( 357 [&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) { 358 assert(!isAssumedSizeExtent && "result cannot be assumed-size"); 359 lengths.emplace_back(lowerSpecExpr(e)); 360 }); 361 362 // Result length parameters should not be provided to box storage 363 // allocation and save_results, but they are still useful information to 364 // keep in the ExtendedValue if non-deferred. 365 if (!mlir::isa<fir::BoxType>(type)) { 366 if (fir::isa_char(fir::unwrapSequenceType(type)) && lengths.empty()) { 367 // Calling an assumed length function. This is only possible if this 368 // is a call to a character dummy procedure. 369 if (!charFuncPointerLength) 370 fir::emitFatalError(loc, "failed to retrieve character function " 371 "length while calling it"); 372 lengths.push_back(charFuncPointerLength); 373 } 374 resultLengths = lengths; 375 } 376 377 if (!extents.empty()) 378 arrayResultShape = builder.genShape(loc, extents); 379 380 if (isExprCall) { 381 mlir::Type exprType = hlfir::getExprType(type); 382 evaluateInMemory = builder.create<hlfir::EvaluateInMemoryOp>( 383 loc, exprType, arrayResultShape, resultLengths); 384 builder.setInsertionPointToStart(&evaluateInMemory.getBody().front()); 385 return toExtendedValue(loc, evaluateInMemory.getMemory(), extents, 386 lengths); 387 } 388 389 if ((!extents.empty() || !lengths.empty()) && !isElemental) { 390 // Note: in the elemental context, the alloca ownership inside the 391 // elemental region is implicit, and later pass in lowering (stack 392 // reclaim) fir.do_loop will be in charge of emitting any stack 393 // save/restore if needed. 394 auto *bldr = &converter.getFirOpBuilder(); 395 mlir::Value sp = bldr->genStackSave(loc); 396 stmtCtx.attachCleanup( 397 [bldr, loc, sp]() { bldr->genStackRestore(loc, sp); }); 398 } 399 mlir::Value temp = 400 builder.createTemporary(loc, type, ".result", extents, resultLengths); 401 return toExtendedValue(loc, temp, extents, lengths); 402 }(); 403 404 if (mustPopSymMap) 405 symMap.popScope(); 406 407 // Place allocated result 408 if (allocatedResult) { 409 if (std::optional<Fortran::lower::CallInterface< 410 Fortran::lower::CallerInterface>::PassedEntity> 411 resultArg = caller.getPassedResult()) { 412 if (resultArg->passBy == PassBy::AddressAndLength) 413 caller.placeAddressAndLengthInput(*resultArg, 414 fir::getBase(*allocatedResult), 415 fir::getLen(*allocatedResult)); 416 else if (resultArg->passBy == PassBy::BaseAddress) 417 caller.placeInput(*resultArg, fir::getBase(*allocatedResult)); 418 else 419 fir::emitFatalError( 420 loc, "only expect character scalar result to be passed by ref"); 421 } 422 } 423 424 // In older Fortran, procedure argument types are inferred. This may lead 425 // different view of what the function signature is in different locations. 426 // Casts are inserted as needed below to accommodate this. 427 428 // The mlir::func::FuncOp type prevails, unless it has a different number of 429 // arguments which can happen in legal program if it was passed as a dummy 430 // procedure argument earlier with no further type information. 431 mlir::SymbolRefAttr funcSymbolAttr; 432 bool addHostAssociations = false; 433 if (!funcPointer) { 434 mlir::FunctionType funcOpType = caller.getFuncOp().getFunctionType(); 435 mlir::SymbolRefAttr symbolAttr = 436 builder.getSymbolRefAttr(caller.getMangledName()); 437 if (callSiteType.getNumResults() == funcOpType.getNumResults() && 438 callSiteType.getNumInputs() + 1 == funcOpType.getNumInputs() && 439 fir::anyFuncArgsHaveAttr(caller.getFuncOp(), 440 fir::getHostAssocAttrName())) { 441 // The number of arguments is off by one, and we're lowering a function 442 // with host associations. Modify call to include host associations 443 // argument by appending the value at the end of the operands. 444 assert(funcOpType.getInput(findHostAssocTuplePos(caller.getFuncOp())) == 445 converter.hostAssocTupleValue().getType()); 446 addHostAssociations = true; 447 } 448 // When this is not a call to an internal procedure (where there is a 449 // mismatch due to the extra argument, but the interface is otherwise 450 // explicit and safe), handle interface mismatch due to F77 implicit 451 // interface "abuse" with a function address cast if needed. 452 if (!addHostAssociations && 453 mustCastFuncOpToCopeWithImplicitInterfaceMismatch( 454 loc, converter, callSiteType, funcOpType)) 455 funcPointer = builder.create<fir::AddrOfOp>(loc, funcOpType, symbolAttr); 456 else 457 funcSymbolAttr = symbolAttr; 458 459 // Issue a warning if the procedure name conflicts with 460 // a runtime function name a call to which has been already 461 // lowered (implying that the FuncOp has been created). 462 // The behavior is undefined in this case. 463 if (caller.getFuncOp()->hasAttrOfType<mlir::UnitAttr>( 464 fir::FIROpsDialect::getFirRuntimeAttrName())) 465 LLVM_DEBUG(mlir::emitWarning( 466 loc, 467 llvm::Twine("function name '") + 468 llvm::Twine(symbolAttr.getLeafReference()) + 469 llvm::Twine("' conflicts with a runtime function name used by " 470 "Flang - this may lead to undefined behavior"))); 471 } 472 473 mlir::FunctionType funcType = 474 funcPointer ? callSiteType : caller.getFuncOp().getFunctionType(); 475 llvm::SmallVector<mlir::Value> operands; 476 // First operand of indirect call is the function pointer. Cast it to 477 // required function type for the call to handle procedures that have a 478 // compatible interface in Fortran, but that have different signatures in 479 // FIR. 480 if (funcPointer) { 481 operands.push_back( 482 mlir::isa<fir::BoxProcType>(funcPointer.getType()) 483 ? builder.create<fir::BoxAddrOp>(loc, funcType, funcPointer) 484 : builder.createConvert(loc, funcType, funcPointer)); 485 } 486 487 // Deal with potential mismatches in arguments types. Passing an array to a 488 // scalar argument should for instance be tolerated here. 489 bool callingImplicitInterface = caller.canBeCalledViaImplicitInterface(); 490 for (auto [fst, snd] : llvm::zip(caller.getInputs(), funcType.getInputs())) { 491 // When passing arguments to a procedure that can be called by implicit 492 // interface, allow any character actual arguments to be passed to dummy 493 // arguments of any type and vice versa. 494 mlir::Value cast; 495 auto *context = builder.getContext(); 496 if (mlir::isa<fir::BoxProcType>(snd) && 497 mlir::isa<mlir::FunctionType>(fst.getType())) { 498 auto funcTy = 499 mlir::FunctionType::get(context, std::nullopt, std::nullopt); 500 auto boxProcTy = builder.getBoxProcType(funcTy); 501 if (mlir::Value host = argumentHostAssocs(converter, fst)) { 502 cast = builder.create<fir::EmboxProcOp>( 503 loc, boxProcTy, llvm::ArrayRef<mlir::Value>{fst, host}); 504 } else { 505 cast = builder.create<fir::EmboxProcOp>(loc, boxProcTy, fst); 506 } 507 } else { 508 mlir::Type fromTy = fir::unwrapRefType(fst.getType()); 509 if (fir::isa_builtin_cptr_type(fromTy) && 510 Fortran::lower::isCPtrArgByValueType(snd)) { 511 cast = genRecordCPtrValueArg(builder, loc, fst, fromTy); 512 } else if (fir::isa_derived(snd) && !fir::isa_derived(fst.getType())) { 513 // TODO: remove this TODO once the old lowering is gone. 514 TODO(loc, "derived type argument passed by value"); 515 } else { 516 // With the lowering to HLFIR, box arguments have already been built 517 // according to the attributes, rank, bounds, and type they should have. 518 // Do not attempt any reboxing here that could break this. 519 bool legacyLowering = 520 !converter.getLoweringOptions().getLowerToHighLevelFIR(); 521 cast = builder.convertWithSemantics(loc, snd, fst, 522 callingImplicitInterface, 523 /*allowRebox=*/legacyLowering); 524 } 525 } 526 operands.push_back(cast); 527 } 528 529 // Add host associations as necessary. 530 if (addHostAssociations) 531 operands.push_back(converter.hostAssocTupleValue()); 532 533 mlir::Value callResult; 534 unsigned callNumResults; 535 fir::FortranProcedureFlagsEnumAttr procAttrs = 536 caller.getProcedureAttrs(builder.getContext()); 537 538 if (!caller.getCallDescription().chevrons().empty()) { 539 // A call to a CUDA kernel with the chevron syntax. 540 541 mlir::Type i32Ty = builder.getI32Type(); 542 mlir::Value one = builder.createIntegerConstant(loc, i32Ty, 1); 543 544 mlir::Value grid_x, grid_y, grid_z; 545 if (caller.getCallDescription().chevrons()[0].GetType()->category() == 546 Fortran::common::TypeCategory::Integer) { 547 // If grid is an integer, it is converted to dim3(grid,1,1). Since z is 548 // not used for the number of thread blocks, it is omitted in the op. 549 grid_x = builder.createConvert( 550 loc, i32Ty, 551 fir::getBase(converter.genExprValue( 552 caller.getCallDescription().chevrons()[0], stmtCtx))); 553 grid_y = one; 554 grid_z = one; 555 } else { 556 auto dim3Addr = converter.genExprAddr( 557 caller.getCallDescription().chevrons()[0], stmtCtx); 558 grid_x = readDim3Value(builder, loc, fir::getBase(dim3Addr), "x"); 559 grid_y = readDim3Value(builder, loc, fir::getBase(dim3Addr), "y"); 560 grid_z = readDim3Value(builder, loc, fir::getBase(dim3Addr), "z"); 561 } 562 563 mlir::Value block_x, block_y, block_z; 564 if (caller.getCallDescription().chevrons()[1].GetType()->category() == 565 Fortran::common::TypeCategory::Integer) { 566 // If block is an integer, it is converted to dim3(block,1,1). 567 block_x = builder.createConvert( 568 loc, i32Ty, 569 fir::getBase(converter.genExprValue( 570 caller.getCallDescription().chevrons()[1], stmtCtx))); 571 block_y = one; 572 block_z = one; 573 } else { 574 auto dim3Addr = converter.genExprAddr( 575 caller.getCallDescription().chevrons()[1], stmtCtx); 576 block_x = readDim3Value(builder, loc, fir::getBase(dim3Addr), "x"); 577 block_y = readDim3Value(builder, loc, fir::getBase(dim3Addr), "y"); 578 block_z = readDim3Value(builder, loc, fir::getBase(dim3Addr), "z"); 579 } 580 581 mlir::Value bytes; // bytes is optional. 582 if (caller.getCallDescription().chevrons().size() > 2) 583 bytes = builder.createConvert( 584 loc, i32Ty, 585 fir::getBase(converter.genExprValue( 586 caller.getCallDescription().chevrons()[2], stmtCtx))); 587 588 mlir::Value stream; // stream is optional. 589 if (caller.getCallDescription().chevrons().size() > 3) 590 stream = builder.createConvert( 591 loc, i32Ty, 592 fir::getBase(converter.genExprValue( 593 caller.getCallDescription().chevrons()[3], stmtCtx))); 594 595 builder.create<cuf::KernelLaunchOp>( 596 loc, funcType.getResults(), funcSymbolAttr, grid_x, grid_y, grid_z, 597 block_x, block_y, block_z, bytes, stream, operands); 598 callNumResults = 0; 599 } else if (caller.requireDispatchCall()) { 600 // Procedure call requiring a dynamic dispatch. Call is created with 601 // fir.dispatch. 602 603 // Get the raw procedure name. The procedure name is not mangled in the 604 // binding table, but there can be a suffix to distinguish bindings of 605 // the same name (which happens only when PRIVATE bindings exist in 606 // ancestor types in other modules). 607 const auto &ultimateSymbol = 608 caller.getCallDescription().proc().GetSymbol()->GetUltimate(); 609 std::string procName = ultimateSymbol.name().ToString(); 610 if (const auto &binding{ 611 ultimateSymbol.get<Fortran::semantics::ProcBindingDetails>()}; 612 binding.numPrivatesNotOverridden() > 0) 613 procName += "."s + std::to_string(binding.numPrivatesNotOverridden()); 614 fir::DispatchOp dispatch; 615 if (std::optional<unsigned> passArg = caller.getPassArgIndex()) { 616 // PASS, PASS(arg-name) 617 // Note that caller.getInputs is used instead of operands to get the 618 // passed object because interface mismatch issues may have inserted a 619 // cast to the operand with a different declared type, which would break 620 // later type bound call resolution in the FIR to FIR pass. 621 dispatch = builder.create<fir::DispatchOp>( 622 loc, funcType.getResults(), builder.getStringAttr(procName), 623 caller.getInputs()[*passArg], operands, 624 builder.getI32IntegerAttr(*passArg), procAttrs); 625 } else { 626 // NOPASS 627 const Fortran::evaluate::Component *component = 628 caller.getCallDescription().proc().GetComponent(); 629 assert(component && "expect component for type-bound procedure call."); 630 631 fir::ExtendedValue dataRefValue = Fortran::lower::convertDataRefToValue( 632 loc, converter, component->base(), symMap, stmtCtx); 633 mlir::Value passObject = fir::getBase(dataRefValue); 634 635 if (fir::isa_ref_type(passObject.getType())) 636 passObject = builder.create<fir::LoadOp>(loc, passObject); 637 dispatch = builder.create<fir::DispatchOp>( 638 loc, funcType.getResults(), builder.getStringAttr(procName), 639 passObject, operands, nullptr, procAttrs); 640 } 641 callNumResults = dispatch.getNumResults(); 642 if (callNumResults != 0) 643 callResult = dispatch.getResult(0); 644 } else { 645 // Standard procedure call with fir.call. 646 auto call = builder.create<fir::CallOp>( 647 loc, funcType.getResults(), funcSymbolAttr, operands, procAttrs); 648 649 callNumResults = call.getNumResults(); 650 if (callNumResults != 0) 651 callResult = call.getResult(0); 652 } 653 654 std::optional<Fortran::evaluate::DynamicType> retTy = 655 caller.getCallDescription().proc().GetType(); 656 // With HLFIR lowering, isElemental must be set to true 657 // if we are producing an elemental call. In this case, 658 // the elemental results must not be destroyed, instead, 659 // the resulting array result will be finalized/destroyed 660 // as needed by hlfir.destroy. 661 const bool mustFinalizeResult = 662 !isElemental && callSiteType.getNumResults() > 0 && 663 !fir::isPointerType(callSiteType.getResult(0)) && retTy.has_value() && 664 (retTy->category() == Fortran::common::TypeCategory::Derived || 665 retTy->IsPolymorphic() || retTy->IsUnlimitedPolymorphic()); 666 667 if (caller.mustSaveResult()) { 668 assert(allocatedResult.has_value()); 669 builder.create<fir::SaveResultOp>(loc, callResult, 670 fir::getBase(*allocatedResult), 671 arrayResultShape, resultLengths); 672 } 673 674 if (evaluateInMemory) { 675 builder.setInsertionPointAfter(evaluateInMemory); 676 mlir::Value expr = evaluateInMemory.getResult(); 677 fir::FirOpBuilder *bldr = &converter.getFirOpBuilder(); 678 if (!isElemental) 679 stmtCtx.attachCleanup([bldr, loc, expr, mustFinalizeResult]() { 680 bldr->create<hlfir::DestroyOp>(loc, expr, 681 /*finalize=*/mustFinalizeResult); 682 }); 683 return {LoweredResult{hlfir::EntityWithAttributes{expr}}, 684 mustFinalizeResult}; 685 } 686 687 if (allocatedResult) { 688 // The result must be optionally destroyed (if it is of a derived type 689 // that may need finalization or deallocation of the components). 690 // For an allocatable result we have to free the memory allocated 691 // for the top-level entity. Note that the Destroy calls below 692 // do not deallocate the top-level entity. The two clean-ups 693 // must be pushed in reverse order, so that the final order is: 694 // Destroy(desc) 695 // free(desc->base_addr) 696 allocatedResult->match( 697 [&](const fir::MutableBoxValue &box) { 698 if (box.isAllocatable()) { 699 // 9.7.3.2 point 4. Deallocate allocatable results. Note that 700 // finalization was done independently by calling 701 // genDerivedTypeDestroy above and is not triggered by this inline 702 // deallocation. 703 fir::FirOpBuilder *bldr = &converter.getFirOpBuilder(); 704 stmtCtx.attachCleanup([bldr, loc, box]() { 705 fir::factory::genFreememIfAllocated(*bldr, loc, box); 706 }); 707 } 708 }, 709 [](const auto &) {}); 710 711 // 7.5.6.3 point 5. Derived-type finalization for nonpointer function. 712 bool resultIsFinalized = false; 713 // Check if the derived-type is finalizable if it is a monomorphic 714 // derived-type. 715 // For polymorphic and unlimited polymorphic enities call the runtime 716 // in any cases. 717 if (mustFinalizeResult) { 718 if (retTy->IsPolymorphic() || retTy->IsUnlimitedPolymorphic()) { 719 auto *bldr = &converter.getFirOpBuilder(); 720 stmtCtx.attachCleanup([bldr, loc, allocatedResult]() { 721 fir::runtime::genDerivedTypeDestroy(*bldr, loc, 722 fir::getBase(*allocatedResult)); 723 }); 724 resultIsFinalized = true; 725 } else { 726 const Fortran::semantics::DerivedTypeSpec &typeSpec = 727 retTy->GetDerivedTypeSpec(); 728 // If the result type may require finalization 729 // or have allocatable components, we need to make sure 730 // everything is properly finalized/deallocated. 731 if (Fortran::semantics::MayRequireFinalization(typeSpec) || 732 // We can use DerivedTypeDestroy even if finalization is not needed. 733 hlfir::mayHaveAllocatableComponent(funcType.getResults()[0])) { 734 auto *bldr = &converter.getFirOpBuilder(); 735 stmtCtx.attachCleanup([bldr, loc, allocatedResult]() { 736 mlir::Value box = bldr->createBox(loc, *allocatedResult); 737 fir::runtime::genDerivedTypeDestroy(*bldr, loc, box); 738 }); 739 resultIsFinalized = true; 740 } 741 } 742 } 743 return {LoweredResult{*allocatedResult}, resultIsFinalized}; 744 } 745 746 // subroutine call 747 if (!resultType) 748 return {LoweredResult{fir::ExtendedValue{mlir::Value{}}}, 749 /*resultIsFinalized=*/false}; 750 751 // For now, Fortran return values are implemented with a single MLIR 752 // function return value. 753 assert(callNumResults == 1 && "Expected exactly one result in FUNCTION call"); 754 (void)callNumResults; 755 756 // Call a BIND(C) function that return a char. 757 if (caller.characterize().IsBindC() && 758 mlir::isa<fir::CharacterType>(funcType.getResults()[0])) { 759 fir::CharacterType charTy = 760 mlir::dyn_cast<fir::CharacterType>(funcType.getResults()[0]); 761 mlir::Value len = builder.createIntegerConstant( 762 loc, builder.getCharacterLengthType(), charTy.getLen()); 763 return { 764 LoweredResult{fir::ExtendedValue{fir::CharBoxValue{callResult, len}}}, 765 /*resultIsFinalized=*/false}; 766 } 767 768 return {LoweredResult{fir::ExtendedValue{callResult}}, 769 /*resultIsFinalized=*/false}; 770 } 771 772 static hlfir::EntityWithAttributes genStmtFunctionRef( 773 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 774 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx, 775 const Fortran::evaluate::ProcedureRef &procRef) { 776 const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol(); 777 assert(symbol && "expected symbol in ProcedureRef of statement functions"); 778 const auto &details = symbol->get<Fortran::semantics::SubprogramDetails>(); 779 fir::FirOpBuilder &builder = converter.getFirOpBuilder(); 780 781 // Statement functions have their own scope, we just need to associate 782 // the dummy symbols to argument expressions. There are no 783 // optional/alternate return arguments. Statement functions cannot be 784 // recursive (directly or indirectly) so it is safe to add dummy symbols to 785 // the local map here. 786 symMap.pushScope(); 787 llvm::SmallVector<hlfir::AssociateOp> exprAssociations; 788 for (auto [arg, bind] : llvm::zip(details.dummyArgs(), procRef.arguments())) { 789 assert(arg && "alternate return in statement function"); 790 assert(bind && "optional argument in statement function"); 791 const auto *expr = bind->UnwrapExpr(); 792 // TODO: assumed type in statement function, that surprisingly seems 793 // allowed, probably because nobody thought of restricting this usage. 794 // gfortran/ifort compiles this. 795 assert(expr && "assumed type used as statement function argument"); 796 // As per Fortran 2018 C1580, statement function arguments can only be 797 // scalars. 798 // The only care is to use the dummy character explicit length if any 799 // instead of the actual argument length (that can be bigger). 800 hlfir::EntityWithAttributes loweredArg = Fortran::lower::convertExprToHLFIR( 801 loc, converter, *expr, symMap, stmtCtx); 802 fir::FortranVariableOpInterface variableIface = loweredArg.getIfVariable(); 803 if (!variableIface) { 804 // So far only FortranVariableOpInterface can be mapped to symbols. 805 // Create an hlfir.associate to create a variable from a potential 806 // value argument. 807 mlir::Type argType = converter.genType(*arg); 808 auto associate = hlfir::genAssociateExpr( 809 loc, builder, loweredArg, argType, toStringRef(arg->name())); 810 exprAssociations.push_back(associate); 811 variableIface = associate; 812 } 813 const Fortran::semantics::DeclTypeSpec *type = arg->GetType(); 814 if (type && 815 type->category() == Fortran::semantics::DeclTypeSpec::Character) { 816 // Instantiate character as if it was a normal dummy argument so that the 817 // statement function dummy character length is applied and dealt with 818 // correctly. 819 symMap.addSymbol(*arg, variableIface.getBase()); 820 Fortran::lower::mapSymbolAttributes(converter, *arg, symMap, stmtCtx); 821 } else { 822 // No need to create an extra hlfir.declare otherwise for 823 // numerical and logical scalar dummies. 824 symMap.addVariableDefinition(*arg, variableIface); 825 } 826 } 827 828 // Explicitly map statement function host associated symbols to their 829 // parent scope lowered symbol box. 830 for (const Fortran::semantics::SymbolRef &sym : 831 Fortran::evaluate::CollectSymbols(*details.stmtFunction())) 832 if (const auto *details = 833 sym->detailsIf<Fortran::semantics::HostAssocDetails>()) 834 converter.copySymbolBinding(details->symbol(), sym); 835 836 hlfir::Entity result = Fortran::lower::convertExprToHLFIR( 837 loc, converter, details.stmtFunction().value(), symMap, stmtCtx); 838 symMap.popScope(); 839 // The result must not be a variable. 840 result = hlfir::loadTrivialScalar(loc, builder, result); 841 if (result.isVariable()) 842 result = hlfir::Entity{builder.create<hlfir::AsExprOp>(loc, result)}; 843 for (auto associate : exprAssociations) 844 builder.create<hlfir::EndAssociateOp>(loc, associate); 845 return hlfir::EntityWithAttributes{result}; 846 } 847 848 namespace { 849 // Structure to hold the information about the call and the lowering context. 850 // This structure is intended to help threading the information 851 // through the various lowering calls without having to pass every 852 // required structure one by one. 853 struct CallContext { 854 CallContext(const Fortran::evaluate::ProcedureRef &procRef, 855 std::optional<mlir::Type> resultType, mlir::Location loc, 856 Fortran::lower::AbstractConverter &converter, 857 Fortran::lower::SymMap &symMap, 858 Fortran::lower::StatementContext &stmtCtx) 859 : procRef{procRef}, converter{converter}, symMap{symMap}, 860 stmtCtx{stmtCtx}, resultType{resultType}, loc{loc} {} 861 862 fir::FirOpBuilder &getBuilder() { return converter.getFirOpBuilder(); } 863 864 std::string getProcedureName() const { 865 if (const Fortran::semantics::Symbol *sym = procRef.proc().GetSymbol()) 866 return sym->GetUltimate().name().ToString(); 867 return procRef.proc().GetName(); 868 } 869 870 /// Is this a call to an elemental procedure with at least one array argument? 871 bool isElementalProcWithArrayArgs() const { 872 if (procRef.IsElemental()) 873 for (const std::optional<Fortran::evaluate::ActualArgument> &arg : 874 procRef.arguments()) 875 if (arg && arg->Rank() != 0) 876 return true; 877 return false; 878 } 879 880 /// Is this a statement function reference? 881 bool isStatementFunctionCall() const { 882 if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol()) 883 if (const auto *details = 884 symbol->detailsIf<Fortran::semantics::SubprogramDetails>()) 885 return details->stmtFunction().has_value(); 886 return false; 887 } 888 889 /// Is this a call to a BIND(C) procedure? 890 bool isBindcCall() const { 891 if (const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol()) 892 return Fortran::semantics::IsBindCProcedure(*symbol); 893 return false; 894 } 895 896 const Fortran::evaluate::ProcedureRef &procRef; 897 Fortran::lower::AbstractConverter &converter; 898 Fortran::lower::SymMap &symMap; 899 Fortran::lower::StatementContext &stmtCtx; 900 std::optional<mlir::Type> resultType; 901 mlir::Location loc; 902 }; 903 904 using ExvAndCleanup = 905 std::pair<fir::ExtendedValue, std::optional<hlfir::CleanupFunction>>; 906 } // namespace 907 908 // Helper to transform a fir::ExtendedValue to an hlfir::EntityWithAttributes. 909 static hlfir::EntityWithAttributes 910 extendedValueToHlfirEntity(mlir::Location loc, fir::FirOpBuilder &builder, 911 const fir::ExtendedValue &exv, 912 llvm::StringRef name) { 913 mlir::Value firBase = fir::getBase(exv); 914 mlir::Type firBaseTy = firBase.getType(); 915 if (fir::isa_trivial(firBaseTy)) 916 return hlfir::EntityWithAttributes{firBase}; 917 if (auto charTy = mlir::dyn_cast<fir::CharacterType>(firBase.getType())) { 918 // CHAR() intrinsic and BIND(C) procedures returning CHARACTER(1) 919 // are lowered to a fir.char<kind,1> that is not in memory. 920 // This tends to cause a lot of bugs because the rest of the 921 // infrastructure is mostly tested with characters that are 922 // in memory. 923 // To avoid having to deal with this special case here and there, 924 // place it in memory here. If this turns out to be suboptimal, 925 // this could be fixed, but for now llvm opt -O1 is able to get 926 // rid of the memory indirection in a = char(b), so there is 927 // little incentive to increase the compiler complexity. 928 hlfir::Entity storage{builder.createTemporary(loc, charTy)}; 929 builder.create<fir::StoreOp>(loc, firBase, storage); 930 auto asExpr = builder.create<hlfir::AsExprOp>( 931 loc, storage, /*mustFree=*/builder.createBool(loc, false)); 932 return hlfir::EntityWithAttributes{asExpr.getResult()}; 933 } 934 return hlfir::genDeclare(loc, builder, exv, name, 935 fir::FortranVariableFlagsAttr{}); 936 } 937 namespace { 938 /// Structure to hold the clean-up related to a dummy argument preparation 939 /// that may have to be done after a call (copy-out or temporary deallocation). 940 struct CallCleanUp { 941 struct CopyIn { 942 void genCleanUp(mlir::Location loc, fir::FirOpBuilder &builder) { 943 builder.create<hlfir::CopyOutOp>(loc, tempBox, wasCopied, copyBackVar); 944 } 945 // address of the descriptor holding the temp if a temp was created. 946 mlir::Value tempBox; 947 // Boolean indicating if a copy was made or not. 948 mlir::Value wasCopied; 949 // copyBackVar may be null if copy back is not needed. 950 mlir::Value copyBackVar; 951 }; 952 struct ExprAssociate { 953 void genCleanUp(mlir::Location loc, fir::FirOpBuilder &builder) { 954 builder.create<hlfir::EndAssociateOp>(loc, tempVar, mustFree); 955 } 956 mlir::Value tempVar; 957 mlir::Value mustFree; 958 }; 959 void genCleanUp(mlir::Location loc, fir::FirOpBuilder &builder) { 960 Fortran::common::visit([&](auto &c) { c.genCleanUp(loc, builder); }, 961 cleanUp); 962 } 963 std::variant<CopyIn, ExprAssociate> cleanUp; 964 }; 965 966 /// Structure representing a prepared dummy argument. 967 /// It holds the value to be passed in the call and any related 968 /// clean-ups to be done after the call. 969 struct PreparedDummyArgument { 970 void pushCopyInCleanUp(mlir::Value tempBox, mlir::Value wasCopied, 971 mlir::Value copyBackVar) { 972 cleanups.emplace_back( 973 CallCleanUp{CallCleanUp::CopyIn{tempBox, wasCopied, copyBackVar}}); 974 } 975 void pushExprAssociateCleanUp(mlir::Value tempVar, mlir::Value wasCopied) { 976 cleanups.emplace_back( 977 CallCleanUp{CallCleanUp::ExprAssociate{tempVar, wasCopied}}); 978 } 979 void pushExprAssociateCleanUp(hlfir::AssociateOp associate) { 980 mlir::Value hlfirBase = associate.getBase(); 981 mlir::Value firBase = associate.getFirBase(); 982 cleanups.emplace_back(CallCleanUp{CallCleanUp::ExprAssociate{ 983 hlfir::mayHaveAllocatableComponent(hlfirBase.getType()) ? hlfirBase 984 : firBase, 985 associate.getMustFreeStrorageFlag()}}); 986 } 987 988 mlir::Value dummy; 989 // NOTE: the clean-ups are executed in reverse order. 990 llvm::SmallVector<CallCleanUp, 2> cleanups; 991 }; 992 993 /// Structure to help conditionally preparing a dummy argument based 994 /// on the actual argument presence. 995 /// It helps "wrapping" the dummy and the clean-up information in 996 /// an if (present) {...}: 997 /// 998 /// %conditionallyPrepared = fir.if (%present) { 999 /// fir.result %preparedDummy 1000 /// } else { 1001 /// fir.result %absent 1002 /// } 1003 /// 1004 struct ConditionallyPreparedDummy { 1005 /// Create ConditionallyPreparedDummy from a preparedDummy that must 1006 /// be wrapped in a fir.if. 1007 ConditionallyPreparedDummy(PreparedDummyArgument &preparedDummy) { 1008 thenResultValues.push_back(preparedDummy.dummy); 1009 for (const CallCleanUp &c : preparedDummy.cleanups) { 1010 if (const auto *copyInCleanUp = 1011 std::get_if<CallCleanUp::CopyIn>(&c.cleanUp)) { 1012 thenResultValues.push_back(copyInCleanUp->wasCopied); 1013 if (copyInCleanUp->copyBackVar) 1014 thenResultValues.push_back(copyInCleanUp->copyBackVar); 1015 } else { 1016 const auto &exprAssociate = 1017 std::get<CallCleanUp::ExprAssociate>(c.cleanUp); 1018 thenResultValues.push_back(exprAssociate.tempVar); 1019 thenResultValues.push_back(exprAssociate.mustFree); 1020 } 1021 } 1022 } 1023 1024 /// Get the result types of the wrapping fir.if that must be created. 1025 llvm::SmallVector<mlir::Type> getIfResulTypes() const { 1026 llvm::SmallVector<mlir::Type> types; 1027 for (mlir::Value res : thenResultValues) 1028 types.push_back(res.getType()); 1029 return types; 1030 } 1031 1032 /// Generate the "fir.result %preparedDummy" in the then branch of the 1033 /// wrapping fir.if. 1034 void genThenResult(mlir::Location loc, fir::FirOpBuilder &builder) const { 1035 builder.create<fir::ResultOp>(loc, thenResultValues); 1036 } 1037 1038 /// Generate the "fir.result %absent" in the else branch of the 1039 /// wrapping fir.if. 1040 void genElseResult(mlir::Location loc, fir::FirOpBuilder &builder) const { 1041 llvm::SmallVector<mlir::Value> elseResultValues; 1042 mlir::Type i1Type = builder.getI1Type(); 1043 for (mlir::Value res : thenResultValues) { 1044 mlir::Type type = res.getType(); 1045 if (type == i1Type) 1046 elseResultValues.push_back(builder.createBool(loc, false)); 1047 else 1048 elseResultValues.push_back(builder.genAbsentOp(loc, type)); 1049 } 1050 builder.create<fir::ResultOp>(loc, elseResultValues); 1051 } 1052 1053 /// Once the fir.if has been created, get the resulting %conditionallyPrepared 1054 /// dummy argument. 1055 PreparedDummyArgument 1056 getPreparedDummy(fir::IfOp ifOp, 1057 const PreparedDummyArgument &unconditionalDummy) { 1058 PreparedDummyArgument preparedDummy; 1059 preparedDummy.dummy = ifOp.getResults()[0]; 1060 for (const CallCleanUp &c : unconditionalDummy.cleanups) { 1061 if (const auto *copyInCleanUp = 1062 std::get_if<CallCleanUp::CopyIn>(&c.cleanUp)) { 1063 mlir::Value copyBackVar; 1064 if (copyInCleanUp->copyBackVar) 1065 copyBackVar = ifOp.getResults().back(); 1066 // tempBox is an hlfir.copy_in argument created outside of the 1067 // fir.if region. It needs not to be threaded as a fir.if result. 1068 preparedDummy.pushCopyInCleanUp(copyInCleanUp->tempBox, 1069 ifOp.getResults()[1], copyBackVar); 1070 } else { 1071 preparedDummy.pushExprAssociateCleanUp(ifOp.getResults()[1], 1072 ifOp.getResults()[2]); 1073 } 1074 } 1075 return preparedDummy; 1076 } 1077 1078 llvm::SmallVector<mlir::Value> thenResultValues; 1079 }; 1080 } // namespace 1081 1082 /// Fix-up the fact that it is supported to pass a character procedure 1083 /// designator to a non character procedure dummy procedure and vice-versa, even 1084 /// in case of explicit interface. Uglier cases where an object is passed as 1085 /// procedure designator or vice versa are handled only for implicit interfaces 1086 /// (refused by semantics with explicit interface), and handled with a funcOp 1087 /// cast like other implicit interface mismatches. 1088 static hlfir::Entity fixProcedureDummyMismatch(mlir::Location loc, 1089 fir::FirOpBuilder &builder, 1090 hlfir::Entity actual, 1091 mlir::Type dummyType) { 1092 if (mlir::isa<fir::BoxProcType>(actual.getType()) && 1093 fir::isCharacterProcedureTuple(dummyType)) { 1094 mlir::Value length = 1095 builder.create<fir::UndefOp>(loc, builder.getCharacterLengthType()); 1096 mlir::Value tuple = fir::factory::createCharacterProcedureTuple( 1097 builder, loc, dummyType, actual, length); 1098 return hlfir::Entity{tuple}; 1099 } 1100 assert(fir::isCharacterProcedureTuple(actual.getType()) && 1101 mlir::isa<fir::BoxProcType>(dummyType) && 1102 "unsupported dummy procedure mismatch with the actual argument"); 1103 mlir::Value boxProc = fir::factory::extractCharacterProcedureTuple( 1104 builder, loc, actual, /*openBoxProc=*/false) 1105 .first; 1106 return hlfir::Entity{boxProc}; 1107 } 1108 1109 mlir::Value static getZeroLowerBounds(mlir::Location loc, 1110 fir::FirOpBuilder &builder, 1111 hlfir::Entity entity) { 1112 assert(!entity.isAssumedRank() && 1113 "assumed-rank must use fir.rebox_assumed_rank"); 1114 if (entity.getRank() < 1) 1115 return {}; 1116 mlir::Value zero = 1117 builder.createIntegerConstant(loc, builder.getIndexType(), 0); 1118 llvm::SmallVector<mlir::Value> lowerBounds(entity.getRank(), zero); 1119 return builder.genShift(loc, lowerBounds); 1120 } 1121 1122 static bool 1123 isSimplyContiguous(const Fortran::evaluate::ActualArgument &arg, 1124 Fortran::evaluate::FoldingContext &foldingContext) { 1125 if (const auto *expr = arg.UnwrapExpr()) 1126 return Fortran::evaluate::IsSimplyContiguous(*expr, foldingContext); 1127 const Fortran::semantics::Symbol *sym = arg.GetAssumedTypeDummy(); 1128 assert(sym && 1129 "expect ActualArguments to be expression or assumed-type symbols"); 1130 return sym->Rank() == 0 || 1131 Fortran::evaluate::IsSimplyContiguous(*sym, foldingContext); 1132 } 1133 1134 /// When dummy is not ALLOCATABLE, POINTER and is not passed in register, 1135 /// prepare the actual argument according to the interface. Do as needed: 1136 /// - address element if this is an array argument in an elemental call. 1137 /// - set dynamic type to the dummy type if the dummy is not polymorphic. 1138 /// - copy-in into contiguous variable if the dummy must be contiguous 1139 /// - copy into a temporary if the dummy has the VALUE attribute. 1140 /// - package the prepared dummy as required (fir.box, fir.class, 1141 /// fir.box_char...). 1142 /// This function should only be called with an actual that is present. 1143 /// The optional aspects must be handled by this function user. 1144 static PreparedDummyArgument preparePresentUserCallActualArgument( 1145 mlir::Location loc, fir::FirOpBuilder &builder, 1146 const Fortran::lower::PreparedActualArgument &preparedActual, 1147 mlir::Type dummyType, 1148 const Fortran::lower::CallerInterface::PassedEntity &arg, 1149 CallContext &callContext) { 1150 1151 Fortran::evaluate::FoldingContext &foldingContext = 1152 callContext.converter.getFoldingContext(); 1153 1154 // Step 1: get the actual argument, which includes addressing the 1155 // element if this is an array in an elemental call. 1156 hlfir::Entity actual = preparedActual.getActual(loc, builder); 1157 1158 // Handle procedure arguments (procedure pointers should go through 1159 // prepareProcedurePointerActualArgument). 1160 if (hlfir::isFortranProcedureValue(dummyType)) { 1161 // Procedure pointer or function returns procedure pointer actual to 1162 // procedure dummy. 1163 if (actual.isProcedurePointer()) { 1164 actual = hlfir::derefPointersAndAllocatables(loc, builder, actual); 1165 return PreparedDummyArgument{actual, /*cleanups=*/{}}; 1166 } 1167 // Procedure actual to procedure dummy. 1168 assert(actual.isProcedure()); 1169 // Do nothing if this is a procedure argument. It is already a 1170 // fir.boxproc/fir.tuple<fir.boxproc, len> as it should. 1171 if (!mlir::isa<fir::BoxProcType>(actual.getType()) && 1172 actual.getType() != dummyType) 1173 // The actual argument may be a procedure that returns character (a 1174 // fir.tuple<fir.boxproc, len>) while the dummy is not. Extract the tuple 1175 // in that case. 1176 actual = fixProcedureDummyMismatch(loc, builder, actual, dummyType); 1177 return PreparedDummyArgument{actual, /*cleanups=*/{}}; 1178 } 1179 1180 const bool ignoreTKRtype = arg.testTKR(Fortran::common::IgnoreTKR::Type); 1181 const bool passingPolymorphicToNonPolymorphic = 1182 actual.isPolymorphic() && !fir::isPolymorphicType(dummyType) && 1183 !ignoreTKRtype; 1184 1185 // When passing a CLASS(T) to TYPE(T), only the "T" part must be 1186 // passed. Unless the entity is a scalar passed by raw address, a 1187 // new descriptor must be made using the dummy argument type as 1188 // dynamic type. This must be done before any copy/copy-in because the 1189 // dynamic type matters to determine the contiguity. 1190 const bool mustSetDynamicTypeToDummyType = 1191 passingPolymorphicToNonPolymorphic && 1192 (actual.isArray() || mlir::isa<fir::BaseBoxType>(dummyType)); 1193 1194 // The simple contiguity of the actual is "lost" when passing a polymorphic 1195 // to a non polymorphic entity because the dummy dynamic type matters for 1196 // the contiguity. 1197 const bool mustDoCopyInOut = 1198 actual.isArray() && arg.mustBeMadeContiguous() && 1199 (passingPolymorphicToNonPolymorphic || 1200 !isSimplyContiguous(*arg.entity, foldingContext)); 1201 1202 const bool actualIsAssumedRank = actual.isAssumedRank(); 1203 // Create dummy type with actual argument rank when the dummy is an assumed 1204 // rank. That way, all the operation to create dummy descriptors are ranked if 1205 // the actual argument is ranked, which allows simple code generation. 1206 // Also do the same when the dummy is a sequence associated descriptor 1207 // because the actual shape/rank may mismatch with the dummy, and the dummy 1208 // may be an assumed-size array, so any descriptor manipulation should use the 1209 // actual argument shape information. A descriptor with the dummy shape 1210 // information will be created later when all actual arguments are ready. 1211 mlir::Type dummyTypeWithActualRank = dummyType; 1212 if (auto baseBoxDummy = mlir::dyn_cast<fir::BaseBoxType>(dummyType)) { 1213 if (baseBoxDummy.isAssumedRank() || 1214 arg.testTKR(Fortran::common::IgnoreTKR::Rank) || 1215 arg.isSequenceAssociatedDescriptor()) { 1216 mlir::Type actualTy = 1217 hlfir::getFortranElementOrSequenceType(actual.getType()); 1218 dummyTypeWithActualRank = baseBoxDummy.getBoxTypeWithNewShape(actualTy); 1219 } 1220 } 1221 // Preserve the actual type in the argument preparation in case IgnoreTKR(t) 1222 // is set (descriptors must be created with the actual type in this case, and 1223 // copy-in/copy-out should be driven by the contiguity with regard to the 1224 // actual type). 1225 if (ignoreTKRtype) { 1226 if (auto boxCharType = 1227 mlir::dyn_cast<fir::BoxCharType>(dummyTypeWithActualRank)) { 1228 auto maybeActualCharType = 1229 mlir::dyn_cast<fir::CharacterType>(actual.getFortranElementType()); 1230 if (!maybeActualCharType || 1231 maybeActualCharType.getFKind() != boxCharType.getKind()) { 1232 // When passing to a fir.boxchar with ignore(tk), prepare the argument 1233 // as if only the raw address must be passed. 1234 dummyTypeWithActualRank = 1235 fir::ReferenceType::get(actual.getElementOrSequenceType()); 1236 } 1237 // Otherwise, the actual is already a character with the same kind as the 1238 // dummy and can be passed normally. 1239 } else { 1240 dummyTypeWithActualRank = fir::changeElementType( 1241 dummyTypeWithActualRank, actual.getFortranElementType(), 1242 actual.isPolymorphic()); 1243 } 1244 } 1245 1246 PreparedDummyArgument preparedDummy; 1247 1248 // Helpers to generate hlfir.copy_in operation and register the related 1249 // hlfir.copy_out creation. 1250 auto genCopyIn = [&](hlfir::Entity var, bool doCopyOut) -> hlfir::Entity { 1251 auto baseBoxTy = mlir::dyn_cast<fir::BaseBoxType>(var.getType()); 1252 assert(baseBoxTy && "expect non simply contiguous variables to be boxes"); 1253 // Create allocatable descriptor for the potential temporary. 1254 mlir::Type tempBoxType = baseBoxTy.getBoxTypeWithNewAttr( 1255 fir::BaseBoxType::Attribute::Allocatable); 1256 mlir::Value tempBox = builder.createTemporary(loc, tempBoxType); 1257 auto copyIn = builder.create<hlfir::CopyInOp>( 1258 loc, var, tempBox, /*var_is_present=*/mlir::Value{}); 1259 // Register the copy-out after the call. 1260 preparedDummy.pushCopyInCleanUp(copyIn.getTempBox(), copyIn.getWasCopied(), 1261 doCopyOut ? copyIn.getVar() 1262 : mlir::Value{}); 1263 return hlfir::Entity{copyIn.getCopiedIn()}; 1264 }; 1265 1266 auto genSetDynamicTypeToDummyType = [&](hlfir::Entity var) -> hlfir::Entity { 1267 fir::BaseBoxType boxType = fir::BoxType::get( 1268 hlfir::getFortranElementOrSequenceType(dummyTypeWithActualRank)); 1269 if (actualIsAssumedRank) 1270 return hlfir::Entity{builder.create<fir::ReboxAssumedRankOp>( 1271 loc, boxType, var, fir::LowerBoundModifierAttribute::SetToOnes)}; 1272 // Use actual shape when creating descriptor with dummy type, the dummy 1273 // shape may be unknown in case of sequence association. 1274 mlir::Type actualTy = 1275 hlfir::getFortranElementOrSequenceType(actual.getType()); 1276 boxType = boxType.getBoxTypeWithNewShape(actualTy); 1277 return hlfir::Entity{builder.create<fir::ReboxOp>(loc, boxType, var, 1278 /*shape=*/mlir::Value{}, 1279 /*slice=*/mlir::Value{})}; 1280 }; 1281 1282 // Step 2: prepare the storage for the dummy arguments, ensuring that it 1283 // matches the dummy requirements (e.g., must be contiguous or must be 1284 // a temporary). 1285 hlfir::Entity entity = 1286 hlfir::derefPointersAndAllocatables(loc, builder, actual); 1287 if (entity.isVariable()) { 1288 // Set dynamic type if needed before any copy-in or copy so that the dummy 1289 // is contiguous according to the dummy type. 1290 if (mustSetDynamicTypeToDummyType) 1291 entity = genSetDynamicTypeToDummyType(entity); 1292 if (arg.hasValueAttribute() || 1293 // Constant expressions might be lowered as variables with 1294 // 'parameter' attribute. Even though the constant expressions 1295 // are not definable and explicit assignments to them are not 1296 // possible, we have to create a temporary copies when we pass 1297 // them down the call stack. 1298 entity.isParameter()) { 1299 // Make a copy in a temporary. 1300 auto copy = builder.create<hlfir::AsExprOp>(loc, entity); 1301 mlir::Type storageType = entity.getType(); 1302 mlir::NamedAttribute byRefAttr = fir::getAdaptToByRefAttr(builder); 1303 hlfir::AssociateOp associate = hlfir::genAssociateExpr( 1304 loc, builder, hlfir::Entity{copy}, storageType, "", byRefAttr); 1305 entity = hlfir::Entity{associate.getBase()}; 1306 // Register the temporary destruction after the call. 1307 preparedDummy.pushExprAssociateCleanUp(associate); 1308 } else if (mustDoCopyInOut) { 1309 // Copy-in non contiguous variables. 1310 // TODO: for non-finalizable monomorphic derived type actual 1311 // arguments associated with INTENT(OUT) dummy arguments 1312 // we may avoid doing the copy and only allocate the temporary. 1313 // The codegen would do a "mold" allocation instead of "sourced" 1314 // allocation for the temp in this case. We can communicate 1315 // this to the codegen via some CopyInOp flag. 1316 // This is a performance concern. 1317 entity = genCopyIn(entity, arg.mayBeModifiedByCall()); 1318 } 1319 } else { 1320 const Fortran::lower::SomeExpr *expr = arg.entity->UnwrapExpr(); 1321 assert(expr && "expression actual argument cannot be an assumed type"); 1322 // The actual is an expression value, place it into a temporary 1323 // and register the temporary destruction after the call. 1324 mlir::Type storageType = callContext.converter.genType(*expr); 1325 mlir::NamedAttribute byRefAttr = fir::getAdaptToByRefAttr(builder); 1326 hlfir::AssociateOp associate = hlfir::genAssociateExpr( 1327 loc, builder, entity, storageType, "", byRefAttr); 1328 entity = hlfir::Entity{associate.getBase()}; 1329 preparedDummy.pushExprAssociateCleanUp(associate); 1330 // Rebox the actual argument to the dummy argument's type, and make sure 1331 // that we pass a contiguous entity (i.e. make copy-in, if needed). 1332 // 1333 // TODO: this can probably be optimized by associating the expression with 1334 // properly typed temporary, but this needs either a new operation or 1335 // making the hlfir.associate more complex. 1336 if (mustSetDynamicTypeToDummyType) { 1337 entity = genSetDynamicTypeToDummyType(entity); 1338 entity = genCopyIn(entity, /*doCopyOut=*/false); 1339 } 1340 } 1341 1342 // Step 3: now that the dummy argument storage has been prepared, package 1343 // it according to the interface. 1344 mlir::Value addr; 1345 if (mlir::isa<fir::BoxCharType>(dummyTypeWithActualRank)) { 1346 addr = hlfir::genVariableBoxChar(loc, builder, entity); 1347 } else if (mlir::isa<fir::BaseBoxType>(dummyTypeWithActualRank)) { 1348 entity = hlfir::genVariableBox(loc, builder, entity); 1349 // Ensures the box has the right attributes and that it holds an 1350 // addendum if needed. 1351 fir::BaseBoxType actualBoxType = 1352 mlir::cast<fir::BaseBoxType>(entity.getType()); 1353 mlir::Type boxEleType = actualBoxType.getEleTy(); 1354 // For now, assume it is not OK to pass the allocatable/pointer 1355 // descriptor to a non pointer/allocatable dummy. That is a strict 1356 // interpretation of 18.3.6 point 4 that stipulates the descriptor 1357 // has the dummy attributes in BIND(C) contexts. 1358 const bool actualBoxHasAllocatableOrPointerFlag = 1359 fir::isa_ref_type(boxEleType); 1360 // Fortran 2018 18.5.3, pp3: BIND(C) non pointer allocatable descriptors 1361 // must have zero lower bounds. 1362 bool needsZeroLowerBounds = callContext.isBindcCall() && entity.isArray(); 1363 // On the callee side, the current code generated for unlimited 1364 // polymorphic might unconditionally read the addendum. Intrinsic type 1365 // descriptors may not have an addendum, the rebox below will create a 1366 // descriptor with an addendum in such case. 1367 const bool actualBoxHasAddendum = fir::boxHasAddendum(actualBoxType); 1368 const bool needToAddAddendum = 1369 fir::isUnlimitedPolymorphicType(dummyTypeWithActualRank) && 1370 !actualBoxHasAddendum; 1371 if (needToAddAddendum || actualBoxHasAllocatableOrPointerFlag || 1372 needsZeroLowerBounds) { 1373 if (actualIsAssumedRank) { 1374 auto lbModifier = needsZeroLowerBounds 1375 ? fir::LowerBoundModifierAttribute::SetToZeroes 1376 : fir::LowerBoundModifierAttribute::SetToOnes; 1377 entity = hlfir::Entity{builder.create<fir::ReboxAssumedRankOp>( 1378 loc, dummyTypeWithActualRank, entity, lbModifier)}; 1379 } else { 1380 mlir::Value shift{}; 1381 if (needsZeroLowerBounds) 1382 shift = getZeroLowerBounds(loc, builder, entity); 1383 entity = hlfir::Entity{builder.create<fir::ReboxOp>( 1384 loc, dummyTypeWithActualRank, entity, /*shape=*/shift, 1385 /*slice=*/mlir::Value{})}; 1386 } 1387 } 1388 addr = entity; 1389 } else { 1390 addr = hlfir::genVariableRawAddress(loc, builder, entity); 1391 } 1392 1393 // For ranked actual passed to assumed-rank dummy, the cast to assumed-rank 1394 // box is inserted when building the fir.call op. Inserting it here would 1395 // cause the fir.if results to be assumed-rank in case of OPTIONAL dummy, 1396 // causing extra runtime costs due to the unknown runtime size of assumed-rank 1397 // descriptors. 1398 preparedDummy.dummy = 1399 builder.createConvert(loc, dummyTypeWithActualRank, addr); 1400 return preparedDummy; 1401 } 1402 1403 /// When dummy is not ALLOCATABLE, POINTER and is not passed in register, 1404 /// prepare the actual argument according to the interface, taking care 1405 /// of any optional aspect. 1406 static PreparedDummyArgument prepareUserCallActualArgument( 1407 mlir::Location loc, fir::FirOpBuilder &builder, 1408 const Fortran::lower::PreparedActualArgument &preparedActual, 1409 mlir::Type dummyType, 1410 const Fortran::lower::CallerInterface::PassedEntity &arg, 1411 CallContext &callContext) { 1412 if (!preparedActual.handleDynamicOptional()) 1413 return preparePresentUserCallActualArgument(loc, builder, preparedActual, 1414 dummyType, arg, callContext); 1415 1416 // Conditional dummy argument preparation. The actual may be absent 1417 // at runtime, causing any addressing, copy, and packaging to have 1418 // undefined behavior. 1419 // To simplify the handling of this case, the "normal" dummy preparation 1420 // helper is used, except its generated code is wrapped inside a 1421 // fir.if(present). 1422 mlir::Value isPresent = preparedActual.getIsPresent(); 1423 mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint(); 1424 1425 // Code generated in a preparation block that will become the 1426 // "then" block in "if (present) then {} else {}". The reason 1427 // for this unusual if/then/else generation is that the number 1428 // and types of the if results will depend on how the argument 1429 // is prepared, and forecasting that here would be brittle. 1430 auto badIfOp = builder.create<fir::IfOp>(loc, dummyType, isPresent, 1431 /*withElseRegion=*/false); 1432 mlir::Block *preparationBlock = &badIfOp.getThenRegion().front(); 1433 builder.setInsertionPointToStart(preparationBlock); 1434 PreparedDummyArgument unconditionalDummy = 1435 preparePresentUserCallActualArgument(loc, builder, preparedActual, 1436 dummyType, arg, callContext); 1437 builder.restoreInsertionPoint(insertPt); 1438 1439 // TODO: when forwarding an optional to an optional of the same kind 1440 // (i.e, unconditionalDummy.dummy was not created in preparationBlock), 1441 // the if/then/else generation could be skipped to improve the generated 1442 // code. 1443 1444 // Now that the result types of the ifOp can be deduced, generate 1445 // the "real" ifOp (operation result types cannot be changed, so 1446 // badIfOp cannot be modified and used here). 1447 llvm::SmallVector<mlir::Type> ifOpResultTypes; 1448 ConditionallyPreparedDummy conditionalDummy(unconditionalDummy); 1449 auto ifOp = builder.create<fir::IfOp>(loc, conditionalDummy.getIfResulTypes(), 1450 isPresent, 1451 /*withElseRegion=*/true); 1452 // Move "preparationBlock" into the "then" of the new 1453 // fir.if operation and create fir.result propagating 1454 // unconditionalDummy. 1455 preparationBlock->moveBefore(&ifOp.getThenRegion().back()); 1456 ifOp.getThenRegion().back().erase(); 1457 builder.setInsertionPointToEnd(&ifOp.getThenRegion().front()); 1458 conditionalDummy.genThenResult(loc, builder); 1459 1460 // Generate "else" branch with returning absent values. 1461 builder.setInsertionPointToStart(&ifOp.getElseRegion().front()); 1462 conditionalDummy.genElseResult(loc, builder); 1463 1464 // Build dummy from IfOpResults. 1465 builder.setInsertionPointAfter(ifOp); 1466 PreparedDummyArgument result = 1467 conditionalDummy.getPreparedDummy(ifOp, unconditionalDummy); 1468 badIfOp->erase(); 1469 return result; 1470 } 1471 1472 /// Prepare actual argument for a procedure pointer dummy. 1473 static PreparedDummyArgument prepareProcedurePointerActualArgument( 1474 mlir::Location loc, fir::FirOpBuilder &builder, 1475 const Fortran::lower::PreparedActualArgument &preparedActual, 1476 mlir::Type dummyType, 1477 const Fortran::lower::CallerInterface::PassedEntity &arg, 1478 CallContext &callContext) { 1479 1480 // NULL() actual to procedure pointer dummy 1481 if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>( 1482 *arg.entity) && 1483 fir::isBoxProcAddressType(dummyType)) { 1484 auto boxTy{Fortran::lower::getUntypedBoxProcType(builder.getContext())}; 1485 auto tempBoxProc{builder.createTemporary(loc, boxTy)}; 1486 hlfir::Entity nullBoxProc( 1487 fir::factory::createNullBoxProc(builder, loc, boxTy)); 1488 builder.create<fir::StoreOp>(loc, nullBoxProc, tempBoxProc); 1489 return PreparedDummyArgument{tempBoxProc, /*cleanups=*/{}}; 1490 } 1491 hlfir::Entity actual = preparedActual.getActual(loc, builder); 1492 if (actual.isProcedurePointer()) 1493 return PreparedDummyArgument{actual, /*cleanups=*/{}}; 1494 assert(actual.isProcedure()); 1495 // Procedure actual to procedure pointer dummy. 1496 auto tempBoxProc{builder.createTemporary(loc, actual.getType())}; 1497 builder.create<fir::StoreOp>(loc, actual, tempBoxProc); 1498 return PreparedDummyArgument{tempBoxProc, /*cleanups=*/{}}; 1499 } 1500 1501 /// Prepare arguments of calls to user procedures with actual arguments that 1502 /// have been pre-lowered but not yet prepared according to the interface. 1503 void prepareUserCallArguments( 1504 Fortran::lower::PreparedActualArguments &loweredActuals, 1505 Fortran::lower::CallerInterface &caller, mlir::FunctionType callSiteType, 1506 CallContext &callContext, llvm::SmallVector<CallCleanUp> &callCleanUps) { 1507 using PassBy = Fortran::lower::CallerInterface::PassEntityBy; 1508 mlir::Location loc = callContext.loc; 1509 bool mustRemapActualToDummyDescriptors = false; 1510 fir::FirOpBuilder &builder = callContext.getBuilder(); 1511 for (auto [preparedActual, arg] : 1512 llvm::zip(loweredActuals, caller.getPassedArguments())) { 1513 mlir::Type argTy = callSiteType.getInput(arg.firArgument); 1514 if (!preparedActual) { 1515 // Optional dummy argument for which there is no actual argument. 1516 caller.placeInput(arg, builder.genAbsentOp(loc, argTy)); 1517 continue; 1518 } 1519 1520 switch (arg.passBy) { 1521 case PassBy::Value: { 1522 // True pass-by-value semantics. 1523 assert(!preparedActual->handleDynamicOptional() && "cannot be optional"); 1524 hlfir::Entity actual = preparedActual->getActual(loc, builder); 1525 hlfir::Entity value = hlfir::loadTrivialScalar(loc, builder, actual); 1526 1527 mlir::Type eleTy = value.getFortranElementType(); 1528 if (fir::isa_builtin_cptr_type(eleTy)) { 1529 // Pass-by-value argument of type(C_PTR/C_FUNPTR). 1530 // Load the __address component and pass it by value. 1531 if (value.isValue()) { 1532 auto associate = hlfir::genAssociateExpr(loc, builder, value, eleTy, 1533 "adapt.cptrbyval"); 1534 value = hlfir::Entity{genRecordCPtrValueArg( 1535 builder, loc, associate.getFirBase(), eleTy)}; 1536 builder.create<hlfir::EndAssociateOp>(loc, associate); 1537 } else { 1538 value = 1539 hlfir::Entity{genRecordCPtrValueArg(builder, loc, value, eleTy)}; 1540 } 1541 } else if (fir::isa_derived(value.getFortranElementType()) || 1542 value.isCharacter()) { 1543 // BIND(C), VALUE derived type or character. The value must really 1544 // be loaded here. 1545 auto [exv, cleanup] = hlfir::convertToValue(loc, builder, value); 1546 mlir::Value loadedValue = fir::getBase(exv); 1547 // Character actual arguments may have unknown length or a length longer 1548 // than one. Cast the memory ref to the dummy type so that the load is 1549 // valid and only loads what is needed. 1550 if (mlir::Type baseTy = fir::dyn_cast_ptrEleTy(loadedValue.getType())) 1551 if (fir::isa_char(baseTy)) 1552 loadedValue = builder.createConvert( 1553 loc, fir::ReferenceType::get(argTy), loadedValue); 1554 if (fir::isa_ref_type(loadedValue.getType())) 1555 loadedValue = builder.create<fir::LoadOp>(loc, loadedValue); 1556 caller.placeInput(arg, loadedValue); 1557 if (cleanup) 1558 (*cleanup)(); 1559 break; 1560 } 1561 caller.placeInput(arg, builder.createConvert(loc, argTy, value)); 1562 } break; 1563 case PassBy::BaseAddressValueAttribute: 1564 case PassBy::CharBoxValueAttribute: 1565 case PassBy::Box: 1566 case PassBy::BaseAddress: 1567 case PassBy::BoxChar: { 1568 PreparedDummyArgument preparedDummy = prepareUserCallActualArgument( 1569 loc, builder, *preparedActual, argTy, arg, callContext); 1570 callCleanUps.append(preparedDummy.cleanups.rbegin(), 1571 preparedDummy.cleanups.rend()); 1572 caller.placeInput(arg, preparedDummy.dummy); 1573 if (arg.passBy == PassBy::Box) 1574 mustRemapActualToDummyDescriptors |= 1575 arg.isSequenceAssociatedDescriptor(); 1576 } break; 1577 case PassBy::BoxProcRef: { 1578 PreparedDummyArgument preparedDummy = 1579 prepareProcedurePointerActualArgument(loc, builder, *preparedActual, 1580 argTy, arg, callContext); 1581 callCleanUps.append(preparedDummy.cleanups.rbegin(), 1582 preparedDummy.cleanups.rend()); 1583 caller.placeInput(arg, preparedDummy.dummy); 1584 } break; 1585 case PassBy::AddressAndLength: 1586 // PassBy::AddressAndLength is only used for character results. Results 1587 // are not handled here. 1588 fir::emitFatalError( 1589 loc, "unexpected PassBy::AddressAndLength for actual arguments"); 1590 break; 1591 case PassBy::CharProcTuple: { 1592 hlfir::Entity actual = preparedActual->getActual(loc, builder); 1593 if (actual.isProcedurePointer()) 1594 actual = hlfir::derefPointersAndAllocatables(loc, builder, actual); 1595 if (!fir::isCharacterProcedureTuple(actual.getType())) 1596 actual = fixProcedureDummyMismatch(loc, builder, actual, argTy); 1597 caller.placeInput(arg, actual); 1598 } break; 1599 case PassBy::MutableBox: { 1600 const Fortran::lower::SomeExpr *expr = arg.entity->UnwrapExpr(); 1601 // C709 and C710. 1602 assert(expr && "cannot pass TYPE(*) to POINTER or ALLOCATABLE"); 1603 hlfir::Entity actual = preparedActual->getActual(loc, builder); 1604 if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>( 1605 *expr)) { 1606 // If expr is NULL(), the mutableBox created must be a deallocated 1607 // pointer with the dummy argument characteristics (see table 16.5 1608 // in Fortran 2018 standard). 1609 // No length parameters are set for the created box because any non 1610 // deferred type parameters of the dummy will be evaluated on the 1611 // callee side, and it is illegal to use NULL without a MOLD if any 1612 // dummy length parameters are assumed. 1613 mlir::Type boxTy = fir::dyn_cast_ptrEleTy(argTy); 1614 assert(boxTy && mlir::isa<fir::BaseBoxType>(boxTy) && 1615 "must be a fir.box type"); 1616 mlir::Value boxStorage = 1617 fir::factory::genNullBoxStorage(builder, loc, boxTy); 1618 caller.placeInput(arg, boxStorage); 1619 continue; 1620 } 1621 if (fir::isPointerType(argTy) && 1622 !Fortran::evaluate::IsObjectPointer(*expr)) { 1623 // Passing a non POINTER actual argument to a POINTER dummy argument. 1624 // Create a pointer of the dummy argument type and assign the actual 1625 // argument to it. 1626 auto dataTy = llvm::cast<fir::BaseBoxType>(fir::unwrapRefType(argTy)); 1627 fir::ExtendedValue actualExv = Fortran::lower::convertToAddress( 1628 loc, callContext.converter, actual, callContext.stmtCtx, 1629 hlfir::getFortranElementType(dataTy)); 1630 // If the dummy is an assumed-rank pointer, allocate a pointer 1631 // descriptor with the actual argument rank (if it is not assumed-rank 1632 // itself). 1633 if (dataTy.isAssumedRank()) { 1634 dataTy = 1635 dataTy.getBoxTypeWithNewShape(fir::getBase(actualExv).getType()); 1636 } 1637 mlir::Value irBox = builder.createTemporary(loc, dataTy); 1638 fir::MutableBoxValue ptrBox(irBox, 1639 /*nonDeferredParams=*/mlir::ValueRange{}, 1640 /*mutableProperties=*/{}); 1641 fir::factory::associateMutableBox(builder, loc, ptrBox, actualExv, 1642 /*lbounds=*/std::nullopt); 1643 caller.placeInput(arg, irBox); 1644 continue; 1645 } 1646 // Passing a POINTER to a POINTER, or an ALLOCATABLE to an ALLOCATABLE. 1647 assert(actual.isMutableBox() && "actual must be a mutable box"); 1648 if (fir::isAllocatableType(argTy) && arg.isIntentOut() && 1649 callContext.isBindcCall()) { 1650 // INTENT(OUT) allocatables are deallocated on the callee side, 1651 // but BIND(C) procedures may be implemented in C, so deallocation is 1652 // also done on the caller side (if the procedure is implemented in 1653 // Fortran, the deallocation attempt in the callee will be a no-op). 1654 auto [exv, cleanup] = 1655 hlfir::translateToExtendedValue(loc, builder, actual); 1656 const auto *mutableBox = exv.getBoxOf<fir::MutableBoxValue>(); 1657 assert(mutableBox && !cleanup && "expect allocatable"); 1658 Fortran::lower::genDeallocateIfAllocated(callContext.converter, 1659 *mutableBox, loc); 1660 } 1661 caller.placeInput(arg, actual); 1662 } break; 1663 } 1664 } 1665 1666 // Handle cases where caller must allocate the result or a fir.box for it. 1667 if (mustRemapActualToDummyDescriptors) 1668 remapActualToDummyDescriptors(loc, callContext.converter, 1669 callContext.symMap, loweredActuals, caller, 1670 callContext.isBindcCall()); 1671 } 1672 1673 /// Lower calls to user procedures with actual arguments that have been 1674 /// pre-lowered but not yet prepared according to the interface. 1675 /// This can be called for elemental procedures, but only with scalar 1676 /// arguments: if there are array arguments, it must be provided with 1677 /// the array argument elements value and will return the corresponding 1678 /// scalar result value. 1679 static std::optional<hlfir::EntityWithAttributes> 1680 genUserCall(Fortran::lower::PreparedActualArguments &loweredActuals, 1681 Fortran::lower::CallerInterface &caller, 1682 mlir::FunctionType callSiteType, CallContext &callContext) { 1683 mlir::Location loc = callContext.loc; 1684 llvm::SmallVector<CallCleanUp> callCleanUps; 1685 fir::FirOpBuilder &builder = callContext.getBuilder(); 1686 1687 prepareUserCallArguments(loweredActuals, caller, callSiteType, callContext, 1688 callCleanUps); 1689 1690 // Prepare lowered arguments according to the interface 1691 // and map the lowered values to the dummy 1692 // arguments. 1693 auto [loweredResult, resultIsFinalized] = Fortran::lower::genCallOpAndResult( 1694 loc, callContext.converter, callContext.symMap, callContext.stmtCtx, 1695 caller, callSiteType, callContext.resultType, 1696 callContext.isElementalProcWithArrayArgs()); 1697 1698 /// Clean-up associations and copy-in. 1699 for (auto cleanUp : callCleanUps) 1700 cleanUp.genCleanUp(loc, builder); 1701 1702 if (auto *entity = std::get_if<hlfir::EntityWithAttributes>(&loweredResult)) 1703 return *entity; 1704 1705 auto &result = std::get<fir::ExtendedValue>(loweredResult); 1706 1707 // For procedure pointer function result, just return the call. 1708 if (callContext.resultType && 1709 mlir::isa<fir::BoxProcType>(*callContext.resultType)) 1710 return hlfir::EntityWithAttributes(fir::getBase(result)); 1711 1712 if (!fir::getBase(result)) 1713 return std::nullopt; // subroutine call. 1714 1715 if (fir::isPointerType(fir::getBase(result).getType())) 1716 return extendedValueToHlfirEntity(loc, builder, result, tempResultName); 1717 1718 if (!resultIsFinalized) { 1719 hlfir::Entity resultEntity = 1720 extendedValueToHlfirEntity(loc, builder, result, tempResultName); 1721 resultEntity = loadTrivialScalar(loc, builder, resultEntity); 1722 if (resultEntity.isVariable()) { 1723 // If the result has no finalization, it can be moved into an expression. 1724 // In such case, the expression should not be freed after its use since 1725 // the result is stack allocated or deallocation (for allocatable results) 1726 // was already inserted in genCallOpAndResult. 1727 auto asExpr = builder.create<hlfir::AsExprOp>( 1728 loc, resultEntity, /*mustFree=*/builder.createBool(loc, false)); 1729 return hlfir::EntityWithAttributes{asExpr.getResult()}; 1730 } 1731 return hlfir::EntityWithAttributes{resultEntity}; 1732 } 1733 // If the result has finalization, it cannot be moved because use of its 1734 // value have been created in the statement context and may be emitted 1735 // after the hlfir.expr destroy, so the result is kept as a variable in 1736 // HLFIR. This may lead to copies when passing the result to an argument 1737 // with VALUE, and this do not convey the fact that the result will not 1738 // change, but is correct, and using hlfir.expr without the move would 1739 // trigger a copy that may be avoided. 1740 1741 // Load allocatable results before emitting the hlfir.declare and drop its 1742 // lower bounds: this is not a variable From the Fortran point of view, so 1743 // the lower bounds are ones when inquired on the caller side. 1744 const auto *allocatable = result.getBoxOf<fir::MutableBoxValue>(); 1745 fir::ExtendedValue loadedResult = 1746 allocatable 1747 ? fir::factory::genMutableBoxRead(builder, loc, *allocatable, 1748 /*mayBePolymorphic=*/true, 1749 /*preserveLowerBounds=*/false) 1750 : result; 1751 return extendedValueToHlfirEntity(loc, builder, loadedResult, tempResultName); 1752 } 1753 1754 /// Create an optional dummy argument value from an entity that may be 1755 /// absent. \p actualGetter callback returns hlfir::Entity denoting 1756 /// the lowered actual argument. \p actualGetter can only return numerical 1757 /// or logical scalar entity. 1758 /// If the entity is considered absent according to 15.5.2.12 point 1., the 1759 /// returned value is zero (or false), otherwise it is the value of the entity. 1760 /// \p eleType specifies the entity's Fortran element type. 1761 template <typename T> 1762 static ExvAndCleanup genOptionalValue(fir::FirOpBuilder &builder, 1763 mlir::Location loc, mlir::Type eleType, 1764 T actualGetter, mlir::Value isPresent) { 1765 return {builder 1766 .genIfOp(loc, {eleType}, isPresent, 1767 /*withElseRegion=*/true) 1768 .genThen([&]() { 1769 hlfir::Entity entity = actualGetter(loc, builder); 1770 assert(eleType == entity.getFortranElementType() && 1771 "result type mismatch in genOptionalValue"); 1772 assert(entity.isScalar() && fir::isa_trivial(eleType) && 1773 "must be a numerical or logical scalar"); 1774 mlir::Value val = 1775 hlfir::loadTrivialScalar(loc, builder, entity); 1776 builder.create<fir::ResultOp>(loc, val); 1777 }) 1778 .genElse([&]() { 1779 mlir::Value zero = 1780 fir::factory::createZeroValue(builder, loc, eleType); 1781 builder.create<fir::ResultOp>(loc, zero); 1782 }) 1783 .getResults()[0], 1784 std::nullopt}; 1785 } 1786 1787 /// Create an optional dummy argument address from \p entity that may be 1788 /// absent. If \p entity is considered absent according to 15.5.2.12 point 1., 1789 /// the returned value is a null pointer, otherwise it is the address of \p 1790 /// entity. 1791 static ExvAndCleanup genOptionalAddr(fir::FirOpBuilder &builder, 1792 mlir::Location loc, hlfir::Entity entity, 1793 mlir::Value isPresent) { 1794 auto [exv, cleanup] = hlfir::translateToExtendedValue(loc, builder, entity); 1795 // If it is an exv pointer/allocatable, then it cannot be absent 1796 // because it is passed to a non-pointer/non-allocatable. 1797 if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>()) 1798 return {fir::factory::genMutableBoxRead(builder, loc, *box), cleanup}; 1799 // If this is not a POINTER or ALLOCATABLE, then it is already an OPTIONAL 1800 // address and can be passed directly. 1801 return {exv, cleanup}; 1802 } 1803 1804 /// Create an optional dummy argument address from \p entity that may be 1805 /// absent. If \p entity is considered absent according to 15.5.2.12 point 1., 1806 /// the returned value is an absent fir.box, otherwise it is a fir.box 1807 /// describing \p entity. 1808 static ExvAndCleanup genOptionalBox(fir::FirOpBuilder &builder, 1809 mlir::Location loc, hlfir::Entity entity, 1810 mlir::Value isPresent) { 1811 auto [exv, cleanup] = hlfir::translateToExtendedValue(loc, builder, entity); 1812 1813 // Non allocatable/pointer optional box -> simply forward 1814 if (exv.getBoxOf<fir::BoxValue>()) 1815 return {exv, cleanup}; 1816 1817 fir::ExtendedValue newExv = exv; 1818 // Optional allocatable/pointer -> Cannot be absent, but need to translate 1819 // unallocated/diassociated into absent fir.box. 1820 if (const auto *box = exv.getBoxOf<fir::MutableBoxValue>()) 1821 newExv = fir::factory::genMutableBoxRead(builder, loc, *box); 1822 1823 // createBox will not do create any invalid memory dereferences if exv is 1824 // absent. The created fir.box will not be usable, but the SelectOp below 1825 // ensures it won't be. 1826 mlir::Value box = builder.createBox(loc, newExv); 1827 mlir::Type boxType = box.getType(); 1828 auto absent = builder.create<fir::AbsentOp>(loc, boxType); 1829 auto boxOrAbsent = builder.create<mlir::arith::SelectOp>( 1830 loc, boxType, isPresent, box, absent); 1831 return {fir::BoxValue(boxOrAbsent), cleanup}; 1832 } 1833 1834 /// Lower calls to intrinsic procedures with custom optional handling where the 1835 /// actual arguments have been pre-lowered 1836 static std::optional<hlfir::EntityWithAttributes> genCustomIntrinsicRefCore( 1837 Fortran::lower::PreparedActualArguments &loweredActuals, 1838 const Fortran::evaluate::SpecificIntrinsic *intrinsic, 1839 CallContext &callContext) { 1840 auto &builder = callContext.getBuilder(); 1841 const auto &loc = callContext.loc; 1842 assert(intrinsic && 1843 Fortran::lower::intrinsicRequiresCustomOptionalHandling( 1844 callContext.procRef, *intrinsic, callContext.converter)); 1845 1846 // helper to get a particular prepared argument 1847 auto getArgument = [&](std::size_t i, bool loadArg) -> fir::ExtendedValue { 1848 if (!loweredActuals[i]) 1849 return fir::getAbsentIntrinsicArgument(); 1850 hlfir::Entity actual = loweredActuals[i]->getActual(loc, builder); 1851 if (loadArg && fir::conformsWithPassByRef(actual.getType())) { 1852 return hlfir::loadTrivialScalar(loc, builder, actual); 1853 } 1854 return Fortran::lower::translateToExtendedValue(loc, builder, actual, 1855 callContext.stmtCtx); 1856 }; 1857 // helper to get the isPresent flag for a particular prepared argument 1858 auto isPresent = [&](std::size_t i) -> std::optional<mlir::Value> { 1859 if (!loweredActuals[i]) 1860 return {builder.createBool(loc, false)}; 1861 if (loweredActuals[i]->handleDynamicOptional()) 1862 return {loweredActuals[i]->getIsPresent()}; 1863 return std::nullopt; 1864 }; 1865 1866 assert(callContext.resultType && 1867 "the elemental intrinsics with custom handling are all functions"); 1868 // if callContext.resultType is an array then this was originally an elemental 1869 // call. What we are lowering here is inside the kernel of the hlfir.elemental 1870 // so we should return the scalar type. If the return type is already a scalar 1871 // then it should be unchanged here. 1872 mlir::Type resTy = hlfir::getFortranElementType(*callContext.resultType); 1873 fir::ExtendedValue result = Fortran::lower::lowerCustomIntrinsic( 1874 builder, loc, callContext.getProcedureName(), resTy, isPresent, 1875 getArgument, loweredActuals.size(), callContext.stmtCtx); 1876 1877 return {hlfir::EntityWithAttributes{extendedValueToHlfirEntity( 1878 loc, builder, result, ".tmp.custom_intrinsic_result")}}; 1879 } 1880 1881 /// Lower calls to intrinsic procedures with actual arguments that have been 1882 /// pre-lowered but have not yet been prepared according to the interface. 1883 static std::optional<hlfir::EntityWithAttributes> 1884 genIntrinsicRefCore(Fortran::lower::PreparedActualArguments &loweredActuals, 1885 const Fortran::evaluate::SpecificIntrinsic *intrinsic, 1886 const fir::IntrinsicHandlerEntry &intrinsicEntry, 1887 CallContext &callContext) { 1888 auto &converter = callContext.converter; 1889 if (intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling( 1890 callContext.procRef, *intrinsic, converter)) 1891 return genCustomIntrinsicRefCore(loweredActuals, intrinsic, callContext); 1892 llvm::SmallVector<fir::ExtendedValue> operands; 1893 llvm::SmallVector<hlfir::CleanupFunction> cleanupFns; 1894 auto addToCleanups = [&cleanupFns](std::optional<hlfir::CleanupFunction> fn) { 1895 if (fn) 1896 cleanupFns.emplace_back(std::move(*fn)); 1897 }; 1898 auto &stmtCtx = callContext.stmtCtx; 1899 fir::FirOpBuilder &builder = callContext.getBuilder(); 1900 mlir::Location loc = callContext.loc; 1901 const fir::IntrinsicArgumentLoweringRules *argLowering = 1902 intrinsicEntry.getArgumentLoweringRules(); 1903 for (auto arg : llvm::enumerate(loweredActuals)) { 1904 if (!arg.value()) { 1905 operands.emplace_back(fir::getAbsentIntrinsicArgument()); 1906 continue; 1907 } 1908 if (!argLowering) { 1909 // No argument lowering instruction, lower by value. 1910 assert(!arg.value()->handleDynamicOptional() && 1911 "should use genOptionalValue"); 1912 hlfir::Entity actual = arg.value()->getActual(loc, builder); 1913 operands.emplace_back( 1914 Fortran::lower::convertToValue(loc, converter, actual, stmtCtx)); 1915 continue; 1916 } 1917 // Helper to get the type of the Fortran expression in case it is a 1918 // computed value that must be placed in memory (logicals are computed as 1919 // i1, but must be placed in memory as fir.logical). 1920 auto getActualFortranElementType = [&]() -> mlir::Type { 1921 if (const Fortran::lower::SomeExpr *expr = 1922 callContext.procRef.UnwrapArgExpr(arg.index())) { 1923 1924 mlir::Type type = converter.genType(*expr); 1925 return hlfir::getFortranElementType(type); 1926 } 1927 // TYPE(*): is already in memory anyway. Can return none 1928 // here. 1929 return builder.getNoneType(); 1930 }; 1931 // Ad-hoc argument lowering handling. 1932 fir::ArgLoweringRule argRules = 1933 fir::lowerIntrinsicArgumentAs(*argLowering, arg.index()); 1934 if (arg.value()->handleDynamicOptional()) { 1935 mlir::Value isPresent = arg.value()->getIsPresent(); 1936 switch (argRules.lowerAs) { 1937 case fir::LowerIntrinsicArgAs::Value: { 1938 // In case of elemental call, getActual() may produce 1939 // a designator denoting the array element to be passed 1940 // to the subprogram. If the actual array is dynamically 1941 // optional the designator must be generated under 1942 // isPresent check, because the box bounds reads will be 1943 // generated in the codegen. These reads are illegal, 1944 // if the dynamically optional argument is absent. 1945 auto getActualCb = [&](mlir::Location loc, 1946 fir::FirOpBuilder &builder) -> hlfir::Entity { 1947 return arg.value()->getActual(loc, builder); 1948 }; 1949 auto [exv, cleanup] = 1950 genOptionalValue(builder, loc, getActualFortranElementType(), 1951 getActualCb, isPresent); 1952 addToCleanups(std::move(cleanup)); 1953 operands.emplace_back(exv); 1954 continue; 1955 } 1956 case fir::LowerIntrinsicArgAs::Addr: { 1957 hlfir::Entity actual = arg.value()->getActual(loc, builder); 1958 auto [exv, cleanup] = genOptionalAddr(builder, loc, actual, isPresent); 1959 addToCleanups(std::move(cleanup)); 1960 operands.emplace_back(exv); 1961 continue; 1962 } 1963 case fir::LowerIntrinsicArgAs::Box: { 1964 hlfir::Entity actual = arg.value()->getActual(loc, builder); 1965 auto [exv, cleanup] = genOptionalBox(builder, loc, actual, isPresent); 1966 addToCleanups(std::move(cleanup)); 1967 operands.emplace_back(exv); 1968 continue; 1969 } 1970 case fir::LowerIntrinsicArgAs::Inquired: { 1971 hlfir::Entity actual = arg.value()->getActual(loc, builder); 1972 auto [exv, cleanup] = 1973 hlfir::translateToExtendedValue(loc, builder, actual); 1974 addToCleanups(std::move(cleanup)); 1975 operands.emplace_back(exv); 1976 continue; 1977 } 1978 } 1979 llvm_unreachable("bad switch"); 1980 } 1981 1982 hlfir::Entity actual = arg.value()->getActual(loc, builder); 1983 switch (argRules.lowerAs) { 1984 case fir::LowerIntrinsicArgAs::Value: 1985 operands.emplace_back( 1986 Fortran::lower::convertToValue(loc, converter, actual, stmtCtx)); 1987 continue; 1988 case fir::LowerIntrinsicArgAs::Addr: 1989 operands.emplace_back(Fortran::lower::convertToAddress( 1990 loc, converter, actual, stmtCtx, getActualFortranElementType())); 1991 continue; 1992 case fir::LowerIntrinsicArgAs::Box: 1993 operands.emplace_back(Fortran::lower::convertToBox( 1994 loc, converter, actual, stmtCtx, getActualFortranElementType())); 1995 continue; 1996 case fir::LowerIntrinsicArgAs::Inquired: 1997 if (const Fortran::lower::SomeExpr *expr = 1998 callContext.procRef.UnwrapArgExpr(arg.index())) { 1999 if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>( 2000 *expr)) { 2001 // NULL() pointer without a MOLD must be passed as a deallocated 2002 // pointer (see table 16.5 in Fortran 2018 standard). 2003 // !fir.box<!fir.ptr<none>> should always be valid in this context. 2004 mlir::Type noneTy = mlir::NoneType::get(builder.getContext()); 2005 mlir::Type nullPtrTy = fir::PointerType::get(noneTy); 2006 mlir::Type boxTy = fir::BoxType::get(nullPtrTy); 2007 mlir::Value boxStorage = 2008 fir::factory::genNullBoxStorage(builder, loc, boxTy); 2009 hlfir::EntityWithAttributes nullBoxEntity = 2010 extendedValueToHlfirEntity(loc, builder, boxStorage, 2011 ".tmp.null_box"); 2012 operands.emplace_back(Fortran::lower::translateToExtendedValue( 2013 loc, builder, nullBoxEntity, stmtCtx)); 2014 continue; 2015 } 2016 } 2017 // Place hlfir.expr in memory, and unbox fir.boxchar. Other entities 2018 // are translated to fir::ExtendedValue without transformation (notably, 2019 // pointers/allocatable are not dereferenced). 2020 // TODO: once lowering to FIR retires, UBOUND and LBOUND can be simplified 2021 // since the fir.box lowered here are now guaranteed to contain the local 2022 // lower bounds thanks to the hlfir.declare (the extra rebox can be 2023 // removed). 2024 operands.emplace_back(Fortran::lower::translateToExtendedValue( 2025 loc, builder, actual, stmtCtx)); 2026 continue; 2027 } 2028 llvm_unreachable("bad switch"); 2029 } 2030 // genIntrinsicCall needs the scalar type, even if this is a transformational 2031 // procedure returning an array. 2032 std::optional<mlir::Type> scalarResultType; 2033 if (callContext.resultType) 2034 scalarResultType = hlfir::getFortranElementType(*callContext.resultType); 2035 const std::string intrinsicName = callContext.getProcedureName(); 2036 // Let the intrinsic library lower the intrinsic procedure call. 2037 auto [resultExv, mustBeFreed] = genIntrinsicCall( 2038 builder, loc, intrinsicEntry, scalarResultType, operands, &converter); 2039 for (const hlfir::CleanupFunction &fn : cleanupFns) 2040 fn(); 2041 if (!fir::getBase(resultExv)) 2042 return std::nullopt; 2043 hlfir::EntityWithAttributes resultEntity = extendedValueToHlfirEntity( 2044 loc, builder, resultExv, ".tmp.intrinsic_result"); 2045 // Move result into memory into an hlfir.expr since they are immutable from 2046 // that point, and the result storage is some temp. "Null" is special: it 2047 // returns a null pointer variable that should not be transformed into a value 2048 // (what matters is the memory address). 2049 if (resultEntity.isVariable() && intrinsicName != "null") { 2050 assert(!fir::isa_trivial(fir::unwrapRefType(resultEntity.getType())) && 2051 "expect intrinsic scalar results to not be in memory"); 2052 hlfir::AsExprOp asExpr; 2053 // Character/Derived MERGE lowering returns one of its argument address 2054 // (this is the only intrinsic implemented in that way so far). The 2055 // ownership of this address cannot be taken here since it may not be a 2056 // temp. 2057 if (intrinsicName == "merge") 2058 asExpr = builder.create<hlfir::AsExprOp>(loc, resultEntity); 2059 else 2060 asExpr = builder.create<hlfir::AsExprOp>( 2061 loc, resultEntity, builder.createBool(loc, mustBeFreed)); 2062 resultEntity = hlfir::EntityWithAttributes{asExpr.getResult()}; 2063 } 2064 return resultEntity; 2065 } 2066 2067 /// Lower calls to intrinsic procedures with actual arguments that have been 2068 /// pre-lowered but have not yet been prepared according to the interface. 2069 static std::optional<hlfir::EntityWithAttributes> genHLFIRIntrinsicRefCore( 2070 Fortran::lower::PreparedActualArguments &loweredActuals, 2071 const Fortran::evaluate::SpecificIntrinsic *intrinsic, 2072 const fir::IntrinsicHandlerEntry &intrinsicEntry, 2073 CallContext &callContext) { 2074 // Try lowering transformational intrinsic ops to HLFIR ops if enabled 2075 // (transformational always have a result type) 2076 if (useHlfirIntrinsicOps && callContext.resultType) { 2077 fir::FirOpBuilder &builder = callContext.getBuilder(); 2078 mlir::Location loc = callContext.loc; 2079 const std::string intrinsicName = callContext.getProcedureName(); 2080 const fir::IntrinsicArgumentLoweringRules *argLowering = 2081 intrinsicEntry.getArgumentLoweringRules(); 2082 std::optional<hlfir::EntityWithAttributes> res = 2083 Fortran::lower::lowerHlfirIntrinsic(builder, loc, intrinsicName, 2084 loweredActuals, argLowering, 2085 *callContext.resultType); 2086 if (res) 2087 return res; 2088 } 2089 2090 // fallback to calling the intrinsic via fir.call 2091 return genIntrinsicRefCore(loweredActuals, intrinsic, intrinsicEntry, 2092 callContext); 2093 } 2094 2095 namespace { 2096 template <typename ElementalCallBuilderImpl> 2097 class ElementalCallBuilder { 2098 public: 2099 std::optional<hlfir::EntityWithAttributes> 2100 genElementalCall(Fortran::lower::PreparedActualArguments &loweredActuals, 2101 bool isImpure, CallContext &callContext) { 2102 mlir::Location loc = callContext.loc; 2103 fir::FirOpBuilder &builder = callContext.getBuilder(); 2104 unsigned numArgs = loweredActuals.size(); 2105 // Step 1: dereference pointers/allocatables and compute elemental shape. 2106 mlir::Value shape; 2107 Fortran::lower::PreparedActualArgument *optionalWithShape; 2108 // 10.1.4 p5. Impure elemental procedures must be called in element order. 2109 bool mustBeOrdered = isImpure; 2110 for (unsigned i = 0; i < numArgs; ++i) { 2111 auto &preparedActual = loweredActuals[i]; 2112 if (preparedActual) { 2113 // Elemental procedure dummy arguments cannot be pointer/allocatables 2114 // (C15100), so it is safe to dereference any pointer or allocatable 2115 // actual argument now instead of doing this inside the elemental 2116 // region. 2117 preparedActual->derefPointersAndAllocatables(loc, builder); 2118 // Better to load scalars outside of the loop when possible. 2119 if (!preparedActual->handleDynamicOptional() && 2120 impl().canLoadActualArgumentBeforeLoop(i)) 2121 preparedActual->loadTrivialScalar(loc, builder); 2122 // TODO: merge shape instead of using the first one. 2123 if (!shape && preparedActual->isArray()) { 2124 if (preparedActual->handleDynamicOptional()) 2125 optionalWithShape = &*preparedActual; 2126 else 2127 shape = preparedActual->genShape(loc, builder); 2128 } 2129 // 15.8.3 p1. Elemental procedure with intent(out)/intent(inout) 2130 // arguments must be called in element order. 2131 if (impl().argMayBeModifiedByCall(i)) 2132 mustBeOrdered = true; 2133 } 2134 } 2135 if (!shape && optionalWithShape) { 2136 // If all array operands appear in optional positions, then none of them 2137 // is allowed to be absent as per 15.5.2.12 point 3. (6). Just pick the 2138 // first operand. 2139 shape = optionalWithShape->genShape(loc, builder); 2140 // TODO: There is an opportunity to add a runtime check here that 2141 // this array is present as required. Also, the optionality of all actual 2142 // could be checked and reset given the Fortran requirement. 2143 optionalWithShape->resetOptionalAspect(); 2144 } 2145 assert(shape && 2146 "elemental array calls must have at least one array arguments"); 2147 2148 // Evaluate the actual argument array expressions before the elemental 2149 // call of an impure subprogram or a subprogram with intent(out) or 2150 // intent(inout) arguments. Note that the scalar arguments are handled 2151 // above. 2152 if (mustBeOrdered) { 2153 for (auto &preparedActual : loweredActuals) { 2154 if (preparedActual) { 2155 if (hlfir::AssociateOp associate = 2156 preparedActual->associateIfArrayExpr(loc, builder)) { 2157 fir::FirOpBuilder *bldr = &builder; 2158 callContext.stmtCtx.attachCleanup( 2159 [=]() { bldr->create<hlfir::EndAssociateOp>(loc, associate); }); 2160 } 2161 } 2162 } 2163 } 2164 2165 // Push a new local scope so that any temps made inside the elemental 2166 // iterations are cleaned up inside the iterations. 2167 if (!callContext.resultType) { 2168 // Subroutine case. Generate call inside loop nest. 2169 hlfir::LoopNest loopNest = 2170 hlfir::genLoopNest(loc, builder, shape, !mustBeOrdered); 2171 mlir::ValueRange oneBasedIndices = loopNest.oneBasedIndices; 2172 auto insPt = builder.saveInsertionPoint(); 2173 builder.setInsertionPointToStart(loopNest.body); 2174 callContext.stmtCtx.pushScope(); 2175 for (auto &preparedActual : loweredActuals) 2176 if (preparedActual) 2177 preparedActual->setElementalIndices(oneBasedIndices); 2178 impl().genElementalKernel(loweredActuals, callContext); 2179 callContext.stmtCtx.finalizeAndPop(); 2180 builder.restoreInsertionPoint(insPt); 2181 return std::nullopt; 2182 } 2183 // Function case: generate call inside hlfir.elemental 2184 mlir::Type elementType = 2185 hlfir::getFortranElementType(*callContext.resultType); 2186 // Get result length parameters. 2187 llvm::SmallVector<mlir::Value> typeParams; 2188 if (mlir::isa<fir::CharacterType>(elementType) || 2189 fir::isRecordWithTypeParameters(elementType)) { 2190 auto charType = mlir::dyn_cast<fir::CharacterType>(elementType); 2191 if (charType && charType.hasConstantLen()) 2192 typeParams.push_back(builder.createIntegerConstant( 2193 loc, builder.getIndexType(), charType.getLen())); 2194 else if (charType) 2195 typeParams.push_back(impl().computeDynamicCharacterResultLength( 2196 loweredActuals, callContext)); 2197 else 2198 TODO( 2199 loc, 2200 "compute elemental PDT function result length parameters in HLFIR"); 2201 } 2202 auto genKernel = [&](mlir::Location l, fir::FirOpBuilder &b, 2203 mlir::ValueRange oneBasedIndices) -> hlfir::Entity { 2204 callContext.stmtCtx.pushScope(); 2205 for (auto &preparedActual : loweredActuals) 2206 if (preparedActual) 2207 preparedActual->setElementalIndices(oneBasedIndices); 2208 auto res = *impl().genElementalKernel(loweredActuals, callContext); 2209 callContext.stmtCtx.finalizeAndPop(); 2210 // Note that an hlfir.destroy is not emitted for the result since it 2211 // is still used by the hlfir.yield_element that also marks its last 2212 // use. 2213 return res; 2214 }; 2215 mlir::Value polymorphicMold; 2216 if (fir::isPolymorphicType(*callContext.resultType)) 2217 polymorphicMold = 2218 impl().getPolymorphicResultMold(loweredActuals, callContext); 2219 mlir::Value elemental = 2220 hlfir::genElementalOp(loc, builder, elementType, shape, typeParams, 2221 genKernel, !mustBeOrdered, polymorphicMold); 2222 // If the function result requires finalization, then it has to be done 2223 // for the array result of the elemental call. We have to communicate 2224 // this via the DestroyOp's attribute. 2225 bool mustFinalizeExpr = impl().resultMayRequireFinalization(callContext); 2226 fir::FirOpBuilder *bldr = &builder; 2227 callContext.stmtCtx.attachCleanup([=]() { 2228 bldr->create<hlfir::DestroyOp>(loc, elemental, mustFinalizeExpr); 2229 }); 2230 return hlfir::EntityWithAttributes{elemental}; 2231 } 2232 2233 private: 2234 ElementalCallBuilderImpl &impl() { 2235 return *static_cast<ElementalCallBuilderImpl *>(this); 2236 } 2237 }; 2238 2239 class ElementalUserCallBuilder 2240 : public ElementalCallBuilder<ElementalUserCallBuilder> { 2241 public: 2242 ElementalUserCallBuilder(Fortran::lower::CallerInterface &caller, 2243 mlir::FunctionType callSiteType) 2244 : caller{caller}, callSiteType{callSiteType} {} 2245 std::optional<hlfir::Entity> 2246 genElementalKernel(Fortran::lower::PreparedActualArguments &loweredActuals, 2247 CallContext &callContext) { 2248 return genUserCall(loweredActuals, caller, callSiteType, callContext); 2249 } 2250 2251 bool argMayBeModifiedByCall(unsigned argIdx) const { 2252 assert(argIdx < caller.getPassedArguments().size() && "bad argument index"); 2253 return caller.getPassedArguments()[argIdx].mayBeModifiedByCall(); 2254 } 2255 2256 bool canLoadActualArgumentBeforeLoop(unsigned argIdx) const { 2257 using PassBy = Fortran::lower::CallerInterface::PassEntityBy; 2258 const auto &passedArgs{caller.getPassedArguments()}; 2259 assert(argIdx < passedArgs.size() && "bad argument index"); 2260 // If the actual argument does not need to be passed via an address, 2261 // or will be passed in the address of a temporary copy, it can be loaded 2262 // before the elemental loop nest. 2263 const auto &arg{passedArgs[argIdx]}; 2264 return arg.passBy == PassBy::Value || 2265 arg.passBy == PassBy::BaseAddressValueAttribute; 2266 } 2267 2268 mlir::Value computeDynamicCharacterResultLength( 2269 Fortran::lower::PreparedActualArguments &loweredActuals, 2270 CallContext &callContext) { 2271 fir::FirOpBuilder &builder = callContext.getBuilder(); 2272 mlir::Location loc = callContext.loc; 2273 auto &converter = callContext.converter; 2274 mlir::Type idxTy = builder.getIndexType(); 2275 llvm::SmallVector<CallCleanUp> callCleanUps; 2276 2277 prepareUserCallArguments(loweredActuals, caller, callSiteType, callContext, 2278 callCleanUps); 2279 2280 callContext.symMap.pushScope(); 2281 2282 // Map prepared argument to dummy symbol to be able to lower spec expr. 2283 for (const auto &arg : caller.getPassedArguments()) { 2284 const Fortran::semantics::Symbol *sym = caller.getDummySymbol(arg); 2285 assert(sym && "expect symbol for dummy argument"); 2286 auto input = caller.getInput(arg); 2287 fir::ExtendedValue exv = Fortran::lower::translateToExtendedValue( 2288 loc, builder, hlfir::Entity{input}, callContext.stmtCtx); 2289 fir::FortranVariableOpInterface variableIface = hlfir::genDeclare( 2290 loc, builder, exv, "dummy.tmp", fir::FortranVariableFlagsAttr{}); 2291 callContext.symMap.addVariableDefinition(*sym, variableIface); 2292 } 2293 2294 auto lowerSpecExpr = [&](const auto &expr) -> mlir::Value { 2295 mlir::Value convertExpr = builder.createConvert( 2296 loc, idxTy, 2297 fir::getBase(converter.genExprValue(expr, callContext.stmtCtx))); 2298 return fir::factory::genMaxWithZero(builder, loc, convertExpr); 2299 }; 2300 2301 llvm::SmallVector<mlir::Value> lengths; 2302 caller.walkResultLengths( 2303 [&](const Fortran::lower::SomeExpr &e, bool isAssumedSizeExtent) { 2304 assert(!isAssumedSizeExtent && "result cannot be assumed-size"); 2305 lengths.emplace_back(lowerSpecExpr(e)); 2306 }); 2307 callContext.symMap.popScope(); 2308 assert(lengths.size() == 1 && "expect 1 length parameter for the result"); 2309 return lengths[0]; 2310 } 2311 2312 mlir::Value getPolymorphicResultMold( 2313 Fortran::lower::PreparedActualArguments &loweredActuals, 2314 CallContext &callContext) { 2315 fir::emitFatalError(callContext.loc, 2316 "elemental function call with polymorphic result"); 2317 return {}; 2318 } 2319 2320 bool resultMayRequireFinalization(CallContext &callContext) const { 2321 std::optional<Fortran::evaluate::DynamicType> retTy = 2322 caller.getCallDescription().proc().GetType(); 2323 if (!retTy) 2324 return false; 2325 2326 if (retTy->IsPolymorphic() || retTy->IsUnlimitedPolymorphic()) 2327 fir::emitFatalError( 2328 callContext.loc, 2329 "elemental function call with [unlimited-]polymorphic result"); 2330 2331 if (retTy->category() == Fortran::common::TypeCategory::Derived) { 2332 const Fortran::semantics::DerivedTypeSpec &typeSpec = 2333 retTy->GetDerivedTypeSpec(); 2334 return Fortran::semantics::IsFinalizable(typeSpec); 2335 } 2336 2337 return false; 2338 } 2339 2340 private: 2341 Fortran::lower::CallerInterface &caller; 2342 mlir::FunctionType callSiteType; 2343 }; 2344 2345 class ElementalIntrinsicCallBuilder 2346 : public ElementalCallBuilder<ElementalIntrinsicCallBuilder> { 2347 public: 2348 ElementalIntrinsicCallBuilder( 2349 const Fortran::evaluate::SpecificIntrinsic *intrinsic, 2350 const fir::IntrinsicHandlerEntry &intrinsicEntry, bool isFunction) 2351 : intrinsic{intrinsic}, intrinsicEntry{intrinsicEntry}, 2352 isFunction{isFunction} {} 2353 std::optional<hlfir::Entity> 2354 genElementalKernel(Fortran::lower::PreparedActualArguments &loweredActuals, 2355 CallContext &callContext) { 2356 return genHLFIRIntrinsicRefCore(loweredActuals, intrinsic, intrinsicEntry, 2357 callContext); 2358 } 2359 // Elemental intrinsic functions cannot modify their arguments. 2360 bool argMayBeModifiedByCall(int) const { return !isFunction; } 2361 bool canLoadActualArgumentBeforeLoop(int) const { 2362 // Elemental intrinsic functions never need the actual addresses 2363 // of their arguments. 2364 return isFunction; 2365 } 2366 2367 mlir::Value computeDynamicCharacterResultLength( 2368 Fortran::lower::PreparedActualArguments &loweredActuals, 2369 CallContext &callContext) { 2370 if (intrinsic) 2371 if (intrinsic->name == "adjustr" || intrinsic->name == "adjustl" || 2372 intrinsic->name == "merge") 2373 return loweredActuals[0].value().genCharLength( 2374 callContext.loc, callContext.getBuilder()); 2375 // Character MIN/MAX is the min/max of the arguments length that are 2376 // present. 2377 TODO(callContext.loc, 2378 "compute elemental character min/max function result length in HLFIR"); 2379 } 2380 2381 mlir::Value getPolymorphicResultMold( 2382 Fortran::lower::PreparedActualArguments &loweredActuals, 2383 CallContext &callContext) { 2384 if (!intrinsic) 2385 return {}; 2386 2387 if (intrinsic->name == "merge") { 2388 // MERGE seems to be the only elemental function that can produce 2389 // polymorphic result. The MERGE's result is polymorphic iff 2390 // both TSOURCE and FSOURCE are polymorphic, and they also must have 2391 // the same declared and dynamic types. So any of them can be used 2392 // for the mold. 2393 assert(!loweredActuals.empty()); 2394 return loweredActuals.front()->getPolymorphicMold(callContext.loc); 2395 } 2396 2397 return {}; 2398 } 2399 2400 bool resultMayRequireFinalization( 2401 [[maybe_unused]] CallContext &callContext) const { 2402 // FIXME: need access to the CallerInterface's return type 2403 // to check if the result may need finalization (e.g. the result 2404 // of MERGE). 2405 return false; 2406 } 2407 2408 private: 2409 const Fortran::evaluate::SpecificIntrinsic *intrinsic; 2410 fir::IntrinsicHandlerEntry intrinsicEntry; 2411 const bool isFunction; 2412 }; 2413 } // namespace 2414 2415 static std::optional<mlir::Value> 2416 genIsPresentIfArgMaybeAbsent(mlir::Location loc, hlfir::Entity actual, 2417 const Fortran::lower::SomeExpr &expr, 2418 CallContext &callContext, 2419 bool passAsAllocatableOrPointer) { 2420 if (!Fortran::evaluate::MayBePassedAsAbsentOptional(expr)) 2421 return std::nullopt; 2422 fir::FirOpBuilder &builder = callContext.getBuilder(); 2423 if (!passAsAllocatableOrPointer && 2424 Fortran::evaluate::IsAllocatableOrPointerObject(expr)) { 2425 // Passing Allocatable/Pointer to non-pointer/non-allocatable OPTIONAL. 2426 // Fortran 2018 15.5.2.12 point 1: If unallocated/disassociated, it is 2427 // as if the argument was absent. The main care here is to not do a 2428 // copy-in/copy-out because the temp address, even though pointing to a 2429 // null size storage, would not be a nullptr and therefore the argument 2430 // would not be considered absent on the callee side. Note: if the 2431 // allocatable/pointer is also optional, it cannot be absent as per 2432 // 15.5.2.12 point 7. and 8. We rely on this to un-conditionally read 2433 // the allocatable/pointer descriptor here. 2434 mlir::Value addr = genVariableRawAddress(loc, builder, actual); 2435 return builder.genIsNotNullAddr(loc, addr); 2436 } 2437 // TODO: what if passing allocatable target to optional intent(in) pointer? 2438 // May fall into the category above if the allocatable is not optional. 2439 2440 // Passing an optional to an optional. 2441 return builder.create<fir::IsPresentOp>(loc, builder.getI1Type(), actual) 2442 .getResult(); 2443 } 2444 2445 // Lower a reference to an elemental intrinsic procedure with array arguments 2446 // and custom optional handling 2447 static std::optional<hlfir::EntityWithAttributes> 2448 genCustomElementalIntrinsicRef( 2449 const Fortran::evaluate::SpecificIntrinsic *intrinsic, 2450 CallContext &callContext) { 2451 assert(callContext.isElementalProcWithArrayArgs() && 2452 "Use genCustomIntrinsicRef for scalar calls"); 2453 mlir::Location loc = callContext.loc; 2454 auto &converter = callContext.converter; 2455 Fortran::lower::PreparedActualArguments operands; 2456 assert(intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling( 2457 callContext.procRef, *intrinsic, converter)); 2458 2459 // callback for optional arguments 2460 auto prepareOptionalArg = [&](const Fortran::lower::SomeExpr &expr) { 2461 hlfir::EntityWithAttributes actual = Fortran::lower::convertExprToHLFIR( 2462 loc, converter, expr, callContext.symMap, callContext.stmtCtx); 2463 std::optional<mlir::Value> isPresent = 2464 genIsPresentIfArgMaybeAbsent(loc, actual, expr, callContext, 2465 /*passAsAllocatableOrPointer=*/false); 2466 operands.emplace_back( 2467 Fortran::lower::PreparedActualArgument{actual, isPresent}); 2468 }; 2469 2470 // callback for non-optional arguments 2471 auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr, 2472 fir::LowerIntrinsicArgAs lowerAs) { 2473 hlfir::EntityWithAttributes actual = Fortran::lower::convertExprToHLFIR( 2474 loc, converter, expr, callContext.symMap, callContext.stmtCtx); 2475 operands.emplace_back(Fortran::lower::PreparedActualArgument{ 2476 actual, /*isPresent=*/std::nullopt}); 2477 }; 2478 2479 Fortran::lower::prepareCustomIntrinsicArgument( 2480 callContext.procRef, *intrinsic, callContext.resultType, 2481 prepareOptionalArg, prepareOtherArg, converter); 2482 2483 std::optional<fir::IntrinsicHandlerEntry> intrinsicEntry = 2484 fir::lookupIntrinsicHandler(callContext.getBuilder(), 2485 callContext.getProcedureName(), 2486 callContext.resultType); 2487 assert(intrinsicEntry.has_value() && 2488 "intrinsic with custom handling for OPTIONAL arguments must have " 2489 "lowering entries"); 2490 // All of the custom intrinsic elementals with custom handling are pure 2491 // functions 2492 return ElementalIntrinsicCallBuilder{intrinsic, *intrinsicEntry, 2493 /*isFunction=*/true} 2494 .genElementalCall(operands, /*isImpure=*/false, callContext); 2495 } 2496 2497 // Lower a reference to an intrinsic procedure with custom optional handling 2498 static std::optional<hlfir::EntityWithAttributes> 2499 genCustomIntrinsicRef(const Fortran::evaluate::SpecificIntrinsic *intrinsic, 2500 CallContext &callContext) { 2501 assert(!callContext.isElementalProcWithArrayArgs() && 2502 "Needs to be run through ElementalIntrinsicCallBuilder first"); 2503 mlir::Location loc = callContext.loc; 2504 fir::FirOpBuilder &builder = callContext.getBuilder(); 2505 auto &converter = callContext.converter; 2506 auto &stmtCtx = callContext.stmtCtx; 2507 assert(intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling( 2508 callContext.procRef, *intrinsic, converter)); 2509 Fortran::lower::PreparedActualArguments loweredActuals; 2510 2511 // callback for optional arguments 2512 auto prepareOptionalArg = [&](const Fortran::lower::SomeExpr &expr) { 2513 hlfir::EntityWithAttributes actual = Fortran::lower::convertExprToHLFIR( 2514 loc, converter, expr, callContext.symMap, callContext.stmtCtx); 2515 mlir::Value isPresent = 2516 genIsPresentIfArgMaybeAbsent(loc, actual, expr, callContext, 2517 /*passAsAllocatableOrPointer*/ false) 2518 .value(); 2519 loweredActuals.emplace_back( 2520 Fortran::lower::PreparedActualArgument{actual, {isPresent}}); 2521 }; 2522 2523 // callback for non-optional arguments 2524 auto prepareOtherArg = [&](const Fortran::lower::SomeExpr &expr, 2525 fir::LowerIntrinsicArgAs lowerAs) { 2526 auto getActualFortranElementType = [&]() -> mlir::Type { 2527 return hlfir::getFortranElementType(converter.genType(expr)); 2528 }; 2529 hlfir::EntityWithAttributes actual = Fortran::lower::convertExprToHLFIR( 2530 loc, converter, expr, callContext.symMap, callContext.stmtCtx); 2531 std::optional<fir::ExtendedValue> exv; 2532 switch (lowerAs) { 2533 case fir::LowerIntrinsicArgAs::Value: 2534 exv = Fortran::lower::convertToValue(loc, converter, actual, stmtCtx); 2535 break; 2536 case fir::LowerIntrinsicArgAs::Addr: 2537 exv = Fortran::lower::convertToAddress(loc, converter, actual, stmtCtx, 2538 getActualFortranElementType()); 2539 break; 2540 case fir::LowerIntrinsicArgAs::Box: 2541 exv = Fortran::lower::convertToBox(loc, converter, actual, stmtCtx, 2542 getActualFortranElementType()); 2543 break; 2544 case fir::LowerIntrinsicArgAs::Inquired: 2545 exv = Fortran::lower::translateToExtendedValue(loc, builder, actual, 2546 stmtCtx); 2547 break; 2548 } 2549 if (!exv) 2550 llvm_unreachable("bad switch"); 2551 actual = extendedValueToHlfirEntity(loc, builder, exv.value(), 2552 "tmp.custom_intrinsic_arg"); 2553 loweredActuals.emplace_back(Fortran::lower::PreparedActualArgument{ 2554 actual, /*isPresent=*/std::nullopt}); 2555 }; 2556 2557 Fortran::lower::prepareCustomIntrinsicArgument( 2558 callContext.procRef, *intrinsic, callContext.resultType, 2559 prepareOptionalArg, prepareOtherArg, converter); 2560 2561 return genCustomIntrinsicRefCore(loweredActuals, intrinsic, callContext); 2562 } 2563 2564 /// Lower an intrinsic procedure reference. 2565 /// \p intrinsic is null if this is an intrinsic module procedure that must be 2566 /// lowered as if it were an intrinsic module procedure (like C_LOC which is a 2567 /// procedure from intrinsic module iso_c_binding). Otherwise, \p intrinsic 2568 /// must not be null. 2569 2570 static std::optional<hlfir::EntityWithAttributes> 2571 genIntrinsicRef(const Fortran::evaluate::SpecificIntrinsic *intrinsic, 2572 const fir::IntrinsicHandlerEntry &intrinsicEntry, 2573 CallContext &callContext) { 2574 mlir::Location loc = callContext.loc; 2575 Fortran::lower::PreparedActualArguments loweredActuals; 2576 const fir::IntrinsicArgumentLoweringRules *argLowering = 2577 intrinsicEntry.getArgumentLoweringRules(); 2578 for (const auto &arg : llvm::enumerate(callContext.procRef.arguments())) { 2579 2580 if (!arg.value()) { 2581 // Absent optional. 2582 loweredActuals.push_back(std::nullopt); 2583 continue; 2584 } 2585 auto *expr = 2586 Fortran::evaluate::UnwrapExpr<Fortran::lower::SomeExpr>(arg.value()); 2587 if (!expr) { 2588 // TYPE(*) dummy. They are only allowed as argument of a few intrinsics 2589 // that do not take optional arguments: see Fortran 2018 standard C710. 2590 const Fortran::evaluate::Symbol *assumedTypeSym = 2591 arg.value()->GetAssumedTypeDummy(); 2592 if (!assumedTypeSym) 2593 fir::emitFatalError(loc, 2594 "expected assumed-type symbol as actual argument"); 2595 std::optional<fir::FortranVariableOpInterface> var = 2596 callContext.symMap.lookupVariableDefinition(*assumedTypeSym); 2597 if (!var) 2598 fir::emitFatalError(loc, "assumed-type symbol was not lowered"); 2599 assert( 2600 (!argLowering || 2601 !fir::lowerIntrinsicArgumentAs(*argLowering, arg.index()) 2602 .handleDynamicOptional) && 2603 "TYPE(*) are not expected to appear as optional intrinsic arguments"); 2604 loweredActuals.push_back(Fortran::lower::PreparedActualArgument{ 2605 hlfir::Entity{*var}, /*isPresent=*/std::nullopt}); 2606 continue; 2607 } 2608 // arguments of bitwise comparison functions may not have nsw flag 2609 // even if -fno-wrapv is enabled 2610 mlir::arith::IntegerOverflowFlags iofBackup{}; 2611 auto isBitwiseComparison = [](const std::string intrinsicName) -> bool { 2612 if (intrinsicName == "bge" || intrinsicName == "bgt" || 2613 intrinsicName == "ble" || intrinsicName == "blt") 2614 return true; 2615 return false; 2616 }; 2617 if (isBitwiseComparison(callContext.getProcedureName())) { 2618 iofBackup = callContext.getBuilder().getIntegerOverflowFlags(); 2619 callContext.getBuilder().setIntegerOverflowFlags( 2620 mlir::arith::IntegerOverflowFlags::none); 2621 } 2622 auto loweredActual = Fortran::lower::convertExprToHLFIR( 2623 loc, callContext.converter, *expr, callContext.symMap, 2624 callContext.stmtCtx); 2625 if (isBitwiseComparison(callContext.getProcedureName())) 2626 callContext.getBuilder().setIntegerOverflowFlags(iofBackup); 2627 2628 std::optional<mlir::Value> isPresent; 2629 if (argLowering) { 2630 fir::ArgLoweringRule argRules = 2631 fir::lowerIntrinsicArgumentAs(*argLowering, arg.index()); 2632 if (argRules.handleDynamicOptional) 2633 isPresent = 2634 genIsPresentIfArgMaybeAbsent(loc, loweredActual, *expr, callContext, 2635 /*passAsAllocatableOrPointer=*/false); 2636 } 2637 loweredActuals.push_back( 2638 Fortran::lower::PreparedActualArgument{loweredActual, isPresent}); 2639 } 2640 2641 if (callContext.isElementalProcWithArrayArgs()) { 2642 // All intrinsic elemental functions are pure. 2643 const bool isFunction = callContext.resultType.has_value(); 2644 return ElementalIntrinsicCallBuilder{intrinsic, intrinsicEntry, isFunction} 2645 .genElementalCall(loweredActuals, /*isImpure=*/!isFunction, 2646 callContext); 2647 } 2648 std::optional<hlfir::EntityWithAttributes> result = genHLFIRIntrinsicRefCore( 2649 loweredActuals, intrinsic, intrinsicEntry, callContext); 2650 if (result && mlir::isa<hlfir::ExprType>(result->getType())) { 2651 fir::FirOpBuilder *bldr = &callContext.getBuilder(); 2652 callContext.stmtCtx.attachCleanup( 2653 [=]() { bldr->create<hlfir::DestroyOp>(loc, *result); }); 2654 } 2655 return result; 2656 } 2657 2658 static std::optional<hlfir::EntityWithAttributes> 2659 genIntrinsicRef(const Fortran::evaluate::SpecificIntrinsic *intrinsic, 2660 CallContext &callContext) { 2661 mlir::Location loc = callContext.loc; 2662 auto &converter = callContext.converter; 2663 if (intrinsic && Fortran::lower::intrinsicRequiresCustomOptionalHandling( 2664 callContext.procRef, *intrinsic, converter)) { 2665 if (callContext.isElementalProcWithArrayArgs()) 2666 return genCustomElementalIntrinsicRef(intrinsic, callContext); 2667 return genCustomIntrinsicRef(intrinsic, callContext); 2668 } 2669 std::optional<fir::IntrinsicHandlerEntry> intrinsicEntry = 2670 fir::lookupIntrinsicHandler(callContext.getBuilder(), 2671 callContext.getProcedureName(), 2672 callContext.resultType); 2673 if (!intrinsicEntry) 2674 fir::crashOnMissingIntrinsic(loc, callContext.getProcedureName()); 2675 return genIntrinsicRef(intrinsic, *intrinsicEntry, callContext); 2676 } 2677 2678 /// Main entry point to lower procedure references, regardless of what they are. 2679 static std::optional<hlfir::EntityWithAttributes> 2680 genProcedureRef(CallContext &callContext) { 2681 mlir::Location loc = callContext.loc; 2682 fir::FirOpBuilder &builder = callContext.getBuilder(); 2683 if (auto *intrinsic = callContext.procRef.proc().GetSpecificIntrinsic()) 2684 return genIntrinsicRef(intrinsic, callContext); 2685 // Intercept non BIND(C) module procedure reference that have lowering 2686 // handlers defined for there name. Otherwise, lower them as user 2687 // procedure calls and expect the implementation to be part of 2688 // runtime libraries with the proper name mangling. 2689 if (Fortran::lower::isIntrinsicModuleProcRef(callContext.procRef) && 2690 !callContext.isBindcCall()) 2691 if (std::optional<fir::IntrinsicHandlerEntry> intrinsicEntry = 2692 fir::lookupIntrinsicHandler(builder, callContext.getProcedureName(), 2693 callContext.resultType)) 2694 return genIntrinsicRef(nullptr, *intrinsicEntry, callContext); 2695 2696 if (callContext.isStatementFunctionCall()) 2697 return genStmtFunctionRef(loc, callContext.converter, callContext.symMap, 2698 callContext.stmtCtx, callContext.procRef); 2699 2700 Fortran::lower::CallerInterface caller(callContext.procRef, 2701 callContext.converter); 2702 mlir::FunctionType callSiteType = caller.genFunctionType(); 2703 const bool isElemental = callContext.isElementalProcWithArrayArgs(); 2704 Fortran::lower::PreparedActualArguments loweredActuals; 2705 // Lower the actual arguments 2706 for (const Fortran::lower::CallInterface< 2707 Fortran::lower::CallerInterface>::PassedEntity &arg : 2708 caller.getPassedArguments()) 2709 if (const auto *actual = arg.entity) { 2710 const auto *expr = actual->UnwrapExpr(); 2711 if (!expr) { 2712 // TYPE(*) actual argument. 2713 const Fortran::evaluate::Symbol *assumedTypeSym = 2714 actual->GetAssumedTypeDummy(); 2715 if (!assumedTypeSym) 2716 fir::emitFatalError( 2717 loc, "expected assumed-type symbol as actual argument"); 2718 std::optional<fir::FortranVariableOpInterface> var = 2719 callContext.symMap.lookupVariableDefinition(*assumedTypeSym); 2720 if (!var) 2721 fir::emitFatalError(loc, "assumed-type symbol was not lowered"); 2722 hlfir::Entity actual{*var}; 2723 std::optional<mlir::Value> isPresent; 2724 if (arg.isOptional()) { 2725 // Passing an optional TYPE(*) to an optional TYPE(*). Note that 2726 // TYPE(*) cannot be ALLOCATABLE/POINTER (C709) so there is no 2727 // need to cover the case of passing an ALLOCATABLE/POINTER to an 2728 // OPTIONAL. 2729 isPresent = 2730 builder.create<fir::IsPresentOp>(loc, builder.getI1Type(), actual) 2731 .getResult(); 2732 } 2733 loweredActuals.push_back(Fortran::lower::PreparedActualArgument{ 2734 hlfir::Entity{*var}, isPresent}); 2735 continue; 2736 } 2737 2738 if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>( 2739 *expr)) { 2740 if ((arg.passBy != 2741 Fortran::lower::CallerInterface::PassEntityBy::MutableBox) && 2742 (arg.passBy != 2743 Fortran::lower::CallerInterface::PassEntityBy::BoxProcRef)) { 2744 assert( 2745 arg.isOptional() && 2746 "NULL must be passed only to pointer, allocatable, or OPTIONAL"); 2747 // Trying to lower NULL() outside of any context would lead to 2748 // trouble. NULL() here is equivalent to not providing the 2749 // actual argument. 2750 loweredActuals.emplace_back(std::nullopt); 2751 continue; 2752 } 2753 } 2754 2755 if (isElemental && !arg.hasValueAttribute() && 2756 Fortran::evaluate::IsVariable(*expr) && 2757 Fortran::evaluate::HasVectorSubscript(*expr)) { 2758 // Vector subscripted arguments are copied in calls, except in elemental 2759 // calls without VALUE attribute where Fortran 2018 15.5.2.4 point 21 2760 // does not apply and the address of each element must be passed. 2761 hlfir::ElementalAddrOp elementalAddr = 2762 Fortran::lower::convertVectorSubscriptedExprToElementalAddr( 2763 loc, callContext.converter, *expr, callContext.symMap, 2764 callContext.stmtCtx); 2765 loweredActuals.emplace_back( 2766 Fortran::lower::PreparedActualArgument{elementalAddr}); 2767 continue; 2768 } 2769 2770 auto loweredActual = Fortran::lower::convertExprToHLFIR( 2771 loc, callContext.converter, *expr, callContext.symMap, 2772 callContext.stmtCtx); 2773 std::optional<mlir::Value> isPresent; 2774 if (arg.isOptional()) 2775 isPresent = genIsPresentIfArgMaybeAbsent( 2776 loc, loweredActual, *expr, callContext, 2777 arg.passBy == 2778 Fortran::lower::CallerInterface::PassEntityBy::MutableBox); 2779 2780 loweredActuals.emplace_back( 2781 Fortran::lower::PreparedActualArgument{loweredActual, isPresent}); 2782 } else { 2783 // Optional dummy argument for which there is no actual argument. 2784 loweredActuals.emplace_back(std::nullopt); 2785 } 2786 if (isElemental) { 2787 bool isImpure = false; 2788 if (const Fortran::semantics::Symbol *procSym = 2789 callContext.procRef.proc().GetSymbol()) 2790 isImpure = !Fortran::semantics::IsPureProcedure(*procSym); 2791 return ElementalUserCallBuilder{caller, callSiteType}.genElementalCall( 2792 loweredActuals, isImpure, callContext); 2793 } 2794 return genUserCall(loweredActuals, caller, callSiteType, callContext); 2795 } 2796 2797 hlfir::Entity Fortran::lower::PreparedActualArgument::getActual( 2798 mlir::Location loc, fir::FirOpBuilder &builder) const { 2799 if (auto *actualEntity = std::get_if<hlfir::Entity>(&actual)) { 2800 if (oneBasedElementalIndices) 2801 return hlfir::getElementAt(loc, builder, *actualEntity, 2802 *oneBasedElementalIndices); 2803 return *actualEntity; 2804 } 2805 assert(oneBasedElementalIndices && "expect elemental context"); 2806 hlfir::ElementalAddrOp elementalAddr = 2807 std::get<hlfir::ElementalAddrOp>(actual); 2808 mlir::IRMapping mapper; 2809 auto alwaysFalse = [](hlfir::ElementalOp) -> bool { return false; }; 2810 mlir::Value addr = hlfir::inlineElementalOp( 2811 loc, builder, elementalAddr, *oneBasedElementalIndices, mapper, 2812 /*mustRecursivelyInline=*/alwaysFalse); 2813 assert(elementalAddr.getCleanup().empty() && "no clean-up expected"); 2814 elementalAddr.erase(); 2815 return hlfir::Entity{addr}; 2816 } 2817 2818 bool Fortran::lower::isIntrinsicModuleProcRef( 2819 const Fortran::evaluate::ProcedureRef &procRef) { 2820 const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol(); 2821 if (!symbol) 2822 return false; 2823 const Fortran::semantics::Symbol *module = 2824 symbol->GetUltimate().owner().GetSymbol(); 2825 return module && module->attrs().test(Fortran::semantics::Attr::INTRINSIC); 2826 } 2827 2828 static bool isInWhereMaskedExpression(fir::FirOpBuilder &builder) { 2829 // The MASK of the outer WHERE is not masked itself. 2830 mlir::Operation *op = builder.getRegion().getParentOp(); 2831 return op && op->getParentOfType<hlfir::WhereOp>(); 2832 } 2833 2834 std::optional<hlfir::EntityWithAttributes> Fortran::lower::convertCallToHLFIR( 2835 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 2836 const evaluate::ProcedureRef &procRef, std::optional<mlir::Type> resultType, 2837 Fortran::lower::SymMap &symMap, Fortran::lower::StatementContext &stmtCtx) { 2838 auto &builder = converter.getFirOpBuilder(); 2839 if (resultType && !procRef.IsElemental() && 2840 isInWhereMaskedExpression(builder) && 2841 !builder.getRegion().getParentOfType<hlfir::ExactlyOnceOp>()) { 2842 // Non elemental calls inside a where-assignment-stmt must be executed 2843 // exactly once without mask control. Lower them in a special region so that 2844 // this can be enforced whenscheduling forall/where expression evaluations. 2845 Fortran::lower::StatementContext localStmtCtx; 2846 mlir::Type bogusType = builder.getIndexType(); 2847 auto exactlyOnce = builder.create<hlfir::ExactlyOnceOp>(loc, bogusType); 2848 mlir::Block *block = builder.createBlock(&exactlyOnce.getBody()); 2849 builder.setInsertionPointToStart(block); 2850 CallContext callContext(procRef, resultType, loc, converter, symMap, 2851 localStmtCtx); 2852 std::optional<hlfir::EntityWithAttributes> res = 2853 genProcedureRef(callContext); 2854 assert(res.has_value() && "must be a function"); 2855 auto yield = builder.create<hlfir::YieldOp>(loc, *res); 2856 Fortran::lower::genCleanUpInRegionIfAny(loc, builder, yield.getCleanup(), 2857 localStmtCtx); 2858 builder.setInsertionPointAfter(exactlyOnce); 2859 exactlyOnce->getResult(0).setType(res->getType()); 2860 if (hlfir::isFortranValue(exactlyOnce.getResult())) 2861 return hlfir::EntityWithAttributes{exactlyOnce.getResult()}; 2862 // Create hlfir.declare for the result to satisfy 2863 // hlfir::EntityWithAttributes requirements. 2864 auto [exv, cleanup] = hlfir::translateToExtendedValue( 2865 loc, builder, hlfir::Entity{exactlyOnce}); 2866 assert(!cleanup && "resut is a variable"); 2867 return hlfir::genDeclare(loc, builder, exv, ".func.pointer.result", 2868 fir::FortranVariableFlagsAttr{}); 2869 } 2870 CallContext callContext(procRef, resultType, loc, converter, symMap, stmtCtx); 2871 return genProcedureRef(callContext); 2872 } 2873 2874 void Fortran::lower::convertUserDefinedAssignmentToHLFIR( 2875 mlir::Location loc, Fortran::lower::AbstractConverter &converter, 2876 const evaluate::ProcedureRef &procRef, hlfir::Entity lhs, hlfir::Entity rhs, 2877 Fortran::lower::SymMap &symMap) { 2878 Fortran::lower::StatementContext definedAssignmentContext; 2879 CallContext callContext(procRef, /*resultType=*/std::nullopt, loc, converter, 2880 symMap, definedAssignmentContext); 2881 Fortran::lower::CallerInterface caller(procRef, converter); 2882 mlir::FunctionType callSiteType = caller.genFunctionType(); 2883 PreparedActualArgument preparedLhs{lhs, /*isPresent=*/std::nullopt}; 2884 PreparedActualArgument preparedRhs{rhs, /*isPresent=*/std::nullopt}; 2885 PreparedActualArguments loweredActuals{preparedLhs, preparedRhs}; 2886 genUserCall(loweredActuals, caller, callSiteType, callContext); 2887 return; 2888 } 2889