1a2e7af75SValentin Clement //===-- MutableBox.cpp -- MutableBox utilities ----------------------------===//
2a2e7af75SValentin Clement //
3a2e7af75SValentin Clement // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a2e7af75SValentin Clement // See https://llvm.org/LICENSE.txt for license information.
5a2e7af75SValentin Clement // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a2e7af75SValentin Clement //
7a2e7af75SValentin Clement //===----------------------------------------------------------------------===//
8a2e7af75SValentin Clement //
9a2e7af75SValentin Clement // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10a2e7af75SValentin Clement //
11a2e7af75SValentin Clement //===----------------------------------------------------------------------===//
12a2e7af75SValentin Clement 
13a2e7af75SValentin Clement #include "flang/Optimizer/Builder/MutableBox.h"
14a2e7af75SValentin Clement #include "flang/Optimizer/Builder/Character.h"
15a2e7af75SValentin Clement #include "flang/Optimizer/Builder/FIRBuilder.h"
162a59ead1SValentin Clement #include "flang/Optimizer/Builder/Runtime/Derived.h"
172a59ead1SValentin Clement #include "flang/Optimizer/Builder/Runtime/Stop.h"
185b66cc10SValentin Clement #include "flang/Optimizer/Builder/Todo.h"
19a2e7af75SValentin Clement #include "flang/Optimizer/Dialect/FIROps.h"
20a2e7af75SValentin Clement #include "flang/Optimizer/Dialect/FIROpsSupport.h"
21a2e7af75SValentin Clement #include "flang/Optimizer/Support/FatalError.h"
22a2e7af75SValentin Clement 
232a59ead1SValentin Clement /// Create a fir.box describing the new address, bounds, and length parameters
242a59ead1SValentin Clement /// for a MutableBox \p box.
createNewFirBox(fir::FirOpBuilder & builder,mlir::Location loc,const fir::MutableBoxValue & box,mlir::Value addr,mlir::ValueRange lbounds,mlir::ValueRange extents,mlir::ValueRange lengths)252a59ead1SValentin Clement static mlir::Value createNewFirBox(fir::FirOpBuilder &builder,
262a59ead1SValentin Clement                                    mlir::Location loc,
272a59ead1SValentin Clement                                    const fir::MutableBoxValue &box,
282a59ead1SValentin Clement                                    mlir::Value addr, mlir::ValueRange lbounds,
292a59ead1SValentin Clement                                    mlir::ValueRange extents,
302a59ead1SValentin Clement                                    mlir::ValueRange lengths) {
312a59ead1SValentin Clement   if (addr.getType().isa<fir::BoxType>())
322a59ead1SValentin Clement     // The entity is already boxed.
332a59ead1SValentin Clement     return builder.createConvert(loc, box.getBoxTy(), addr);
342a59ead1SValentin Clement 
352a59ead1SValentin Clement   mlir::Value shape;
362a59ead1SValentin Clement   if (!extents.empty()) {
372a59ead1SValentin Clement     if (lbounds.empty()) {
382a59ead1SValentin Clement       auto shapeType =
392a59ead1SValentin Clement           fir::ShapeType::get(builder.getContext(), extents.size());
402a59ead1SValentin Clement       shape = builder.create<fir::ShapeOp>(loc, shapeType, extents);
412a59ead1SValentin Clement     } else {
422a59ead1SValentin Clement       llvm::SmallVector<mlir::Value> shapeShiftBounds;
432a59ead1SValentin Clement       for (auto [lb, extent] : llvm::zip(lbounds, extents)) {
442a59ead1SValentin Clement         shapeShiftBounds.emplace_back(lb);
452a59ead1SValentin Clement         shapeShiftBounds.emplace_back(extent);
462a59ead1SValentin Clement       }
472a59ead1SValentin Clement       auto shapeShiftType =
482a59ead1SValentin Clement           fir::ShapeShiftType::get(builder.getContext(), extents.size());
492a59ead1SValentin Clement       shape = builder.create<fir::ShapeShiftOp>(loc, shapeShiftType,
502a59ead1SValentin Clement                                                 shapeShiftBounds);
512a59ead1SValentin Clement     }
522a59ead1SValentin Clement   } // Otherwise, this a scalar. Leave the shape empty.
532a59ead1SValentin Clement 
542a59ead1SValentin Clement   // Ignore lengths if already constant in the box type (this would trigger an
552a59ead1SValentin Clement   // error in the embox).
562a59ead1SValentin Clement   llvm::SmallVector<mlir::Value> cleanedLengths;
572a59ead1SValentin Clement   auto cleanedAddr = addr;
582a59ead1SValentin Clement   if (auto charTy = box.getEleTy().dyn_cast<fir::CharacterType>()) {
592a59ead1SValentin Clement     // Cast address to box type so that both input and output type have
602a59ead1SValentin Clement     // unknown or constant lengths.
612a59ead1SValentin Clement     auto bt = box.getBaseTy();
622a59ead1SValentin Clement     auto addrTy = addr.getType();
632a59ead1SValentin Clement     auto type = addrTy.isa<fir::HeapType>()      ? fir::HeapType::get(bt)
642a59ead1SValentin Clement                 : addrTy.isa<fir::PointerType>() ? fir::PointerType::get(bt)
652a59ead1SValentin Clement                                                  : builder.getRefType(bt);
662a59ead1SValentin Clement     cleanedAddr = builder.createConvert(loc, type, addr);
672a59ead1SValentin Clement     if (charTy.getLen() == fir::CharacterType::unknownLen())
682a59ead1SValentin Clement       cleanedLengths.append(lengths.begin(), lengths.end());
691bffc753SEric Schweitz   } else if (box.isDerivedWithLenParameters()) {
702a59ead1SValentin Clement     TODO(loc, "updating mutablebox of derived type with length parameters");
712a59ead1SValentin Clement     cleanedLengths = lengths;
722a59ead1SValentin Clement   }
732a59ead1SValentin Clement   mlir::Value emptySlice;
742a59ead1SValentin Clement   return builder.create<fir::EmboxOp>(loc, box.getBoxTy(), cleanedAddr, shape,
752a59ead1SValentin Clement                                       emptySlice, cleanedLengths);
762a59ead1SValentin Clement }
772a59ead1SValentin Clement 
78a2e7af75SValentin Clement //===----------------------------------------------------------------------===//
79a2e7af75SValentin Clement // MutableBoxValue writer and reader
80a2e7af75SValentin Clement //===----------------------------------------------------------------------===//
81a2e7af75SValentin Clement 
82a2e7af75SValentin Clement namespace {
83a2e7af75SValentin Clement /// MutablePropertyWriter and MutablePropertyReader implementations are the only
84a2e7af75SValentin Clement /// places that depend on how the properties of MutableBoxValue (pointers and
85a2e7af75SValentin Clement /// allocatables) that can be modified in the lifetime of the entity (address,
86a2e7af75SValentin Clement /// extents, lower bounds, length parameters) are represented.
87a2e7af75SValentin Clement /// That is, the properties may be only stored in a fir.box in memory if we
88a2e7af75SValentin Clement /// need to enforce a single point of truth for the properties across calls.
89a2e7af75SValentin Clement /// Or, they can be tracked as independent local variables when it is safe to
90a2e7af75SValentin Clement /// do so. Using bare variables benefits from all optimization passes, even
91a2e7af75SValentin Clement /// when they are not aware of what a fir.box is and fir.box have not been
92a2e7af75SValentin Clement /// optimized out yet.
93a2e7af75SValentin Clement 
94a2e7af75SValentin Clement /// MutablePropertyWriter allows reading the properties of a MutableBoxValue.
95a2e7af75SValentin Clement class MutablePropertyReader {
96a2e7af75SValentin Clement public:
MutablePropertyReader(fir::FirOpBuilder & builder,mlir::Location loc,const fir::MutableBoxValue & box,bool forceIRBoxRead=false)97a2e7af75SValentin Clement   MutablePropertyReader(fir::FirOpBuilder &builder, mlir::Location loc,
98a2e7af75SValentin Clement                         const fir::MutableBoxValue &box,
99a2e7af75SValentin Clement                         bool forceIRBoxRead = false)
100a2e7af75SValentin Clement       : builder{builder}, loc{loc}, box{box} {
101a2e7af75SValentin Clement     if (forceIRBoxRead || !box.isDescribedByVariables())
102a2e7af75SValentin Clement       irBox = builder.create<fir::LoadOp>(loc, box.getAddr());
103a2e7af75SValentin Clement   }
104a2e7af75SValentin Clement   /// Get base address of allocated/associated entity.
readBaseAddress()105a2e7af75SValentin Clement   mlir::Value readBaseAddress() {
106a2e7af75SValentin Clement     if (irBox) {
10796d9df41SValentin Clement       auto memrefTy = box.getBoxTy().getEleTy();
10896d9df41SValentin Clement       if (!fir::isa_ref_type(memrefTy))
10996d9df41SValentin Clement         memrefTy = builder.getRefType(memrefTy);
11096d9df41SValentin Clement       return builder.create<fir::BoxAddrOp>(loc, memrefTy, irBox);
111a2e7af75SValentin Clement     }
112a2e7af75SValentin Clement     auto addrVar = box.getMutableProperties().addr;
113a2e7af75SValentin Clement     return builder.create<fir::LoadOp>(loc, addrVar);
114a2e7af75SValentin Clement   }
115a2e7af75SValentin Clement   /// Return {lbound, extent} values read from the MutableBoxValue given
116a2e7af75SValentin Clement   /// the dimension.
readShape(unsigned dim)117a2e7af75SValentin Clement   std::pair<mlir::Value, mlir::Value> readShape(unsigned dim) {
118a2e7af75SValentin Clement     auto idxTy = builder.getIndexType();
119a2e7af75SValentin Clement     if (irBox) {
120a2e7af75SValentin Clement       auto dimVal = builder.createIntegerConstant(loc, idxTy, dim);
121a2e7af75SValentin Clement       auto dimInfo = builder.create<fir::BoxDimsOp>(loc, idxTy, idxTy, idxTy,
122a2e7af75SValentin Clement                                                     irBox, dimVal);
123a2e7af75SValentin Clement       return {dimInfo.getResult(0), dimInfo.getResult(1)};
124a2e7af75SValentin Clement     }
125a2e7af75SValentin Clement     const auto &mutableProperties = box.getMutableProperties();
126a2e7af75SValentin Clement     auto lb = builder.create<fir::LoadOp>(loc, mutableProperties.lbounds[dim]);
127a2e7af75SValentin Clement     auto ext = builder.create<fir::LoadOp>(loc, mutableProperties.extents[dim]);
128a2e7af75SValentin Clement     return {lb, ext};
129a2e7af75SValentin Clement   }
130a2e7af75SValentin Clement 
131a2e7af75SValentin Clement   /// Return the character length. If the length was not deferred, the value
132a2e7af75SValentin Clement   /// that was specified is returned (The mutable fields is not read).
readCharacterLength()133a2e7af75SValentin Clement   mlir::Value readCharacterLength() {
134a2e7af75SValentin Clement     if (box.hasNonDeferredLenParams())
135a2e7af75SValentin Clement       return box.nonDeferredLenParams()[0];
136a2e7af75SValentin Clement     if (irBox)
137a2e7af75SValentin Clement       return fir::factory::CharacterExprHelper{builder, loc}.readLengthFromBox(
138a2e7af75SValentin Clement           irBox);
139a2e7af75SValentin Clement     const auto &deferred = box.getMutableProperties().deferredParams;
140a2e7af75SValentin Clement     if (deferred.empty())
141a2e7af75SValentin Clement       fir::emitFatalError(loc, "allocatable entity has no length property");
142a2e7af75SValentin Clement     return builder.create<fir::LoadOp>(loc, deferred[0]);
143a2e7af75SValentin Clement   }
144a2e7af75SValentin Clement 
145a2e7af75SValentin Clement   /// Read and return all extents. If \p lbounds vector is provided, lbounds are
146a2e7af75SValentin Clement   /// also read into it.
147a2e7af75SValentin Clement   llvm::SmallVector<mlir::Value>
readShape(llvm::SmallVectorImpl<mlir::Value> * lbounds=nullptr)148a2e7af75SValentin Clement   readShape(llvm::SmallVectorImpl<mlir::Value> *lbounds = nullptr) {
14996d9df41SValentin Clement     llvm::SmallVector<mlir::Value> extents;
150a2e7af75SValentin Clement     auto rank = box.rank();
151a2e7af75SValentin Clement     for (decltype(rank) dim = 0; dim < rank; ++dim) {
152a2e7af75SValentin Clement       auto [lb, extent] = readShape(dim);
153a2e7af75SValentin Clement       if (lbounds)
154a2e7af75SValentin Clement         lbounds->push_back(lb);
155a2e7af75SValentin Clement       extents.push_back(extent);
156a2e7af75SValentin Clement     }
157a2e7af75SValentin Clement     return extents;
158a2e7af75SValentin Clement   }
159a2e7af75SValentin Clement 
160a2e7af75SValentin Clement   /// Read all mutable properties. Return the base address.
read(llvm::SmallVectorImpl<mlir::Value> & lbounds,llvm::SmallVectorImpl<mlir::Value> & extents,llvm::SmallVectorImpl<mlir::Value> & lengths)161a2e7af75SValentin Clement   mlir::Value read(llvm::SmallVectorImpl<mlir::Value> &lbounds,
162a2e7af75SValentin Clement                    llvm::SmallVectorImpl<mlir::Value> &extents,
163a2e7af75SValentin Clement                    llvm::SmallVectorImpl<mlir::Value> &lengths) {
164a2e7af75SValentin Clement     extents = readShape(&lbounds);
165a2e7af75SValentin Clement     if (box.isCharacter())
166a2e7af75SValentin Clement       lengths.emplace_back(readCharacterLength());
1671bffc753SEric Schweitz     else if (box.isDerivedWithLenParameters())
168a2e7af75SValentin Clement       TODO(loc, "read allocatable or pointer derived type LEN parameters");
169a2e7af75SValentin Clement     return readBaseAddress();
170a2e7af75SValentin Clement   }
171a2e7af75SValentin Clement 
172a2e7af75SValentin Clement   /// Return the loaded fir.box.
getIrBox() const173a2e7af75SValentin Clement   mlir::Value getIrBox() const {
174a2e7af75SValentin Clement     assert(irBox);
175a2e7af75SValentin Clement     return irBox;
176a2e7af75SValentin Clement   }
177a2e7af75SValentin Clement 
178a2e7af75SValentin Clement   /// Read the lower bounds
getLowerBounds(llvm::SmallVectorImpl<mlir::Value> & lbounds)179a2e7af75SValentin Clement   void getLowerBounds(llvm::SmallVectorImpl<mlir::Value> &lbounds) {
180a2e7af75SValentin Clement     auto rank = box.rank();
181a2e7af75SValentin Clement     for (decltype(rank) dim = 0; dim < rank; ++dim)
182a2e7af75SValentin Clement       lbounds.push_back(std::get<0>(readShape(dim)));
183a2e7af75SValentin Clement   }
184a2e7af75SValentin Clement 
185a2e7af75SValentin Clement private:
186a2e7af75SValentin Clement   fir::FirOpBuilder &builder;
187a2e7af75SValentin Clement   mlir::Location loc;
188a2e7af75SValentin Clement   fir::MutableBoxValue box;
189a2e7af75SValentin Clement   mlir::Value irBox;
190a2e7af75SValentin Clement };
191a2e7af75SValentin Clement 
192a2e7af75SValentin Clement /// MutablePropertyWriter allows modifying the properties of a MutableBoxValue.
193a2e7af75SValentin Clement class MutablePropertyWriter {
194a2e7af75SValentin Clement public:
MutablePropertyWriter(fir::FirOpBuilder & builder,mlir::Location loc,const fir::MutableBoxValue & box)195a2e7af75SValentin Clement   MutablePropertyWriter(fir::FirOpBuilder &builder, mlir::Location loc,
196a2e7af75SValentin Clement                         const fir::MutableBoxValue &box)
197a2e7af75SValentin Clement       : builder{builder}, loc{loc}, box{box} {}
198a2e7af75SValentin Clement   /// Update MutableBoxValue with new address, shape and length parameters.
199a2e7af75SValentin Clement   /// Extents and lbounds must all have index type.
200a2e7af75SValentin Clement   /// lbounds can be empty in which case all ones is assumed.
201a2e7af75SValentin Clement   /// Length parameters must be provided for the length parameters that are
202a2e7af75SValentin Clement   /// deferred.
updateMutableBox(mlir::Value addr,mlir::ValueRange lbounds,mlir::ValueRange extents,mlir::ValueRange lengths)203a2e7af75SValentin Clement   void updateMutableBox(mlir::Value addr, mlir::ValueRange lbounds,
204a2e7af75SValentin Clement                         mlir::ValueRange extents, mlir::ValueRange lengths) {
205a2e7af75SValentin Clement     if (box.isDescribedByVariables())
206a2e7af75SValentin Clement       updateMutableProperties(addr, lbounds, extents, lengths);
207a2e7af75SValentin Clement     else
208a2e7af75SValentin Clement       updateIRBox(addr, lbounds, extents, lengths);
209a2e7af75SValentin Clement   }
210a2e7af75SValentin Clement 
211a2e7af75SValentin Clement   /// Update MutableBoxValue with a new fir.box. This requires that the mutable
212a2e7af75SValentin Clement   /// box is not described by a set of variables, since they could not describe
213a2e7af75SValentin Clement   /// all that can be described in the new fir.box (e.g. non contiguous entity).
updateWithIrBox(mlir::Value newBox)214a2e7af75SValentin Clement   void updateWithIrBox(mlir::Value newBox) {
215a2e7af75SValentin Clement     assert(!box.isDescribedByVariables());
216a2e7af75SValentin Clement     builder.create<fir::StoreOp>(loc, newBox, box.getAddr());
217a2e7af75SValentin Clement   }
218a2e7af75SValentin Clement   /// Set unallocated/disassociated status for the entity described by
219a2e7af75SValentin Clement   /// MutableBoxValue. Deallocation is not performed by this helper.
setUnallocatedStatus()220a2e7af75SValentin Clement   void setUnallocatedStatus() {
221a2e7af75SValentin Clement     if (box.isDescribedByVariables()) {
222a2e7af75SValentin Clement       auto addrVar = box.getMutableProperties().addr;
223a2e7af75SValentin Clement       auto nullTy = fir::dyn_cast_ptrEleTy(addrVar.getType());
224a2e7af75SValentin Clement       builder.create<fir::StoreOp>(loc, builder.createNullConstant(loc, nullTy),
225a2e7af75SValentin Clement                                    addrVar);
226a2e7af75SValentin Clement     } else {
227a2e7af75SValentin Clement       // Note that the dynamic type of polymorphic entities must be reset to the
228a2e7af75SValentin Clement       // declaration type of the mutable box. See Fortran 2018 7.8.2 NOTE 1.
229a2e7af75SValentin Clement       // For those, we cannot simply set the address to zero. The way we are
230a2e7af75SValentin Clement       // currently unallocating fir.box guarantees that we are resetting the
231a2e7af75SValentin Clement       // type to the declared type. Beware if changing this.
232a2e7af75SValentin Clement       // Note: the standard is not clear in Deallocate and p => NULL semantics
233a2e7af75SValentin Clement       // regarding the new dynamic type the entity must have. So far, assume
234a2e7af75SValentin Clement       // this is just like NULLIFY and the dynamic type must be set to the
235a2e7af75SValentin Clement       // declared type, not retain the previous dynamic type.
236a2e7af75SValentin Clement       auto deallocatedBox = fir::factory::createUnallocatedBox(
237a2e7af75SValentin Clement           builder, loc, box.getBoxTy(), box.nonDeferredLenParams());
238a2e7af75SValentin Clement       builder.create<fir::StoreOp>(loc, deallocatedBox, box.getAddr());
239a2e7af75SValentin Clement     }
240a2e7af75SValentin Clement   }
241a2e7af75SValentin Clement 
242a2e7af75SValentin Clement   /// Copy Values from the fir.box into the property variables if any.
syncMutablePropertiesFromIRBox()243a2e7af75SValentin Clement   void syncMutablePropertiesFromIRBox() {
244a2e7af75SValentin Clement     if (!box.isDescribedByVariables())
245a2e7af75SValentin Clement       return;
246a2e7af75SValentin Clement     llvm::SmallVector<mlir::Value> lbounds;
247a2e7af75SValentin Clement     llvm::SmallVector<mlir::Value> extents;
248a2e7af75SValentin Clement     llvm::SmallVector<mlir::Value> lengths;
249a2e7af75SValentin Clement     auto addr =
250a2e7af75SValentin Clement         MutablePropertyReader{builder, loc, box, /*forceIRBoxRead=*/true}.read(
251a2e7af75SValentin Clement             lbounds, extents, lengths);
252a2e7af75SValentin Clement     updateMutableProperties(addr, lbounds, extents, lengths);
253a2e7af75SValentin Clement   }
254a2e7af75SValentin Clement 
255a2e7af75SValentin Clement   /// Copy Values from property variables, if any, into the fir.box.
syncIRBoxFromMutableProperties()256a2e7af75SValentin Clement   void syncIRBoxFromMutableProperties() {
257a2e7af75SValentin Clement     if (!box.isDescribedByVariables())
258a2e7af75SValentin Clement       return;
259a2e7af75SValentin Clement     llvm::SmallVector<mlir::Value> lbounds;
260a2e7af75SValentin Clement     llvm::SmallVector<mlir::Value> extents;
261a2e7af75SValentin Clement     llvm::SmallVector<mlir::Value> lengths;
262a2e7af75SValentin Clement     auto addr = MutablePropertyReader{builder, loc, box}.read(lbounds, extents,
263a2e7af75SValentin Clement                                                               lengths);
264a2e7af75SValentin Clement     updateIRBox(addr, lbounds, extents, lengths);
265a2e7af75SValentin Clement   }
266a2e7af75SValentin Clement 
267a2e7af75SValentin Clement private:
268a2e7af75SValentin Clement   /// Update the IR box (fir.ref<fir.box<T>>) of the MutableBoxValue.
updateIRBox(mlir::Value addr,mlir::ValueRange lbounds,mlir::ValueRange extents,mlir::ValueRange lengths)269a2e7af75SValentin Clement   void updateIRBox(mlir::Value addr, mlir::ValueRange lbounds,
270a2e7af75SValentin Clement                    mlir::ValueRange extents, mlir::ValueRange lengths) {
271fe252f8eSValentin Clement     mlir::Value irBox =
272fe252f8eSValentin Clement         createNewFirBox(builder, loc, box, addr, lbounds, extents, lengths);
273a2e7af75SValentin Clement     builder.create<fir::StoreOp>(loc, irBox, box.getAddr());
274a2e7af75SValentin Clement   }
275a2e7af75SValentin Clement 
276a2e7af75SValentin Clement   /// Update the set of property variables of the MutableBoxValue.
updateMutableProperties(mlir::Value addr,mlir::ValueRange lbounds,mlir::ValueRange extents,mlir::ValueRange lengths)277a2e7af75SValentin Clement   void updateMutableProperties(mlir::Value addr, mlir::ValueRange lbounds,
278a2e7af75SValentin Clement                                mlir::ValueRange extents,
279a2e7af75SValentin Clement                                mlir::ValueRange lengths) {
280a2e7af75SValentin Clement     auto castAndStore = [&](mlir::Value val, mlir::Value addr) {
281a2e7af75SValentin Clement       auto type = fir::dyn_cast_ptrEleTy(addr.getType());
282a2e7af75SValentin Clement       builder.create<fir::StoreOp>(loc, builder.createConvert(loc, type, val),
283a2e7af75SValentin Clement                                    addr);
284a2e7af75SValentin Clement     };
285a2e7af75SValentin Clement     const auto &mutableProperties = box.getMutableProperties();
286a2e7af75SValentin Clement     castAndStore(addr, mutableProperties.addr);
287a2e7af75SValentin Clement     for (auto [extent, extentVar] :
288a2e7af75SValentin Clement          llvm::zip(extents, mutableProperties.extents))
289a2e7af75SValentin Clement       castAndStore(extent, extentVar);
290a2e7af75SValentin Clement     if (!mutableProperties.lbounds.empty()) {
291a2e7af75SValentin Clement       if (lbounds.empty()) {
292a2e7af75SValentin Clement         auto one =
293a2e7af75SValentin Clement             builder.createIntegerConstant(loc, builder.getIndexType(), 1);
294a2e7af75SValentin Clement         for (auto lboundVar : mutableProperties.lbounds)
295a2e7af75SValentin Clement           castAndStore(one, lboundVar);
296a2e7af75SValentin Clement       } else {
297a2e7af75SValentin Clement         for (auto [lbound, lboundVar] :
298a2e7af75SValentin Clement              llvm::zip(lbounds, mutableProperties.lbounds))
299a2e7af75SValentin Clement           castAndStore(lbound, lboundVar);
300a2e7af75SValentin Clement       }
301a2e7af75SValentin Clement     }
302a2e7af75SValentin Clement     if (box.isCharacter())
303a2e7af75SValentin Clement       // llvm::zip account for the fact that the length only needs to be stored
304a2e7af75SValentin Clement       // when it is specified in the allocation and deferred in the
305a2e7af75SValentin Clement       // MutableBoxValue.
306a2e7af75SValentin Clement       for (auto [len, lenVar] :
307a2e7af75SValentin Clement            llvm::zip(lengths, mutableProperties.deferredParams))
308a2e7af75SValentin Clement         castAndStore(len, lenVar);
3091bffc753SEric Schweitz     else if (box.isDerivedWithLenParameters())
310a2e7af75SValentin Clement       TODO(loc, "update allocatable derived type length parameters");
311a2e7af75SValentin Clement   }
312a2e7af75SValentin Clement   fir::FirOpBuilder &builder;
313a2e7af75SValentin Clement   mlir::Location loc;
314a2e7af75SValentin Clement   fir::MutableBoxValue box;
315a2e7af75SValentin Clement };
316a2e7af75SValentin Clement 
317a2e7af75SValentin Clement } // namespace
318a2e7af75SValentin Clement 
319a2e7af75SValentin Clement mlir::Value
createUnallocatedBox(fir::FirOpBuilder & builder,mlir::Location loc,mlir::Type boxType,mlir::ValueRange nonDeferredParams)320a2e7af75SValentin Clement fir::factory::createUnallocatedBox(fir::FirOpBuilder &builder,
321a2e7af75SValentin Clement                                    mlir::Location loc, mlir::Type boxType,
322a2e7af75SValentin Clement                                    mlir::ValueRange nonDeferredParams) {
32394a11063SValentin Clement   auto baseAddrType = boxType.dyn_cast<fir::BoxType>().getEleTy();
32494a11063SValentin Clement   if (!fir::isa_ref_type(baseAddrType))
32594a11063SValentin Clement     baseAddrType = builder.getRefType(baseAddrType);
32694a11063SValentin Clement   auto type = fir::unwrapRefType(baseAddrType);
32794a11063SValentin Clement   auto eleTy = fir::unwrapSequenceType(type);
328a2e7af75SValentin Clement   if (auto recTy = eleTy.dyn_cast<fir::RecordType>())
329a2e7af75SValentin Clement     if (recTy.getNumLenParams() > 0)
330a2e7af75SValentin Clement       TODO(loc, "creating unallocated fir.box of derived type with length "
331a2e7af75SValentin Clement                 "parameters");
33294a11063SValentin Clement   auto nullAddr = builder.createNullConstant(loc, baseAddrType);
333a2e7af75SValentin Clement   mlir::Value shape;
334a2e7af75SValentin Clement   if (auto seqTy = type.dyn_cast<fir::SequenceType>()) {
335a2e7af75SValentin Clement     auto zero = builder.createIntegerConstant(loc, builder.getIndexType(), 0);
336a2e7af75SValentin Clement     llvm::SmallVector<mlir::Value> extents(seqTy.getDimension(), zero);
337a2e7af75SValentin Clement     shape = builder.createShape(
338a2e7af75SValentin Clement         loc, fir::ArrayBoxValue{nullAddr, extents, /*lbounds=*/llvm::None});
339a2e7af75SValentin Clement   }
340a2e7af75SValentin Clement   // Provide dummy length parameters if they are dynamic. If a length parameter
341a2e7af75SValentin Clement   // is deferred. It is set to zero here and will be set on allocation.
342a2e7af75SValentin Clement   llvm::SmallVector<mlir::Value> lenParams;
343a2e7af75SValentin Clement   if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) {
344a2e7af75SValentin Clement     if (charTy.getLen() == fir::CharacterType::unknownLen()) {
345a2e7af75SValentin Clement       if (!nonDeferredParams.empty()) {
346a2e7af75SValentin Clement         lenParams.push_back(nonDeferredParams[0]);
347a2e7af75SValentin Clement       } else {
348a2e7af75SValentin Clement         auto zero = builder.createIntegerConstant(
349a2e7af75SValentin Clement             loc, builder.getCharacterLengthType(), 0);
350a2e7af75SValentin Clement         lenParams.push_back(zero);
351a2e7af75SValentin Clement       }
352a2e7af75SValentin Clement     }
353a2e7af75SValentin Clement   }
354a2e7af75SValentin Clement   mlir::Value emptySlice;
355a2e7af75SValentin Clement   return builder.create<fir::EmboxOp>(loc, boxType, nullAddr, shape, emptySlice,
356a2e7af75SValentin Clement                                       lenParams);
357a2e7af75SValentin Clement }
358a2e7af75SValentin Clement 
359a2e7af75SValentin Clement fir::MutableBoxValue
createTempMutableBox(fir::FirOpBuilder & builder,mlir::Location loc,mlir::Type type,llvm::StringRef name)360a2e7af75SValentin Clement fir::factory::createTempMutableBox(fir::FirOpBuilder &builder,
361a2e7af75SValentin Clement                                    mlir::Location loc, mlir::Type type,
362a2e7af75SValentin Clement                                    llvm::StringRef name) {
363a2e7af75SValentin Clement   auto boxType = fir::BoxType::get(fir::HeapType::get(type));
364a2e7af75SValentin Clement   auto boxAddr = builder.createTemporary(loc, boxType, name);
365a2e7af75SValentin Clement   auto box =
366a2e7af75SValentin Clement       fir::MutableBoxValue(boxAddr, /*nonDeferredParams=*/mlir::ValueRange(),
367a2e7af75SValentin Clement                            /*mutableProperties=*/{});
368a2e7af75SValentin Clement   MutablePropertyWriter{builder, loc, box}.setUnallocatedStatus();
369a2e7af75SValentin Clement   return box;
370a2e7af75SValentin Clement }
371a2e7af75SValentin Clement 
372a2e7af75SValentin Clement /// Helper to decide if a MutableBoxValue must be read to a BoxValue or
373a2e7af75SValentin Clement /// can be read to a reified box value.
readToBoxValue(const fir::MutableBoxValue & box,bool mayBePolymorphic)374a2e7af75SValentin Clement static bool readToBoxValue(const fir::MutableBoxValue &box,
375a2e7af75SValentin Clement                            bool mayBePolymorphic) {
376a2e7af75SValentin Clement   // If this is described by a set of local variables, the value
377a2e7af75SValentin Clement   // should not be tracked as a fir.box.
378a2e7af75SValentin Clement   if (box.isDescribedByVariables())
379a2e7af75SValentin Clement     return false;
380a2e7af75SValentin Clement   // Polymorphism might be a source of discontiguity, even on allocatables.
381a2e7af75SValentin Clement   // Track value as fir.box
382a2e7af75SValentin Clement   if ((box.isDerived() && mayBePolymorphic) || box.isUnlimitedPolymorphic())
383a2e7af75SValentin Clement     return true;
384a2e7af75SValentin Clement   // Intrinsic allocatables are contiguous, no need to track the value by
385a2e7af75SValentin Clement   // fir.box.
386a2e7af75SValentin Clement   if (box.isAllocatable() || box.rank() == 0)
387a2e7af75SValentin Clement     return false;
388a2e7af75SValentin Clement   // Pointers are known to be contiguous at compile time iff they have the
389a2e7af75SValentin Clement   // CONTIGUOUS attribute.
390a2e7af75SValentin Clement   return !fir::valueHasFirAttribute(box.getAddr(),
391a2e7af75SValentin Clement                                     fir::getContiguousAttrName());
392a2e7af75SValentin Clement }
393a2e7af75SValentin Clement 
394a2e7af75SValentin Clement fir::ExtendedValue
genMutableBoxRead(fir::FirOpBuilder & builder,mlir::Location loc,const fir::MutableBoxValue & box,bool mayBePolymorphic)395a2e7af75SValentin Clement fir::factory::genMutableBoxRead(fir::FirOpBuilder &builder, mlir::Location loc,
396a2e7af75SValentin Clement                                 const fir::MutableBoxValue &box,
397a2e7af75SValentin Clement                                 bool mayBePolymorphic) {
398a2e7af75SValentin Clement   if (box.hasAssumedRank())
399331145e6SValentin Clement     TODO(loc, "assumed rank allocatables or pointers");
400a2e7af75SValentin Clement   llvm::SmallVector<mlir::Value> lbounds;
401a2e7af75SValentin Clement   llvm::SmallVector<mlir::Value> extents;
402a2e7af75SValentin Clement   llvm::SmallVector<mlir::Value> lengths;
403a2e7af75SValentin Clement   if (readToBoxValue(box, mayBePolymorphic)) {
404a2e7af75SValentin Clement     auto reader = MutablePropertyReader(builder, loc, box);
405a2e7af75SValentin Clement     reader.getLowerBounds(lbounds);
406a2e7af75SValentin Clement     return fir::BoxValue{reader.getIrBox(), lbounds,
407a2e7af75SValentin Clement                          box.nonDeferredLenParams()};
408a2e7af75SValentin Clement   }
409a2e7af75SValentin Clement   // Contiguous intrinsic type entity: all the data can be extracted from the
410a2e7af75SValentin Clement   // fir.box.
411a2e7af75SValentin Clement   auto addr =
412a2e7af75SValentin Clement       MutablePropertyReader(builder, loc, box).read(lbounds, extents, lengths);
413a2e7af75SValentin Clement   auto rank = box.rank();
414a2e7af75SValentin Clement   if (box.isCharacter()) {
415a2e7af75SValentin Clement     auto len = lengths.empty() ? mlir::Value{} : lengths[0];
416a2e7af75SValentin Clement     if (rank)
417a2e7af75SValentin Clement       return fir::CharArrayBoxValue{addr, len, extents, lbounds};
418a2e7af75SValentin Clement     return fir::CharBoxValue{addr, len};
419a2e7af75SValentin Clement   }
420a2e7af75SValentin Clement   if (rank)
421a2e7af75SValentin Clement     return fir::ArrayBoxValue{addr, extents, lbounds};
422a2e7af75SValentin Clement   return addr;
423a2e7af75SValentin Clement }
424a2e7af75SValentin Clement 
425a2e7af75SValentin Clement mlir::Value
genIsAllocatedOrAssociatedTest(fir::FirOpBuilder & builder,mlir::Location loc,const fir::MutableBoxValue & box)426a2e7af75SValentin Clement fir::factory::genIsAllocatedOrAssociatedTest(fir::FirOpBuilder &builder,
427a2e7af75SValentin Clement                                              mlir::Location loc,
428a2e7af75SValentin Clement                                              const fir::MutableBoxValue &box) {
429a2e7af75SValentin Clement   auto addr = MutablePropertyReader(builder, loc, box).readBaseAddress();
43070ade047Svdonaldson   return builder.genIsNotNullAddr(loc, addr);
431a2e7af75SValentin Clement }
432a2e7af75SValentin Clement 
433a2e7af75SValentin Clement /// Generate finalizer call and inlined free. This does not check that the
434a2e7af75SValentin Clement /// address was allocated.
genFinalizeAndFree(fir::FirOpBuilder & builder,mlir::Location loc,mlir::Value addr)435a2e7af75SValentin Clement static void genFinalizeAndFree(fir::FirOpBuilder &builder, mlir::Location loc,
436a2e7af75SValentin Clement                                mlir::Value addr) {
437a2e7af75SValentin Clement   // TODO: call finalizer if any.
438a2e7af75SValentin Clement 
439a2e7af75SValentin Clement   // A heap (ALLOCATABLE) object may have been converted to a ptr (POINTER),
440a2e7af75SValentin Clement   // so make sure the heap type is restored before deallocation.
441a2e7af75SValentin Clement   auto cast = builder.createConvert(
442a2e7af75SValentin Clement       loc, fir::HeapType::get(fir::dyn_cast_ptrEleTy(addr.getType())), addr);
443a2e7af75SValentin Clement   builder.create<fir::FreeMemOp>(loc, cast);
444a2e7af75SValentin Clement }
445a2e7af75SValentin Clement 
genFinalization(fir::FirOpBuilder & builder,mlir::Location loc,const fir::MutableBoxValue & box)446a2e7af75SValentin Clement void fir::factory::genFinalization(fir::FirOpBuilder &builder,
447a2e7af75SValentin Clement                                    mlir::Location loc,
448a2e7af75SValentin Clement                                    const fir::MutableBoxValue &box) {
449a2e7af75SValentin Clement   auto addr = MutablePropertyReader(builder, loc, box).readBaseAddress();
45070ade047Svdonaldson   auto isAllocated = builder.genIsNotNullAddr(loc, addr);
451a2e7af75SValentin Clement   auto ifOp = builder.create<fir::IfOp>(loc, isAllocated,
452a2e7af75SValentin Clement                                         /*withElseRegion=*/false);
453a2e7af75SValentin Clement   auto insPt = builder.saveInsertionPoint();
454149ad3d5SShraiysh Vaishay   builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
455a2e7af75SValentin Clement   genFinalizeAndFree(builder, loc, addr);
456a2e7af75SValentin Clement   builder.restoreInsertionPoint(insPt);
457a2e7af75SValentin Clement }
458a2e7af75SValentin Clement 
459a2e7af75SValentin Clement //===----------------------------------------------------------------------===//
460a2e7af75SValentin Clement // MutableBoxValue writing interface implementation
461a2e7af75SValentin Clement //===----------------------------------------------------------------------===//
462a2e7af75SValentin Clement 
associateMutableBox(fir::FirOpBuilder & builder,mlir::Location loc,const fir::MutableBoxValue & box,const fir::ExtendedValue & source,mlir::ValueRange lbounds)463a2e7af75SValentin Clement void fir::factory::associateMutableBox(fir::FirOpBuilder &builder,
464a2e7af75SValentin Clement                                        mlir::Location loc,
465a2e7af75SValentin Clement                                        const fir::MutableBoxValue &box,
466a2e7af75SValentin Clement                                        const fir::ExtendedValue &source,
467a2e7af75SValentin Clement                                        mlir::ValueRange lbounds) {
468a2e7af75SValentin Clement   MutablePropertyWriter writer(builder, loc, box);
469a2e7af75SValentin Clement   source.match(
470a2e7af75SValentin Clement       [&](const fir::UnboxedValue &addr) {
471a2e7af75SValentin Clement         writer.updateMutableBox(addr, /*lbounds=*/llvm::None,
472a2e7af75SValentin Clement                                 /*extents=*/llvm::None, /*lengths=*/llvm::None);
473a2e7af75SValentin Clement       },
474a2e7af75SValentin Clement       [&](const fir::CharBoxValue &ch) {
475a2e7af75SValentin Clement         writer.updateMutableBox(ch.getAddr(), /*lbounds=*/llvm::None,
476a2e7af75SValentin Clement                                 /*extents=*/llvm::None, {ch.getLen()});
477a2e7af75SValentin Clement       },
478a2e7af75SValentin Clement       [&](const fir::ArrayBoxValue &arr) {
479a2e7af75SValentin Clement         writer.updateMutableBox(arr.getAddr(),
480a2e7af75SValentin Clement                                 lbounds.empty() ? arr.getLBounds() : lbounds,
481a2e7af75SValentin Clement                                 arr.getExtents(), /*lengths=*/llvm::None);
482a2e7af75SValentin Clement       },
483a2e7af75SValentin Clement       [&](const fir::CharArrayBoxValue &arr) {
484a2e7af75SValentin Clement         writer.updateMutableBox(arr.getAddr(),
485a2e7af75SValentin Clement                                 lbounds.empty() ? arr.getLBounds() : lbounds,
486a2e7af75SValentin Clement                                 arr.getExtents(), {arr.getLen()});
487a2e7af75SValentin Clement       },
488a2e7af75SValentin Clement       [&](const fir::BoxValue &arr) {
489a2e7af75SValentin Clement         // Rebox array fir.box to the pointer type and apply potential new lower
490a2e7af75SValentin Clement         // bounds.
491a2e7af75SValentin Clement         mlir::ValueRange newLbounds = lbounds.empty()
492a2e7af75SValentin Clement                                           ? mlir::ValueRange{arr.getLBounds()}
493a2e7af75SValentin Clement                                           : mlir::ValueRange{lbounds};
494a2e7af75SValentin Clement         if (box.isDescribedByVariables()) {
495a2e7af75SValentin Clement           // LHS is a contiguous pointer described by local variables. Open RHS
496a2e7af75SValentin Clement           // fir.box to update the LHS.
497a2e7af75SValentin Clement           auto rawAddr = builder.create<fir::BoxAddrOp>(loc, arr.getMemTy(),
498a2e7af75SValentin Clement                                                         arr.getAddr());
4991bffc753SEric Schweitz           auto extents = fir::factory::getExtents(loc, builder, source);
500a2e7af75SValentin Clement           llvm::SmallVector<mlir::Value> lenParams;
501a2e7af75SValentin Clement           if (arr.isCharacter()) {
502a2e7af75SValentin Clement             lenParams.emplace_back(
503a2e7af75SValentin Clement                 fir::factory::readCharLen(builder, loc, source));
5041bffc753SEric Schweitz           } else if (arr.isDerivedWithLenParameters()) {
505a2e7af75SValentin Clement             TODO(loc, "pointer assignment to derived with length parameters");
506a2e7af75SValentin Clement           }
507a2e7af75SValentin Clement           writer.updateMutableBox(rawAddr, newLbounds, extents, lenParams);
508a2e7af75SValentin Clement         } else {
509a2e7af75SValentin Clement           mlir::Value shift;
510a2e7af75SValentin Clement           if (!newLbounds.empty()) {
511a2e7af75SValentin Clement             auto shiftType =
512a2e7af75SValentin Clement                 fir::ShiftType::get(builder.getContext(), newLbounds.size());
513a2e7af75SValentin Clement             shift = builder.create<fir::ShiftOp>(loc, shiftType, newLbounds);
514a2e7af75SValentin Clement           }
515a2e7af75SValentin Clement           auto reboxed =
516a2e7af75SValentin Clement               builder.create<fir::ReboxOp>(loc, box.getBoxTy(), arr.getAddr(),
517a2e7af75SValentin Clement                                            shift, /*slice=*/mlir::Value());
518a2e7af75SValentin Clement           writer.updateWithIrBox(reboxed);
519a2e7af75SValentin Clement         }
520a2e7af75SValentin Clement       },
521a2e7af75SValentin Clement       [&](const fir::MutableBoxValue &) {
522a2e7af75SValentin Clement         // No point implementing this, if right-hand side is a
523a2e7af75SValentin Clement         // pointer/allocatable, the related MutableBoxValue has been read into
524a2e7af75SValentin Clement         // another ExtendedValue category.
525a2e7af75SValentin Clement         fir::emitFatalError(loc,
526a2e7af75SValentin Clement                             "Cannot write MutableBox to another MutableBox");
527a2e7af75SValentin Clement       },
528a2e7af75SValentin Clement       [&](const fir::ProcBoxValue &) {
529331145e6SValentin Clement         TODO(loc, "procedure pointer assignment");
530a2e7af75SValentin Clement       });
531a2e7af75SValentin Clement }
532a2e7af75SValentin Clement 
associateMutableBoxWithRemap(fir::FirOpBuilder & builder,mlir::Location loc,const fir::MutableBoxValue & box,const fir::ExtendedValue & source,mlir::ValueRange lbounds,mlir::ValueRange ubounds)533a2e7af75SValentin Clement void fir::factory::associateMutableBoxWithRemap(
534a2e7af75SValentin Clement     fir::FirOpBuilder &builder, mlir::Location loc,
535a2e7af75SValentin Clement     const fir::MutableBoxValue &box, const fir::ExtendedValue &source,
536a2e7af75SValentin Clement     mlir::ValueRange lbounds, mlir::ValueRange ubounds) {
537a2e7af75SValentin Clement   // Compute new extents
538a2e7af75SValentin Clement   llvm::SmallVector<mlir::Value> extents;
539a2e7af75SValentin Clement   auto idxTy = builder.getIndexType();
540a2e7af75SValentin Clement   if (!lbounds.empty()) {
541a2e7af75SValentin Clement     auto one = builder.createIntegerConstant(loc, idxTy, 1);
542a2e7af75SValentin Clement     for (auto [lb, ub] : llvm::zip(lbounds, ubounds)) {
543a2e7af75SValentin Clement       auto lbi = builder.createConvert(loc, idxTy, lb);
544a2e7af75SValentin Clement       auto ubi = builder.createConvert(loc, idxTy, ub);
545092601d4SAndrzej Warzynski       auto diff = builder.create<mlir::arith::SubIOp>(loc, idxTy, ubi, lbi);
546a2e7af75SValentin Clement       extents.emplace_back(
547092601d4SAndrzej Warzynski           builder.create<mlir::arith::AddIOp>(loc, idxTy, diff, one));
548a2e7af75SValentin Clement     }
549a2e7af75SValentin Clement   } else {
550a2e7af75SValentin Clement     // lbounds are default. Upper bounds and extents are the same.
551a2e7af75SValentin Clement     for (auto ub : ubounds) {
552a2e7af75SValentin Clement       auto cast = builder.createConvert(loc, idxTy, ub);
553a2e7af75SValentin Clement       extents.emplace_back(cast);
554a2e7af75SValentin Clement     }
555a2e7af75SValentin Clement   }
556a2e7af75SValentin Clement   const auto newRank = extents.size();
557a2e7af75SValentin Clement   auto cast = [&](mlir::Value addr) -> mlir::Value {
558a2e7af75SValentin Clement     // Cast base addr to new sequence type.
559a2e7af75SValentin Clement     auto ty = fir::dyn_cast_ptrEleTy(addr.getType());
560a2e7af75SValentin Clement     if (auto seqTy = ty.dyn_cast<fir::SequenceType>()) {
561a2e7af75SValentin Clement       fir::SequenceType::Shape shape(newRank,
562a2e7af75SValentin Clement                                      fir::SequenceType::getUnknownExtent());
563a2e7af75SValentin Clement       ty = fir::SequenceType::get(shape, seqTy.getEleTy());
564a2e7af75SValentin Clement     }
565a2e7af75SValentin Clement     return builder.createConvert(loc, builder.getRefType(ty), addr);
566a2e7af75SValentin Clement   };
567a2e7af75SValentin Clement   MutablePropertyWriter writer(builder, loc, box);
568a2e7af75SValentin Clement   source.match(
569a2e7af75SValentin Clement       [&](const fir::UnboxedValue &addr) {
570a2e7af75SValentin Clement         writer.updateMutableBox(cast(addr), lbounds, extents,
571a2e7af75SValentin Clement                                 /*lengths=*/llvm::None);
572a2e7af75SValentin Clement       },
573a2e7af75SValentin Clement       [&](const fir::CharBoxValue &ch) {
574a2e7af75SValentin Clement         writer.updateMutableBox(cast(ch.getAddr()), lbounds, extents,
575a2e7af75SValentin Clement                                 {ch.getLen()});
576a2e7af75SValentin Clement       },
577a2e7af75SValentin Clement       [&](const fir::ArrayBoxValue &arr) {
578a2e7af75SValentin Clement         writer.updateMutableBox(cast(arr.getAddr()), lbounds, extents,
579a2e7af75SValentin Clement                                 /*lengths=*/llvm::None);
580a2e7af75SValentin Clement       },
581a2e7af75SValentin Clement       [&](const fir::CharArrayBoxValue &arr) {
582a2e7af75SValentin Clement         writer.updateMutableBox(cast(arr.getAddr()), lbounds, extents,
583a2e7af75SValentin Clement                                 {arr.getLen()});
584a2e7af75SValentin Clement       },
585a2e7af75SValentin Clement       [&](const fir::BoxValue &arr) {
586a2e7af75SValentin Clement         // Rebox right-hand side fir.box with a new shape and type.
587a2e7af75SValentin Clement         if (box.isDescribedByVariables()) {
588a2e7af75SValentin Clement           // LHS is a contiguous pointer described by local variables. Open RHS
589a2e7af75SValentin Clement           // fir.box to update the LHS.
590a2e7af75SValentin Clement           auto rawAddr = builder.create<fir::BoxAddrOp>(loc, arr.getMemTy(),
591a2e7af75SValentin Clement                                                         arr.getAddr());
592a2e7af75SValentin Clement           llvm::SmallVector<mlir::Value> lenParams;
593a2e7af75SValentin Clement           if (arr.isCharacter()) {
594a2e7af75SValentin Clement             lenParams.emplace_back(
595a2e7af75SValentin Clement                 fir::factory::readCharLen(builder, loc, source));
5961bffc753SEric Schweitz           } else if (arr.isDerivedWithLenParameters()) {
597a2e7af75SValentin Clement             TODO(loc, "pointer assignment to derived with length parameters");
598a2e7af75SValentin Clement           }
599a2e7af75SValentin Clement           writer.updateMutableBox(rawAddr, lbounds, extents, lenParams);
600a2e7af75SValentin Clement         } else {
601a2e7af75SValentin Clement           auto shapeType =
602a2e7af75SValentin Clement               fir::ShapeShiftType::get(builder.getContext(), extents.size());
603a2e7af75SValentin Clement           llvm::SmallVector<mlir::Value> shapeArgs;
604a2e7af75SValentin Clement           auto idxTy = builder.getIndexType();
605a2e7af75SValentin Clement           for (auto [lbnd, ext] : llvm::zip(lbounds, extents)) {
606a2e7af75SValentin Clement             auto lb = builder.createConvert(loc, idxTy, lbnd);
607a2e7af75SValentin Clement             shapeArgs.push_back(lb);
608a2e7af75SValentin Clement             shapeArgs.push_back(ext);
609a2e7af75SValentin Clement           }
610a2e7af75SValentin Clement           auto shape =
611a2e7af75SValentin Clement               builder.create<fir::ShapeShiftOp>(loc, shapeType, shapeArgs);
612a2e7af75SValentin Clement           auto reboxed =
613a2e7af75SValentin Clement               builder.create<fir::ReboxOp>(loc, box.getBoxTy(), arr.getAddr(),
614a2e7af75SValentin Clement                                            shape, /*slice=*/mlir::Value());
615a2e7af75SValentin Clement           writer.updateWithIrBox(reboxed);
616a2e7af75SValentin Clement         }
617a2e7af75SValentin Clement       },
618a2e7af75SValentin Clement       [&](const fir::MutableBoxValue &) {
619a2e7af75SValentin Clement         // No point implementing this, if right-hand side is a pointer or
620a2e7af75SValentin Clement         // allocatable, the related MutableBoxValue has already been read into
621a2e7af75SValentin Clement         // another ExtendedValue category.
622a2e7af75SValentin Clement         fir::emitFatalError(loc,
623a2e7af75SValentin Clement                             "Cannot write MutableBox to another MutableBox");
624a2e7af75SValentin Clement       },
625a2e7af75SValentin Clement       [&](const fir::ProcBoxValue &) {
626331145e6SValentin Clement         TODO(loc, "procedure pointer assignment");
627a2e7af75SValentin Clement       });
628a2e7af75SValentin Clement }
629a2e7af75SValentin Clement 
disassociateMutableBox(fir::FirOpBuilder & builder,mlir::Location loc,const fir::MutableBoxValue & box)630a2e7af75SValentin Clement void fir::factory::disassociateMutableBox(fir::FirOpBuilder &builder,
631a2e7af75SValentin Clement                                           mlir::Location loc,
632a2e7af75SValentin Clement                                           const fir::MutableBoxValue &box) {
633a2e7af75SValentin Clement   MutablePropertyWriter{builder, loc, box}.setUnallocatedStatus();
634a2e7af75SValentin Clement }
635a2e7af75SValentin Clement 
6362a59ead1SValentin Clement static llvm::SmallVector<mlir::Value>
getNewLengths(fir::FirOpBuilder & builder,mlir::Location loc,const fir::MutableBoxValue & box,mlir::ValueRange lenParams)6372a59ead1SValentin Clement getNewLengths(fir::FirOpBuilder &builder, mlir::Location loc,
6382a59ead1SValentin Clement               const fir::MutableBoxValue &box, mlir::ValueRange lenParams) {
6392a59ead1SValentin Clement   llvm::SmallVector<mlir::Value> lengths;
6402a59ead1SValentin Clement   auto idxTy = builder.getIndexType();
6412a59ead1SValentin Clement   if (auto charTy = box.getEleTy().dyn_cast<fir::CharacterType>()) {
6422a59ead1SValentin Clement     if (charTy.getLen() == fir::CharacterType::unknownLen()) {
643c8a9afe7SJean Perier       if (box.hasNonDeferredLenParams()) {
6442a59ead1SValentin Clement         lengths.emplace_back(
6452a59ead1SValentin Clement             builder.createConvert(loc, idxTy, box.nonDeferredLenParams()[0]));
646c8a9afe7SJean Perier       } else if (!lenParams.empty()) {
647c8a9afe7SJean Perier         mlir::Value len =
648c8a9afe7SJean Perier             fir::factory::genMaxWithZero(builder, loc, lenParams[0]);
649c8a9afe7SJean Perier         lengths.emplace_back(builder.createConvert(loc, idxTy, len));
650c8a9afe7SJean Perier       } else {
6512a59ead1SValentin Clement         fir::emitFatalError(
6522a59ead1SValentin Clement             loc, "could not deduce character lengths in character allocation");
6532a59ead1SValentin Clement       }
6542a59ead1SValentin Clement     }
655c8a9afe7SJean Perier   }
6562a59ead1SValentin Clement   return lengths;
6572a59ead1SValentin Clement }
6582a59ead1SValentin Clement 
allocateAndInitNewStorage(fir::FirOpBuilder & builder,mlir::Location loc,const fir::MutableBoxValue & box,mlir::ValueRange extents,mlir::ValueRange lenParams,llvm::StringRef allocName)6592a59ead1SValentin Clement static mlir::Value allocateAndInitNewStorage(fir::FirOpBuilder &builder,
6602a59ead1SValentin Clement                                              mlir::Location loc,
6612a59ead1SValentin Clement                                              const fir::MutableBoxValue &box,
6622a59ead1SValentin Clement                                              mlir::ValueRange extents,
6632a59ead1SValentin Clement                                              mlir::ValueRange lenParams,
6642a59ead1SValentin Clement                                              llvm::StringRef allocName) {
6652a59ead1SValentin Clement   auto lengths = getNewLengths(builder, loc, box, lenParams);
6662a59ead1SValentin Clement   auto newStorage = builder.create<fir::AllocMemOp>(
6672a59ead1SValentin Clement       loc, box.getBaseTy(), allocName, lengths, extents);
6682a59ead1SValentin Clement   if (box.getEleTy().isa<fir::RecordType>()) {
6692a59ead1SValentin Clement     // TODO: skip runtime initialization if this is not required. Currently,
6702a59ead1SValentin Clement     // there is no way to know here if a derived type needs it or not. But the
6712a59ead1SValentin Clement     // information is available at compile time and could be reflected here
6722a59ead1SValentin Clement     // somehow.
6732a59ead1SValentin Clement     mlir::Value irBox = createNewFirBox(builder, loc, box, newStorage,
6742a59ead1SValentin Clement                                         llvm::None, extents, lengths);
6752a59ead1SValentin Clement     fir::runtime::genDerivedTypeInitialize(builder, loc, irBox);
6762a59ead1SValentin Clement   }
6772a59ead1SValentin Clement   return newStorage;
6782a59ead1SValentin Clement }
6792a59ead1SValentin Clement 
genInlinedAllocation(fir::FirOpBuilder & builder,mlir::Location loc,const fir::MutableBoxValue & box,mlir::ValueRange lbounds,mlir::ValueRange extents,mlir::ValueRange lenParams,llvm::StringRef allocName)680a2e7af75SValentin Clement void fir::factory::genInlinedAllocation(fir::FirOpBuilder &builder,
681a2e7af75SValentin Clement                                         mlir::Location loc,
682a2e7af75SValentin Clement                                         const fir::MutableBoxValue &box,
683a2e7af75SValentin Clement                                         mlir::ValueRange lbounds,
684a2e7af75SValentin Clement                                         mlir::ValueRange extents,
685a2e7af75SValentin Clement                                         mlir::ValueRange lenParams,
686a2e7af75SValentin Clement                                         llvm::StringRef allocName) {
687fe252f8eSValentin Clement   auto lengths = getNewLengths(builder, loc, box, lenParams);
688c8a9afe7SJean Perier   llvm::SmallVector<mlir::Value> safeExtents;
689c8a9afe7SJean Perier   for (mlir::Value extent : extents)
690c8a9afe7SJean Perier     safeExtents.push_back(fir::factory::genMaxWithZero(builder, loc, extent));
691fe252f8eSValentin Clement   auto heap = builder.create<fir::AllocMemOp>(loc, box.getBaseTy(), allocName,
692c8a9afe7SJean Perier                                               lengths, safeExtents);
693c8a9afe7SJean Perier   MutablePropertyWriter{builder, loc, box}.updateMutableBox(
694c8a9afe7SJean Perier       heap, lbounds, safeExtents, lengths);
695fe252f8eSValentin Clement   if (box.getEleTy().isa<fir::RecordType>()) {
696fe252f8eSValentin Clement     // TODO: skip runtime initialization if this is not required. Currently,
697fe252f8eSValentin Clement     // there is no way to know here if a derived type needs it or not. But the
698fe252f8eSValentin Clement     // information is available at compile time and could be reflected here
699fe252f8eSValentin Clement     // somehow.
700fe252f8eSValentin Clement     mlir::Value irBox = fir::factory::getMutableIRBox(builder, loc, box);
701fe252f8eSValentin Clement     fir::runtime::genDerivedTypeInitialize(builder, loc, irBox);
702fe252f8eSValentin Clement   }
703a2e7af75SValentin Clement }
704a2e7af75SValentin Clement 
genInlinedDeallocate(fir::FirOpBuilder & builder,mlir::Location loc,const fir::MutableBoxValue & box)705a2e7af75SValentin Clement void fir::factory::genInlinedDeallocate(fir::FirOpBuilder &builder,
706a2e7af75SValentin Clement                                         mlir::Location loc,
707a2e7af75SValentin Clement                                         const fir::MutableBoxValue &box) {
708a2e7af75SValentin Clement   auto addr = MutablePropertyReader(builder, loc, box).readBaseAddress();
709a2e7af75SValentin Clement   genFinalizeAndFree(builder, loc, addr);
710a2e7af75SValentin Clement   MutablePropertyWriter{builder, loc, box}.setUnallocatedStatus();
711a2e7af75SValentin Clement }
712a2e7af75SValentin Clement 
genReallocIfNeeded(fir::FirOpBuilder & builder,mlir::Location loc,const fir::MutableBoxValue & box,mlir::ValueRange shape,mlir::ValueRange lengthParams,fir::factory::ReallocStorageHandlerFunc storageHandler)713*73026a4fSSlava Zakharin fir::factory::MutableBoxReallocation fir::factory::genReallocIfNeeded(
714*73026a4fSSlava Zakharin     fir::FirOpBuilder &builder, mlir::Location loc,
715*73026a4fSSlava Zakharin     const fir::MutableBoxValue &box, mlir::ValueRange shape,
716*73026a4fSSlava Zakharin     mlir::ValueRange lengthParams,
717*73026a4fSSlava Zakharin     fir::factory::ReallocStorageHandlerFunc storageHandler) {
718a2e7af75SValentin Clement   // Implement 10.2.1.3 point 3 logic when lhs is an array.
719a2e7af75SValentin Clement   auto reader = MutablePropertyReader(builder, loc, box);
720a2e7af75SValentin Clement   auto addr = reader.readBaseAddress();
7212a59ead1SValentin Clement   auto i1Type = builder.getI1Type();
7222a59ead1SValentin Clement   auto addrType = addr.getType();
72370ade047Svdonaldson   auto isAllocated = builder.genIsNotNullAddr(loc, addr);
724*73026a4fSSlava Zakharin   auto getExtValForStorage = [&](mlir::Value newAddr) -> fir::ExtendedValue {
725*73026a4fSSlava Zakharin     mlir::SmallVector<mlir::Value> extents;
726*73026a4fSSlava Zakharin     if (box.hasRank()) {
727*73026a4fSSlava Zakharin       if (shape.empty())
728*73026a4fSSlava Zakharin         extents = reader.readShape();
729*73026a4fSSlava Zakharin       else
730*73026a4fSSlava Zakharin         extents.append(shape.begin(), shape.end());
731*73026a4fSSlava Zakharin     }
732*73026a4fSSlava Zakharin     if (box.isCharacter()) {
733*73026a4fSSlava Zakharin       auto len = box.hasNonDeferredLenParams() ? reader.readCharacterLength()
734*73026a4fSSlava Zakharin                                                : lengthParams[0];
735*73026a4fSSlava Zakharin       if (box.hasRank())
736*73026a4fSSlava Zakharin         return fir::CharArrayBoxValue{newAddr, len, extents};
737*73026a4fSSlava Zakharin       return fir::CharBoxValue{newAddr, len};
738*73026a4fSSlava Zakharin     }
739*73026a4fSSlava Zakharin     if (box.isDerivedWithLenParameters())
740*73026a4fSSlava Zakharin       TODO(loc, "reallocation of derived type entities with length parameters");
741*73026a4fSSlava Zakharin     if (box.hasRank())
742*73026a4fSSlava Zakharin       return fir::ArrayBoxValue{newAddr, extents};
743*73026a4fSSlava Zakharin     return newAddr;
744*73026a4fSSlava Zakharin   };
7452a59ead1SValentin Clement   auto ifOp =
7462a59ead1SValentin Clement       builder
7472a59ead1SValentin Clement           .genIfOp(loc, {i1Type, addrType}, isAllocated,
7482a59ead1SValentin Clement                    /*withElseRegion=*/true)
749a2e7af75SValentin Clement           .genThen([&]() {
7502a59ead1SValentin Clement             // The box is allocated. Check if it must be reallocated and
7512a59ead1SValentin Clement             // reallocate.
7522a59ead1SValentin Clement             auto mustReallocate = builder.createBool(loc, false);
7532a59ead1SValentin Clement             auto compareProperty = [&](mlir::Value previous,
7542a59ead1SValentin Clement                                        mlir::Value required) {
755a2e7af75SValentin Clement               auto castPrevious =
756a2e7af75SValentin Clement                   builder.createConvert(loc, required.getType(), previous);
7572a59ead1SValentin Clement               auto cmp = builder.create<mlir::arith::CmpIOp>(
7582a59ead1SValentin Clement                   loc, mlir::arith::CmpIPredicate::ne, castPrevious, required);
759dec8af70SRiver Riddle               mustReallocate = builder.create<mlir::arith::SelectOp>(
760dec8af70SRiver Riddle                   loc, cmp, cmp, mustReallocate);
761a2e7af75SValentin Clement             };
7622a59ead1SValentin Clement             llvm::SmallVector<mlir::Value> previousExtents = reader.readShape();
763a2e7af75SValentin Clement             if (!shape.empty())
764a2e7af75SValentin Clement               for (auto [previousExtent, requested] :
765a2e7af75SValentin Clement                    llvm::zip(previousExtents, shape))
766a2e7af75SValentin Clement                 compareProperty(previousExtent, requested);
767a2e7af75SValentin Clement 
768a2e7af75SValentin Clement             if (box.isCharacter() && !box.hasNonDeferredLenParams()) {
769a2e7af75SValentin Clement               // When the allocatable length is not deferred, it must not be
7702a59ead1SValentin Clement               // reallocated in case of length mismatch, instead,
7712a59ead1SValentin Clement               // padding/trimming will occur in later assignment to it.
772a2e7af75SValentin Clement               assert(!lengthParams.empty() &&
773a2e7af75SValentin Clement                      "must provide length parameters for character");
774a2e7af75SValentin Clement               compareProperty(reader.readCharacterLength(), lengthParams[0]);
7751bffc753SEric Schweitz             } else if (box.isDerivedWithLenParameters()) {
7762a59ead1SValentin Clement               TODO(loc, "automatic allocation of derived type allocatable with "
7772a59ead1SValentin Clement                         "length parameters");
778a2e7af75SValentin Clement             }
7792a59ead1SValentin Clement             auto ifOp =
7802a59ead1SValentin Clement                 builder
7812a59ead1SValentin Clement                     .genIfOp(loc, {addrType}, mustReallocate,
7822a59ead1SValentin Clement                              /*withElseRegion=*/true)
783a2e7af75SValentin Clement                     .genThen([&]() {
7842a59ead1SValentin Clement                       // If shape or length mismatch, allocate new storage.
785a2e7af75SValentin Clement                       // When rhs is a scalar, keep the previous shape
7862a59ead1SValentin Clement                       auto extents = shape.empty()
7872a59ead1SValentin Clement                                          ? mlir::ValueRange(previousExtents)
7882a59ead1SValentin Clement                                          : shape;
7892a59ead1SValentin Clement                       auto heap = allocateAndInitNewStorage(
7902a59ead1SValentin Clement                           builder, loc, box, extents, lengthParams,
7912a59ead1SValentin Clement                           ".auto.alloc");
792*73026a4fSSlava Zakharin                       if (storageHandler)
793*73026a4fSSlava Zakharin                         storageHandler(getExtValForStorage(heap));
7942a59ead1SValentin Clement                       builder.create<fir::ResultOp>(loc, heap);
795a2e7af75SValentin Clement                     })
796*73026a4fSSlava Zakharin                     .genElse([&]() {
797*73026a4fSSlava Zakharin                       if (storageHandler)
798*73026a4fSSlava Zakharin                         storageHandler(getExtValForStorage(addr));
799*73026a4fSSlava Zakharin                       builder.create<fir::ResultOp>(loc, addr);
800*73026a4fSSlava Zakharin                     });
8012a59ead1SValentin Clement             ifOp.end();
8022a59ead1SValentin Clement             auto newAddr = ifOp.getResults()[0];
8032a59ead1SValentin Clement             builder.create<fir::ResultOp>(
8042a59ead1SValentin Clement                 loc, mlir::ValueRange{mustReallocate, newAddr});
805a2e7af75SValentin Clement           })
806a2e7af75SValentin Clement           .genElse([&]() {
8072a59ead1SValentin Clement             auto trueValue = builder.createBool(loc, true);
808a2e7af75SValentin Clement             // The box is not yet allocated, simply allocate it.
809a2e7af75SValentin Clement             if (shape.empty() && box.rank() != 0) {
8102a59ead1SValentin Clement               // See 10.2.1.3 p3.
8112a59ead1SValentin Clement               fir::runtime::genReportFatalUserError(
8122a59ead1SValentin Clement                   builder, loc,
8132a59ead1SValentin Clement                   "array left hand side must be allocated when the right hand "
8142a59ead1SValentin Clement                   "side is a scalar");
8152a59ead1SValentin Clement               builder.create<fir::ResultOp>(loc,
8162a59ead1SValentin Clement                                             mlir::ValueRange{trueValue, addr});
817a2e7af75SValentin Clement             } else {
8182a59ead1SValentin Clement               auto heap = allocateAndInitNewStorage(
8192a59ead1SValentin Clement                   builder, loc, box, shape, lengthParams, ".auto.alloc");
820*73026a4fSSlava Zakharin               if (storageHandler)
821*73026a4fSSlava Zakharin                 storageHandler(getExtValForStorage(heap));
8222a59ead1SValentin Clement               builder.create<fir::ResultOp>(loc,
8232a59ead1SValentin Clement                                             mlir::ValueRange{trueValue, heap});
824a2e7af75SValentin Clement             }
8252a59ead1SValentin Clement           });
8262a59ead1SValentin Clement   ifOp.end();
8272a59ead1SValentin Clement   auto wasReallocated = ifOp.getResults()[0];
8282a59ead1SValentin Clement   auto newAddr = ifOp.getResults()[1];
8292a59ead1SValentin Clement   // Create an ExtentedValue for the new storage.
830*73026a4fSSlava Zakharin   auto newValue = getExtValForStorage(newAddr);
8312a59ead1SValentin Clement   return {newValue, addr, wasReallocated, isAllocated};
8322a59ead1SValentin Clement }
8332a59ead1SValentin Clement 
finalizeRealloc(fir::FirOpBuilder & builder,mlir::Location loc,const fir::MutableBoxValue & box,mlir::ValueRange lbounds,bool takeLboundsIfRealloc,const MutableBoxReallocation & realloc)8342a59ead1SValentin Clement void fir::factory::finalizeRealloc(fir::FirOpBuilder &builder,
8352a59ead1SValentin Clement                                    mlir::Location loc,
8362a59ead1SValentin Clement                                    const fir::MutableBoxValue &box,
8372a59ead1SValentin Clement                                    mlir::ValueRange lbounds,
8382a59ead1SValentin Clement                                    bool takeLboundsIfRealloc,
8392a59ead1SValentin Clement                                    const MutableBoxReallocation &realloc) {
8402a59ead1SValentin Clement   builder.genIfThen(loc, realloc.wasReallocated)
8412a59ead1SValentin Clement       .genThen([&]() {
8422a59ead1SValentin Clement         auto reader = MutablePropertyReader(builder, loc, box);
8432a59ead1SValentin Clement         llvm::SmallVector<mlir::Value> previousLbounds;
8442a59ead1SValentin Clement         if (!takeLboundsIfRealloc && box.hasRank())
8452a59ead1SValentin Clement           reader.readShape(&previousLbounds);
8462a59ead1SValentin Clement         auto lbs =
8472a59ead1SValentin Clement             takeLboundsIfRealloc ? lbounds : mlir::ValueRange{previousLbounds};
8482a59ead1SValentin Clement         llvm::SmallVector<mlir::Value> lenParams;
8492a59ead1SValentin Clement         if (box.isCharacter())
8502a59ead1SValentin Clement           lenParams.push_back(fir::getLen(realloc.newValue));
8511bffc753SEric Schweitz         if (box.isDerivedWithLenParameters())
8522a59ead1SValentin Clement           TODO(loc,
8532a59ead1SValentin Clement                "reallocation of derived type entities with length parameters");
8542a59ead1SValentin Clement         auto lengths = getNewLengths(builder, loc, box, lenParams);
8552a59ead1SValentin Clement         auto heap = fir::getBase(realloc.newValue);
8561bffc753SEric Schweitz         auto extents = fir::factory::getExtents(loc, builder, realloc.newValue);
8572a59ead1SValentin Clement         builder.genIfThen(loc, realloc.oldAddressWasAllocated)
8582a59ead1SValentin Clement             .genThen(
8592a59ead1SValentin Clement                 [&]() { genFinalizeAndFree(builder, loc, realloc.oldAddress); })
8602a59ead1SValentin Clement             .end();
8612a59ead1SValentin Clement         MutablePropertyWriter{builder, loc, box}.updateMutableBox(
8622a59ead1SValentin Clement             heap, lbs, extents, lengths);
863a2e7af75SValentin Clement       })
864a2e7af75SValentin Clement       .end();
865a2e7af75SValentin Clement }
866a2e7af75SValentin Clement 
867a2e7af75SValentin Clement //===----------------------------------------------------------------------===//
868a2e7af75SValentin Clement // MutableBoxValue syncing implementation
869a2e7af75SValentin Clement //===----------------------------------------------------------------------===//
870a2e7af75SValentin Clement 
871a2e7af75SValentin Clement /// Depending on the implementation, allocatable/pointer descriptor and the
872a2e7af75SValentin Clement /// MutableBoxValue need to be synced before and after calls passing the
873a2e7af75SValentin Clement /// descriptor. These calls will generate the syncing if needed or be no-op.
getMutableIRBox(fir::FirOpBuilder & builder,mlir::Location loc,const fir::MutableBoxValue & box)874a2e7af75SValentin Clement mlir::Value fir::factory::getMutableIRBox(fir::FirOpBuilder &builder,
875a2e7af75SValentin Clement                                           mlir::Location loc,
876a2e7af75SValentin Clement                                           const fir::MutableBoxValue &box) {
877a2e7af75SValentin Clement   MutablePropertyWriter{builder, loc, box}.syncIRBoxFromMutableProperties();
878a2e7af75SValentin Clement   return box.getAddr();
879a2e7af75SValentin Clement }
syncMutableBoxFromIRBox(fir::FirOpBuilder & builder,mlir::Location loc,const fir::MutableBoxValue & box)880a2e7af75SValentin Clement void fir::factory::syncMutableBoxFromIRBox(fir::FirOpBuilder &builder,
881a2e7af75SValentin Clement                                            mlir::Location loc,
882a2e7af75SValentin Clement                                            const fir::MutableBoxValue &box) {
883a2e7af75SValentin Clement   MutablePropertyWriter{builder, loc, box}.syncMutablePropertiesFromIRBox();
884a2e7af75SValentin Clement }
885