1 //===-- MutableBox.cpp -- MutableBox utilities ----------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
11 //===----------------------------------------------------------------------===//
13 #include "flang/Optimizer/Builder/MutableBox.h"
14 #include "flang/Optimizer/Builder/Character.h"
15 #include "flang/Optimizer/Builder/FIRBuilder.h"
16 #include "flang/Optimizer/Builder/Runtime/Derived.h"
17 #include "flang/Optimizer/Builder/Runtime/Stop.h"
18 #include "flang/Optimizer/Builder/Todo.h"
19 #include "flang/Optimizer/Dialect/FIRAttr.h"
20 #include "flang/Optimizer/Dialect/FIROps.h"
21 #include "flang/Optimizer/Dialect/FIROpsSupport.h"
22 #include "flang/Optimizer/Support/FatalError.h"
24 /// Create a fir.box describing the new address, bounds, and length parameters
25 /// for a MutableBox \p box.
27 createNewFirBox(fir::FirOpBuilder
&builder
, mlir::Location loc
,
28 const fir::MutableBoxValue
&box
, mlir::Value addr
,
29 mlir::ValueRange lbounds
, mlir::ValueRange extents
,
30 mlir::ValueRange lengths
, mlir::Value tdesc
= {}) {
31 if (mlir::isa
<fir::BaseBoxType
>(addr
.getType()))
32 // The entity is already boxed.
33 return builder
.createConvert(loc
, box
.getBoxTy(), addr
);
36 if (!extents
.empty()) {
37 if (lbounds
.empty()) {
38 shape
= builder
.create
<fir::ShapeOp
>(loc
, extents
);
40 llvm::SmallVector
<mlir::Value
> shapeShiftBounds
;
41 for (auto [lb
, extent
] : llvm::zip(lbounds
, extents
)) {
42 shapeShiftBounds
.emplace_back(lb
);
43 shapeShiftBounds
.emplace_back(extent
);
46 fir::ShapeShiftType::get(builder
.getContext(), extents
.size());
47 shape
= builder
.create
<fir::ShapeShiftOp
>(loc
, shapeShiftType
,
50 } // Otherwise, this a scalar. Leave the shape empty.
52 // Ignore lengths if already constant in the box type (this would trigger an
53 // error in the embox).
54 llvm::SmallVector
<mlir::Value
> cleanedLengths
;
55 auto cleanedAddr
= addr
;
56 if (auto charTy
= mlir::dyn_cast
<fir::CharacterType
>(box
.getEleTy())) {
57 // Cast address to box type so that both input and output type have
58 // unknown or constant lengths.
59 auto bt
= box
.getBaseTy();
60 auto addrTy
= addr
.getType();
61 auto type
= mlir::isa
<fir::HeapType
>(addrTy
) ? fir::HeapType::get(bt
)
62 : mlir::isa
<fir::PointerType
>(addrTy
)
63 ? fir::PointerType::get(bt
)
64 : builder
.getRefType(bt
);
65 cleanedAddr
= builder
.createConvert(loc
, type
, addr
);
66 if (charTy
.getLen() == fir::CharacterType::unknownLen())
67 cleanedLengths
.append(lengths
.begin(), lengths
.end());
68 } else if (fir::isUnlimitedPolymorphicType(box
.getBoxTy())) {
69 if (auto charTy
= mlir::dyn_cast
<fir::CharacterType
>(
70 fir::dyn_cast_ptrEleTy(addr
.getType()))) {
71 if (charTy
.getLen() == fir::CharacterType::unknownLen())
72 cleanedLengths
.append(lengths
.begin(), lengths
.end());
74 } else if (box
.isDerivedWithLenParameters()) {
75 TODO(loc
, "updating mutablebox of derived type with length parameters");
76 cleanedLengths
= lengths
;
78 mlir::Value emptySlice
;
79 return builder
.create
<fir::EmboxOp
>(loc
, box
.getBoxTy(), cleanedAddr
, shape
,
80 emptySlice
, cleanedLengths
, tdesc
);
83 //===----------------------------------------------------------------------===//
84 // MutableBoxValue writer and reader
85 //===----------------------------------------------------------------------===//
88 /// MutablePropertyWriter and MutablePropertyReader implementations are the only
89 /// places that depend on how the properties of MutableBoxValue (pointers and
90 /// allocatables) that can be modified in the lifetime of the entity (address,
91 /// extents, lower bounds, length parameters) are represented.
92 /// That is, the properties may be only stored in a fir.box in memory if we
93 /// need to enforce a single point of truth for the properties across calls.
94 /// Or, they can be tracked as independent local variables when it is safe to
95 /// do so. Using bare variables benefits from all optimization passes, even
96 /// when they are not aware of what a fir.box is and fir.box have not been
97 /// optimized out yet.
99 /// MutablePropertyWriter allows reading the properties of a MutableBoxValue.
100 class MutablePropertyReader
{
102 MutablePropertyReader(fir::FirOpBuilder
&builder
, mlir::Location loc
,
103 const fir::MutableBoxValue
&box
,
104 bool forceIRBoxRead
= false)
105 : builder
{builder
}, loc
{loc
}, box
{box
} {
106 if (forceIRBoxRead
|| !box
.isDescribedByVariables())
107 irBox
= builder
.create
<fir::LoadOp
>(loc
, box
.getAddr());
109 /// Get base address of allocated/associated entity.
110 mlir::Value
readBaseAddress() {
112 auto memrefTy
= box
.getBoxTy().getEleTy();
113 if (!fir::isa_ref_type(memrefTy
))
114 memrefTy
= builder
.getRefType(memrefTy
);
115 return builder
.create
<fir::BoxAddrOp
>(loc
, memrefTy
, irBox
);
117 auto addrVar
= box
.getMutableProperties().addr
;
118 return builder
.create
<fir::LoadOp
>(loc
, addrVar
);
120 /// Return {lbound, extent} values read from the MutableBoxValue given
122 std::pair
<mlir::Value
, mlir::Value
> readShape(unsigned dim
) {
123 auto idxTy
= builder
.getIndexType();
125 auto dimVal
= builder
.createIntegerConstant(loc
, idxTy
, dim
);
126 auto dimInfo
= builder
.create
<fir::BoxDimsOp
>(loc
, idxTy
, idxTy
, idxTy
,
128 return {dimInfo
.getResult(0), dimInfo
.getResult(1)};
130 const auto &mutableProperties
= box
.getMutableProperties();
131 auto lb
= builder
.create
<fir::LoadOp
>(loc
, mutableProperties
.lbounds
[dim
]);
132 auto ext
= builder
.create
<fir::LoadOp
>(loc
, mutableProperties
.extents
[dim
]);
136 /// Return the character length. If the length was not deferred, the value
137 /// that was specified is returned (The mutable fields is not read).
138 mlir::Value
readCharacterLength() {
139 if (box
.hasNonDeferredLenParams())
140 return box
.nonDeferredLenParams()[0];
142 return fir::factory::CharacterExprHelper
{builder
, loc
}.readLengthFromBox(
144 const auto &deferred
= box
.getMutableProperties().deferredParams
;
145 if (deferred
.empty())
146 fir::emitFatalError(loc
, "allocatable entity has no length property");
147 return builder
.create
<fir::LoadOp
>(loc
, deferred
[0]);
150 /// Read and return all extents. If \p lbounds vector is provided, lbounds are
151 /// also read into it.
152 llvm::SmallVector
<mlir::Value
>
153 readShape(llvm::SmallVectorImpl
<mlir::Value
> *lbounds
= nullptr) {
154 llvm::SmallVector
<mlir::Value
> extents
;
155 auto rank
= box
.rank();
156 for (decltype(rank
) dim
= 0; dim
< rank
; ++dim
) {
157 auto [lb
, extent
] = readShape(dim
);
159 lbounds
->push_back(lb
);
160 extents
.push_back(extent
);
165 /// Read all mutable properties. Return the base address.
166 mlir::Value
read(llvm::SmallVectorImpl
<mlir::Value
> &lbounds
,
167 llvm::SmallVectorImpl
<mlir::Value
> &extents
,
168 llvm::SmallVectorImpl
<mlir::Value
> &lengths
) {
169 extents
= readShape(&lbounds
);
170 if (box
.isCharacter())
171 lengths
.emplace_back(readCharacterLength());
172 else if (box
.isDerivedWithLenParameters())
173 TODO(loc
, "read allocatable or pointer derived type LEN parameters");
174 return readBaseAddress();
177 /// Return the loaded fir.box.
178 mlir::Value
getIrBox() const {
183 /// Read the lower bounds
184 void getLowerBounds(llvm::SmallVectorImpl
<mlir::Value
> &lbounds
) {
185 auto rank
= box
.rank();
186 for (decltype(rank
) dim
= 0; dim
< rank
; ++dim
)
187 lbounds
.push_back(std::get
<0>(readShape(dim
)));
191 fir::FirOpBuilder
&builder
;
193 fir::MutableBoxValue box
;
197 /// MutablePropertyWriter allows modifying the properties of a MutableBoxValue.
198 class MutablePropertyWriter
{
200 MutablePropertyWriter(fir::FirOpBuilder
&builder
, mlir::Location loc
,
201 const fir::MutableBoxValue
&box
,
202 mlir::Value typeSourceBox
= {}, unsigned allocator
= 0)
203 : builder
{builder
}, loc
{loc
}, box
{box
}, typeSourceBox
{typeSourceBox
},
204 allocator
{allocator
} {}
205 /// Update MutableBoxValue with new address, shape and length parameters.
206 /// Extents and lbounds must all have index type.
207 /// lbounds can be empty in which case all ones is assumed.
208 /// Length parameters must be provided for the length parameters that are
210 void updateMutableBox(mlir::Value addr
, mlir::ValueRange lbounds
,
211 mlir::ValueRange extents
, mlir::ValueRange lengths
,
212 mlir::Value tdesc
= {}) {
213 if (box
.isDescribedByVariables())
214 updateMutableProperties(addr
, lbounds
, extents
, lengths
);
216 updateIRBox(addr
, lbounds
, extents
, lengths
, tdesc
);
219 /// Update MutableBoxValue with a new fir.box. This requires that the mutable
220 /// box is not described by a set of variables, since they could not describe
221 /// all that can be described in the new fir.box (e.g. non contiguous entity).
222 void updateWithIrBox(mlir::Value newBox
) {
223 assert(!box
.isDescribedByVariables());
224 builder
.create
<fir::StoreOp
>(loc
, newBox
, box
.getAddr());
226 /// Set unallocated/disassociated status for the entity described by
227 /// MutableBoxValue. Deallocation is not performed by this helper.
228 void setUnallocatedStatus() {
229 if (box
.isDescribedByVariables()) {
230 auto addrVar
= box
.getMutableProperties().addr
;
231 auto nullTy
= fir::dyn_cast_ptrEleTy(addrVar
.getType());
232 builder
.create
<fir::StoreOp
>(loc
, builder
.createNullConstant(loc
, nullTy
),
235 // Note that the dynamic type of polymorphic entities must be reset to the
236 // declaration type of the mutable box. See Fortran 2018 7.8.2 NOTE 1.
237 // For those, we cannot simply set the address to zero. The way we are
238 // currently unallocating fir.box guarantees that we are resetting the
239 // type to the declared type. Beware if changing this.
240 // Note: the standard is not clear in Deallocate and p => NULL semantics
241 // regarding the new dynamic type the entity must have. So far, assume
242 // this is just like NULLIFY and the dynamic type must be set to the
243 // declared type, not retain the previous dynamic type.
244 auto deallocatedBox
= fir::factory::createUnallocatedBox(
245 builder
, loc
, box
.getBoxTy(), box
.nonDeferredLenParams(),
246 typeSourceBox
, allocator
);
247 builder
.create
<fir::StoreOp
>(loc
, deallocatedBox
, box
.getAddr());
251 /// Copy Values from the fir.box into the property variables if any.
252 void syncMutablePropertiesFromIRBox() {
253 if (!box
.isDescribedByVariables())
255 llvm::SmallVector
<mlir::Value
> lbounds
;
256 llvm::SmallVector
<mlir::Value
> extents
;
257 llvm::SmallVector
<mlir::Value
> lengths
;
259 MutablePropertyReader
{builder
, loc
, box
, /*forceIRBoxRead=*/true}.read(
260 lbounds
, extents
, lengths
);
261 updateMutableProperties(addr
, lbounds
, extents
, lengths
);
264 /// Copy Values from property variables, if any, into the fir.box.
265 void syncIRBoxFromMutableProperties() {
266 if (!box
.isDescribedByVariables())
268 llvm::SmallVector
<mlir::Value
> lbounds
;
269 llvm::SmallVector
<mlir::Value
> extents
;
270 llvm::SmallVector
<mlir::Value
> lengths
;
271 auto addr
= MutablePropertyReader
{builder
, loc
, box
}.read(lbounds
, extents
,
273 updateIRBox(addr
, lbounds
, extents
, lengths
);
277 /// Update the IR box (fir.ref<fir.box<T>>) of the MutableBoxValue.
278 void updateIRBox(mlir::Value addr
, mlir::ValueRange lbounds
,
279 mlir::ValueRange extents
, mlir::ValueRange lengths
,
280 mlir::Value tdesc
= {},
281 unsigned allocator
= kDefaultAllocator
) {
282 mlir::Value irBox
= createNewFirBox(builder
, loc
, box
, addr
, lbounds
,
283 extents
, lengths
, tdesc
);
284 builder
.create
<fir::StoreOp
>(loc
, irBox
, box
.getAddr());
287 /// Update the set of property variables of the MutableBoxValue.
288 void updateMutableProperties(mlir::Value addr
, mlir::ValueRange lbounds
,
289 mlir::ValueRange extents
,
290 mlir::ValueRange lengths
) {
291 auto castAndStore
= [&](mlir::Value val
, mlir::Value addr
) {
292 auto type
= fir::dyn_cast_ptrEleTy(addr
.getType());
293 builder
.create
<fir::StoreOp
>(loc
, builder
.createConvert(loc
, type
, val
),
296 const auto &mutableProperties
= box
.getMutableProperties();
297 castAndStore(addr
, mutableProperties
.addr
);
298 for (auto [extent
, extentVar
] :
299 llvm::zip(extents
, mutableProperties
.extents
))
300 castAndStore(extent
, extentVar
);
301 if (!mutableProperties
.lbounds
.empty()) {
302 if (lbounds
.empty()) {
304 builder
.createIntegerConstant(loc
, builder
.getIndexType(), 1);
305 for (auto lboundVar
: mutableProperties
.lbounds
)
306 castAndStore(one
, lboundVar
);
308 for (auto [lbound
, lboundVar
] :
309 llvm::zip(lbounds
, mutableProperties
.lbounds
))
310 castAndStore(lbound
, lboundVar
);
313 if (box
.isCharacter())
314 // llvm::zip account for the fact that the length only needs to be stored
315 // when it is specified in the allocation and deferred in the
317 for (auto [len
, lenVar
] :
318 llvm::zip(lengths
, mutableProperties
.deferredParams
))
319 castAndStore(len
, lenVar
);
320 else if (box
.isDerivedWithLenParameters())
321 TODO(loc
, "update allocatable derived type length parameters");
323 fir::FirOpBuilder
&builder
;
325 fir::MutableBoxValue box
;
326 mlir::Value typeSourceBox
;
332 mlir::Value
fir::factory::createUnallocatedBox(
333 fir::FirOpBuilder
&builder
, mlir::Location loc
, mlir::Type boxType
,
334 mlir::ValueRange nonDeferredParams
, mlir::Value typeSourceBox
,
335 unsigned allocator
) {
336 auto baseBoxType
= mlir::cast
<fir::BaseBoxType
>(boxType
);
337 // Giving unallocated/disassociated status to assumed-rank POINTER/
338 // ALLOCATABLE is not directly possible to a Fortran user. But the
339 // compiler may need to create such temporary descriptor to deal with
340 // cases like ENTRY or host association. In such case, all that mater
341 // is that the base address is set to zero and the rank is set to
342 // some defined value. Hence, a scalar descriptor is created and
343 // cast to assumed-rank.
344 const bool isAssumedRank
= baseBoxType
.isAssumedRank();
346 baseBoxType
= baseBoxType
.getBoxTypeWithNewShape(/*rank=*/0);
347 auto baseAddrType
= baseBoxType
.getEleTy();
348 if (!fir::isa_ref_type(baseAddrType
))
349 baseAddrType
= builder
.getRefType(baseAddrType
);
350 auto type
= fir::unwrapRefType(baseAddrType
);
351 auto eleTy
= fir::unwrapSequenceType(type
);
352 if (auto recTy
= mlir::dyn_cast
<fir::RecordType
>(eleTy
))
353 if (recTy
.getNumLenParams() > 0)
354 TODO(loc
, "creating unallocated fir.box of derived type with length "
356 auto nullAddr
= builder
.createNullConstant(loc
, baseAddrType
);
358 if (auto seqTy
= mlir::dyn_cast
<fir::SequenceType
>(type
)) {
359 auto zero
= builder
.createIntegerConstant(loc
, builder
.getIndexType(), 0);
360 llvm::SmallVector
<mlir::Value
> extents(seqTy
.getDimension(), zero
);
361 shape
= builder
.createShape(
362 loc
, fir::ArrayBoxValue
{nullAddr
, extents
, /*lbounds=*/std::nullopt
});
364 // Provide dummy length parameters if they are dynamic. If a length parameter
365 // is deferred. It is set to zero here and will be set on allocation.
366 llvm::SmallVector
<mlir::Value
> lenParams
;
367 if (auto charTy
= mlir::dyn_cast
<fir::CharacterType
>(eleTy
)) {
368 if (charTy
.getLen() == fir::CharacterType::unknownLen()) {
369 if (!nonDeferredParams
.empty()) {
370 lenParams
.push_back(nonDeferredParams
[0]);
372 auto zero
= builder
.createIntegerConstant(
373 loc
, builder
.getCharacterLengthType(), 0);
374 lenParams
.push_back(zero
);
378 mlir::Value emptySlice
;
379 auto embox
= builder
.create
<fir::EmboxOp
>(
380 loc
, baseBoxType
, nullAddr
, shape
, emptySlice
, lenParams
, typeSourceBox
);
382 embox
.setAllocatorIdx(allocator
);
384 return builder
.createConvert(loc
, boxType
, embox
);
388 fir::MutableBoxValue
fir::factory::createTempMutableBox(
389 fir::FirOpBuilder
&builder
, mlir::Location loc
, mlir::Type type
,
390 llvm::StringRef name
, mlir::Value typeSourceBox
, bool isPolymorphic
) {
392 if (typeSourceBox
|| isPolymorphic
)
393 boxType
= fir::ClassType::get(fir::HeapType::get(type
));
395 boxType
= fir::BoxType::get(fir::HeapType::get(type
));
396 auto boxAddr
= builder
.createTemporary(loc
, boxType
, name
);
398 fir::MutableBoxValue(boxAddr
, /*nonDeferredParams=*/mlir::ValueRange(),
399 /*mutableProperties=*/{});
400 MutablePropertyWriter
{builder
, loc
, box
, typeSourceBox
}
401 .setUnallocatedStatus();
405 /// Helper to decide if a MutableBoxValue must be read to a BoxValue or
406 /// can be read to a reified box value.
407 static bool readToBoxValue(const fir::MutableBoxValue
&box
,
408 bool mayBePolymorphic
) {
409 // If this is described by a set of local variables, the value
410 // should not be tracked as a fir.box.
411 if (box
.isDescribedByVariables())
413 // Polymorphism might be a source of discontiguity, even on allocatables.
414 // Track value as fir.box
415 if ((box
.isDerived() && mayBePolymorphic
) || box
.isUnlimitedPolymorphic())
417 if (box
.hasAssumedRank())
419 // Intrinsic allocatables are contiguous, no need to track the value by
421 if (box
.isAllocatable() || box
.rank() == 0)
423 // Pointers are known to be contiguous at compile time iff they have the
424 // CONTIGUOUS attribute.
425 return !fir::valueHasFirAttribute(box
.getAddr(),
426 fir::getContiguousAttrName());
430 fir::factory::genMutableBoxRead(fir::FirOpBuilder
&builder
, mlir::Location loc
,
431 const fir::MutableBoxValue
&box
,
432 bool mayBePolymorphic
,
433 bool preserveLowerBounds
) {
434 llvm::SmallVector
<mlir::Value
> lbounds
;
435 llvm::SmallVector
<mlir::Value
> extents
;
436 llvm::SmallVector
<mlir::Value
> lengths
;
437 if (readToBoxValue(box
, mayBePolymorphic
)) {
438 auto reader
= MutablePropertyReader(builder
, loc
, box
);
439 if (preserveLowerBounds
&& !box
.hasAssumedRank())
440 reader
.getLowerBounds(lbounds
);
441 return fir::BoxValue
{reader
.getIrBox(), lbounds
,
442 box
.nonDeferredLenParams()};
444 // Contiguous intrinsic type entity: all the data can be extracted from the
447 MutablePropertyReader(builder
, loc
, box
).read(lbounds
, extents
, lengths
);
448 if (!preserveLowerBounds
)
450 auto rank
= box
.rank();
451 if (box
.isCharacter()) {
452 auto len
= lengths
.empty() ? mlir::Value
{} : lengths
[0];
454 return fir::CharArrayBoxValue
{addr
, len
, extents
, lbounds
};
455 return fir::CharBoxValue
{addr
, len
};
457 mlir::Value sourceBox
;
458 if (box
.isPolymorphic())
459 sourceBox
= builder
.create
<fir::LoadOp
>(loc
, box
.getAddr());
461 return fir::ArrayBoxValue
{addr
, extents
, lbounds
, sourceBox
};
462 if (box
.isPolymorphic())
463 return fir::PolymorphicValue(addr
, sourceBox
);
468 fir::factory::genIsAllocatedOrAssociatedTest(fir::FirOpBuilder
&builder
,
470 const fir::MutableBoxValue
&box
) {
471 auto addr
= MutablePropertyReader(builder
, loc
, box
).readBaseAddress();
472 return builder
.genIsNotNullAddr(loc
, addr
);
475 mlir::Value
fir::factory::genIsNotAllocatedOrAssociatedTest(
476 fir::FirOpBuilder
&builder
, mlir::Location loc
,
477 const fir::MutableBoxValue
&box
) {
478 auto addr
= MutablePropertyReader(builder
, loc
, box
).readBaseAddress();
479 return builder
.genIsNullAddr(loc
, addr
);
482 /// Call freemem. This does not check that the
483 /// address was allocated.
484 static void genFreemem(fir::FirOpBuilder
&builder
, mlir::Location loc
,
486 // A heap (ALLOCATABLE) object may have been converted to a ptr (POINTER),
487 // so make sure the heap type is restored before deallocation.
488 auto cast
= builder
.createConvert(
489 loc
, fir::HeapType::get(fir::dyn_cast_ptrEleTy(addr
.getType())), addr
);
490 builder
.create
<fir::FreeMemOp
>(loc
, cast
);
493 void fir::factory::genFreememIfAllocated(fir::FirOpBuilder
&builder
,
495 const fir::MutableBoxValue
&box
) {
496 auto addr
= MutablePropertyReader(builder
, loc
, box
).readBaseAddress();
497 auto isAllocated
= builder
.genIsNotNullAddr(loc
, addr
);
498 auto ifOp
= builder
.create
<fir::IfOp
>(loc
, isAllocated
,
499 /*withElseRegion=*/false);
500 auto insPt
= builder
.saveInsertionPoint();
501 builder
.setInsertionPointToStart(&ifOp
.getThenRegion().front());
502 ::genFreemem(builder
, loc
, addr
);
503 builder
.restoreInsertionPoint(insPt
);
506 //===----------------------------------------------------------------------===//
507 // MutableBoxValue writing interface implementation
508 //===----------------------------------------------------------------------===//
510 void fir::factory::associateMutableBox(fir::FirOpBuilder
&builder
,
512 const fir::MutableBoxValue
&box
,
513 const fir::ExtendedValue
&source
,
514 mlir::ValueRange lbounds
) {
515 MutablePropertyWriter
writer(builder
, loc
, box
);
517 [&](const fir::PolymorphicValue
&p
) {
518 mlir::Value sourceBox
;
519 if (auto polyBox
= source
.getBoxOf
<fir::PolymorphicValue
>())
520 sourceBox
= polyBox
->getSourceBox();
521 writer
.updateMutableBox(p
.getAddr(), /*lbounds=*/std::nullopt
,
522 /*extents=*/std::nullopt
,
523 /*lengths=*/std::nullopt
, sourceBox
);
525 [&](const fir::UnboxedValue
&addr
) {
526 writer
.updateMutableBox(addr
, /*lbounds=*/std::nullopt
,
527 /*extents=*/std::nullopt
,
528 /*lengths=*/std::nullopt
);
530 [&](const fir::CharBoxValue
&ch
) {
531 writer
.updateMutableBox(ch
.getAddr(), /*lbounds=*/std::nullopt
,
532 /*extents=*/std::nullopt
, {ch
.getLen()});
534 [&](const fir::ArrayBoxValue
&arr
) {
535 writer
.updateMutableBox(arr
.getAddr(),
536 lbounds
.empty() ? arr
.getLBounds() : lbounds
,
537 arr
.getExtents(), /*lengths=*/std::nullopt
);
539 [&](const fir::CharArrayBoxValue
&arr
) {
540 writer
.updateMutableBox(arr
.getAddr(),
541 lbounds
.empty() ? arr
.getLBounds() : lbounds
,
542 arr
.getExtents(), {arr
.getLen()});
544 [&](const fir::BoxValue
&arr
) {
545 // Rebox array fir.box to the pointer type and apply potential new lower
547 mlir::ValueRange newLbounds
= lbounds
.empty()
548 ? mlir::ValueRange
{arr
.getLBounds()}
549 : mlir::ValueRange
{lbounds
};
550 if (box
.hasAssumedRank()) {
551 assert(arr
.hasAssumedRank() &&
552 "expect both arr and box to be assumed-rank");
553 mlir::Value reboxed
= builder
.create
<fir::ReboxAssumedRankOp
>(
554 loc
, box
.getBoxTy(), arr
.getAddr(),
555 fir::LowerBoundModifierAttribute::Preserve
);
556 writer
.updateWithIrBox(reboxed
);
557 } else if (box
.isDescribedByVariables()) {
558 // LHS is a contiguous pointer described by local variables. Open RHS
559 // fir.box to update the LHS.
560 auto rawAddr
= builder
.create
<fir::BoxAddrOp
>(loc
, arr
.getMemTy(),
562 auto extents
= fir::factory::getExtents(loc
, builder
, source
);
563 llvm::SmallVector
<mlir::Value
> lenParams
;
564 if (arr
.isCharacter()) {
565 lenParams
.emplace_back(
566 fir::factory::readCharLen(builder
, loc
, source
));
567 } else if (arr
.isDerivedWithLenParameters()) {
568 TODO(loc
, "pointer assignment to derived with length parameters");
570 writer
.updateMutableBox(rawAddr
, newLbounds
, extents
, lenParams
);
573 if (!newLbounds
.empty()) {
575 fir::ShiftType::get(builder
.getContext(), newLbounds
.size());
576 shift
= builder
.create
<fir::ShiftOp
>(loc
, shiftType
, newLbounds
);
579 builder
.create
<fir::ReboxOp
>(loc
, box
.getBoxTy(), arr
.getAddr(),
580 shift
, /*slice=*/mlir::Value());
581 writer
.updateWithIrBox(reboxed
);
584 [&](const fir::MutableBoxValue
&) {
585 // No point implementing this, if right-hand side is a
586 // pointer/allocatable, the related MutableBoxValue has been read into
587 // another ExtendedValue category.
588 fir::emitFatalError(loc
,
589 "Cannot write MutableBox to another MutableBox");
591 [&](const fir::ProcBoxValue
&) {
592 TODO(loc
, "procedure pointer assignment");
596 void fir::factory::associateMutableBoxWithRemap(
597 fir::FirOpBuilder
&builder
, mlir::Location loc
,
598 const fir::MutableBoxValue
&box
, const fir::ExtendedValue
&source
,
599 mlir::ValueRange lbounds
, mlir::ValueRange ubounds
) {
600 // Compute new extents
601 llvm::SmallVector
<mlir::Value
> extents
;
602 auto idxTy
= builder
.getIndexType();
603 if (!lbounds
.empty()) {
604 auto one
= builder
.createIntegerConstant(loc
, idxTy
, 1);
605 for (auto [lb
, ub
] : llvm::zip(lbounds
, ubounds
)) {
606 auto lbi
= builder
.createConvert(loc
, idxTy
, lb
);
607 auto ubi
= builder
.createConvert(loc
, idxTy
, ub
);
608 auto diff
= builder
.create
<mlir::arith::SubIOp
>(loc
, idxTy
, ubi
, lbi
);
609 extents
.emplace_back(
610 builder
.create
<mlir::arith::AddIOp
>(loc
, idxTy
, diff
, one
));
613 // lbounds are default. Upper bounds and extents are the same.
614 for (auto ub
: ubounds
) {
615 auto cast
= builder
.createConvert(loc
, idxTy
, ub
);
616 extents
.emplace_back(cast
);
619 const auto newRank
= extents
.size();
620 auto cast
= [&](mlir::Value addr
) -> mlir::Value
{
621 // Cast base addr to new sequence type.
622 auto ty
= fir::dyn_cast_ptrEleTy(addr
.getType());
623 if (auto seqTy
= mlir::dyn_cast
<fir::SequenceType
>(ty
)) {
624 fir::SequenceType::Shape
shape(newRank
,
625 fir::SequenceType::getUnknownExtent());
626 ty
= fir::SequenceType::get(shape
, seqTy
.getEleTy());
628 return builder
.createConvert(loc
, builder
.getRefType(ty
), addr
);
630 MutablePropertyWriter
writer(builder
, loc
, box
);
632 [&](const fir::PolymorphicValue
&p
) {
633 writer
.updateMutableBox(cast(p
.getAddr()), lbounds
, extents
,
634 /*lengths=*/std::nullopt
);
636 [&](const fir::UnboxedValue
&addr
) {
637 writer
.updateMutableBox(cast(addr
), lbounds
, extents
,
638 /*lengths=*/std::nullopt
);
640 [&](const fir::CharBoxValue
&ch
) {
641 writer
.updateMutableBox(cast(ch
.getAddr()), lbounds
, extents
,
644 [&](const fir::ArrayBoxValue
&arr
) {
645 writer
.updateMutableBox(cast(arr
.getAddr()), lbounds
, extents
,
646 /*lengths=*/std::nullopt
);
648 [&](const fir::CharArrayBoxValue
&arr
) {
649 writer
.updateMutableBox(cast(arr
.getAddr()), lbounds
, extents
,
652 [&](const fir::BoxValue
&arr
) {
653 // Rebox right-hand side fir.box with a new shape and type.
654 if (box
.isDescribedByVariables()) {
655 // LHS is a contiguous pointer described by local variables. Open RHS
656 // fir.box to update the LHS.
657 auto rawAddr
= builder
.create
<fir::BoxAddrOp
>(loc
, arr
.getMemTy(),
659 llvm::SmallVector
<mlir::Value
> lenParams
;
660 if (arr
.isCharacter()) {
661 lenParams
.emplace_back(
662 fir::factory::readCharLen(builder
, loc
, source
));
663 } else if (arr
.isDerivedWithLenParameters()) {
664 TODO(loc
, "pointer assignment to derived with length parameters");
666 writer
.updateMutableBox(rawAddr
, lbounds
, extents
, lenParams
);
669 fir::ShapeShiftType::get(builder
.getContext(), extents
.size());
670 llvm::SmallVector
<mlir::Value
> shapeArgs
;
671 auto idxTy
= builder
.getIndexType();
672 for (auto [lbnd
, ext
] : llvm::zip(lbounds
, extents
)) {
673 auto lb
= builder
.createConvert(loc
, idxTy
, lbnd
);
674 shapeArgs
.push_back(lb
);
675 shapeArgs
.push_back(ext
);
678 builder
.create
<fir::ShapeShiftOp
>(loc
, shapeType
, shapeArgs
);
680 builder
.create
<fir::ReboxOp
>(loc
, box
.getBoxTy(), arr
.getAddr(),
681 shape
, /*slice=*/mlir::Value());
682 writer
.updateWithIrBox(reboxed
);
685 [&](const fir::MutableBoxValue
&) {
686 // No point implementing this, if right-hand side is a pointer or
687 // allocatable, the related MutableBoxValue has already been read into
688 // another ExtendedValue category.
689 fir::emitFatalError(loc
,
690 "Cannot write MutableBox to another MutableBox");
692 [&](const fir::ProcBoxValue
&) {
693 TODO(loc
, "procedure pointer assignment");
697 void fir::factory::disassociateMutableBox(fir::FirOpBuilder
&builder
,
699 const fir::MutableBoxValue
&box
,
700 bool polymorphicSetType
,
701 unsigned allocator
) {
702 if (box
.isPolymorphic() && polymorphicSetType
) {
703 // 7.3.2.3 point 7. The dynamic type of a disassociated pointer is the
704 // same as its declared type.
705 auto boxTy
= mlir::dyn_cast
<fir::BaseBoxType
>(box
.getBoxTy());
706 auto eleTy
= fir::unwrapPassByRefType(boxTy
.getEleTy());
707 mlir::Type derivedType
= fir::getDerivedType(eleTy
);
708 if (auto recTy
= mlir::dyn_cast
<fir::RecordType
>(derivedType
)) {
709 fir::runtime::genNullifyDerivedType(builder
, loc
, box
.getAddr(), recTy
,
714 MutablePropertyWriter
{builder
, loc
, box
, {}, allocator
}
715 .setUnallocatedStatus();
718 static llvm::SmallVector
<mlir::Value
>
719 getNewLengths(fir::FirOpBuilder
&builder
, mlir::Location loc
,
720 const fir::MutableBoxValue
&box
, mlir::ValueRange lenParams
) {
721 llvm::SmallVector
<mlir::Value
> lengths
;
722 auto idxTy
= builder
.getIndexType();
723 if (auto charTy
= mlir::dyn_cast
<fir::CharacterType
>(box
.getEleTy())) {
724 if (charTy
.getLen() == fir::CharacterType::unknownLen()) {
725 if (box
.hasNonDeferredLenParams()) {
726 lengths
.emplace_back(
727 builder
.createConvert(loc
, idxTy
, box
.nonDeferredLenParams()[0]));
728 } else if (!lenParams
.empty()) {
730 fir::factory::genMaxWithZero(builder
, loc
, lenParams
[0]);
731 lengths
.emplace_back(builder
.createConvert(loc
, idxTy
, len
));
734 loc
, "could not deduce character lengths in character allocation");
741 static mlir::Value
allocateAndInitNewStorage(fir::FirOpBuilder
&builder
,
743 const fir::MutableBoxValue
&box
,
744 mlir::ValueRange extents
,
745 mlir::ValueRange lenParams
,
746 llvm::StringRef allocName
) {
747 auto lengths
= getNewLengths(builder
, loc
, box
, lenParams
);
748 auto newStorage
= builder
.create
<fir::AllocMemOp
>(
749 loc
, box
.getBaseTy(), allocName
, lengths
, extents
);
750 if (mlir::isa
<fir::RecordType
>(box
.getEleTy())) {
751 // TODO: skip runtime initialization if this is not required. Currently,
752 // there is no way to know here if a derived type needs it or not. But the
753 // information is available at compile time and could be reflected here
755 mlir::Value irBox
= createNewFirBox(builder
, loc
, box
, newStorage
,
756 std::nullopt
, extents
, lengths
);
757 fir::runtime::genDerivedTypeInitialize(builder
, loc
, irBox
);
762 void fir::factory::genInlinedAllocation(
763 fir::FirOpBuilder
&builder
, mlir::Location loc
,
764 const fir::MutableBoxValue
&box
, mlir::ValueRange lbounds
,
765 mlir::ValueRange extents
, mlir::ValueRange lenParams
,
766 llvm::StringRef allocName
, bool mustBeHeap
) {
767 auto lengths
= getNewLengths(builder
, loc
, box
, lenParams
);
768 llvm::SmallVector
<mlir::Value
> safeExtents
;
769 for (mlir::Value extent
: extents
)
770 safeExtents
.push_back(fir::factory::genMaxWithZero(builder
, loc
, extent
));
771 auto heap
= builder
.create
<fir::AllocMemOp
>(loc
, box
.getBaseTy(), allocName
,
772 lengths
, safeExtents
);
773 MutablePropertyWriter
{builder
, loc
, box
}.updateMutableBox(
774 heap
, lbounds
, safeExtents
, lengths
);
775 if (mlir::isa
<fir::RecordType
>(box
.getEleTy())) {
776 // TODO: skip runtime initialization if this is not required. Currently,
777 // there is no way to know here if a derived type needs it or not. But the
778 // information is available at compile time and could be reflected here
780 mlir::Value irBox
= fir::factory::getMutableIRBox(builder
, loc
, box
);
781 fir::runtime::genDerivedTypeInitialize(builder
, loc
, irBox
);
784 heap
->setAttr(fir::MustBeHeapAttr::getAttrName(),
785 fir::MustBeHeapAttr::get(builder
.getContext(), mustBeHeap
));
788 mlir::Value
fir::factory::genFreemem(fir::FirOpBuilder
&builder
,
790 const fir::MutableBoxValue
&box
) {
791 auto addr
= MutablePropertyReader(builder
, loc
, box
).readBaseAddress();
792 ::genFreemem(builder
, loc
, addr
);
793 MutablePropertyWriter
{builder
, loc
, box
}.setUnallocatedStatus();
797 fir::factory::MutableBoxReallocation
fir::factory::genReallocIfNeeded(
798 fir::FirOpBuilder
&builder
, mlir::Location loc
,
799 const fir::MutableBoxValue
&box
, mlir::ValueRange shape
,
800 mlir::ValueRange lengthParams
,
801 fir::factory::ReallocStorageHandlerFunc storageHandler
) {
802 // Implement 10.2.1.3 point 3 logic when lhs is an array.
803 auto reader
= MutablePropertyReader(builder
, loc
, box
);
804 auto addr
= reader
.readBaseAddress();
805 auto i1Type
= builder
.getI1Type();
806 auto addrType
= addr
.getType();
807 auto isAllocated
= builder
.genIsNotNullAddr(loc
, addr
);
808 auto getExtValForStorage
= [&](mlir::Value newAddr
) -> fir::ExtendedValue
{
809 mlir::SmallVector
<mlir::Value
> extents
;
812 extents
= reader
.readShape();
814 extents
.append(shape
.begin(), shape
.end());
816 if (box
.isCharacter()) {
817 auto len
= box
.hasNonDeferredLenParams() ? reader
.readCharacterLength()
820 return fir::CharArrayBoxValue
{newAddr
, len
, extents
};
821 return fir::CharBoxValue
{newAddr
, len
};
823 if (box
.isDerivedWithLenParameters())
824 TODO(loc
, "reallocation of derived type entities with length parameters");
826 return fir::ArrayBoxValue
{newAddr
, extents
};
831 .genIfOp(loc
, {i1Type
, addrType
}, isAllocated
,
832 /*withElseRegion=*/true)
834 // The box is allocated. Check if it must be reallocated and
836 auto mustReallocate
= builder
.createBool(loc
, false);
837 auto compareProperty
= [&](mlir::Value previous
,
838 mlir::Value required
) {
840 builder
.createConvert(loc
, required
.getType(), previous
);
841 auto cmp
= builder
.create
<mlir::arith::CmpIOp
>(
842 loc
, mlir::arith::CmpIPredicate::ne
, castPrevious
, required
);
843 mustReallocate
= builder
.create
<mlir::arith::SelectOp
>(
844 loc
, cmp
, cmp
, mustReallocate
);
846 llvm::SmallVector
<mlir::Value
> previousExtents
= reader
.readShape();
848 for (auto [previousExtent
, requested
] :
849 llvm::zip(previousExtents
, shape
))
850 compareProperty(previousExtent
, requested
);
852 if (box
.isCharacter() && !box
.hasNonDeferredLenParams()) {
853 // When the allocatable length is not deferred, it must not be
854 // reallocated in case of length mismatch, instead,
855 // padding/trimming will occur in later assignment to it.
856 assert(!lengthParams
.empty() &&
857 "must provide length parameters for character");
858 compareProperty(reader
.readCharacterLength(), lengthParams
[0]);
859 } else if (box
.isDerivedWithLenParameters()) {
860 TODO(loc
, "automatic allocation of derived type allocatable with "
861 "length parameters");
864 .genIfOp(loc
, {addrType
}, mustReallocate
,
865 /*withElseRegion=*/true)
867 // If shape or length mismatch, allocate new
868 // storage. When rhs is a scalar, keep the
872 ? mlir::ValueRange(previousExtents
)
874 auto heap
= allocateAndInitNewStorage(
875 builder
, loc
, box
, extents
, lengthParams
,
878 storageHandler(getExtValForStorage(heap
));
879 builder
.create
<fir::ResultOp
>(loc
, heap
);
883 storageHandler(getExtValForStorage(addr
));
884 builder
.create
<fir::ResultOp
>(loc
, addr
);
887 auto newAddr
= ifOp
.getResults()[0];
888 builder
.create
<fir::ResultOp
>(
889 loc
, mlir::ValueRange
{mustReallocate
, newAddr
});
892 auto trueValue
= builder
.createBool(loc
, true);
893 // The box is not yet allocated, simply allocate it.
894 if (shape
.empty() && box
.rank() != 0) {
896 fir::runtime::genReportFatalUserError(
898 "array left hand side must be allocated when the right hand "
900 builder
.create
<fir::ResultOp
>(loc
,
901 mlir::ValueRange
{trueValue
, addr
});
903 auto heap
= allocateAndInitNewStorage(
904 builder
, loc
, box
, shape
, lengthParams
, ".auto.alloc");
906 storageHandler(getExtValForStorage(heap
));
907 builder
.create
<fir::ResultOp
>(loc
,
908 mlir::ValueRange
{trueValue
, heap
});
912 auto wasReallocated
= ifOp
.getResults()[0];
913 auto newAddr
= ifOp
.getResults()[1];
914 // Create an ExtentedValue for the new storage.
915 auto newValue
= getExtValForStorage(newAddr
);
916 return {newValue
, addr
, wasReallocated
, isAllocated
};
919 void fir::factory::finalizeRealloc(fir::FirOpBuilder
&builder
,
921 const fir::MutableBoxValue
&box
,
922 mlir::ValueRange lbounds
,
923 bool takeLboundsIfRealloc
,
924 const MutableBoxReallocation
&realloc
) {
925 builder
.genIfThen(loc
, realloc
.wasReallocated
)
927 auto reader
= MutablePropertyReader(builder
, loc
, box
);
928 llvm::SmallVector
<mlir::Value
> previousLbounds
;
929 if (!takeLboundsIfRealloc
&& box
.hasRank())
930 reader
.readShape(&previousLbounds
);
932 takeLboundsIfRealloc
? lbounds
: mlir::ValueRange
{previousLbounds
};
933 llvm::SmallVector
<mlir::Value
> lenParams
;
934 if (box
.isCharacter())
935 lenParams
.push_back(fir::getLen(realloc
.newValue
));
936 if (box
.isDerivedWithLenParameters())
938 "reallocation of derived type entities with length parameters");
939 auto lengths
= getNewLengths(builder
, loc
, box
, lenParams
);
940 auto heap
= fir::getBase(realloc
.newValue
);
941 auto extents
= fir::factory::getExtents(loc
, builder
, realloc
.newValue
);
942 builder
.genIfThen(loc
, realloc
.oldAddressWasAllocated
)
943 .genThen([&]() { ::genFreemem(builder
, loc
, realloc
.oldAddress
); })
945 MutablePropertyWriter
{builder
, loc
, box
}.updateMutableBox(
946 heap
, lbs
, extents
, lengths
);
951 //===----------------------------------------------------------------------===//
952 // MutableBoxValue syncing implementation
953 //===----------------------------------------------------------------------===//
955 /// Depending on the implementation, allocatable/pointer descriptor and the
956 /// MutableBoxValue need to be synced before and after calls passing the
957 /// descriptor. These calls will generate the syncing if needed or be no-op.
958 mlir::Value
fir::factory::getMutableIRBox(fir::FirOpBuilder
&builder
,
960 const fir::MutableBoxValue
&box
) {
961 MutablePropertyWriter
{builder
, loc
, box
}.syncIRBoxFromMutableProperties();
962 return box
.getAddr();
964 void fir::factory::syncMutableBoxFromIRBox(fir::FirOpBuilder
&builder
,
966 const fir::MutableBoxValue
&box
) {
967 MutablePropertyWriter
{builder
, loc
, box
}.syncMutablePropertiesFromIRBox();
970 mlir::Value
fir::factory::genNullBoxStorage(fir::FirOpBuilder
&builder
,
973 mlir::Value boxStorage
= builder
.createTemporary(loc
, boxTy
);
974 mlir::Value nullBox
= fir::factory::createUnallocatedBox(
975 builder
, loc
, boxTy
, /*nonDeferredParams=*/{});
976 builder
.create
<fir::StoreOp
>(loc
, nullBox
, boxStorage
);