1 //===-- MutableBox.cpp -- MutableBox utilities ----------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "flang/Optimizer/Builder/MutableBox.h" 14 #include "flang/Lower/Todo.h" 15 #include "flang/Optimizer/Builder/Character.h" 16 #include "flang/Optimizer/Builder/FIRBuilder.h" 17 #include "flang/Optimizer/Dialect/FIROps.h" 18 #include "flang/Optimizer/Dialect/FIROpsSupport.h" 19 #include "flang/Optimizer/Support/FatalError.h" 20 21 //===----------------------------------------------------------------------===// 22 // MutableBoxValue writer and reader 23 //===----------------------------------------------------------------------===// 24 25 namespace { 26 /// MutablePropertyWriter and MutablePropertyReader implementations are the only 27 /// places that depend on how the properties of MutableBoxValue (pointers and 28 /// allocatables) that can be modified in the lifetime of the entity (address, 29 /// extents, lower bounds, length parameters) are represented. 30 /// That is, the properties may be only stored in a fir.box in memory if we 31 /// need to enforce a single point of truth for the properties across calls. 32 /// Or, they can be tracked as independent local variables when it is safe to 33 /// do so. Using bare variables benefits from all optimization passes, even 34 /// when they are not aware of what a fir.box is and fir.box have not been 35 /// optimized out yet. 36 37 /// MutablePropertyWriter allows reading the properties of a MutableBoxValue. 38 class MutablePropertyReader { 39 public: 40 MutablePropertyReader(fir::FirOpBuilder &builder, mlir::Location loc, 41 const fir::MutableBoxValue &box, 42 bool forceIRBoxRead = false) 43 : builder{builder}, loc{loc}, box{box} { 44 if (forceIRBoxRead || !box.isDescribedByVariables()) 45 irBox = builder.create<fir::LoadOp>(loc, box.getAddr()); 46 } 47 /// Get base address of allocated/associated entity. 48 mlir::Value readBaseAddress() { 49 if (irBox) { 50 auto heapOrPtrTy = box.getBoxTy().getEleTy(); 51 return builder.create<fir::BoxAddrOp>(loc, heapOrPtrTy, irBox); 52 } 53 auto addrVar = box.getMutableProperties().addr; 54 return builder.create<fir::LoadOp>(loc, addrVar); 55 } 56 /// Return {lbound, extent} values read from the MutableBoxValue given 57 /// the dimension. 58 std::pair<mlir::Value, mlir::Value> readShape(unsigned dim) { 59 auto idxTy = builder.getIndexType(); 60 if (irBox) { 61 auto dimVal = builder.createIntegerConstant(loc, idxTy, dim); 62 auto dimInfo = builder.create<fir::BoxDimsOp>(loc, idxTy, idxTy, idxTy, 63 irBox, dimVal); 64 return {dimInfo.getResult(0), dimInfo.getResult(1)}; 65 } 66 const auto &mutableProperties = box.getMutableProperties(); 67 auto lb = builder.create<fir::LoadOp>(loc, mutableProperties.lbounds[dim]); 68 auto ext = builder.create<fir::LoadOp>(loc, mutableProperties.extents[dim]); 69 return {lb, ext}; 70 } 71 72 /// Return the character length. If the length was not deferred, the value 73 /// that was specified is returned (The mutable fields is not read). 74 mlir::Value readCharacterLength() { 75 if (box.hasNonDeferredLenParams()) 76 return box.nonDeferredLenParams()[0]; 77 if (irBox) 78 return fir::factory::CharacterExprHelper{builder, loc}.readLengthFromBox( 79 irBox); 80 const auto &deferred = box.getMutableProperties().deferredParams; 81 if (deferred.empty()) 82 fir::emitFatalError(loc, "allocatable entity has no length property"); 83 return builder.create<fir::LoadOp>(loc, deferred[0]); 84 } 85 86 /// Read and return all extents. If \p lbounds vector is provided, lbounds are 87 /// also read into it. 88 llvm::SmallVector<mlir::Value> 89 readShape(llvm::SmallVectorImpl<mlir::Value> *lbounds = nullptr) { 90 llvm::SmallVector<mlir::Value> extents(box.rank()); 91 auto rank = box.rank(); 92 for (decltype(rank) dim = 0; dim < rank; ++dim) { 93 auto [lb, extent] = readShape(dim); 94 if (lbounds) 95 lbounds->push_back(lb); 96 extents.push_back(extent); 97 } 98 return extents; 99 } 100 101 /// Read all mutable properties. Return the base address. 102 mlir::Value read(llvm::SmallVectorImpl<mlir::Value> &lbounds, 103 llvm::SmallVectorImpl<mlir::Value> &extents, 104 llvm::SmallVectorImpl<mlir::Value> &lengths) { 105 extents = readShape(&lbounds); 106 if (box.isCharacter()) 107 lengths.emplace_back(readCharacterLength()); 108 else if (box.isDerivedWithLengthParameters()) 109 TODO(loc, "read allocatable or pointer derived type LEN parameters"); 110 return readBaseAddress(); 111 } 112 113 /// Return the loaded fir.box. 114 mlir::Value getIrBox() const { 115 assert(irBox); 116 return irBox; 117 } 118 119 /// Read the lower bounds 120 void getLowerBounds(llvm::SmallVectorImpl<mlir::Value> &lbounds) { 121 auto rank = box.rank(); 122 for (decltype(rank) dim = 0; dim < rank; ++dim) 123 lbounds.push_back(std::get<0>(readShape(dim))); 124 } 125 126 private: 127 fir::FirOpBuilder &builder; 128 mlir::Location loc; 129 fir::MutableBoxValue box; 130 mlir::Value irBox; 131 }; 132 133 /// MutablePropertyWriter allows modifying the properties of a MutableBoxValue. 134 class MutablePropertyWriter { 135 public: 136 MutablePropertyWriter(fir::FirOpBuilder &builder, mlir::Location loc, 137 const fir::MutableBoxValue &box) 138 : builder{builder}, loc{loc}, box{box} {} 139 /// Update MutableBoxValue with new address, shape and length parameters. 140 /// Extents and lbounds must all have index type. 141 /// lbounds can be empty in which case all ones is assumed. 142 /// Length parameters must be provided for the length parameters that are 143 /// deferred. 144 void updateMutableBox(mlir::Value addr, mlir::ValueRange lbounds, 145 mlir::ValueRange extents, mlir::ValueRange lengths) { 146 if (box.isDescribedByVariables()) 147 updateMutableProperties(addr, lbounds, extents, lengths); 148 else 149 updateIRBox(addr, lbounds, extents, lengths); 150 } 151 152 /// Update MutableBoxValue with a new fir.box. This requires that the mutable 153 /// box is not described by a set of variables, since they could not describe 154 /// all that can be described in the new fir.box (e.g. non contiguous entity). 155 void updateWithIrBox(mlir::Value newBox) { 156 assert(!box.isDescribedByVariables()); 157 builder.create<fir::StoreOp>(loc, newBox, box.getAddr()); 158 } 159 /// Set unallocated/disassociated status for the entity described by 160 /// MutableBoxValue. Deallocation is not performed by this helper. 161 void setUnallocatedStatus() { 162 if (box.isDescribedByVariables()) { 163 auto addrVar = box.getMutableProperties().addr; 164 auto nullTy = fir::dyn_cast_ptrEleTy(addrVar.getType()); 165 builder.create<fir::StoreOp>(loc, builder.createNullConstant(loc, nullTy), 166 addrVar); 167 } else { 168 // Note that the dynamic type of polymorphic entities must be reset to the 169 // declaration type of the mutable box. See Fortran 2018 7.8.2 NOTE 1. 170 // For those, we cannot simply set the address to zero. The way we are 171 // currently unallocating fir.box guarantees that we are resetting the 172 // type to the declared type. Beware if changing this. 173 // Note: the standard is not clear in Deallocate and p => NULL semantics 174 // regarding the new dynamic type the entity must have. So far, assume 175 // this is just like NULLIFY and the dynamic type must be set to the 176 // declared type, not retain the previous dynamic type. 177 auto deallocatedBox = fir::factory::createUnallocatedBox( 178 builder, loc, box.getBoxTy(), box.nonDeferredLenParams()); 179 builder.create<fir::StoreOp>(loc, deallocatedBox, box.getAddr()); 180 } 181 } 182 183 /// Copy Values from the fir.box into the property variables if any. 184 void syncMutablePropertiesFromIRBox() { 185 if (!box.isDescribedByVariables()) 186 return; 187 llvm::SmallVector<mlir::Value> lbounds; 188 llvm::SmallVector<mlir::Value> extents; 189 llvm::SmallVector<mlir::Value> lengths; 190 auto addr = 191 MutablePropertyReader{builder, loc, box, /*forceIRBoxRead=*/true}.read( 192 lbounds, extents, lengths); 193 updateMutableProperties(addr, lbounds, extents, lengths); 194 } 195 196 /// Copy Values from property variables, if any, into the fir.box. 197 void syncIRBoxFromMutableProperties() { 198 if (!box.isDescribedByVariables()) 199 return; 200 llvm::SmallVector<mlir::Value> lbounds; 201 llvm::SmallVector<mlir::Value> extents; 202 llvm::SmallVector<mlir::Value> lengths; 203 auto addr = MutablePropertyReader{builder, loc, box}.read(lbounds, extents, 204 lengths); 205 updateIRBox(addr, lbounds, extents, lengths); 206 } 207 208 private: 209 /// Update the IR box (fir.ref<fir.box<T>>) of the MutableBoxValue. 210 void updateIRBox(mlir::Value addr, mlir::ValueRange lbounds, 211 mlir::ValueRange extents, mlir::ValueRange lengths) { 212 mlir::Value shape; 213 if (!extents.empty()) { 214 if (lbounds.empty()) { 215 auto shapeType = 216 fir::ShapeType::get(builder.getContext(), extents.size()); 217 shape = builder.create<fir::ShapeOp>(loc, shapeType, extents); 218 } else { 219 llvm::SmallVector<mlir::Value> shapeShiftBounds; 220 for (auto [lb, extent] : llvm::zip(lbounds, extents)) { 221 shapeShiftBounds.emplace_back(lb); 222 shapeShiftBounds.emplace_back(extent); 223 } 224 auto shapeShiftType = 225 fir::ShapeShiftType::get(builder.getContext(), extents.size()); 226 shape = builder.create<fir::ShapeShiftOp>(loc, shapeShiftType, 227 shapeShiftBounds); 228 } 229 } 230 mlir::Value emptySlice; 231 // Ignore lengths if already constant in the box type (this would trigger an 232 // error in the embox). 233 llvm::SmallVector<mlir::Value> cleanedLengths; 234 mlir::Value irBox; 235 if (addr.getType().isa<fir::BoxType>()) { 236 // The entity is already boxed. 237 irBox = builder.createConvert(loc, box.getBoxTy(), addr); 238 } else { 239 auto cleanedAddr = addr; 240 if (auto charTy = box.getEleTy().dyn_cast<fir::CharacterType>()) { 241 // Cast address to box type so that both input and output type have 242 // unknown or constant lengths. 243 auto bt = box.getBaseTy(); 244 auto addrTy = addr.getType(); 245 auto type = addrTy.isa<fir::HeapType>() ? fir::HeapType::get(bt) 246 : addrTy.isa<fir::PointerType>() ? fir::PointerType::get(bt) 247 : builder.getRefType(bt); 248 cleanedAddr = builder.createConvert(loc, type, addr); 249 if (charTy.getLen() == fir::CharacterType::unknownLen()) 250 cleanedLengths.append(lengths.begin(), lengths.end()); 251 } else if (box.isDerivedWithLengthParameters()) { 252 TODO(loc, "updating mutablebox of derived type with length parameters"); 253 cleanedLengths = lengths; 254 } 255 irBox = builder.create<fir::EmboxOp>(loc, box.getBoxTy(), cleanedAddr, 256 shape, emptySlice, cleanedLengths); 257 } 258 builder.create<fir::StoreOp>(loc, irBox, box.getAddr()); 259 } 260 261 /// Update the set of property variables of the MutableBoxValue. 262 void updateMutableProperties(mlir::Value addr, mlir::ValueRange lbounds, 263 mlir::ValueRange extents, 264 mlir::ValueRange lengths) { 265 auto castAndStore = [&](mlir::Value val, mlir::Value addr) { 266 auto type = fir::dyn_cast_ptrEleTy(addr.getType()); 267 builder.create<fir::StoreOp>(loc, builder.createConvert(loc, type, val), 268 addr); 269 }; 270 const auto &mutableProperties = box.getMutableProperties(); 271 castAndStore(addr, mutableProperties.addr); 272 for (auto [extent, extentVar] : 273 llvm::zip(extents, mutableProperties.extents)) 274 castAndStore(extent, extentVar); 275 if (!mutableProperties.lbounds.empty()) { 276 if (lbounds.empty()) { 277 auto one = 278 builder.createIntegerConstant(loc, builder.getIndexType(), 1); 279 for (auto lboundVar : mutableProperties.lbounds) 280 castAndStore(one, lboundVar); 281 } else { 282 for (auto [lbound, lboundVar] : 283 llvm::zip(lbounds, mutableProperties.lbounds)) 284 castAndStore(lbound, lboundVar); 285 } 286 } 287 if (box.isCharacter()) 288 // llvm::zip account for the fact that the length only needs to be stored 289 // when it is specified in the allocation and deferred in the 290 // MutableBoxValue. 291 for (auto [len, lenVar] : 292 llvm::zip(lengths, mutableProperties.deferredParams)) 293 castAndStore(len, lenVar); 294 else if (box.isDerivedWithLengthParameters()) 295 TODO(loc, "update allocatable derived type length parameters"); 296 } 297 fir::FirOpBuilder &builder; 298 mlir::Location loc; 299 fir::MutableBoxValue box; 300 }; 301 302 } // namespace 303 304 mlir::Value 305 fir::factory::createUnallocatedBox(fir::FirOpBuilder &builder, 306 mlir::Location loc, mlir::Type boxType, 307 mlir::ValueRange nonDeferredParams) { 308 auto heapType = boxType.dyn_cast<fir::BoxType>().getEleTy(); 309 auto type = fir::dyn_cast_ptrEleTy(heapType); 310 auto eleTy = type; 311 if (auto seqType = eleTy.dyn_cast<fir::SequenceType>()) 312 eleTy = seqType.getEleTy(); 313 if (auto recTy = eleTy.dyn_cast<fir::RecordType>()) 314 if (recTy.getNumLenParams() > 0) 315 TODO(loc, "creating unallocated fir.box of derived type with length " 316 "parameters"); 317 auto nullAddr = builder.createNullConstant(loc, heapType); 318 mlir::Value shape; 319 if (auto seqTy = type.dyn_cast<fir::SequenceType>()) { 320 auto zero = builder.createIntegerConstant(loc, builder.getIndexType(), 0); 321 llvm::SmallVector<mlir::Value> extents(seqTy.getDimension(), zero); 322 shape = builder.createShape( 323 loc, fir::ArrayBoxValue{nullAddr, extents, /*lbounds=*/llvm::None}); 324 } 325 // Provide dummy length parameters if they are dynamic. If a length parameter 326 // is deferred. It is set to zero here and will be set on allocation. 327 llvm::SmallVector<mlir::Value> lenParams; 328 if (auto charTy = eleTy.dyn_cast<fir::CharacterType>()) { 329 if (charTy.getLen() == fir::CharacterType::unknownLen()) { 330 if (!nonDeferredParams.empty()) { 331 lenParams.push_back(nonDeferredParams[0]); 332 } else { 333 auto zero = builder.createIntegerConstant( 334 loc, builder.getCharacterLengthType(), 0); 335 lenParams.push_back(zero); 336 } 337 } 338 } 339 mlir::Value emptySlice; 340 return builder.create<fir::EmboxOp>(loc, boxType, nullAddr, shape, emptySlice, 341 lenParams); 342 } 343 344 fir::MutableBoxValue 345 fir::factory::createTempMutableBox(fir::FirOpBuilder &builder, 346 mlir::Location loc, mlir::Type type, 347 llvm::StringRef name) { 348 auto boxType = fir::BoxType::get(fir::HeapType::get(type)); 349 auto boxAddr = builder.createTemporary(loc, boxType, name); 350 auto box = 351 fir::MutableBoxValue(boxAddr, /*nonDeferredParams=*/mlir::ValueRange(), 352 /*mutableProperties=*/{}); 353 MutablePropertyWriter{builder, loc, box}.setUnallocatedStatus(); 354 return box; 355 } 356 357 /// Helper to decide if a MutableBoxValue must be read to a BoxValue or 358 /// can be read to a reified box value. 359 static bool readToBoxValue(const fir::MutableBoxValue &box, 360 bool mayBePolymorphic) { 361 // If this is described by a set of local variables, the value 362 // should not be tracked as a fir.box. 363 if (box.isDescribedByVariables()) 364 return false; 365 // Polymorphism might be a source of discontiguity, even on allocatables. 366 // Track value as fir.box 367 if ((box.isDerived() && mayBePolymorphic) || box.isUnlimitedPolymorphic()) 368 return true; 369 // Intrinsic allocatables are contiguous, no need to track the value by 370 // fir.box. 371 if (box.isAllocatable() || box.rank() == 0) 372 return false; 373 // Pointers are known to be contiguous at compile time iff they have the 374 // CONTIGUOUS attribute. 375 return !fir::valueHasFirAttribute(box.getAddr(), 376 fir::getContiguousAttrName()); 377 } 378 379 fir::ExtendedValue 380 fir::factory::genMutableBoxRead(fir::FirOpBuilder &builder, mlir::Location loc, 381 const fir::MutableBoxValue &box, 382 bool mayBePolymorphic) { 383 if (box.hasAssumedRank()) 384 TODO(loc, "Assumed rank allocatables or pointers"); 385 llvm::SmallVector<mlir::Value> lbounds; 386 llvm::SmallVector<mlir::Value> extents; 387 llvm::SmallVector<mlir::Value> lengths; 388 if (readToBoxValue(box, mayBePolymorphic)) { 389 auto reader = MutablePropertyReader(builder, loc, box); 390 reader.getLowerBounds(lbounds); 391 return fir::BoxValue{reader.getIrBox(), lbounds, 392 box.nonDeferredLenParams()}; 393 } 394 // Contiguous intrinsic type entity: all the data can be extracted from the 395 // fir.box. 396 auto addr = 397 MutablePropertyReader(builder, loc, box).read(lbounds, extents, lengths); 398 auto rank = box.rank(); 399 if (box.isCharacter()) { 400 auto len = lengths.empty() ? mlir::Value{} : lengths[0]; 401 if (rank) 402 return fir::CharArrayBoxValue{addr, len, extents, lbounds}; 403 return fir::CharBoxValue{addr, len}; 404 } 405 if (rank) 406 return fir::ArrayBoxValue{addr, extents, lbounds}; 407 return addr; 408 } 409 410 mlir::Value 411 fir::factory::genIsAllocatedOrAssociatedTest(fir::FirOpBuilder &builder, 412 mlir::Location loc, 413 const fir::MutableBoxValue &box) { 414 auto addr = MutablePropertyReader(builder, loc, box).readBaseAddress(); 415 return builder.genIsNotNull(loc, addr); 416 } 417 418 /// Generate finalizer call and inlined free. This does not check that the 419 /// address was allocated. 420 static void genFinalizeAndFree(fir::FirOpBuilder &builder, mlir::Location loc, 421 mlir::Value addr) { 422 // TODO: call finalizer if any. 423 424 // A heap (ALLOCATABLE) object may have been converted to a ptr (POINTER), 425 // so make sure the heap type is restored before deallocation. 426 auto cast = builder.createConvert( 427 loc, fir::HeapType::get(fir::dyn_cast_ptrEleTy(addr.getType())), addr); 428 builder.create<fir::FreeMemOp>(loc, cast); 429 } 430 431 void fir::factory::genFinalization(fir::FirOpBuilder &builder, 432 mlir::Location loc, 433 const fir::MutableBoxValue &box) { 434 auto addr = MutablePropertyReader(builder, loc, box).readBaseAddress(); 435 auto isAllocated = builder.genIsNotNull(loc, addr); 436 auto ifOp = builder.create<fir::IfOp>(loc, isAllocated, 437 /*withElseRegion=*/false); 438 auto insPt = builder.saveInsertionPoint(); 439 builder.setInsertionPointToStart(&ifOp.thenRegion().front()); 440 genFinalizeAndFree(builder, loc, addr); 441 builder.restoreInsertionPoint(insPt); 442 } 443 444 //===----------------------------------------------------------------------===// 445 // MutableBoxValue writing interface implementation 446 //===----------------------------------------------------------------------===// 447 448 void fir::factory::associateMutableBox(fir::FirOpBuilder &builder, 449 mlir::Location loc, 450 const fir::MutableBoxValue &box, 451 const fir::ExtendedValue &source, 452 mlir::ValueRange lbounds) { 453 MutablePropertyWriter writer(builder, loc, box); 454 source.match( 455 [&](const fir::UnboxedValue &addr) { 456 writer.updateMutableBox(addr, /*lbounds=*/llvm::None, 457 /*extents=*/llvm::None, /*lengths=*/llvm::None); 458 }, 459 [&](const fir::CharBoxValue &ch) { 460 writer.updateMutableBox(ch.getAddr(), /*lbounds=*/llvm::None, 461 /*extents=*/llvm::None, {ch.getLen()}); 462 }, 463 [&](const fir::ArrayBoxValue &arr) { 464 writer.updateMutableBox(arr.getAddr(), 465 lbounds.empty() ? arr.getLBounds() : lbounds, 466 arr.getExtents(), /*lengths=*/llvm::None); 467 }, 468 [&](const fir::CharArrayBoxValue &arr) { 469 writer.updateMutableBox(arr.getAddr(), 470 lbounds.empty() ? arr.getLBounds() : lbounds, 471 arr.getExtents(), {arr.getLen()}); 472 }, 473 [&](const fir::BoxValue &arr) { 474 // Rebox array fir.box to the pointer type and apply potential new lower 475 // bounds. 476 mlir::ValueRange newLbounds = lbounds.empty() 477 ? mlir::ValueRange{arr.getLBounds()} 478 : mlir::ValueRange{lbounds}; 479 if (box.isDescribedByVariables()) { 480 // LHS is a contiguous pointer described by local variables. Open RHS 481 // fir.box to update the LHS. 482 auto rawAddr = builder.create<fir::BoxAddrOp>(loc, arr.getMemTy(), 483 arr.getAddr()); 484 auto extents = fir::factory::getExtents(builder, loc, source); 485 llvm::SmallVector<mlir::Value> lenParams; 486 if (arr.isCharacter()) { 487 lenParams.emplace_back( 488 fir::factory::readCharLen(builder, loc, source)); 489 } else if (arr.isDerivedWithLengthParameters()) { 490 TODO(loc, "pointer assignment to derived with length parameters"); 491 } 492 writer.updateMutableBox(rawAddr, newLbounds, extents, lenParams); 493 } else { 494 mlir::Value shift; 495 if (!newLbounds.empty()) { 496 auto shiftType = 497 fir::ShiftType::get(builder.getContext(), newLbounds.size()); 498 shift = builder.create<fir::ShiftOp>(loc, shiftType, newLbounds); 499 } 500 auto reboxed = 501 builder.create<fir::ReboxOp>(loc, box.getBoxTy(), arr.getAddr(), 502 shift, /*slice=*/mlir::Value()); 503 writer.updateWithIrBox(reboxed); 504 } 505 }, 506 [&](const fir::MutableBoxValue &) { 507 // No point implementing this, if right-hand side is a 508 // pointer/allocatable, the related MutableBoxValue has been read into 509 // another ExtendedValue category. 510 fir::emitFatalError(loc, 511 "Cannot write MutableBox to another MutableBox"); 512 }, 513 [&](const fir::ProcBoxValue &) { 514 TODO(loc, "Procedure pointer assignment"); 515 }); 516 } 517 518 void fir::factory::associateMutableBoxWithRemap( 519 fir::FirOpBuilder &builder, mlir::Location loc, 520 const fir::MutableBoxValue &box, const fir::ExtendedValue &source, 521 mlir::ValueRange lbounds, mlir::ValueRange ubounds) { 522 // Compute new extents 523 llvm::SmallVector<mlir::Value> extents; 524 auto idxTy = builder.getIndexType(); 525 if (!lbounds.empty()) { 526 auto one = builder.createIntegerConstant(loc, idxTy, 1); 527 for (auto [lb, ub] : llvm::zip(lbounds, ubounds)) { 528 auto lbi = builder.createConvert(loc, idxTy, lb); 529 auto ubi = builder.createConvert(loc, idxTy, ub); 530 auto diff = builder.create<arith::SubIOp>(loc, idxTy, ubi, lbi); 531 extents.emplace_back( 532 builder.create<arith::AddIOp>(loc, idxTy, diff, one)); 533 } 534 } else { 535 // lbounds are default. Upper bounds and extents are the same. 536 for (auto ub : ubounds) { 537 auto cast = builder.createConvert(loc, idxTy, ub); 538 extents.emplace_back(cast); 539 } 540 } 541 const auto newRank = extents.size(); 542 auto cast = [&](mlir::Value addr) -> mlir::Value { 543 // Cast base addr to new sequence type. 544 auto ty = fir::dyn_cast_ptrEleTy(addr.getType()); 545 if (auto seqTy = ty.dyn_cast<fir::SequenceType>()) { 546 fir::SequenceType::Shape shape(newRank, 547 fir::SequenceType::getUnknownExtent()); 548 ty = fir::SequenceType::get(shape, seqTy.getEleTy()); 549 } 550 return builder.createConvert(loc, builder.getRefType(ty), addr); 551 }; 552 MutablePropertyWriter writer(builder, loc, box); 553 source.match( 554 [&](const fir::UnboxedValue &addr) { 555 writer.updateMutableBox(cast(addr), lbounds, extents, 556 /*lengths=*/llvm::None); 557 }, 558 [&](const fir::CharBoxValue &ch) { 559 writer.updateMutableBox(cast(ch.getAddr()), lbounds, extents, 560 {ch.getLen()}); 561 }, 562 [&](const fir::ArrayBoxValue &arr) { 563 writer.updateMutableBox(cast(arr.getAddr()), lbounds, extents, 564 /*lengths=*/llvm::None); 565 }, 566 [&](const fir::CharArrayBoxValue &arr) { 567 writer.updateMutableBox(cast(arr.getAddr()), lbounds, extents, 568 {arr.getLen()}); 569 }, 570 [&](const fir::BoxValue &arr) { 571 // Rebox right-hand side fir.box with a new shape and type. 572 if (box.isDescribedByVariables()) { 573 // LHS is a contiguous pointer described by local variables. Open RHS 574 // fir.box to update the LHS. 575 auto rawAddr = builder.create<fir::BoxAddrOp>(loc, arr.getMemTy(), 576 arr.getAddr()); 577 llvm::SmallVector<mlir::Value> lenParams; 578 if (arr.isCharacter()) { 579 lenParams.emplace_back( 580 fir::factory::readCharLen(builder, loc, source)); 581 } else if (arr.isDerivedWithLengthParameters()) { 582 TODO(loc, "pointer assignment to derived with length parameters"); 583 } 584 writer.updateMutableBox(rawAddr, lbounds, extents, lenParams); 585 } else { 586 auto shapeType = 587 fir::ShapeShiftType::get(builder.getContext(), extents.size()); 588 llvm::SmallVector<mlir::Value> shapeArgs; 589 auto idxTy = builder.getIndexType(); 590 for (auto [lbnd, ext] : llvm::zip(lbounds, extents)) { 591 auto lb = builder.createConvert(loc, idxTy, lbnd); 592 shapeArgs.push_back(lb); 593 shapeArgs.push_back(ext); 594 } 595 auto shape = 596 builder.create<fir::ShapeShiftOp>(loc, shapeType, shapeArgs); 597 auto reboxed = 598 builder.create<fir::ReboxOp>(loc, box.getBoxTy(), arr.getAddr(), 599 shape, /*slice=*/mlir::Value()); 600 writer.updateWithIrBox(reboxed); 601 } 602 }, 603 [&](const fir::MutableBoxValue &) { 604 // No point implementing this, if right-hand side is a pointer or 605 // allocatable, the related MutableBoxValue has already been read into 606 // another ExtendedValue category. 607 fir::emitFatalError(loc, 608 "Cannot write MutableBox to another MutableBox"); 609 }, 610 [&](const fir::ProcBoxValue &) { 611 TODO(loc, "Procedure pointer assignment"); 612 }); 613 } 614 615 void fir::factory::disassociateMutableBox(fir::FirOpBuilder &builder, 616 mlir::Location loc, 617 const fir::MutableBoxValue &box) { 618 MutablePropertyWriter{builder, loc, box}.setUnallocatedStatus(); 619 } 620 621 void fir::factory::genInlinedAllocation(fir::FirOpBuilder &builder, 622 mlir::Location loc, 623 const fir::MutableBoxValue &box, 624 mlir::ValueRange lbounds, 625 mlir::ValueRange extents, 626 mlir::ValueRange lenParams, 627 llvm::StringRef allocName) { 628 auto idxTy = builder.getIndexType(); 629 llvm::SmallVector<mlir::Value> lengths; 630 if (auto charTy = box.getEleTy().dyn_cast<fir::CharacterType>()) { 631 if (charTy.getLen() == fir::CharacterType::unknownLen()) { 632 if (box.hasNonDeferredLenParams()) 633 lengths.emplace_back( 634 builder.createConvert(loc, idxTy, box.nonDeferredLenParams()[0])); 635 else if (!lenParams.empty()) 636 lengths.emplace_back(builder.createConvert(loc, idxTy, lenParams[0])); 637 else 638 fir::emitFatalError( 639 loc, "could not deduce character lengths in character allocation"); 640 } 641 } 642 mlir::Value heap = builder.create<fir::AllocMemOp>( 643 loc, box.getBaseTy(), allocName, lengths, extents); 644 // TODO: run initializer if any. Currently, there is no way to know this is 645 // required here. 646 MutablePropertyWriter{builder, loc, box}.updateMutableBox(heap, lbounds, 647 extents, lengths); 648 } 649 650 void fir::factory::genInlinedDeallocate(fir::FirOpBuilder &builder, 651 mlir::Location loc, 652 const fir::MutableBoxValue &box) { 653 auto addr = MutablePropertyReader(builder, loc, box).readBaseAddress(); 654 genFinalizeAndFree(builder, loc, addr); 655 MutablePropertyWriter{builder, loc, box}.setUnallocatedStatus(); 656 } 657 658 void fir::factory::genReallocIfNeeded(fir::FirOpBuilder &builder, 659 mlir::Location loc, 660 const fir::MutableBoxValue &box, 661 mlir::ValueRange lbounds, 662 mlir::ValueRange shape, 663 mlir::ValueRange lengthParams) { 664 // Implement 10.2.1.3 point 3 logic when lhs is an array. 665 auto reader = MutablePropertyReader(builder, loc, box); 666 auto addr = reader.readBaseAddress(); 667 auto isAllocated = builder.genIsNotNull(loc, addr); 668 builder.genIfThenElse(loc, isAllocated) 669 .genThen([&]() { 670 // The box is allocated. Check if it must be reallocated and reallocate. 671 mlir::Value mustReallocate = builder.createBool(loc, false); 672 auto compareProperty = [&](mlir::Value previous, mlir::Value required) { 673 auto castPrevious = 674 builder.createConvert(loc, required.getType(), previous); 675 // reallocate = reallocate || previous != required 676 auto cmp = builder.create<arith::CmpIOp>( 677 loc, arith::CmpIPredicate::ne, castPrevious, required); 678 mustReallocate = 679 builder.create<mlir::SelectOp>(loc, cmp, cmp, mustReallocate); 680 }; 681 llvm::SmallVector<mlir::Value> previousLbounds; 682 llvm::SmallVector<mlir::Value> previousExtents = 683 reader.readShape(&previousLbounds); 684 if (!shape.empty()) 685 for (auto [previousExtent, requested] : 686 llvm::zip(previousExtents, shape)) 687 compareProperty(previousExtent, requested); 688 689 if (box.isCharacter() && !box.hasNonDeferredLenParams()) { 690 // When the allocatable length is not deferred, it must not be 691 // reallocated in case of length mismatch, instead, padding/trimming 692 // will ocur in later assignment to it. 693 assert(!lengthParams.empty() && 694 "must provide length parameters for character"); 695 compareProperty(reader.readCharacterLength(), lengthParams[0]); 696 } else if (box.isDerivedWithLengthParameters()) { 697 TODO(loc, 698 "automatic allocation of derived type allocatable with length " 699 "parameters"); 700 } 701 builder.genIfThen(loc, mustReallocate) 702 .genThen([&]() { 703 // If shape or length mismatch, deallocate and reallocate. 704 genFinalizeAndFree(builder, loc, addr); 705 // When rhs is a scalar, keep the previous shape 706 auto extents = 707 shape.empty() ? mlir::ValueRange(previousExtents) : shape; 708 auto lbs = 709 shape.empty() ? mlir::ValueRange(previousLbounds) : lbounds; 710 genInlinedAllocation(builder, loc, box, lbs, extents, 711 lengthParams, ".auto.alloc"); 712 }) 713 .end(); 714 }) 715 .genElse([&]() { 716 // The box is not yet allocated, simply allocate it. 717 if (shape.empty() && box.rank() != 0) { 718 // TODO: 719 // runtime error: right hand side must be allocated if right hand 720 // side is a scalar and the box is an array. 721 } else { 722 genInlinedAllocation(builder, loc, box, lbounds, shape, lengthParams, 723 ".auto.alloc"); 724 } 725 }) 726 .end(); 727 } 728 729 //===----------------------------------------------------------------------===// 730 // MutableBoxValue syncing implementation 731 //===----------------------------------------------------------------------===// 732 733 /// Depending on the implementation, allocatable/pointer descriptor and the 734 /// MutableBoxValue need to be synced before and after calls passing the 735 /// descriptor. These calls will generate the syncing if needed or be no-op. 736 mlir::Value fir::factory::getMutableIRBox(fir::FirOpBuilder &builder, 737 mlir::Location loc, 738 const fir::MutableBoxValue &box) { 739 MutablePropertyWriter{builder, loc, box}.syncIRBoxFromMutableProperties(); 740 return box.getAddr(); 741 } 742 void fir::factory::syncMutableBoxFromIRBox(fir::FirOpBuilder &builder, 743 mlir::Location loc, 744 const fir::MutableBoxValue &box) { 745 MutablePropertyWriter{builder, loc, box}.syncMutablePropertiesFromIRBox(); 746 } 747