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