1 | //===-- Allocatable.cpp -- Allocatable statements lowering ----------------===// |
---|---|
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/Allocatable.h" |
14 | #include "flang/Evaluate/tools.h" |
15 | #include "flang/Lower/AbstractConverter.h" |
16 | #include "flang/Lower/ConvertType.h" |
17 | #include "flang/Lower/ConvertVariable.h" |
18 | #include "flang/Lower/Cuda.h" |
19 | #include "flang/Lower/IterationSpace.h" |
20 | #include "flang/Lower/Mangler.h" |
21 | #include "flang/Lower/OpenACC.h" |
22 | #include "flang/Lower/PFTBuilder.h" |
23 | #include "flang/Lower/Runtime.h" |
24 | #include "flang/Lower/StatementContext.h" |
25 | #include "flang/Optimizer/Builder/CUFCommon.h" |
26 | #include "flang/Optimizer/Builder/FIRBuilder.h" |
27 | #include "flang/Optimizer/Builder/Runtime/RTBuilder.h" |
28 | #include "flang/Optimizer/Builder/Todo.h" |
29 | #include "flang/Optimizer/Dialect/CUF/CUFOps.h" |
30 | #include "flang/Optimizer/Dialect/FIROps.h" |
31 | #include "flang/Optimizer/Dialect/FIROpsSupport.h" |
32 | #include "flang/Optimizer/HLFIR/HLFIROps.h" |
33 | #include "flang/Optimizer/Support/FatalError.h" |
34 | #include "flang/Optimizer/Support/InternalNames.h" |
35 | #include "flang/Parser/parse-tree.h" |
36 | #include "flang/Runtime/allocatable.h" |
37 | #include "flang/Runtime/pointer.h" |
38 | #include "flang/Semantics/tools.h" |
39 | #include "flang/Semantics/type.h" |
40 | #include "llvm/Support/CommandLine.h" |
41 | |
42 | /// By default fir memory operation fir::AllocMemOp/fir::FreeMemOp are used. |
43 | /// This switch allow forcing the use of runtime and descriptors for everything. |
44 | /// This is mainly intended as a debug switch. |
45 | static llvm::cl::opt<bool> useAllocateRuntime( |
46 | "use-alloc-runtime", |
47 | llvm::cl::desc("Lower allocations to fortran runtime calls"), |
48 | llvm::cl::init(Val: false)); |
49 | /// Switch to force lowering of allocatable and pointers to descriptors in all |
50 | /// cases. This is now turned on by default since that is what will happen with |
51 | /// HLFIR lowering, so this allows getting early feedback of the impact. |
52 | /// If this turns out to cause performance regressions, a dedicated fir.box |
53 | /// "discretization pass" would make more sense to cover all the fir.box usage |
54 | /// (taking advantage of any future inlining for instance). |
55 | static llvm::cl::opt<bool> useDescForMutableBox( |
56 | "use-desc-for-alloc", |
57 | llvm::cl::desc("Always use descriptors for POINTER and ALLOCATABLE"), |
58 | llvm::cl::init(Val: true)); |
59 | |
60 | //===----------------------------------------------------------------------===// |
61 | // Error management |
62 | //===----------------------------------------------------------------------===// |
63 | |
64 | namespace { |
65 | // Manage STAT and ERRMSG specifier information across a sequence of runtime |
66 | // calls for an ALLOCATE/DEALLOCATE stmt. |
67 | struct ErrorManager { |
68 | void init(Fortran::lower::AbstractConverter &converter, mlir::Location loc, |
69 | const Fortran::lower::SomeExpr *statExpr, |
70 | const Fortran::lower::SomeExpr *errMsgExpr) { |
71 | Fortran::lower::StatementContext stmtCtx; |
72 | fir::FirOpBuilder &builder = converter.getFirOpBuilder(); |
73 | hasStat = builder.createBool(loc, statExpr != nullptr); |
74 | statAddr = statExpr |
75 | ? fir::getBase(converter.genExprAddr(loc, statExpr, stmtCtx)) |
76 | : mlir::Value{}; |
77 | errMsgAddr = |
78 | statExpr && errMsgExpr |
79 | ? builder.createBox(loc, |
80 | converter.genExprAddr(loc, errMsgExpr, stmtCtx)) |
81 | : builder.create<fir::AbsentOp>( |
82 | loc, |
83 | fir::BoxType::get(mlir::NoneType::get(builder.getContext()))); |
84 | sourceFile = fir::factory::locationToFilename(builder, loc); |
85 | sourceLine = fir::factory::locationToLineNo(builder, loc, |
86 | builder.getIntegerType(32)); |
87 | } |
88 | |
89 | bool hasStatSpec() const { return static_cast<bool>(statAddr); } |
90 | |
91 | void genStatCheck(fir::FirOpBuilder &builder, mlir::Location loc) { |
92 | if (statValue) { |
93 | mlir::Value zero = |
94 | builder.createIntegerConstant(loc, statValue.getType(), 0); |
95 | auto cmp = builder.create<mlir::arith::CmpIOp>( |
96 | loc, mlir::arith::CmpIPredicate::eq, statValue, zero); |
97 | auto ifOp = builder.create<fir::IfOp>(loc, cmp, |
98 | /*withElseRegion=*/false); |
99 | builder.setInsertionPointToStart(&ifOp.getThenRegion().front()); |
100 | } |
101 | } |
102 | |
103 | void assignStat(fir::FirOpBuilder &builder, mlir::Location loc, |
104 | mlir::Value stat) { |
105 | if (hasStatSpec()) { |
106 | assert(stat && "missing stat value"); |
107 | mlir::Value castStat = builder.createConvert( |
108 | loc, fir::dyn_cast_ptrEleTy(statAddr.getType()), stat); |
109 | builder.create<fir::StoreOp>(loc, castStat, statAddr); |
110 | statValue = stat; |
111 | } |
112 | } |
113 | |
114 | mlir::Value hasStat; |
115 | mlir::Value errMsgAddr; |
116 | mlir::Value sourceFile; |
117 | mlir::Value sourceLine; |
118 | |
119 | private: |
120 | mlir::Value statAddr; // STAT variable address |
121 | mlir::Value statValue; // current runtime STAT value |
122 | }; |
123 | |
124 | //===----------------------------------------------------------------------===// |
125 | // Allocatables runtime call generators |
126 | //===----------------------------------------------------------------------===// |
127 | |
128 | using namespace Fortran::runtime; |
129 | /// Generate a runtime call to set the bounds of an allocatable or pointer |
130 | /// descriptor. |
131 | static void genRuntimeSetBounds(fir::FirOpBuilder &builder, mlir::Location loc, |
132 | const fir::MutableBoxValue &box, |
133 | mlir::Value dimIndex, mlir::Value lowerBound, |
134 | mlir::Value upperBound) { |
135 | mlir::func::FuncOp callee = |
136 | box.isPointer() |
137 | ? fir::runtime::getRuntimeFunc<mkRTKey(PointerSetBounds)>(loc, |
138 | builder) |
139 | : fir::runtime::getRuntimeFunc<mkRTKey(AllocatableSetBounds)>( |
140 | loc, builder); |
141 | const auto args = fir::runtime::createArguments( |
142 | builder, loc, callee.getFunctionType(), box.getAddr(), dimIndex, |
143 | lowerBound, upperBound); |
144 | builder.create<fir::CallOp>(loc, callee, args); |
145 | } |
146 | |
147 | /// Generate runtime call to set the lengths of a character allocatable or |
148 | /// pointer descriptor. |
149 | static void genRuntimeInitCharacter(fir::FirOpBuilder &builder, |
150 | mlir::Location loc, |
151 | const fir::MutableBoxValue &box, |
152 | mlir::Value len, int64_t kind = 0) { |
153 | mlir::func::FuncOp callee = |
154 | box.isPointer() |
155 | ? fir::runtime::getRuntimeFunc<mkRTKey(PointerNullifyCharacter)>( |
156 | loc, builder) |
157 | : fir::runtime::getRuntimeFunc<mkRTKey( |
158 | AllocatableInitCharacterForAllocate)>(loc, builder); |
159 | llvm::ArrayRef<mlir::Type> inputTypes = callee.getFunctionType().getInputs(); |
160 | if (inputTypes.size() != 5) |
161 | fir::emitFatalError( |
162 | loc, "AllocatableInitCharacter runtime interface not as expected"); |
163 | llvm::SmallVector<mlir::Value> args = {box.getAddr(), len}; |
164 | if (kind == 0) |
165 | kind = mlir::cast<fir::CharacterType>(box.getEleTy()).getFKind(); |
166 | args.push_back(builder.createIntegerConstant(loc, inputTypes[2], kind)); |
167 | int rank = box.rank(); |
168 | args.push_back(builder.createIntegerConstant(loc, inputTypes[3], rank)); |
169 | // TODO: coarrays |
170 | int corank = 0; |
171 | args.push_back(builder.createIntegerConstant(loc, inputTypes[4], corank)); |
172 | const auto convertedArgs = fir::runtime::createArguments( |
173 | builder, loc, callee.getFunctionType(), args); |
174 | builder.create<fir::CallOp>(loc, callee, convertedArgs); |
175 | } |
176 | |
177 | /// Generate a sequence of runtime calls to allocate memory. |
178 | static mlir::Value genRuntimeAllocate(fir::FirOpBuilder &builder, |
179 | mlir::Location loc, |
180 | const fir::MutableBoxValue &box, |
181 | ErrorManager &errorManager) { |
182 | mlir::func::FuncOp callee = |
183 | box.isPointer() |
184 | ? fir::runtime::getRuntimeFunc<mkRTKey(PointerAllocate)>(loc, builder) |
185 | : fir::runtime::getRuntimeFunc<mkRTKey(AllocatableAllocate)>(loc, |
186 | builder); |
187 | llvm::SmallVector<mlir::Value> args{box.getAddr()}; |
188 | if (!box.isPointer()) |
189 | args.push_back( |
190 | builder.createIntegerConstant(loc, builder.getI64Type(), -1)); |
191 | args.push_back(errorManager.hasStat); |
192 | args.push_back(errorManager.errMsgAddr); |
193 | args.push_back(errorManager.sourceFile); |
194 | args.push_back(errorManager.sourceLine); |
195 | const auto convertedArgs = fir::runtime::createArguments( |
196 | builder, loc, callee.getFunctionType(), args); |
197 | return builder.create<fir::CallOp>(loc, callee, convertedArgs).getResult(0); |
198 | } |
199 | |
200 | /// Generate a sequence of runtime calls to allocate memory and assign with the |
201 | /// \p source. |
202 | static mlir::Value genRuntimeAllocateSource(fir::FirOpBuilder &builder, |
203 | mlir::Location loc, |
204 | const fir::MutableBoxValue &box, |
205 | fir::ExtendedValue source, |
206 | ErrorManager &errorManager) { |
207 | mlir::func::FuncOp callee = |
208 | box.isPointer() |
209 | ? fir::runtime::getRuntimeFunc<mkRTKey(PointerAllocateSource)>( |
210 | loc, builder) |
211 | : fir::runtime::getRuntimeFunc<mkRTKey(AllocatableAllocateSource)>( |
212 | loc, builder); |
213 | const auto args = fir::runtime::createArguments( |
214 | builder, loc, callee.getFunctionType(), box.getAddr(), |
215 | fir::getBase(source), errorManager.hasStat, errorManager.errMsgAddr, |
216 | errorManager.sourceFile, errorManager.sourceLine); |
217 | return builder.create<fir::CallOp>(loc, callee, args).getResult(0); |
218 | } |
219 | |
220 | /// Generate runtime call to apply mold to the descriptor. |
221 | static void genRuntimeAllocateApplyMold(fir::FirOpBuilder &builder, |
222 | mlir::Location loc, |
223 | const fir::MutableBoxValue &box, |
224 | fir::ExtendedValue mold, int rank) { |
225 | mlir::func::FuncOp callee = |
226 | box.isPointer() |
227 | ? fir::runtime::getRuntimeFunc<mkRTKey(PointerApplyMold)>(loc, |
228 | builder) |
229 | : fir::runtime::getRuntimeFunc<mkRTKey(AllocatableApplyMold)>( |
230 | loc, builder); |
231 | const auto args = fir::runtime::createArguments( |
232 | builder, loc, callee.getFunctionType(), |
233 | fir::factory::getMutableIRBox(builder, loc, box), fir::getBase(mold), |
234 | builder.createIntegerConstant( |
235 | loc, callee.getFunctionType().getInputs()[2], rank)); |
236 | builder.create<fir::CallOp>(loc, callee, args); |
237 | } |
238 | |
239 | /// Generate a runtime call to deallocate memory. |
240 | static mlir::Value genRuntimeDeallocate(fir::FirOpBuilder &builder, |
241 | mlir::Location loc, |
242 | const fir::MutableBoxValue &box, |
243 | ErrorManager &errorManager, |
244 | mlir::Value declaredTypeDesc = {}) { |
245 | // Ensure fir.box is up-to-date before passing it to deallocate runtime. |
246 | mlir::Value boxAddress = fir::factory::getMutableIRBox(builder, loc, box); |
247 | mlir::func::FuncOp callee; |
248 | llvm::SmallVector<mlir::Value> args; |
249 | llvm::SmallVector<mlir::Value> operands; |
250 | if (box.isPolymorphic() || box.isUnlimitedPolymorphic()) { |
251 | callee = box.isPointer() |
252 | ? fir::runtime::getRuntimeFunc<mkRTKey( |
253 | PointerDeallocatePolymorphic)>(loc, builder) |
254 | : fir::runtime::getRuntimeFunc<mkRTKey( |
255 | AllocatableDeallocatePolymorphic)>(loc, builder); |
256 | if (!declaredTypeDesc) |
257 | declaredTypeDesc = builder.createNullConstant(loc); |
258 | operands = fir::runtime::createArguments( |
259 | builder, loc, callee.getFunctionType(), boxAddress, declaredTypeDesc, |
260 | errorManager.hasStat, errorManager.errMsgAddr, errorManager.sourceFile, |
261 | errorManager.sourceLine); |
262 | } else { |
263 | callee = box.isPointer() |
264 | ? fir::runtime::getRuntimeFunc<mkRTKey(PointerDeallocate)>( |
265 | loc, builder) |
266 | : fir::runtime::getRuntimeFunc<mkRTKey(AllocatableDeallocate)>( |
267 | loc, builder); |
268 | operands = fir::runtime::createArguments( |
269 | builder, loc, callee.getFunctionType(), boxAddress, |
270 | errorManager.hasStat, errorManager.errMsgAddr, errorManager.sourceFile, |
271 | errorManager.sourceLine); |
272 | } |
273 | return builder.create<fir::CallOp>(loc, callee, operands).getResult(0); |
274 | } |
275 | |
276 | //===----------------------------------------------------------------------===// |
277 | // Allocate statement implementation |
278 | //===----------------------------------------------------------------------===// |
279 | |
280 | /// Helper to get symbol from AllocateObject. |
281 | static const Fortran::semantics::Symbol & |
282 | unwrapSymbol(const Fortran::parser::AllocateObject &allocObj) { |
283 | const Fortran::parser::Name &lastName = |
284 | Fortran::parser::GetLastName(allocObj); |
285 | assert(lastName.symbol); |
286 | return *lastName.symbol; |
287 | } |
288 | |
289 | static fir::MutableBoxValue |
290 | genMutableBoxValue(Fortran::lower::AbstractConverter &converter, |
291 | mlir::Location loc, |
292 | const Fortran::parser::AllocateObject &allocObj) { |
293 | const Fortran::lower::SomeExpr *expr = Fortran::semantics::GetExpr(allocObj); |
294 | assert(expr && "semantic analysis failure"); |
295 | return converter.genExprMutableBox(loc, *expr); |
296 | } |
297 | |
298 | /// Implement Allocate statement lowering. |
299 | class AllocateStmtHelper { |
300 | public: |
301 | AllocateStmtHelper(Fortran::lower::AbstractConverter &converter, |
302 | const Fortran::parser::AllocateStmt &stmt, |
303 | mlir::Location loc) |
304 | : converter{converter}, builder{converter.getFirOpBuilder()}, stmt{stmt}, |
305 | loc{loc} {} |
306 | |
307 | void lower() { |
308 | visitAllocateOptions(); |
309 | lowerAllocateLengthParameters(); |
310 | errorManager.init(converter, loc, statExpr, errMsgExpr); |
311 | Fortran::lower::StatementContext stmtCtx; |
312 | if (sourceExpr) |
313 | sourceExv = converter.genExprBox(loc, *sourceExpr, stmtCtx); |
314 | if (moldExpr) |
315 | moldExv = converter.genExprBox(loc, *moldExpr, stmtCtx); |
316 | mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint(); |
317 | for (const auto &allocation : |
318 | std::get<std::list<Fortran::parser::Allocation>>(stmt.t)) |
319 | lowerAllocation(unwrapAllocation(allocation)); |
320 | builder.restoreInsertionPoint(insertPt); |
321 | } |
322 | |
323 | private: |
324 | struct Allocation { |
325 | const Fortran::parser::Allocation &alloc; |
326 | const Fortran::semantics::DeclTypeSpec &type; |
327 | bool hasCoarraySpec() const { |
328 | return std::get<std::optional<Fortran::parser::AllocateCoarraySpec>>( |
329 | alloc.t) |
330 | .has_value(); |
331 | } |
332 | const Fortran::parser::AllocateObject &getAllocObj() const { |
333 | return std::get<Fortran::parser::AllocateObject>(alloc.t); |
334 | } |
335 | const Fortran::semantics::Symbol &getSymbol() const { |
336 | return unwrapSymbol(getAllocObj()); |
337 | } |
338 | const std::list<Fortran::parser::AllocateShapeSpec> &getShapeSpecs() const { |
339 | return std::get<std::list<Fortran::parser::AllocateShapeSpec>>(alloc.t); |
340 | } |
341 | }; |
342 | |
343 | Allocation unwrapAllocation(const Fortran::parser::Allocation &alloc) { |
344 | const auto &allocObj = std::get<Fortran::parser::AllocateObject>(alloc.t); |
345 | const Fortran::semantics::Symbol &symbol = unwrapSymbol(allocObj); |
346 | assert(symbol.GetType()); |
347 | return Allocation{alloc, *symbol.GetType()}; |
348 | } |
349 | |
350 | void visitAllocateOptions() { |
351 | for (const auto &allocOption : |
352 | std::get<std::list<Fortran::parser::AllocOpt>>(stmt.t)) |
353 | Fortran::common::visit( |
354 | Fortran::common::visitors{ |
355 | [&](const Fortran::parser::StatOrErrmsg &statOrErr) { |
356 | Fortran::common::visit( |
357 | Fortran::common::visitors{ |
358 | [&](const Fortran::parser::StatVariable &statVar) { |
359 | statExpr = Fortran::semantics::GetExpr(statVar); |
360 | }, |
361 | [&](const Fortran::parser::MsgVariable &errMsgVar) { |
362 | errMsgExpr = Fortran::semantics::GetExpr(errMsgVar); |
363 | }, |
364 | }, |
365 | statOrErr.u); |
366 | }, |
367 | [&](const Fortran::parser::AllocOpt::Source &source) { |
368 | sourceExpr = Fortran::semantics::GetExpr(source.v.value()); |
369 | }, |
370 | [&](const Fortran::parser::AllocOpt::Mold &mold) { |
371 | moldExpr = Fortran::semantics::GetExpr(mold.v.value()); |
372 | }, |
373 | [&](const Fortran::parser::AllocOpt::Stream &stream) { |
374 | streamExpr = Fortran::semantics::GetExpr(stream.v.value()); |
375 | }, |
376 | [&](const Fortran::parser::AllocOpt::Pinned &pinned) { |
377 | pinnedExpr = Fortran::semantics::GetExpr(pinned.v.value()); |
378 | }, |
379 | }, |
380 | allocOption.u); |
381 | } |
382 | |
383 | void lowerAllocation(const Allocation &alloc) { |
384 | fir::MutableBoxValue boxAddr = |
385 | genMutableBoxValue(converter, loc, alloc.getAllocObj()); |
386 | |
387 | if (sourceExpr) |
388 | genSourceMoldAllocation(alloc, boxAddr, /*isSource=*/true); |
389 | else if (moldExpr) |
390 | genSourceMoldAllocation(alloc, boxAddr, /*isSource=*/false); |
391 | else |
392 | genSimpleAllocation(alloc, boxAddr); |
393 | } |
394 | |
395 | static bool lowerBoundsAreOnes(const Allocation &alloc) { |
396 | for (const Fortran::parser::AllocateShapeSpec &shapeSpec : |
397 | alloc.getShapeSpecs()) |
398 | if (std::get<0>(shapeSpec.t)) |
399 | return false; |
400 | return true; |
401 | } |
402 | |
403 | /// Build name for the fir::allocmem generated for alloc. |
404 | std::string mangleAlloc(const Allocation &alloc) { |
405 | return converter.mangleName(alloc.getSymbol()) + ".alloc"; |
406 | } |
407 | |
408 | /// Generate allocation without runtime calls. |
409 | /// Only for intrinsic types. No coarrays, no polymorphism. No error recovery. |
410 | void genInlinedAllocation(const Allocation &alloc, |
411 | const fir::MutableBoxValue &box) { |
412 | llvm::SmallVector<mlir::Value> lbounds; |
413 | llvm::SmallVector<mlir::Value> extents; |
414 | Fortran::lower::StatementContext stmtCtx; |
415 | mlir::Type idxTy = builder.getIndexType(); |
416 | bool lBoundsAreOnes = lowerBoundsAreOnes(alloc); |
417 | mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1); |
418 | for (const Fortran::parser::AllocateShapeSpec &shapeSpec : |
419 | alloc.getShapeSpecs()) { |
420 | mlir::Value lb; |
421 | if (!lBoundsAreOnes) { |
422 | if (const std::optional<Fortran::parser::BoundExpr> &lbExpr = |
423 | std::get<0>(shapeSpec.t)) { |
424 | lb = fir::getBase(converter.genExprValue( |
425 | loc, Fortran::semantics::GetExpr(*lbExpr), stmtCtx)); |
426 | lb = builder.createConvert(loc, idxTy, lb); |
427 | } else { |
428 | lb = one; |
429 | } |
430 | lbounds.emplace_back(lb); |
431 | } |
432 | mlir::Value ub = fir::getBase(converter.genExprValue( |
433 | loc, Fortran::semantics::GetExpr(std::get<1>(shapeSpec.t)), stmtCtx)); |
434 | ub = builder.createConvert(loc, idxTy, ub); |
435 | if (lb) { |
436 | mlir::Value diff = builder.create<mlir::arith::SubIOp>(loc, ub, lb); |
437 | extents.emplace_back( |
438 | builder.create<mlir::arith::AddIOp>(loc, diff, one)); |
439 | } else { |
440 | extents.emplace_back(ub); |
441 | } |
442 | } |
443 | fir::factory::genInlinedAllocation(builder, loc, box, lbounds, extents, |
444 | lenParams, mangleAlloc(alloc), |
445 | /*mustBeHeap=*/true); |
446 | } |
447 | |
448 | void postAllocationAction(const Allocation &alloc) { |
449 | if (alloc.getSymbol().test(Fortran::semantics::Symbol::Flag::AccDeclare)) |
450 | Fortran::lower::attachDeclarePostAllocAction(converter, builder, |
451 | alloc.getSymbol()); |
452 | } |
453 | |
454 | void setPinnedToFalse() { |
455 | if (!pinnedExpr) |
456 | return; |
457 | Fortran::lower::StatementContext stmtCtx; |
458 | mlir::Value pinned = |
459 | fir::getBase(converter.genExprAddr(loc, *pinnedExpr, stmtCtx)); |
460 | mlir::Location loc = pinned.getLoc(); |
461 | mlir::Value falseValue = builder.createBool(loc, false); |
462 | mlir::Value falseConv = builder.createConvert( |
463 | loc, fir::unwrapRefType(pinned.getType()), falseValue); |
464 | builder.create<fir::StoreOp>(loc, falseConv, pinned); |
465 | } |
466 | |
467 | void genSimpleAllocation(const Allocation &alloc, |
468 | const fir::MutableBoxValue &box) { |
469 | bool isCudaSymbol = Fortran::semantics::HasCUDAAttr(alloc.getSymbol()); |
470 | bool isCudaDeviceContext = cuf::isCUDADeviceContext(builder.getRegion()); |
471 | bool inlineAllocation = !box.isDerived() && !errorManager.hasStatSpec() && |
472 | !alloc.type.IsPolymorphic() && |
473 | !alloc.hasCoarraySpec() && !useAllocateRuntime && |
474 | !box.isPointer(); |
475 | unsigned allocatorIdx = Fortran::lower::getAllocatorIdx(alloc.getSymbol()); |
476 | |
477 | if (inlineAllocation && |
478 | ((isCudaSymbol && isCudaDeviceContext) || !isCudaSymbol)) { |
479 | // Pointers must use PointerAllocate so that their deallocations |
480 | // can be validated. |
481 | genInlinedAllocation(alloc, box); |
482 | postAllocationAction(alloc); |
483 | setPinnedToFalse(); |
484 | return; |
485 | } |
486 | |
487 | // Generate a sequence of runtime calls. |
488 | errorManager.genStatCheck(builder, loc); |
489 | genAllocateObjectInit(box, allocatorIdx); |
490 | if (alloc.hasCoarraySpec()) |
491 | TODO(loc, "coarray: allocation of a coarray object"); |
492 | if (alloc.type.IsPolymorphic()) |
493 | genSetType(alloc, box, loc); |
494 | genSetDeferredLengthParameters(alloc, box); |
495 | genAllocateObjectBounds(alloc, box); |
496 | mlir::Value stat; |
497 | if (!isCudaSymbol) { |
498 | stat = genRuntimeAllocate(builder, loc, box, errorManager); |
499 | setPinnedToFalse(); |
500 | } else { |
501 | stat = |
502 | genCudaAllocate(builder, loc, box, errorManager, alloc.getSymbol()); |
503 | } |
504 | fir::factory::syncMutableBoxFromIRBox(builder, loc, box); |
505 | postAllocationAction(alloc); |
506 | errorManager.assignStat(builder, loc, stat); |
507 | } |
508 | |
509 | /// Lower the length parameters that may be specified in the optional |
510 | /// type specification. |
511 | void lowerAllocateLengthParameters() { |
512 | const Fortran::semantics::DeclTypeSpec *typeSpec = |
513 | getIfAllocateStmtTypeSpec(); |
514 | if (!typeSpec) |
515 | return; |
516 | if (const Fortran::semantics::DerivedTypeSpec *derived = |
517 | typeSpec->AsDerived()) |
518 | if (Fortran::semantics::CountLenParameters(*derived) > 0) |
519 | TODO(loc, "setting derived type params in allocation"); |
520 | if (typeSpec->category() == |
521 | Fortran::semantics::DeclTypeSpec::Category::Character) { |
522 | Fortran::semantics::ParamValue lenParam = |
523 | typeSpec->characterTypeSpec().length(); |
524 | if (Fortran::semantics::MaybeIntExpr intExpr = lenParam.GetExplicit()) { |
525 | Fortran::lower::StatementContext stmtCtx; |
526 | Fortran::lower::SomeExpr lenExpr{*intExpr}; |
527 | lenParams.push_back( |
528 | fir::getBase(converter.genExprValue(loc, lenExpr, stmtCtx))); |
529 | } |
530 | } |
531 | } |
532 | |
533 | // Set length parameters in the box stored in boxAddr. |
534 | // This must be called before setting the bounds because it may use |
535 | // Init runtime calls that may set the bounds to zero. |
536 | void genSetDeferredLengthParameters(const Allocation &alloc, |
537 | const fir::MutableBoxValue &box) { |
538 | if (lenParams.empty()) |
539 | return; |
540 | // TODO: in case a length parameter was not deferred, insert a runtime check |
541 | // that the length is the same (AllocatableCheckLengthParameter runtime |
542 | // call). |
543 | if (box.isCharacter()) |
544 | genRuntimeInitCharacter(builder, loc, box, lenParams[0]); |
545 | |
546 | if (box.isDerived()) |
547 | TODO(loc, "derived type length parameters in allocate"); |
548 | } |
549 | |
550 | void genAllocateObjectInit(const fir::MutableBoxValue &box, |
551 | unsigned allocatorIdx) { |
552 | if (box.isPointer()) { |
553 | // For pointers, the descriptor may still be uninitialized (see Fortran |
554 | // 2018 19.5.2.2). The allocation runtime needs to be given a descriptor |
555 | // with initialized rank, types and attributes. Initialize the descriptor |
556 | // here to ensure these constraints are fulfilled. |
557 | mlir::Value nullPointer = fir::factory::createUnallocatedBox( |
558 | builder, loc, box.getBoxTy(), box.nonDeferredLenParams(), |
559 | /*typeSourceBox=*/{}, allocatorIdx); |
560 | builder.create<fir::StoreOp>(loc, nullPointer, box.getAddr()); |
561 | } else { |
562 | assert(box.isAllocatable() && "must be an allocatable"); |
563 | // For allocatables, sync the MutableBoxValue and descriptor before the |
564 | // calls in case it is tracked locally by a set of variables. |
565 | fir::factory::getMutableIRBox(builder, loc, box); |
566 | } |
567 | } |
568 | |
569 | void genAllocateObjectBounds(const Allocation &alloc, |
570 | const fir::MutableBoxValue &box) { |
571 | // Set bounds for arrays |
572 | mlir::Type idxTy = builder.getIndexType(); |
573 | mlir::Type i32Ty = builder.getIntegerType(32); |
574 | Fortran::lower::StatementContext stmtCtx; |
575 | for (const auto &iter : llvm::enumerate(alloc.getShapeSpecs())) { |
576 | mlir::Value lb; |
577 | const auto &bounds = iter.value().t; |
578 | if (const std::optional<Fortran::parser::BoundExpr> &lbExpr = |
579 | std::get<0>(bounds)) |
580 | lb = fir::getBase(converter.genExprValue( |
581 | loc, Fortran::semantics::GetExpr(*lbExpr), stmtCtx)); |
582 | else |
583 | lb = builder.createIntegerConstant(loc, idxTy, 1); |
584 | mlir::Value ub = fir::getBase(converter.genExprValue( |
585 | loc, Fortran::semantics::GetExpr(std::get<1>(bounds)), stmtCtx)); |
586 | mlir::Value dimIndex = |
587 | builder.createIntegerConstant(loc, i32Ty, iter.index()); |
588 | // Runtime call |
589 | genRuntimeSetBounds(builder, loc, box, dimIndex, lb, ub); |
590 | } |
591 | if (sourceExpr && sourceExpr->Rank() > 0 && |
592 | alloc.getShapeSpecs().size() == 0) { |
593 | // If the alloc object does not have shape list, get the bounds from the |
594 | // source expression. |
595 | mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1); |
596 | const auto *sourceBox = sourceExv.getBoxOf<fir::BoxValue>(); |
597 | assert(sourceBox && "source expression should be lowered to one box"); |
598 | for (int i = 0; i < sourceExpr->Rank(); ++i) { |
599 | auto dimVal = builder.createIntegerConstant(loc, idxTy, i); |
600 | auto dimInfo = builder.create<fir::BoxDimsOp>( |
601 | loc, idxTy, idxTy, idxTy, sourceBox->getAddr(), dimVal); |
602 | mlir::Value lb = |
603 | fir::factory::readLowerBound(builder, loc, sourceExv, i, one); |
604 | mlir::Value extent = dimInfo.getResult(1); |
605 | mlir::Value ub = builder.create<mlir::arith::SubIOp>( |
606 | loc, builder.create<mlir::arith::AddIOp>(loc, extent, lb), one); |
607 | mlir::Value dimIndex = builder.createIntegerConstant(loc, i32Ty, i); |
608 | genRuntimeSetBounds(builder, loc, box, dimIndex, lb, ub); |
609 | } |
610 | } |
611 | } |
612 | |
613 | void genSourceMoldAllocation(const Allocation &alloc, |
614 | const fir::MutableBoxValue &box, bool isSource) { |
615 | unsigned allocatorIdx = Fortran::lower::getAllocatorIdx(alloc.getSymbol()); |
616 | fir::ExtendedValue exv = isSource ? sourceExv : moldExv; |
617 | |
618 | // Generate a sequence of runtime calls. |
619 | errorManager.genStatCheck(builder, loc); |
620 | genAllocateObjectInit(box, allocatorIdx); |
621 | if (alloc.hasCoarraySpec()) |
622 | TODO(loc, "coarray: allocation of a coarray object"); |
623 | // Set length of the allocate object if it has. Otherwise, get the length |
624 | // from source for the deferred length parameter. |
625 | const bool isDeferredLengthCharacter = |
626 | box.isCharacter() && !box.hasNonDeferredLenParams(); |
627 | if (lenParams.empty() && isDeferredLengthCharacter) |
628 | lenParams.push_back(fir::factory::readCharLen(builder, loc, exv)); |
629 | if (!isSource || alloc.type.IsPolymorphic()) |
630 | genRuntimeAllocateApplyMold(builder, loc, box, exv, |
631 | alloc.getSymbol().Rank()); |
632 | if (isDeferredLengthCharacter) |
633 | genSetDeferredLengthParameters(alloc, box); |
634 | genAllocateObjectBounds(alloc, box); |
635 | mlir::Value stat; |
636 | if (Fortran::semantics::HasCUDAAttr(alloc.getSymbol())) { |
637 | stat = |
638 | genCudaAllocate(builder, loc, box, errorManager, alloc.getSymbol()); |
639 | } else { |
640 | if (isSource) |
641 | stat = genRuntimeAllocateSource(builder, loc, box, exv, errorManager); |
642 | else |
643 | stat = genRuntimeAllocate(builder, loc, box, errorManager); |
644 | setPinnedToFalse(); |
645 | } |
646 | fir::factory::syncMutableBoxFromIRBox(builder, loc, box); |
647 | postAllocationAction(alloc); |
648 | errorManager.assignStat(builder, loc, stat); |
649 | } |
650 | |
651 | /// Generate call to PointerNullifyDerived or AllocatableInitDerived |
652 | /// to set the dynamic type information. |
653 | void genInitDerived(const fir::MutableBoxValue &box, mlir::Value typeDescAddr, |
654 | int rank, int corank = 0) { |
655 | mlir::func::FuncOp callee = |
656 | box.isPointer() |
657 | ? fir::runtime::getRuntimeFunc<mkRTKey(PointerNullifyDerived)>( |
658 | loc, builder) |
659 | : fir::runtime::getRuntimeFunc<mkRTKey( |
660 | AllocatableInitDerivedForAllocate)>(loc, builder); |
661 | |
662 | llvm::ArrayRef<mlir::Type> inputTypes = |
663 | callee.getFunctionType().getInputs(); |
664 | mlir::Value rankValue = |
665 | builder.createIntegerConstant(loc, inputTypes[2], rank); |
666 | mlir::Value corankValue = |
667 | builder.createIntegerConstant(loc, inputTypes[3], corank); |
668 | const auto args = fir::runtime::createArguments( |
669 | builder, loc, callee.getFunctionType(), box.getAddr(), typeDescAddr, |
670 | rankValue, corankValue); |
671 | builder.create<fir::CallOp>(loc, callee, args); |
672 | } |
673 | |
674 | /// Generate call to PointerNullifyIntrinsic or AllocatableInitIntrinsic to |
675 | /// set the dynamic type information for a polymorphic entity from an |
676 | /// intrinsic type spec. |
677 | void genInitIntrinsic(const fir::MutableBoxValue &box, |
678 | const TypeCategory category, int64_t kind, int rank, |
679 | int corank = 0) { |
680 | mlir::func::FuncOp callee = |
681 | box.isPointer() |
682 | ? fir::runtime::getRuntimeFunc<mkRTKey(PointerNullifyIntrinsic)>( |
683 | loc, builder) |
684 | : fir::runtime::getRuntimeFunc<mkRTKey( |
685 | AllocatableInitIntrinsicForAllocate)>(loc, builder); |
686 | |
687 | llvm::ArrayRef<mlir::Type> inputTypes = |
688 | callee.getFunctionType().getInputs(); |
689 | mlir::Value categoryValue = builder.createIntegerConstant( |
690 | loc, inputTypes[1], static_cast<int32_t>(category)); |
691 | mlir::Value kindValue = |
692 | builder.createIntegerConstant(loc, inputTypes[2], kind); |
693 | mlir::Value rankValue = |
694 | builder.createIntegerConstant(loc, inputTypes[3], rank); |
695 | mlir::Value corankValue = |
696 | builder.createIntegerConstant(loc, inputTypes[4], corank); |
697 | const auto args = fir::runtime::createArguments( |
698 | builder, loc, callee.getFunctionType(), box.getAddr(), categoryValue, |
699 | kindValue, rankValue, corankValue); |
700 | builder.create<fir::CallOp>(loc, callee, args); |
701 | } |
702 | |
703 | /// Generate call to the AllocatableInitDerived to set up the type descriptor |
704 | /// and other part of the descriptor for derived type. |
705 | void genSetType(const Allocation &alloc, const fir::MutableBoxValue &box, |
706 | mlir::Location loc) { |
707 | const Fortran::semantics::DeclTypeSpec *typeSpec = |
708 | getIfAllocateStmtTypeSpec(); |
709 | |
710 | // No type spec provided in allocate statement so the declared type spec is |
711 | // used. |
712 | if (!typeSpec) |
713 | typeSpec = &alloc.type; |
714 | assert(typeSpec && "type spec missing for polymorphic allocation"); |
715 | |
716 | // Set up the descriptor for allocation for intrinsic type spec on |
717 | // unlimited polymorphic entity. |
718 | if (typeSpec->AsIntrinsic() && |
719 | fir::isUnlimitedPolymorphicType(fir::getBase(box).getType())) { |
720 | if (typeSpec->AsIntrinsic()->category() == TypeCategory::Character) { |
721 | genRuntimeInitCharacter( |
722 | builder, loc, box, lenParams[0], |
723 | Fortran::evaluate::ToInt64(typeSpec->AsIntrinsic()->kind()) |
724 | .value()); |
725 | } else { |
726 | genInitIntrinsic( |
727 | box, typeSpec->AsIntrinsic()->category(), |
728 | Fortran::evaluate::ToInt64(typeSpec->AsIntrinsic()->kind()).value(), |
729 | alloc.getSymbol().Rank()); |
730 | } |
731 | return; |
732 | } |
733 | |
734 | // Do not generate calls for non derived-type type spec. |
735 | if (!typeSpec->AsDerived()) |
736 | return; |
737 | |
738 | auto typeDescAddr = Fortran::lower::getTypeDescAddr( |
739 | converter, loc, typeSpec->derivedTypeSpec()); |
740 | genInitDerived(box, typeDescAddr, alloc.getSymbol().Rank()); |
741 | } |
742 | |
743 | /// Returns a pointer to the DeclTypeSpec if a type-spec is provided in the |
744 | /// allocate statement. Returns a null pointer otherwise. |
745 | const Fortran::semantics::DeclTypeSpec *getIfAllocateStmtTypeSpec() const { |
746 | if (const auto &typeSpec = |
747 | std::get<std::optional<Fortran::parser::TypeSpec>>(stmt.t)) |
748 | return typeSpec->declTypeSpec; |
749 | return nullptr; |
750 | } |
751 | |
752 | mlir::Value genCudaAllocate(fir::FirOpBuilder &builder, mlir::Location loc, |
753 | const fir::MutableBoxValue &box, |
754 | ErrorManager &errorManager, |
755 | const Fortran::semantics::Symbol &sym) { |
756 | Fortran::lower::StatementContext stmtCtx; |
757 | cuf::DataAttributeAttr cudaAttr = |
758 | Fortran::lower::translateSymbolCUFDataAttribute(builder.getContext(), |
759 | sym); |
760 | mlir::Value errmsg = errMsgExpr ? errorManager.errMsgAddr : nullptr; |
761 | mlir::Value stream = |
762 | streamExpr |
763 | ? fir::getBase(converter.genExprAddr(loc, *streamExpr, stmtCtx)) |
764 | : nullptr; |
765 | mlir::Value pinned = |
766 | pinnedExpr |
767 | ? fir::getBase(converter.genExprAddr(loc, *pinnedExpr, stmtCtx)) |
768 | : nullptr; |
769 | mlir::Value source = sourceExpr ? fir::getBase(sourceExv) : nullptr; |
770 | |
771 | // Keep return type the same as a standard AllocatableAllocate call. |
772 | mlir::Type retTy = fir::runtime::getModel<int>()(builder.getContext()); |
773 | return builder |
774 | .create<cuf::AllocateOp>( |
775 | loc, retTy, box.getAddr(), errmsg, stream, pinned, source, cudaAttr, |
776 | errorManager.hasStatSpec() ? builder.getUnitAttr() : nullptr) |
777 | .getResult(); |
778 | } |
779 | |
780 | Fortran::lower::AbstractConverter &converter; |
781 | fir::FirOpBuilder &builder; |
782 | const Fortran::parser::AllocateStmt &stmt; |
783 | const Fortran::lower::SomeExpr *sourceExpr{nullptr}; |
784 | const Fortran::lower::SomeExpr *moldExpr{nullptr}; |
785 | const Fortran::lower::SomeExpr *statExpr{nullptr}; |
786 | const Fortran::lower::SomeExpr *errMsgExpr{nullptr}; |
787 | const Fortran::lower::SomeExpr *pinnedExpr{nullptr}; |
788 | const Fortran::lower::SomeExpr *streamExpr{nullptr}; |
789 | // If the allocate has a type spec, lenParams contains the |
790 | // value of the length parameters that were specified inside. |
791 | llvm::SmallVector<mlir::Value> lenParams; |
792 | ErrorManager errorManager; |
793 | // 9.7.1.2(7) The source-expr is evaluated exactly once for each AllocateStmt. |
794 | fir::ExtendedValue sourceExv; |
795 | fir::ExtendedValue moldExv; |
796 | |
797 | mlir::Location loc; |
798 | }; |
799 | } // namespace |
800 | |
801 | void Fortran::lower::genAllocateStmt( |
802 | Fortran::lower::AbstractConverter &converter, |
803 | const Fortran::parser::AllocateStmt &stmt, mlir::Location loc) { |
804 | AllocateStmtHelper{converter, stmt, loc}.lower(); |
805 | } |
806 | |
807 | //===----------------------------------------------------------------------===// |
808 | // Deallocate statement implementation |
809 | //===----------------------------------------------------------------------===// |
810 | |
811 | static void preDeallocationAction(Fortran::lower::AbstractConverter &converter, |
812 | fir::FirOpBuilder &builder, |
813 | mlir::Value beginOpValue, |
814 | const Fortran::semantics::Symbol &sym) { |
815 | if (sym.test(Fortran::semantics::Symbol::Flag::AccDeclare)) |
816 | Fortran::lower::attachDeclarePreDeallocAction(converter, builder, |
817 | beginOpValue, sym); |
818 | } |
819 | |
820 | static void postDeallocationAction(Fortran::lower::AbstractConverter &converter, |
821 | fir::FirOpBuilder &builder, |
822 | const Fortran::semantics::Symbol &sym) { |
823 | if (sym.test(Fortran::semantics::Symbol::Flag::AccDeclare)) |
824 | Fortran::lower::attachDeclarePostDeallocAction(converter, builder, sym); |
825 | } |
826 | |
827 | static mlir::Value genCudaDeallocate(fir::FirOpBuilder &builder, |
828 | mlir::Location loc, |
829 | const fir::MutableBoxValue &box, |
830 | ErrorManager &errorManager, |
831 | const Fortran::semantics::Symbol &sym) { |
832 | cuf::DataAttributeAttr cudaAttr = |
833 | Fortran::lower::translateSymbolCUFDataAttribute(builder.getContext(), |
834 | sym); |
835 | mlir::Value errmsg = |
836 | mlir::isa<fir::AbsentOp>(errorManager.errMsgAddr.getDefiningOp()) |
837 | ? nullptr |
838 | : errorManager.errMsgAddr; |
839 | |
840 | // Keep return type the same as a standard AllocatableAllocate call. |
841 | mlir::Type retTy = fir::runtime::getModel<int>()(builder.getContext()); |
842 | return builder |
843 | .create<cuf::DeallocateOp>( |
844 | loc, retTy, box.getAddr(), errmsg, cudaAttr, |
845 | errorManager.hasStatSpec() ? builder.getUnitAttr() : nullptr) |
846 | .getResult(); |
847 | } |
848 | |
849 | // Generate deallocation of a pointer/allocatable. |
850 | static mlir::Value |
851 | genDeallocate(fir::FirOpBuilder &builder, |
852 | Fortran::lower::AbstractConverter &converter, mlir::Location loc, |
853 | const fir::MutableBoxValue &box, ErrorManager &errorManager, |
854 | mlir::Value declaredTypeDesc = {}, |
855 | const Fortran::semantics::Symbol *symbol = nullptr) { |
856 | bool isCudaSymbol = symbol && Fortran::semantics::HasCUDAAttr(*symbol); |
857 | bool isCudaDeviceContext = cuf::isCUDADeviceContext(builder.getRegion()); |
858 | bool inlineDeallocation = |
859 | !box.isDerived() && !box.isPolymorphic() && !box.hasAssumedRank() && |
860 | !box.isUnlimitedPolymorphic() && !errorManager.hasStatSpec() && |
861 | !useAllocateRuntime && !box.isPointer(); |
862 | // Deallocate intrinsic types inline. |
863 | if (inlineDeallocation && |
864 | ((isCudaSymbol && isCudaDeviceContext) || !isCudaSymbol)) { |
865 | // Pointers must use PointerDeallocate so that their deallocations |
866 | // can be validated. |
867 | mlir::Value ret = fir::factory::genFreemem(builder, loc, box); |
868 | if (symbol) |
869 | postDeallocationAction(converter, builder, *symbol); |
870 | return ret; |
871 | } |
872 | // Use runtime calls to deallocate descriptor cases. Sync MutableBoxValue |
873 | // with its descriptor before and after calls if needed. |
874 | errorManager.genStatCheck(builder, loc); |
875 | mlir::Value stat; |
876 | if (!isCudaSymbol) |
877 | stat = |
878 | genRuntimeDeallocate(builder, loc, box, errorManager, declaredTypeDesc); |
879 | else |
880 | stat = genCudaDeallocate(builder, loc, box, errorManager, *symbol); |
881 | fir::factory::syncMutableBoxFromIRBox(builder, loc, box); |
882 | if (symbol) |
883 | postDeallocationAction(converter, builder, *symbol); |
884 | errorManager.assignStat(builder, loc, stat); |
885 | return stat; |
886 | } |
887 | |
888 | void Fortran::lower::genDeallocateBox( |
889 | Fortran::lower::AbstractConverter &converter, |
890 | const fir::MutableBoxValue &box, mlir::Location loc, |
891 | const Fortran::semantics::Symbol *sym, mlir::Value declaredTypeDesc) { |
892 | const Fortran::lower::SomeExpr *statExpr = nullptr; |
893 | const Fortran::lower::SomeExpr *errMsgExpr = nullptr; |
894 | ErrorManager errorManager; |
895 | errorManager.init(converter, loc, statExpr, errMsgExpr); |
896 | fir::FirOpBuilder &builder = converter.getFirOpBuilder(); |
897 | genDeallocate(builder, converter, loc, box, errorManager, declaredTypeDesc, |
898 | sym); |
899 | } |
900 | |
901 | void Fortran::lower::genDeallocateIfAllocated( |
902 | Fortran::lower::AbstractConverter &converter, |
903 | const fir::MutableBoxValue &box, mlir::Location loc, |
904 | const Fortran::semantics::Symbol *sym) { |
905 | fir::FirOpBuilder &builder = converter.getFirOpBuilder(); |
906 | mlir::Value isAllocated = |
907 | fir::factory::genIsAllocatedOrAssociatedTest(builder, loc, box); |
908 | builder.genIfThen(loc, isAllocated) |
909 | .genThen([&]() { |
910 | if (mlir::Type eleType = box.getEleTy(); |
911 | mlir::isa<fir::RecordType>(eleType) && box.isPolymorphic()) { |
912 | mlir::Value declaredTypeDesc = builder.create<fir::TypeDescOp>( |
913 | loc, mlir::TypeAttr::get(eleType)); |
914 | genDeallocateBox(converter, box, loc, sym, declaredTypeDesc); |
915 | } else { |
916 | genDeallocateBox(converter, box, loc, sym); |
917 | } |
918 | }) |
919 | .end(); |
920 | } |
921 | |
922 | void Fortran::lower::genDeallocateStmt( |
923 | Fortran::lower::AbstractConverter &converter, |
924 | const Fortran::parser::DeallocateStmt &stmt, mlir::Location loc) { |
925 | const Fortran::lower::SomeExpr *statExpr = nullptr; |
926 | const Fortran::lower::SomeExpr *errMsgExpr = nullptr; |
927 | for (const Fortran::parser::StatOrErrmsg &statOrErr : |
928 | std::get<std::list<Fortran::parser::StatOrErrmsg>>(stmt.t)) |
929 | Fortran::common::visit( |
930 | Fortran::common::visitors{ |
931 | [&](const Fortran::parser::StatVariable &statVar) { |
932 | statExpr = Fortran::semantics::GetExpr(statVar); |
933 | }, |
934 | [&](const Fortran::parser::MsgVariable &errMsgVar) { |
935 | errMsgExpr = Fortran::semantics::GetExpr(errMsgVar); |
936 | }, |
937 | }, |
938 | statOrErr.u); |
939 | ErrorManager errorManager; |
940 | errorManager.init(converter, loc, statExpr, errMsgExpr); |
941 | fir::FirOpBuilder &builder = converter.getFirOpBuilder(); |
942 | mlir::OpBuilder::InsertPoint insertPt = builder.saveInsertionPoint(); |
943 | for (const Fortran::parser::AllocateObject &allocateObject : |
944 | std::get<std::list<Fortran::parser::AllocateObject>>(stmt.t)) { |
945 | const Fortran::semantics::Symbol &symbol = unwrapSymbol(allocateObject); |
946 | fir::MutableBoxValue box = |
947 | genMutableBoxValue(converter, loc, allocateObject); |
948 | mlir::Value declaredTypeDesc = {}; |
949 | if (box.isPolymorphic()) { |
950 | mlir::Type eleType = box.getEleTy(); |
951 | if (mlir::isa<fir::RecordType>(eleType)) |
952 | if (const Fortran::semantics::DerivedTypeSpec *derivedTypeSpec = |
953 | symbol.GetType()->AsDerived()) { |
954 | declaredTypeDesc = |
955 | Fortran::lower::getTypeDescAddr(converter, loc, *derivedTypeSpec); |
956 | } |
957 | } |
958 | mlir::Value beginOpValue = genDeallocate( |
959 | builder, converter, loc, box, errorManager, declaredTypeDesc, &symbol); |
960 | preDeallocationAction(converter, builder, beginOpValue, symbol); |
961 | } |
962 | builder.restoreInsertionPoint(insertPt); |
963 | } |
964 | |
965 | //===----------------------------------------------------------------------===// |
966 | // MutableBoxValue creation implementation |
967 | //===----------------------------------------------------------------------===// |
968 | |
969 | /// Is this symbol a pointer to a pointer array that does not have the |
970 | /// CONTIGUOUS attribute ? |
971 | static inline bool |
972 | isNonContiguousArrayPointer(const Fortran::semantics::Symbol &sym) { |
973 | return Fortran::semantics::IsPointer(sym) && sym.Rank() != 0 && |
974 | !sym.attrs().test(Fortran::semantics::Attr::CONTIGUOUS); |
975 | } |
976 | |
977 | /// Is this symbol a polymorphic pointer? |
978 | static inline bool isPolymorphicPointer(const Fortran::semantics::Symbol &sym) { |
979 | return Fortran::semantics::IsPointer(sym) && |
980 | Fortran::semantics::IsPolymorphic(sym); |
981 | } |
982 | |
983 | /// Is this symbol a polymorphic allocatable? |
984 | static inline bool |
985 | isPolymorphicAllocatable(const Fortran::semantics::Symbol &sym) { |
986 | return Fortran::semantics::IsAllocatable(sym) && |
987 | Fortran::semantics::IsPolymorphic(sym); |
988 | } |
989 | |
990 | /// Is this a local procedure symbol in a procedure that contains internal |
991 | /// procedures ? |
992 | static bool mayBeCapturedInInternalProc(const Fortran::semantics::Symbol &sym) { |
993 | const Fortran::semantics::Scope &owner = sym.owner(); |
994 | Fortran::semantics::Scope::Kind kind = owner.kind(); |
995 | // Test if this is a procedure scope that contains a subprogram scope that is |
996 | // not an interface. |
997 | if (kind == Fortran::semantics::Scope::Kind::Subprogram || |
998 | kind == Fortran::semantics::Scope::Kind::MainProgram) |
999 | for (const Fortran::semantics::Scope &childScope : owner.children()) |
1000 | if (childScope.kind() == Fortran::semantics::Scope::Kind::Subprogram) |
1001 | if (const Fortran::semantics::Symbol *childSym = childScope.symbol()) |
1002 | if (const auto *details = |
1003 | childSym->detailsIf<Fortran::semantics::SubprogramDetails>()) |
1004 | if (!details->isInterface()) |
1005 | return true; |
1006 | return false; |
1007 | } |
1008 | |
1009 | /// In case it is safe to track the properties in variables outside a |
1010 | /// descriptor, create the variables to hold the mutable properties of the |
1011 | /// entity var. The variables are not initialized here. |
1012 | static fir::MutableProperties |
1013 | createMutableProperties(Fortran::lower::AbstractConverter &converter, |
1014 | mlir::Location loc, |
1015 | const Fortran::lower::pft::Variable &var, |
1016 | mlir::ValueRange nonDeferredParams, bool alwaysUseBox) { |
1017 | fir::FirOpBuilder &builder = converter.getFirOpBuilder(); |
1018 | const Fortran::semantics::Symbol &sym = var.getSymbol(); |
1019 | // Globals and dummies may be associated, creating local variables would |
1020 | // require keeping the values and descriptor before and after every single |
1021 | // impure calls in the current scope (not only the ones taking the variable as |
1022 | // arguments. All.) Volatile means the variable may change in ways not defined |
1023 | // per Fortran, so lowering can most likely not keep the descriptor and values |
1024 | // in sync as needed. |
1025 | // Pointers to non contiguous arrays need to be represented with a fir.box to |
1026 | // account for the discontiguity. |
1027 | // Pointer/Allocatable in internal procedure are descriptors in the host link, |
1028 | // and it would increase complexity to sync this descriptor with the local |
1029 | // values every time the host link is escaping. |
1030 | if (alwaysUseBox || var.isGlobal() || Fortran::semantics::IsDummy(sym) || |
1031 | Fortran::semantics::IsFunctionResult(sym) || |
1032 | sym.attrs().test(Fortran::semantics::Attr::VOLATILE) || |
1033 | isNonContiguousArrayPointer(sym) || useAllocateRuntime || |
1034 | useDescForMutableBox || mayBeCapturedInInternalProc(sym) || |
1035 | isPolymorphicPointer(sym) || isPolymorphicAllocatable(sym)) |
1036 | return {}; |
1037 | fir::MutableProperties mutableProperties; |
1038 | std::string name = converter.mangleName(sym); |
1039 | mlir::Type baseAddrTy = converter.genType(sym); |
1040 | if (auto boxType = mlir::dyn_cast<fir::BaseBoxType>(baseAddrTy)) |
1041 | baseAddrTy = boxType.getEleTy(); |
1042 | // Allocate and set a variable to hold the address. |
1043 | // It will be set to null in setUnallocatedStatus. |
1044 | mutableProperties.addr = builder.allocateLocal( |
1045 | loc, baseAddrTy, name + ".addr", "", |
1046 | /*shape=*/std::nullopt, /*typeparams=*/std::nullopt); |
1047 | // Allocate variables to hold lower bounds and extents. |
1048 | int rank = sym.Rank(); |
1049 | mlir::Type idxTy = builder.getIndexType(); |
1050 | for (decltype(rank) i = 0; i < rank; ++i) { |
1051 | mlir::Value lboundVar = builder.allocateLocal( |
1052 | loc, idxTy, name + ".lb"+ std::to_string(i), "", |
1053 | /*shape=*/std::nullopt, /*typeparams=*/std::nullopt); |
1054 | mlir::Value extentVar = builder.allocateLocal( |
1055 | loc, idxTy, name + ".ext"+ std::to_string(i), "", |
1056 | /*shape=*/std::nullopt, /*typeparams=*/std::nullopt); |
1057 | mutableProperties.lbounds.emplace_back(lboundVar); |
1058 | mutableProperties.extents.emplace_back(extentVar); |
1059 | } |
1060 | |
1061 | // Allocate variable to hold deferred length parameters. |
1062 | mlir::Type eleTy = baseAddrTy; |
1063 | if (auto newTy = fir::dyn_cast_ptrEleTy(eleTy)) |
1064 | eleTy = newTy; |
1065 | if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(eleTy)) |
1066 | eleTy = seqTy.getEleTy(); |
1067 | if (auto record = mlir::dyn_cast<fir::RecordType>(eleTy)) |
1068 | if (record.getNumLenParams() != 0) |
1069 | TODO(loc, "deferred length type parameters."); |
1070 | if (fir::isa_char(eleTy) && nonDeferredParams.empty()) { |
1071 | mlir::Value lenVar = |
1072 | builder.allocateLocal(loc, builder.getCharacterLengthType(), |
1073 | name + ".len", "", /*shape=*/std::nullopt, |
1074 | /*typeparams=*/std::nullopt); |
1075 | mutableProperties.deferredParams.emplace_back(lenVar); |
1076 | } |
1077 | return mutableProperties; |
1078 | } |
1079 | |
1080 | fir::MutableBoxValue Fortran::lower::createMutableBox( |
1081 | Fortran::lower::AbstractConverter &converter, mlir::Location loc, |
1082 | const Fortran::lower::pft::Variable &var, mlir::Value boxAddr, |
1083 | mlir::ValueRange nonDeferredParams, bool alwaysUseBox, unsigned allocator) { |
1084 | fir::MutableProperties mutableProperties = createMutableProperties( |
1085 | converter, loc, var, nonDeferredParams, alwaysUseBox); |
1086 | fir::MutableBoxValue box(boxAddr, nonDeferredParams, mutableProperties); |
1087 | fir::FirOpBuilder &builder = converter.getFirOpBuilder(); |
1088 | if (!var.isGlobal() && !Fortran::semantics::IsDummy(var.getSymbol())) |
1089 | fir::factory::disassociateMutableBox(builder, loc, box, |
1090 | /*polymorphicSetType=*/false, |
1091 | allocator); |
1092 | return box; |
1093 | } |
1094 | |
1095 | //===----------------------------------------------------------------------===// |
1096 | // MutableBoxValue reading interface implementation |
1097 | //===----------------------------------------------------------------------===// |
1098 | |
1099 | bool Fortran::lower::isArraySectionWithoutVectorSubscript( |
1100 | const Fortran::lower::SomeExpr &expr) { |
1101 | return expr.Rank() > 0 && Fortran::evaluate::IsVariable(expr) && |
1102 | !Fortran::evaluate::UnwrapWholeSymbolDataRef(expr) && |
1103 | !Fortran::evaluate::HasVectorSubscript(expr); |
1104 | } |
1105 | |
1106 | void Fortran::lower::associateMutableBox( |
1107 | Fortran::lower::AbstractConverter &converter, mlir::Location loc, |
1108 | const fir::MutableBoxValue &box, const Fortran::lower::SomeExpr &source, |
1109 | mlir::ValueRange lbounds, Fortran::lower::StatementContext &stmtCtx) { |
1110 | fir::FirOpBuilder &builder = converter.getFirOpBuilder(); |
1111 | if (Fortran::evaluate::UnwrapExpr<Fortran::evaluate::NullPointer>(source)) { |
1112 | fir::factory::disassociateMutableBox(builder, loc, box); |
1113 | cuf::genPointerSync(box.getAddr(), builder); |
1114 | return; |
1115 | } |
1116 | if (converter.getLoweringOptions().getLowerToHighLevelFIR()) { |
1117 | fir::ExtendedValue rhs = converter.genExprAddr(loc, source, stmtCtx); |
1118 | fir::factory::associateMutableBox(builder, loc, box, rhs, lbounds); |
1119 | cuf::genPointerSync(box.getAddr(), builder); |
1120 | return; |
1121 | } |
1122 | // The right hand side is not be evaluated into a temp. Array sections can |
1123 | // typically be represented as a value of type `!fir.box`. However, an |
1124 | // expression that uses vector subscripts cannot be emboxed. In that case, |
1125 | // generate a reference to avoid having to later use a fir.rebox to implement |
1126 | // the pointer association. |
1127 | fir::ExtendedValue rhs = isArraySectionWithoutVectorSubscript(source) |
1128 | ? converter.genExprBox(loc, source, stmtCtx) |
1129 | : converter.genExprAddr(loc, source, stmtCtx); |
1130 | |
1131 | fir::factory::associateMutableBox(builder, loc, box, rhs, lbounds); |
1132 | } |
1133 | |
1134 | bool Fortran::lower::isWholeAllocatable(const Fortran::lower::SomeExpr &expr) { |
1135 | if (const Fortran::semantics::Symbol *sym = |
1136 | Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(expr)) |
1137 | return Fortran::semantics::IsAllocatable(sym->GetUltimate()); |
1138 | return false; |
1139 | } |
1140 | |
1141 | bool Fortran::lower::isWholePointer(const Fortran::lower::SomeExpr &expr) { |
1142 | if (const Fortran::semantics::Symbol *sym = |
1143 | Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(expr)) |
1144 | return Fortran::semantics::IsPointer(sym->GetUltimate()); |
1145 | return false; |
1146 | } |
1147 | |
1148 | mlir::Value Fortran::lower::getAssumedCharAllocatableOrPointerLen( |
1149 | fir::FirOpBuilder &builder, mlir::Location loc, |
1150 | const Fortran::semantics::Symbol &sym, mlir::Value box) { |
1151 | // Read length from fir.box (explicit expr cannot safely be re-evaluated |
1152 | // here). |
1153 | auto readLength = [&]() { |
1154 | fir::BoxValue boxLoad = |
1155 | builder.create<fir::LoadOp>(loc, fir::getBase(box)).getResult(); |
1156 | return fir::factory::readCharLen(builder, loc, boxLoad); |
1157 | }; |
1158 | if (Fortran::semantics::IsOptional(sym)) { |
1159 | mlir::IndexType idxTy = builder.getIndexType(); |
1160 | // It is not safe to unconditionally read boxes of optionals in case |
1161 | // they are absents. According to 15.5.2.12 3 (9), it is illegal to |
1162 | // inquire the length of absent optional, even if non deferred, so |
1163 | // it's fine to use undefOp in this case. |
1164 | auto isPresent = builder.create<fir::IsPresentOp>(loc, builder.getI1Type(), |
1165 | fir::getBase(box)); |
1166 | mlir::Value len = |
1167 | builder.genIfOp(loc, {idxTy}, isPresent, true) |
1168 | .genThen( |
1169 | [&]() { builder.create<fir::ResultOp>(loc, readLength()); }) |
1170 | .genElse([&]() { |
1171 | auto undef = builder.create<fir::UndefOp>(loc, idxTy); |
1172 | builder.create<fir::ResultOp>(loc, undef.getResult()); |
1173 | }) |
1174 | .getResults()[0]; |
1175 | return len; |
1176 | } |
1177 | |
1178 | return readLength(); |
1179 | } |
1180 | |
1181 | mlir::Value Fortran::lower::getTypeDescAddr( |
1182 | AbstractConverter &converter, mlir::Location loc, |
1183 | const Fortran::semantics::DerivedTypeSpec &typeSpec) { |
1184 | mlir::Type typeDesc = |
1185 | Fortran::lower::translateDerivedTypeToFIRType(converter, typeSpec); |
1186 | fir::FirOpBuilder &builder = converter.getFirOpBuilder(); |
1187 | return builder.create<fir::TypeDescOp>(loc, mlir::TypeAttr::get(typeDesc)); |
1188 | } |
1189 |
Definitions
- useAllocateRuntime
- useDescForMutableBox
- ErrorManager
- init
- hasStatSpec
- genStatCheck
- assignStat
- genRuntimeSetBounds
- genRuntimeInitCharacter
- genRuntimeAllocate
- genRuntimeAllocateSource
- genRuntimeAllocateApplyMold
- genRuntimeDeallocate
- unwrapSymbol
- genMutableBoxValue
- AllocateStmtHelper
- AllocateStmtHelper
- lower
- Allocation
- hasCoarraySpec
- getAllocObj
- getSymbol
- getShapeSpecs
- unwrapAllocation
- visitAllocateOptions
- lowerAllocation
- lowerBoundsAreOnes
- mangleAlloc
- genInlinedAllocation
- postAllocationAction
- setPinnedToFalse
- genSimpleAllocation
- lowerAllocateLengthParameters
- genSetDeferredLengthParameters
- genAllocateObjectInit
- genAllocateObjectBounds
- genSourceMoldAllocation
- genInitDerived
- genInitIntrinsic
- genSetType
- getIfAllocateStmtTypeSpec
- genCudaAllocate
- preDeallocationAction
- postDeallocationAction
- genCudaDeallocate
- genDeallocate
- isNonContiguousArrayPointer
- isPolymorphicPointer
- isPolymorphicAllocatable
- mayBeCapturedInInternalProc
Learn to use CMake with our Intro Training
Find out more