| 1 | //===-- FIRBuilder.cpp ----------------------------------------------------===// |
| 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 | #include "flang/Optimizer/Builder/FIRBuilder.h" |
| 10 | #include "flang/Optimizer/Builder/BoxValue.h" |
| 11 | #include "flang/Optimizer/Builder/Character.h" |
| 12 | #include "flang/Optimizer/Builder/Complex.h" |
| 13 | #include "flang/Optimizer/Builder/MutableBox.h" |
| 14 | #include "flang/Optimizer/Builder/Runtime/Allocatable.h" |
| 15 | #include "flang/Optimizer/Builder/Runtime/Assign.h" |
| 16 | #include "flang/Optimizer/Builder/Runtime/Derived.h" |
| 17 | #include "flang/Optimizer/Builder/Todo.h" |
| 18 | #include "flang/Optimizer/Dialect/CUF/CUFOps.h" |
| 19 | #include "flang/Optimizer/Dialect/FIRAttr.h" |
| 20 | #include "flang/Optimizer/Dialect/FIRDialect.h" |
| 21 | #include "flang/Optimizer/Dialect/FIROpsSupport.h" |
| 22 | #include "flang/Optimizer/Dialect/FIRType.h" |
| 23 | #include "flang/Optimizer/Support/DataLayout.h" |
| 24 | #include "flang/Optimizer/Support/FatalError.h" |
| 25 | #include "flang/Optimizer/Support/InternalNames.h" |
| 26 | #include "flang/Optimizer/Support/Utils.h" |
| 27 | #include "mlir/Dialect/LLVMIR/LLVMDialect.h" |
| 28 | #include "mlir/Dialect/OpenACC/OpenACC.h" |
| 29 | #include "mlir/Dialect/OpenMP/OpenMPDialect.h" |
| 30 | #include "llvm/ADT/ArrayRef.h" |
| 31 | #include "llvm/ADT/StringExtras.h" |
| 32 | #include "llvm/Support/CommandLine.h" |
| 33 | #include "llvm/Support/ErrorHandling.h" |
| 34 | #include "llvm/Support/MD5.h" |
| 35 | #include <optional> |
| 36 | |
| 37 | static llvm::cl::opt<std::size_t> |
| 38 | nameLengthHashSize("length-to-hash-string-literal" , |
| 39 | llvm::cl::desc("string literals that exceed this length" |
| 40 | " will use a hash value as their symbol " |
| 41 | "name" ), |
| 42 | llvm::cl::init(Val: 32)); |
| 43 | |
| 44 | mlir::func::FuncOp |
| 45 | fir::FirOpBuilder::createFunction(mlir::Location loc, mlir::ModuleOp module, |
| 46 | llvm::StringRef name, mlir::FunctionType ty, |
| 47 | mlir::SymbolTable *symbolTable) { |
| 48 | return fir::createFuncOp(loc, module, name, ty, /*attrs*/ {}, symbolTable); |
| 49 | } |
| 50 | |
| 51 | mlir::func::FuncOp |
| 52 | fir::FirOpBuilder::createRuntimeFunction(mlir::Location loc, |
| 53 | llvm::StringRef name, |
| 54 | mlir::FunctionType ty, bool isIO) { |
| 55 | mlir::func::FuncOp func = createFunction(loc, name, ty); |
| 56 | func->setAttr(fir::FIROpsDialect::getFirRuntimeAttrName(), getUnitAttr()); |
| 57 | if (isIO) |
| 58 | func->setAttr("fir.io" , getUnitAttr()); |
| 59 | return func; |
| 60 | } |
| 61 | |
| 62 | mlir::func::FuncOp |
| 63 | fir::FirOpBuilder::getNamedFunction(mlir::ModuleOp modOp, |
| 64 | const mlir::SymbolTable *symbolTable, |
| 65 | llvm::StringRef name) { |
| 66 | if (symbolTable) |
| 67 | if (auto func = symbolTable->lookup<mlir::func::FuncOp>(name)) { |
| 68 | #ifdef EXPENSIVE_CHECKS |
| 69 | assert(func == modOp.lookupSymbol<mlir::func::FuncOp>(name) && |
| 70 | "symbolTable and module out of sync" ); |
| 71 | #endif |
| 72 | return func; |
| 73 | } |
| 74 | return modOp.lookupSymbol<mlir::func::FuncOp>(name); |
| 75 | } |
| 76 | |
| 77 | mlir::func::FuncOp |
| 78 | fir::FirOpBuilder::getNamedFunction(mlir::ModuleOp modOp, |
| 79 | const mlir::SymbolTable *symbolTable, |
| 80 | mlir::SymbolRefAttr symbol) { |
| 81 | if (symbolTable) |
| 82 | if (auto func = symbolTable->lookup<mlir::func::FuncOp>( |
| 83 | symbol.getLeafReference())) { |
| 84 | #ifdef EXPENSIVE_CHECKS |
| 85 | assert(func == modOp.lookupSymbol<mlir::func::FuncOp>(symbol) && |
| 86 | "symbolTable and module out of sync" ); |
| 87 | #endif |
| 88 | return func; |
| 89 | } |
| 90 | return modOp.lookupSymbol<mlir::func::FuncOp>(symbol); |
| 91 | } |
| 92 | |
| 93 | fir::GlobalOp |
| 94 | fir::FirOpBuilder::getNamedGlobal(mlir::ModuleOp modOp, |
| 95 | const mlir::SymbolTable *symbolTable, |
| 96 | llvm::StringRef name) { |
| 97 | if (symbolTable) |
| 98 | if (auto global = symbolTable->lookup<fir::GlobalOp>(name)) { |
| 99 | #ifdef EXPENSIVE_CHECKS |
| 100 | assert(global == modOp.lookupSymbol<fir::GlobalOp>(name) && |
| 101 | "symbolTable and module out of sync" ); |
| 102 | #endif |
| 103 | return global; |
| 104 | } |
| 105 | return modOp.lookupSymbol<fir::GlobalOp>(name); |
| 106 | } |
| 107 | |
| 108 | mlir::Type fir::FirOpBuilder::getRefType(mlir::Type eleTy, bool isVolatile) { |
| 109 | assert(!mlir::isa<fir::ReferenceType>(eleTy) && "cannot be a reference type" ); |
| 110 | return fir::ReferenceType::get(eleTy, isVolatile); |
| 111 | } |
| 112 | |
| 113 | mlir::Type fir::FirOpBuilder::getVarLenSeqTy(mlir::Type eleTy, unsigned rank) { |
| 114 | fir::SequenceType::Shape shape(rank, fir::SequenceType::getUnknownExtent()); |
| 115 | return fir::SequenceType::get(shape, eleTy); |
| 116 | } |
| 117 | |
| 118 | mlir::Type fir::FirOpBuilder::getRealType(int kind) { |
| 119 | switch (kindMap.getRealTypeID(kind)) { |
| 120 | case llvm::Type::TypeID::HalfTyID: |
| 121 | return mlir::Float16Type::get(getContext()); |
| 122 | case llvm::Type::TypeID::BFloatTyID: |
| 123 | return mlir::BFloat16Type::get(getContext()); |
| 124 | case llvm::Type::TypeID::FloatTyID: |
| 125 | return mlir::Float32Type::get(getContext()); |
| 126 | case llvm::Type::TypeID::DoubleTyID: |
| 127 | return mlir::Float64Type::get(getContext()); |
| 128 | case llvm::Type::TypeID::X86_FP80TyID: |
| 129 | return mlir::Float80Type::get(getContext()); |
| 130 | case llvm::Type::TypeID::FP128TyID: |
| 131 | return mlir::Float128Type::get(getContext()); |
| 132 | default: |
| 133 | fir::emitFatalError(mlir::UnknownLoc::get(getContext()), |
| 134 | "unsupported type !fir.real<kind>" ); |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | mlir::Value fir::FirOpBuilder::createNullConstant(mlir::Location loc, |
| 139 | mlir::Type ptrType) { |
| 140 | auto ty = ptrType ? ptrType : getRefType(getNoneType()); |
| 141 | return create<fir::ZeroOp>(loc, ty); |
| 142 | } |
| 143 | |
| 144 | mlir::Value fir::FirOpBuilder::createIntegerConstant(mlir::Location loc, |
| 145 | mlir::Type ty, |
| 146 | std::int64_t cst) { |
| 147 | assert((cst >= 0 || mlir::isa<mlir::IndexType>(ty) || |
| 148 | mlir::cast<mlir::IntegerType>(ty).getWidth() <= 64) && |
| 149 | "must use APint" ); |
| 150 | return create<mlir::arith::ConstantOp>(loc, ty, getIntegerAttr(ty, cst)); |
| 151 | } |
| 152 | |
| 153 | mlir::Value fir::FirOpBuilder::createAllOnesInteger(mlir::Location loc, |
| 154 | mlir::Type ty) { |
| 155 | if (mlir::isa<mlir::IndexType>(ty)) |
| 156 | return createIntegerConstant(loc, ty, -1); |
| 157 | llvm::APInt allOnes = |
| 158 | llvm::APInt::getAllOnes(mlir::cast<mlir::IntegerType>(ty).getWidth()); |
| 159 | return create<mlir::arith::ConstantOp>(loc, ty, getIntegerAttr(ty, allOnes)); |
| 160 | } |
| 161 | |
| 162 | mlir::Value |
| 163 | fir::FirOpBuilder::createRealConstant(mlir::Location loc, mlir::Type fltTy, |
| 164 | llvm::APFloat::integerPart val) { |
| 165 | auto apf = [&]() -> llvm::APFloat { |
| 166 | if (fltTy.isF16()) |
| 167 | return llvm::APFloat(llvm::APFloat::IEEEhalf(), val); |
| 168 | if (fltTy.isBF16()) |
| 169 | return llvm::APFloat(llvm::APFloat::BFloat(), val); |
| 170 | if (fltTy.isF32()) |
| 171 | return llvm::APFloat(llvm::APFloat::IEEEsingle(), val); |
| 172 | if (fltTy.isF64()) |
| 173 | return llvm::APFloat(llvm::APFloat::IEEEdouble(), val); |
| 174 | if (fltTy.isF80()) |
| 175 | return llvm::APFloat(llvm::APFloat::x87DoubleExtended(), val); |
| 176 | if (fltTy.isF128()) |
| 177 | return llvm::APFloat(llvm::APFloat::IEEEquad(), val); |
| 178 | llvm_unreachable("unhandled MLIR floating-point type" ); |
| 179 | }; |
| 180 | return createRealConstant(loc, fltTy, apf()); |
| 181 | } |
| 182 | |
| 183 | mlir::Value fir::FirOpBuilder::createRealConstant(mlir::Location loc, |
| 184 | mlir::Type fltTy, |
| 185 | const llvm::APFloat &value) { |
| 186 | if (mlir::isa<mlir::FloatType>(fltTy)) { |
| 187 | auto attr = getFloatAttr(fltTy, value); |
| 188 | return create<mlir::arith::ConstantOp>(loc, fltTy, attr); |
| 189 | } |
| 190 | llvm_unreachable("should use builtin floating-point type" ); |
| 191 | } |
| 192 | |
| 193 | llvm::SmallVector<mlir::Value> |
| 194 | fir::factory::elideExtentsAlreadyInType(mlir::Type type, |
| 195 | mlir::ValueRange shape) { |
| 196 | auto arrTy = mlir::dyn_cast<fir::SequenceType>(type); |
| 197 | if (shape.empty() || !arrTy) |
| 198 | return {}; |
| 199 | // elide the constant dimensions before construction |
| 200 | assert(shape.size() == arrTy.getDimension()); |
| 201 | llvm::SmallVector<mlir::Value> dynamicShape; |
| 202 | auto typeShape = arrTy.getShape(); |
| 203 | for (unsigned i = 0, end = arrTy.getDimension(); i < end; ++i) |
| 204 | if (typeShape[i] == fir::SequenceType::getUnknownExtent()) |
| 205 | dynamicShape.push_back(shape[i]); |
| 206 | return dynamicShape; |
| 207 | } |
| 208 | |
| 209 | llvm::SmallVector<mlir::Value> |
| 210 | fir::factory::elideLengthsAlreadyInType(mlir::Type type, |
| 211 | mlir::ValueRange lenParams) { |
| 212 | if (lenParams.empty()) |
| 213 | return {}; |
| 214 | if (auto arrTy = mlir::dyn_cast<fir::SequenceType>(type)) |
| 215 | type = arrTy.getEleTy(); |
| 216 | if (fir::hasDynamicSize(type)) |
| 217 | return lenParams; |
| 218 | return {}; |
| 219 | } |
| 220 | |
| 221 | /// Allocate a local variable. |
| 222 | /// A local variable ought to have a name in the source code. |
| 223 | mlir::Value fir::FirOpBuilder::allocateLocal( |
| 224 | mlir::Location loc, mlir::Type ty, llvm::StringRef uniqName, |
| 225 | llvm::StringRef name, bool pinned, llvm::ArrayRef<mlir::Value> shape, |
| 226 | llvm::ArrayRef<mlir::Value> lenParams, bool asTarget) { |
| 227 | // Convert the shape extents to `index`, as needed. |
| 228 | llvm::SmallVector<mlir::Value> indices; |
| 229 | llvm::SmallVector<mlir::Value> elidedShape = |
| 230 | fir::factory::elideExtentsAlreadyInType(ty, shape); |
| 231 | llvm::SmallVector<mlir::Value> elidedLenParams = |
| 232 | fir::factory::elideLengthsAlreadyInType(ty, lenParams); |
| 233 | auto idxTy = getIndexType(); |
| 234 | for (mlir::Value sh : elidedShape) |
| 235 | indices.push_back(createConvert(loc, idxTy, sh)); |
| 236 | // Add a target attribute, if needed. |
| 237 | llvm::SmallVector<mlir::NamedAttribute> attrs; |
| 238 | if (asTarget) |
| 239 | attrs.emplace_back( |
| 240 | mlir::StringAttr::get(getContext(), fir::getTargetAttrName()), |
| 241 | getUnitAttr()); |
| 242 | // Create the local variable. |
| 243 | if (name.empty()) { |
| 244 | if (uniqName.empty()) |
| 245 | return create<fir::AllocaOp>(loc, ty, pinned, elidedLenParams, indices, |
| 246 | attrs); |
| 247 | return create<fir::AllocaOp>(loc, ty, uniqName, pinned, elidedLenParams, |
| 248 | indices, attrs); |
| 249 | } |
| 250 | return create<fir::AllocaOp>(loc, ty, uniqName, name, pinned, elidedLenParams, |
| 251 | indices, attrs); |
| 252 | } |
| 253 | |
| 254 | mlir::Value fir::FirOpBuilder::allocateLocal( |
| 255 | mlir::Location loc, mlir::Type ty, llvm::StringRef uniqName, |
| 256 | llvm::StringRef name, llvm::ArrayRef<mlir::Value> shape, |
| 257 | llvm::ArrayRef<mlir::Value> lenParams, bool asTarget) { |
| 258 | return allocateLocal(loc, ty, uniqName, name, /*pinned=*/false, shape, |
| 259 | lenParams, asTarget); |
| 260 | } |
| 261 | |
| 262 | /// Get the block for adding Allocas. |
| 263 | mlir::Block *fir::FirOpBuilder::getAllocaBlock() { |
| 264 | if (auto accComputeRegionIface = |
| 265 | getRegion().getParentOfType<mlir::acc::ComputeRegionOpInterface>()) { |
| 266 | return accComputeRegionIface.getAllocaBlock(); |
| 267 | } |
| 268 | |
| 269 | if (auto ompOutlineableIface = |
| 270 | getRegion() |
| 271 | .getParentOfType<mlir::omp::OutlineableOpenMPOpInterface>()) { |
| 272 | return ompOutlineableIface.getAllocaBlock(); |
| 273 | } |
| 274 | |
| 275 | if (auto recipeIface = |
| 276 | getRegion().getParentOfType<mlir::accomp::RecipeInterface>()) { |
| 277 | return recipeIface.getAllocaBlock(getRegion()); |
| 278 | } |
| 279 | |
| 280 | if (auto cufKernelOp = getRegion().getParentOfType<cuf::KernelOp>()) |
| 281 | return &cufKernelOp.getRegion().front(); |
| 282 | |
| 283 | if (auto doConcurentOp = getRegion().getParentOfType<fir::DoConcurrentOp>()) |
| 284 | return doConcurentOp.getBody(); |
| 285 | |
| 286 | return getEntryBlock(); |
| 287 | } |
| 288 | |
| 289 | static mlir::ArrayAttr makeI64ArrayAttr(llvm::ArrayRef<int64_t> values, |
| 290 | mlir::MLIRContext *context) { |
| 291 | llvm::SmallVector<mlir::Attribute, 4> attrs; |
| 292 | attrs.reserve(N: values.size()); |
| 293 | for (auto &v : values) |
| 294 | attrs.push_back(mlir::IntegerAttr::get(mlir::IntegerType::get(context, 64), |
| 295 | mlir::APInt(64, v))); |
| 296 | return mlir::ArrayAttr::get(context, attrs); |
| 297 | } |
| 298 | |
| 299 | mlir::ArrayAttr fir::FirOpBuilder::create2DI64ArrayAttr( |
| 300 | llvm::SmallVectorImpl<llvm::SmallVector<int64_t>> &intData) { |
| 301 | llvm::SmallVector<mlir::Attribute> arrayAttr; |
| 302 | arrayAttr.reserve(intData.size()); |
| 303 | mlir::MLIRContext *context = getContext(); |
| 304 | for (auto &v : intData) |
| 305 | arrayAttr.push_back(makeI64ArrayAttr(v, context)); |
| 306 | return mlir::ArrayAttr::get(context, arrayAttr); |
| 307 | } |
| 308 | |
| 309 | mlir::Value fir::FirOpBuilder::createTemporaryAlloc( |
| 310 | mlir::Location loc, mlir::Type type, llvm::StringRef name, |
| 311 | mlir::ValueRange lenParams, mlir::ValueRange shape, |
| 312 | llvm::ArrayRef<mlir::NamedAttribute> attrs, |
| 313 | std::optional<Fortran::common::CUDADataAttr> cudaAttr) { |
| 314 | assert(!mlir::isa<fir::ReferenceType>(type) && "cannot be a reference" ); |
| 315 | // If the alloca is inside an OpenMP Op which will be outlined then pin |
| 316 | // the alloca here. |
| 317 | const bool pinned = |
| 318 | getRegion().getParentOfType<mlir::omp::OutlineableOpenMPOpInterface>(); |
| 319 | if (cudaAttr) { |
| 320 | cuf::DataAttributeAttr attr = cuf::getDataAttribute(getContext(), cudaAttr); |
| 321 | return create<cuf::AllocOp>(loc, type, /*unique_name=*/llvm::StringRef{}, |
| 322 | name, attr, lenParams, shape, attrs); |
| 323 | } else { |
| 324 | return create<fir::AllocaOp>(loc, type, /*unique_name=*/llvm::StringRef{}, |
| 325 | name, pinned, lenParams, shape, attrs); |
| 326 | } |
| 327 | } |
| 328 | |
| 329 | /// Create a temporary variable on the stack. Anonymous temporaries have no |
| 330 | /// `name` value. Temporaries do not require a uniqued name. |
| 331 | mlir::Value fir::FirOpBuilder::createTemporary( |
| 332 | mlir::Location loc, mlir::Type type, llvm::StringRef name, |
| 333 | mlir::ValueRange shape, mlir::ValueRange lenParams, |
| 334 | llvm::ArrayRef<mlir::NamedAttribute> attrs, |
| 335 | std::optional<Fortran::common::CUDADataAttr> cudaAttr) { |
| 336 | llvm::SmallVector<mlir::Value> dynamicShape = |
| 337 | fir::factory::elideExtentsAlreadyInType(type, shape); |
| 338 | llvm::SmallVector<mlir::Value> dynamicLength = |
| 339 | fir::factory::elideLengthsAlreadyInType(type, lenParams); |
| 340 | InsertPoint insPt; |
| 341 | const bool hoistAlloc = dynamicShape.empty() && dynamicLength.empty(); |
| 342 | if (hoistAlloc) { |
| 343 | insPt = saveInsertionPoint(); |
| 344 | setInsertionPointToStart(getAllocaBlock()); |
| 345 | } |
| 346 | |
| 347 | mlir::Value ae = createTemporaryAlloc(loc, type, name, dynamicLength, |
| 348 | dynamicShape, attrs, cudaAttr); |
| 349 | |
| 350 | if (hoistAlloc) |
| 351 | restoreInsertionPoint(insPt); |
| 352 | return ae; |
| 353 | } |
| 354 | |
| 355 | mlir::Value fir::FirOpBuilder::createHeapTemporary( |
| 356 | mlir::Location loc, mlir::Type type, llvm::StringRef name, |
| 357 | mlir::ValueRange shape, mlir::ValueRange lenParams, |
| 358 | llvm::ArrayRef<mlir::NamedAttribute> attrs) { |
| 359 | llvm::SmallVector<mlir::Value> dynamicShape = |
| 360 | fir::factory::elideExtentsAlreadyInType(type, shape); |
| 361 | llvm::SmallVector<mlir::Value> dynamicLength = |
| 362 | fir::factory::elideLengthsAlreadyInType(type, lenParams); |
| 363 | |
| 364 | assert(!mlir::isa<fir::ReferenceType>(type) && "cannot be a reference" ); |
| 365 | return create<fir::AllocMemOp>(loc, type, /*unique_name=*/llvm::StringRef{}, |
| 366 | name, dynamicLength, dynamicShape, attrs); |
| 367 | } |
| 368 | |
| 369 | std::pair<mlir::Value, bool> fir::FirOpBuilder::createAndDeclareTemp( |
| 370 | mlir::Location loc, mlir::Type baseType, mlir::Value shape, |
| 371 | llvm::ArrayRef<mlir::Value> extents, llvm::ArrayRef<mlir::Value> typeParams, |
| 372 | const std::function<decltype(FirOpBuilder::genTempDeclareOp)> &genDeclare, |
| 373 | mlir::Value polymorphicMold, bool useStack, llvm::StringRef tmpName) { |
| 374 | if (polymorphicMold) { |
| 375 | // Create *allocated* polymorphic temporary using the dynamic type |
| 376 | // of the mold and the provided shape/extents. |
| 377 | auto boxType = fir::ClassType::get(fir::HeapType::get(baseType)); |
| 378 | mlir::Value boxAddress = fir::factory::getAndEstablishBoxStorage( |
| 379 | *this, loc, boxType, shape, typeParams, polymorphicMold); |
| 380 | fir::runtime::genAllocatableAllocate(*this, loc, boxAddress); |
| 381 | mlir::Value box = create<fir::LoadOp>(loc, boxAddress); |
| 382 | mlir::Value base = |
| 383 | genDeclare(*this, loc, box, tmpName, /*shape=*/mlir::Value{}, |
| 384 | typeParams, fir::FortranVariableFlagsAttr{}); |
| 385 | return {base, /*isHeapAllocation=*/true}; |
| 386 | } |
| 387 | mlir::Value allocmem; |
| 388 | if (useStack) |
| 389 | allocmem = createTemporary(loc, baseType, tmpName, extents, typeParams); |
| 390 | else |
| 391 | allocmem = createHeapTemporary(loc, baseType, tmpName, extents, typeParams); |
| 392 | mlir::Value base = genDeclare(*this, loc, allocmem, tmpName, shape, |
| 393 | typeParams, fir::FortranVariableFlagsAttr{}); |
| 394 | return {base, !useStack}; |
| 395 | } |
| 396 | |
| 397 | mlir::Value fir::FirOpBuilder::genTempDeclareOp( |
| 398 | fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value memref, |
| 399 | llvm::StringRef name, mlir::Value shape, |
| 400 | llvm::ArrayRef<mlir::Value> typeParams, |
| 401 | fir::FortranVariableFlagsAttr fortranAttrs) { |
| 402 | auto nameAttr = mlir::StringAttr::get(builder.getContext(), name); |
| 403 | return builder.create<fir::DeclareOp>(loc, memref.getType(), memref, shape, |
| 404 | typeParams, |
| 405 | /*dummy_scope=*/nullptr, nameAttr, |
| 406 | fortranAttrs, cuf::DataAttributeAttr{}); |
| 407 | } |
| 408 | |
| 409 | mlir::Value fir::FirOpBuilder::genStackSave(mlir::Location loc) { |
| 410 | mlir::Type voidPtr = mlir::LLVM::LLVMPointerType::get( |
| 411 | getContext(), fir::factory::getAllocaAddressSpace(&getDataLayout())); |
| 412 | return create<mlir::LLVM::StackSaveOp>(loc, voidPtr); |
| 413 | } |
| 414 | |
| 415 | void fir::FirOpBuilder::genStackRestore(mlir::Location loc, |
| 416 | mlir::Value stackPointer) { |
| 417 | create<mlir::LLVM::StackRestoreOp>(loc, stackPointer); |
| 418 | } |
| 419 | |
| 420 | /// Create a global variable in the (read-only) data section. A global variable |
| 421 | /// must have a unique name to identify and reference it. |
| 422 | fir::GlobalOp fir::FirOpBuilder::createGlobal( |
| 423 | mlir::Location loc, mlir::Type type, llvm::StringRef name, |
| 424 | mlir::StringAttr linkage, mlir::Attribute value, bool isConst, |
| 425 | bool isTarget, cuf::DataAttributeAttr dataAttr) { |
| 426 | if (auto global = getNamedGlobal(name)) |
| 427 | return global; |
| 428 | auto module = getModule(); |
| 429 | auto insertPt = saveInsertionPoint(); |
| 430 | setInsertionPoint(module.getBody(), module.getBody()->end()); |
| 431 | llvm::SmallVector<mlir::NamedAttribute> attrs; |
| 432 | if (dataAttr) { |
| 433 | auto globalOpName = mlir::OperationName(fir::GlobalOp::getOperationName(), |
| 434 | module.getContext()); |
| 435 | attrs.push_back(mlir::NamedAttribute( |
| 436 | fir::GlobalOp::getDataAttrAttrName(globalOpName), dataAttr)); |
| 437 | } |
| 438 | auto glob = create<fir::GlobalOp>(loc, name, isConst, isTarget, type, value, |
| 439 | linkage, attrs); |
| 440 | restoreInsertionPoint(insertPt); |
| 441 | if (symbolTable) |
| 442 | symbolTable->insert(glob); |
| 443 | return glob; |
| 444 | } |
| 445 | |
| 446 | fir::GlobalOp fir::FirOpBuilder::createGlobal( |
| 447 | mlir::Location loc, mlir::Type type, llvm::StringRef name, bool isConst, |
| 448 | bool isTarget, std::function<void(FirOpBuilder &)> bodyBuilder, |
| 449 | mlir::StringAttr linkage, cuf::DataAttributeAttr dataAttr) { |
| 450 | if (auto global = getNamedGlobal(name)) |
| 451 | return global; |
| 452 | auto module = getModule(); |
| 453 | auto insertPt = saveInsertionPoint(); |
| 454 | setInsertionPoint(module.getBody(), module.getBody()->end()); |
| 455 | auto glob = create<fir::GlobalOp>(loc, name, isConst, isTarget, type, |
| 456 | mlir::Attribute{}, linkage); |
| 457 | auto ®ion = glob.getRegion(); |
| 458 | region.push_back(new mlir::Block); |
| 459 | auto &block = glob.getRegion().back(); |
| 460 | setInsertionPointToStart(&block); |
| 461 | bodyBuilder(*this); |
| 462 | restoreInsertionPoint(insertPt); |
| 463 | if (symbolTable) |
| 464 | symbolTable->insert(glob); |
| 465 | return glob; |
| 466 | } |
| 467 | |
| 468 | std::pair<fir::TypeInfoOp, mlir::OpBuilder::InsertPoint> |
| 469 | fir::FirOpBuilder::createTypeInfoOp(mlir::Location loc, |
| 470 | fir::RecordType recordType, |
| 471 | fir::RecordType parentType) { |
| 472 | mlir::ModuleOp module = getModule(); |
| 473 | if (fir::TypeInfoOp typeInfo = |
| 474 | fir::lookupTypeInfoOp(recordType.getName(), module, symbolTable)) |
| 475 | return {typeInfo, InsertPoint{}}; |
| 476 | InsertPoint insertPoint = saveInsertionPoint(); |
| 477 | setInsertionPoint(module.getBody(), module.getBody()->end()); |
| 478 | auto typeInfo = create<fir::TypeInfoOp>(loc, recordType, parentType); |
| 479 | if (symbolTable) |
| 480 | symbolTable->insert(typeInfo); |
| 481 | return {typeInfo, insertPoint}; |
| 482 | } |
| 483 | |
| 484 | mlir::Value fir::FirOpBuilder::convertWithSemantics( |
| 485 | mlir::Location loc, mlir::Type toTy, mlir::Value val, |
| 486 | bool allowCharacterConversion, bool allowRebox) { |
| 487 | assert(toTy && "store location must be typed" ); |
| 488 | auto fromTy = val.getType(); |
| 489 | if (fromTy == toTy) |
| 490 | return val; |
| 491 | fir::factory::Complex helper{*this, loc}; |
| 492 | if ((fir::isa_real(fromTy) || fir::isa_integer(fromTy)) && |
| 493 | fir::isa_complex(toTy)) { |
| 494 | // imaginary part is zero |
| 495 | auto eleTy = helper.getComplexPartType(toTy); |
| 496 | auto cast = createConvert(loc, eleTy, val); |
| 497 | auto imag = createRealZeroConstant(loc, eleTy); |
| 498 | return helper.createComplex(toTy, cast, imag); |
| 499 | } |
| 500 | if (fir::isa_complex(fromTy) && |
| 501 | (fir::isa_integer(toTy) || fir::isa_real(toTy))) { |
| 502 | // drop the imaginary part |
| 503 | auto rp = helper.extractComplexPart(val, /*isImagPart=*/false); |
| 504 | return createConvert(loc, toTy, rp); |
| 505 | } |
| 506 | if (allowCharacterConversion) { |
| 507 | if (mlir::isa<fir::BoxCharType>(fromTy)) { |
| 508 | // Extract the address of the character string and pass it |
| 509 | fir::factory::CharacterExprHelper charHelper{*this, loc}; |
| 510 | std::pair<mlir::Value, mlir::Value> unboxchar = |
| 511 | charHelper.createUnboxChar(val); |
| 512 | return createConvert(loc, toTy, unboxchar.first); |
| 513 | } |
| 514 | if (auto boxType = mlir::dyn_cast<fir::BoxCharType>(toTy)) { |
| 515 | // Extract the address of the actual argument and create a boxed |
| 516 | // character value with an undefined length |
| 517 | // TODO: We should really calculate the total size of the actual |
| 518 | // argument in characters and use it as the length of the string |
| 519 | auto refType = getRefType(boxType.getEleTy()); |
| 520 | mlir::Value charBase = createConvert(loc, refType, val); |
| 521 | // Do not use fir.undef since llvm optimizer is too harsh when it |
| 522 | // sees such values (may just delete code). |
| 523 | mlir::Value unknownLen = createIntegerConstant(loc, getIndexType(), 0); |
| 524 | fir::factory::CharacterExprHelper charHelper{*this, loc}; |
| 525 | return charHelper.createEmboxChar(charBase, unknownLen); |
| 526 | } |
| 527 | } |
| 528 | if (fir::isa_ref_type(toTy) && fir::isa_box_type(fromTy)) { |
| 529 | // Call is expecting a raw data pointer, not a box. Get the data pointer out |
| 530 | // of the box and pass that. |
| 531 | assert((fir::unwrapRefType(toTy) == |
| 532 | fir::unwrapRefType(fir::unwrapPassByRefType(fromTy)) && |
| 533 | "element types expected to match" )); |
| 534 | return create<fir::BoxAddrOp>(loc, toTy, val); |
| 535 | } |
| 536 | if (fir::isa_ref_type(fromTy) && mlir::isa<fir::BoxProcType>(toTy)) { |
| 537 | // Call is expecting a boxed procedure, not a reference to other data type. |
| 538 | // Convert the reference to a procedure and embox it. |
| 539 | mlir::Type procTy = mlir::cast<fir::BoxProcType>(toTy).getEleTy(); |
| 540 | mlir::Value proc = createConvert(loc, procTy, val); |
| 541 | return create<fir::EmboxProcOp>(loc, toTy, proc); |
| 542 | } |
| 543 | |
| 544 | // Legacy: remove when removing non HLFIR lowering path. |
| 545 | if (allowRebox) |
| 546 | if (((fir::isPolymorphicType(fromTy) && |
| 547 | (fir::isAllocatableType(fromTy) || fir::isPointerType(fromTy)) && |
| 548 | fir::isPolymorphicType(toTy)) || |
| 549 | (fir::isPolymorphicType(fromTy) && mlir::isa<fir::BoxType>(toTy))) && |
| 550 | !(fir::isUnlimitedPolymorphicType(fromTy) && fir::isAssumedType(toTy))) |
| 551 | return create<fir::ReboxOp>(loc, toTy, val, mlir::Value{}, |
| 552 | /*slice=*/mlir::Value{}); |
| 553 | |
| 554 | return createConvert(loc, toTy, val); |
| 555 | } |
| 556 | |
| 557 | mlir::Value fir::FirOpBuilder::createVolatileCast(mlir::Location loc, |
| 558 | bool isVolatile, |
| 559 | mlir::Value val) { |
| 560 | mlir::Type volatileAdjustedType = |
| 561 | fir::updateTypeWithVolatility(val.getType(), isVolatile); |
| 562 | if (volatileAdjustedType == val.getType()) |
| 563 | return val; |
| 564 | return create<fir::VolatileCastOp>(loc, volatileAdjustedType, val); |
| 565 | } |
| 566 | |
| 567 | mlir::Value fir::FirOpBuilder::createConvertWithVolatileCast(mlir::Location loc, |
| 568 | mlir::Type toTy, |
| 569 | mlir::Value val) { |
| 570 | val = createVolatileCast(loc, fir::isa_volatile_type(toTy), val); |
| 571 | return createConvert(loc, toTy, val); |
| 572 | } |
| 573 | |
| 574 | mlir::Value fir::factory::createConvert(mlir::OpBuilder &builder, |
| 575 | mlir::Location loc, mlir::Type toTy, |
| 576 | mlir::Value val) { |
| 577 | if (val.getType() != toTy) { |
| 578 | assert((!fir::isa_derived(toTy) || |
| 579 | mlir::cast<fir::RecordType>(val.getType()).getTypeList() == |
| 580 | mlir::cast<fir::RecordType>(toTy).getTypeList()) && |
| 581 | "incompatible record types" ); |
| 582 | return builder.create<fir::ConvertOp>(loc, toTy, val); |
| 583 | } |
| 584 | return val; |
| 585 | } |
| 586 | |
| 587 | mlir::Value fir::FirOpBuilder::createConvert(mlir::Location loc, |
| 588 | mlir::Type toTy, mlir::Value val) { |
| 589 | return fir::factory::createConvert(*this, loc, toTy, val); |
| 590 | } |
| 591 | |
| 592 | void fir::FirOpBuilder::createStoreWithConvert(mlir::Location loc, |
| 593 | mlir::Value val, |
| 594 | mlir::Value addr) { |
| 595 | mlir::Type unwrapedRefType = fir::unwrapRefType(addr.getType()); |
| 596 | val = createVolatileCast(loc, fir::isa_volatile_type(unwrapedRefType), val); |
| 597 | mlir::Value cast = createConvert(loc, unwrapedRefType, val); |
| 598 | create<fir::StoreOp>(loc, cast, addr); |
| 599 | } |
| 600 | |
| 601 | mlir::Value fir::FirOpBuilder::loadIfRef(mlir::Location loc, mlir::Value val) { |
| 602 | if (fir::isa_ref_type(val.getType())) |
| 603 | return create<fir::LoadOp>(loc, val); |
| 604 | return val; |
| 605 | } |
| 606 | |
| 607 | fir::StringLitOp fir::FirOpBuilder::createStringLitOp(mlir::Location loc, |
| 608 | llvm::StringRef data) { |
| 609 | auto type = fir::CharacterType::get(getContext(), 1, data.size()); |
| 610 | auto strAttr = mlir::StringAttr::get(getContext(), data); |
| 611 | auto valTag = mlir::StringAttr::get(getContext(), fir::StringLitOp::value()); |
| 612 | mlir::NamedAttribute dataAttr(valTag, strAttr); |
| 613 | auto sizeTag = mlir::StringAttr::get(getContext(), fir::StringLitOp::size()); |
| 614 | mlir::NamedAttribute sizeAttr(sizeTag, getI64IntegerAttr(data.size())); |
| 615 | llvm::SmallVector<mlir::NamedAttribute> attrs{dataAttr, sizeAttr}; |
| 616 | return create<fir::StringLitOp>(loc, llvm::ArrayRef<mlir::Type>{type}, |
| 617 | std::nullopt, attrs); |
| 618 | } |
| 619 | |
| 620 | mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc, |
| 621 | llvm::ArrayRef<mlir::Value> exts) { |
| 622 | return create<fir::ShapeOp>(loc, exts); |
| 623 | } |
| 624 | |
| 625 | mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc, |
| 626 | llvm::ArrayRef<mlir::Value> shift, |
| 627 | llvm::ArrayRef<mlir::Value> exts) { |
| 628 | auto shapeType = fir::ShapeShiftType::get(getContext(), exts.size()); |
| 629 | llvm::SmallVector<mlir::Value> shapeArgs; |
| 630 | auto idxTy = getIndexType(); |
| 631 | for (auto [lbnd, ext] : llvm::zip(shift, exts)) { |
| 632 | auto lb = createConvert(loc, idxTy, lbnd); |
| 633 | shapeArgs.push_back(lb); |
| 634 | shapeArgs.push_back(ext); |
| 635 | } |
| 636 | return create<fir::ShapeShiftOp>(loc, shapeType, shapeArgs); |
| 637 | } |
| 638 | |
| 639 | mlir::Value fir::FirOpBuilder::genShape(mlir::Location loc, |
| 640 | const fir::AbstractArrayBox &arr) { |
| 641 | if (arr.lboundsAllOne()) |
| 642 | return genShape(loc, arr.getExtents()); |
| 643 | return genShape(loc, arr.getLBounds(), arr.getExtents()); |
| 644 | } |
| 645 | |
| 646 | mlir::Value fir::FirOpBuilder::genShift(mlir::Location loc, |
| 647 | llvm::ArrayRef<mlir::Value> shift) { |
| 648 | auto shiftType = fir::ShiftType::get(getContext(), shift.size()); |
| 649 | return create<fir::ShiftOp>(loc, shiftType, shift); |
| 650 | } |
| 651 | |
| 652 | mlir::Value fir::FirOpBuilder::createShape(mlir::Location loc, |
| 653 | const fir::ExtendedValue &exv) { |
| 654 | return exv.match( |
| 655 | [&](const fir::ArrayBoxValue &box) { return genShape(loc, box); }, |
| 656 | [&](const fir::CharArrayBoxValue &box) { return genShape(loc, box); }, |
| 657 | [&](const fir::BoxValue &box) -> mlir::Value { |
| 658 | if (!box.getLBounds().empty()) { |
| 659 | auto shiftType = |
| 660 | fir::ShiftType::get(getContext(), box.getLBounds().size()); |
| 661 | return create<fir::ShiftOp>(loc, shiftType, box.getLBounds()); |
| 662 | } |
| 663 | return {}; |
| 664 | }, |
| 665 | [&](const fir::MutableBoxValue &) -> mlir::Value { |
| 666 | // MutableBoxValue must be read into another category to work with them |
| 667 | // outside of allocation/assignment contexts. |
| 668 | fir::emitFatalError(loc, "createShape on MutableBoxValue" ); |
| 669 | }, |
| 670 | [&](auto) -> mlir::Value { fir::emitFatalError(loc, "not an array" ); }); |
| 671 | } |
| 672 | |
| 673 | mlir::Value fir::FirOpBuilder::createSlice(mlir::Location loc, |
| 674 | const fir::ExtendedValue &exv, |
| 675 | mlir::ValueRange triples, |
| 676 | mlir::ValueRange path) { |
| 677 | if (triples.empty()) { |
| 678 | // If there is no slicing by triple notation, then take the whole array. |
| 679 | auto fullShape = [&](const llvm::ArrayRef<mlir::Value> lbounds, |
| 680 | llvm::ArrayRef<mlir::Value> extents) -> mlir::Value { |
| 681 | llvm::SmallVector<mlir::Value> trips; |
| 682 | auto idxTy = getIndexType(); |
| 683 | auto one = createIntegerConstant(loc, idxTy, 1); |
| 684 | if (lbounds.empty()) { |
| 685 | for (auto v : extents) { |
| 686 | trips.push_back(one); |
| 687 | trips.push_back(v); |
| 688 | trips.push_back(one); |
| 689 | } |
| 690 | return create<fir::SliceOp>(loc, trips, path); |
| 691 | } |
| 692 | for (auto [lbnd, extent] : llvm::zip(lbounds, extents)) { |
| 693 | auto lb = createConvert(loc, idxTy, lbnd); |
| 694 | auto ext = createConvert(loc, idxTy, extent); |
| 695 | auto shift = create<mlir::arith::SubIOp>(loc, lb, one); |
| 696 | auto ub = create<mlir::arith::AddIOp>(loc, ext, shift); |
| 697 | trips.push_back(lb); |
| 698 | trips.push_back(ub); |
| 699 | trips.push_back(one); |
| 700 | } |
| 701 | return create<fir::SliceOp>(loc, trips, path); |
| 702 | }; |
| 703 | return exv.match( |
| 704 | [&](const fir::ArrayBoxValue &box) { |
| 705 | return fullShape(box.getLBounds(), box.getExtents()); |
| 706 | }, |
| 707 | [&](const fir::CharArrayBoxValue &box) { |
| 708 | return fullShape(box.getLBounds(), box.getExtents()); |
| 709 | }, |
| 710 | [&](const fir::BoxValue &box) { |
| 711 | auto extents = fir::factory::readExtents(*this, loc, box); |
| 712 | return fullShape(box.getLBounds(), extents); |
| 713 | }, |
| 714 | [&](const fir::MutableBoxValue &) -> mlir::Value { |
| 715 | // MutableBoxValue must be read into another category to work with |
| 716 | // them outside of allocation/assignment contexts. |
| 717 | fir::emitFatalError(loc, "createSlice on MutableBoxValue" ); |
| 718 | }, |
| 719 | [&](auto) -> mlir::Value { fir::emitFatalError(loc, "not an array" ); }); |
| 720 | } |
| 721 | return create<fir::SliceOp>(loc, triples, path); |
| 722 | } |
| 723 | |
| 724 | mlir::Value fir::FirOpBuilder::createBox(mlir::Location loc, |
| 725 | const fir::ExtendedValue &exv, |
| 726 | bool isPolymorphic, |
| 727 | bool isAssumedType) { |
| 728 | mlir::Value itemAddr = fir::getBase(exv); |
| 729 | if (mlir::isa<fir::BaseBoxType>(itemAddr.getType())) |
| 730 | return itemAddr; |
| 731 | auto elementType = fir::dyn_cast_ptrEleTy(itemAddr.getType()); |
| 732 | if (!elementType) { |
| 733 | mlir::emitError(loc, "internal: expected a memory reference type " ) |
| 734 | << itemAddr.getType(); |
| 735 | llvm_unreachable("not a memory reference type" ); |
| 736 | } |
| 737 | const bool isVolatile = fir::isa_volatile_type(itemAddr.getType()); |
| 738 | mlir::Type boxTy; |
| 739 | mlir::Value tdesc; |
| 740 | // Avoid to wrap a box/class with box/class. |
| 741 | if (mlir::isa<fir::BaseBoxType>(elementType)) { |
| 742 | boxTy = elementType; |
| 743 | } else { |
| 744 | boxTy = fir::BoxType::get(elementType, isVolatile); |
| 745 | if (isPolymorphic) { |
| 746 | elementType = fir::updateTypeForUnlimitedPolymorphic(elementType); |
| 747 | if (isAssumedType) |
| 748 | boxTy = fir::BoxType::get(elementType, isVolatile); |
| 749 | else |
| 750 | boxTy = fir::ClassType::get(elementType, isVolatile); |
| 751 | } |
| 752 | } |
| 753 | |
| 754 | return exv.match( |
| 755 | [&](const fir::ArrayBoxValue &box) -> mlir::Value { |
| 756 | mlir::Value empty; |
| 757 | mlir::ValueRange emptyRange; |
| 758 | mlir::Value s = createShape(loc, exv); |
| 759 | return create<fir::EmboxOp>(loc, boxTy, itemAddr, s, /*slice=*/empty, |
| 760 | /*typeparams=*/emptyRange, |
| 761 | isPolymorphic ? box.getSourceBox() : tdesc); |
| 762 | }, |
| 763 | [&](const fir::CharArrayBoxValue &box) -> mlir::Value { |
| 764 | mlir::Value s = createShape(loc, exv); |
| 765 | if (fir::factory::CharacterExprHelper::hasConstantLengthInType(exv)) |
| 766 | return create<fir::EmboxOp>(loc, boxTy, itemAddr, s); |
| 767 | |
| 768 | mlir::Value emptySlice; |
| 769 | llvm::SmallVector<mlir::Value> lenParams{box.getLen()}; |
| 770 | return create<fir::EmboxOp>(loc, boxTy, itemAddr, s, emptySlice, |
| 771 | lenParams); |
| 772 | }, |
| 773 | [&](const fir::CharBoxValue &box) -> mlir::Value { |
| 774 | if (fir::factory::CharacterExprHelper::hasConstantLengthInType(exv)) |
| 775 | return create<fir::EmboxOp>(loc, boxTy, itemAddr); |
| 776 | mlir::Value emptyShape, emptySlice; |
| 777 | llvm::SmallVector<mlir::Value> lenParams{box.getLen()}; |
| 778 | return create<fir::EmboxOp>(loc, boxTy, itemAddr, emptyShape, |
| 779 | emptySlice, lenParams); |
| 780 | }, |
| 781 | [&](const fir::MutableBoxValue &x) -> mlir::Value { |
| 782 | return create<fir::LoadOp>( |
| 783 | loc, fir::factory::getMutableIRBox(*this, loc, x)); |
| 784 | }, |
| 785 | [&](const fir::PolymorphicValue &p) -> mlir::Value { |
| 786 | mlir::Value empty; |
| 787 | mlir::ValueRange emptyRange; |
| 788 | return create<fir::EmboxOp>(loc, boxTy, itemAddr, empty, empty, |
| 789 | emptyRange, |
| 790 | isPolymorphic ? p.getSourceBox() : tdesc); |
| 791 | }, |
| 792 | [&](const auto &) -> mlir::Value { |
| 793 | mlir::Value empty; |
| 794 | mlir::ValueRange emptyRange; |
| 795 | return create<fir::EmboxOp>(loc, boxTy, itemAddr, empty, empty, |
| 796 | emptyRange, tdesc); |
| 797 | }); |
| 798 | } |
| 799 | |
| 800 | mlir::Value fir::FirOpBuilder::createBox(mlir::Location loc, mlir::Type boxType, |
| 801 | mlir::Value addr, mlir::Value shape, |
| 802 | mlir::Value slice, |
| 803 | llvm::ArrayRef<mlir::Value> lengths, |
| 804 | mlir::Value tdesc) { |
| 805 | mlir::Type valueOrSequenceType = fir::unwrapPassByRefType(boxType); |
| 806 | return create<fir::EmboxOp>( |
| 807 | loc, boxType, addr, shape, slice, |
| 808 | fir::factory::elideLengthsAlreadyInType(valueOrSequenceType, lengths), |
| 809 | tdesc); |
| 810 | } |
| 811 | |
| 812 | void fir::FirOpBuilder::dumpFunc() { getFunction().dump(); } |
| 813 | |
| 814 | static mlir::Value |
| 815 | genNullPointerComparison(fir::FirOpBuilder &builder, mlir::Location loc, |
| 816 | mlir::Value addr, |
| 817 | mlir::arith::CmpIPredicate condition) { |
| 818 | auto intPtrTy = builder.getIntPtrType(); |
| 819 | auto ptrToInt = builder.createConvert(loc, intPtrTy, addr); |
| 820 | auto c0 = builder.createIntegerConstant(loc, intPtrTy, 0); |
| 821 | return builder.create<mlir::arith::CmpIOp>(loc, condition, ptrToInt, c0); |
| 822 | } |
| 823 | |
| 824 | mlir::Value fir::FirOpBuilder::genIsNotNullAddr(mlir::Location loc, |
| 825 | mlir::Value addr) { |
| 826 | return genNullPointerComparison(*this, loc, addr, |
| 827 | mlir::arith::CmpIPredicate::ne); |
| 828 | } |
| 829 | |
| 830 | mlir::Value fir::FirOpBuilder::genIsNullAddr(mlir::Location loc, |
| 831 | mlir::Value addr) { |
| 832 | return genNullPointerComparison(*this, loc, addr, |
| 833 | mlir::arith::CmpIPredicate::eq); |
| 834 | } |
| 835 | |
| 836 | mlir::Value fir::FirOpBuilder::genExtentFromTriplet(mlir::Location loc, |
| 837 | mlir::Value lb, |
| 838 | mlir::Value ub, |
| 839 | mlir::Value step, |
| 840 | mlir::Type type) { |
| 841 | auto zero = createIntegerConstant(loc, type, 0); |
| 842 | lb = createConvert(loc, type, lb); |
| 843 | ub = createConvert(loc, type, ub); |
| 844 | step = createConvert(loc, type, step); |
| 845 | auto diff = create<mlir::arith::SubIOp>(loc, ub, lb); |
| 846 | auto add = create<mlir::arith::AddIOp>(loc, diff, step); |
| 847 | auto div = create<mlir::arith::DivSIOp>(loc, add, step); |
| 848 | auto cmp = create<mlir::arith::CmpIOp>(loc, mlir::arith::CmpIPredicate::sgt, |
| 849 | div, zero); |
| 850 | return create<mlir::arith::SelectOp>(loc, cmp, div, zero); |
| 851 | } |
| 852 | |
| 853 | mlir::Value fir::FirOpBuilder::genAbsentOp(mlir::Location loc, |
| 854 | mlir::Type argTy) { |
| 855 | if (!fir::isCharacterProcedureTuple(argTy)) |
| 856 | return create<fir::AbsentOp>(loc, argTy); |
| 857 | |
| 858 | auto boxProc = |
| 859 | create<fir::AbsentOp>(loc, mlir::cast<mlir::TupleType>(argTy).getType(0)); |
| 860 | mlir::Value charLen = create<fir::UndefOp>(loc, getCharacterLengthType()); |
| 861 | return fir::factory::createCharacterProcedureTuple(*this, loc, argTy, boxProc, |
| 862 | charLen); |
| 863 | } |
| 864 | |
| 865 | void fir::FirOpBuilder::setCommonAttributes(mlir::Operation *op) const { |
| 866 | auto fmi = mlir::dyn_cast<mlir::arith::ArithFastMathInterface>(*op); |
| 867 | if (fmi) { |
| 868 | // TODO: use fmi.setFastMathFlagsAttr() after D137114 is merged. |
| 869 | // For now set the attribute by the name. |
| 870 | llvm::StringRef arithFMFAttrName = fmi.getFastMathAttrName(); |
| 871 | if (fastMathFlags != mlir::arith::FastMathFlags::none) |
| 872 | op->setAttr(arithFMFAttrName, mlir::arith::FastMathFlagsAttr::get( |
| 873 | op->getContext(), fastMathFlags)); |
| 874 | } |
| 875 | auto iofi = |
| 876 | mlir::dyn_cast<mlir::arith::ArithIntegerOverflowFlagsInterface>(*op); |
| 877 | if (iofi) { |
| 878 | llvm::StringRef arithIOFAttrName = iofi.getIntegerOverflowAttrName(); |
| 879 | if (integerOverflowFlags != mlir::arith::IntegerOverflowFlags::none) |
| 880 | op->setAttr(arithIOFAttrName, |
| 881 | mlir::arith::IntegerOverflowFlagsAttr::get( |
| 882 | op->getContext(), integerOverflowFlags)); |
| 883 | } |
| 884 | } |
| 885 | |
| 886 | void fir::FirOpBuilder::setFastMathFlags( |
| 887 | Fortran::common::MathOptionsBase options) { |
| 888 | mlir::arith::FastMathFlags arithFMF{}; |
| 889 | if (options.getFPContractEnabled()) { |
| 890 | arithFMF = arithFMF | mlir::arith::FastMathFlags::contract; |
| 891 | } |
| 892 | if (options.getNoHonorInfs()) { |
| 893 | arithFMF = arithFMF | mlir::arith::FastMathFlags::ninf; |
| 894 | } |
| 895 | if (options.getNoHonorNaNs()) { |
| 896 | arithFMF = arithFMF | mlir::arith::FastMathFlags::nnan; |
| 897 | } |
| 898 | if (options.getApproxFunc()) { |
| 899 | arithFMF = arithFMF | mlir::arith::FastMathFlags::afn; |
| 900 | } |
| 901 | if (options.getNoSignedZeros()) { |
| 902 | arithFMF = arithFMF | mlir::arith::FastMathFlags::nsz; |
| 903 | } |
| 904 | if (options.getAssociativeMath()) { |
| 905 | arithFMF = arithFMF | mlir::arith::FastMathFlags::reassoc; |
| 906 | } |
| 907 | if (options.getReciprocalMath()) { |
| 908 | arithFMF = arithFMF | mlir::arith::FastMathFlags::arcp; |
| 909 | } |
| 910 | setFastMathFlags(arithFMF); |
| 911 | } |
| 912 | |
| 913 | // Construction of an mlir::DataLayout is expensive so only do it on demand and |
| 914 | // memoise it in the builder instance |
| 915 | mlir::DataLayout &fir::FirOpBuilder::getDataLayout() { |
| 916 | if (dataLayout) |
| 917 | return *dataLayout; |
| 918 | dataLayout = std::make_unique<mlir::DataLayout>(getModule()); |
| 919 | return *dataLayout; |
| 920 | } |
| 921 | |
| 922 | //===--------------------------------------------------------------------===// |
| 923 | // ExtendedValue inquiry helper implementation |
| 924 | //===--------------------------------------------------------------------===// |
| 925 | |
| 926 | mlir::Value fir::factory::readCharLen(fir::FirOpBuilder &builder, |
| 927 | mlir::Location loc, |
| 928 | const fir::ExtendedValue &box) { |
| 929 | return box.match( |
| 930 | [&](const fir::CharBoxValue &x) -> mlir::Value { return x.getLen(); }, |
| 931 | [&](const fir::CharArrayBoxValue &x) -> mlir::Value { |
| 932 | return x.getLen(); |
| 933 | }, |
| 934 | [&](const fir::BoxValue &x) -> mlir::Value { |
| 935 | assert(x.isCharacter()); |
| 936 | if (!x.getExplicitParameters().empty()) |
| 937 | return x.getExplicitParameters()[0]; |
| 938 | return fir::factory::CharacterExprHelper{builder, loc} |
| 939 | .readLengthFromBox(x.getAddr()); |
| 940 | }, |
| 941 | [&](const fir::MutableBoxValue &x) -> mlir::Value { |
| 942 | return readCharLen(builder, loc, |
| 943 | fir::factory::genMutableBoxRead(builder, loc, x)); |
| 944 | }, |
| 945 | [&](const auto &) -> mlir::Value { |
| 946 | fir::emitFatalError( |
| 947 | loc, "Character length inquiry on a non-character entity" ); |
| 948 | }); |
| 949 | } |
| 950 | |
| 951 | mlir::Value fir::factory::readExtent(fir::FirOpBuilder &builder, |
| 952 | mlir::Location loc, |
| 953 | const fir::ExtendedValue &box, |
| 954 | unsigned dim) { |
| 955 | assert(box.rank() > dim); |
| 956 | return box.match( |
| 957 | [&](const fir::ArrayBoxValue &x) -> mlir::Value { |
| 958 | return x.getExtents()[dim]; |
| 959 | }, |
| 960 | [&](const fir::CharArrayBoxValue &x) -> mlir::Value { |
| 961 | return x.getExtents()[dim]; |
| 962 | }, |
| 963 | [&](const fir::BoxValue &x) -> mlir::Value { |
| 964 | if (!x.getExplicitExtents().empty()) |
| 965 | return x.getExplicitExtents()[dim]; |
| 966 | auto idxTy = builder.getIndexType(); |
| 967 | auto dimVal = builder.createIntegerConstant(loc, idxTy, dim); |
| 968 | return builder |
| 969 | .create<fir::BoxDimsOp>(loc, idxTy, idxTy, idxTy, x.getAddr(), |
| 970 | dimVal) |
| 971 | .getResult(1); |
| 972 | }, |
| 973 | [&](const fir::MutableBoxValue &x) -> mlir::Value { |
| 974 | return readExtent(builder, loc, |
| 975 | fir::factory::genMutableBoxRead(builder, loc, x), |
| 976 | dim); |
| 977 | }, |
| 978 | [&](const auto &) -> mlir::Value { |
| 979 | fir::emitFatalError(loc, "extent inquiry on scalar" ); |
| 980 | }); |
| 981 | } |
| 982 | |
| 983 | mlir::Value fir::factory::readLowerBound(fir::FirOpBuilder &builder, |
| 984 | mlir::Location loc, |
| 985 | const fir::ExtendedValue &box, |
| 986 | unsigned dim, |
| 987 | mlir::Value defaultValue) { |
| 988 | assert(box.rank() > dim); |
| 989 | auto lb = box.match( |
| 990 | [&](const fir::ArrayBoxValue &x) -> mlir::Value { |
| 991 | return x.getLBounds().empty() ? mlir::Value{} : x.getLBounds()[dim]; |
| 992 | }, |
| 993 | [&](const fir::CharArrayBoxValue &x) -> mlir::Value { |
| 994 | return x.getLBounds().empty() ? mlir::Value{} : x.getLBounds()[dim]; |
| 995 | }, |
| 996 | [&](const fir::BoxValue &x) -> mlir::Value { |
| 997 | return x.getLBounds().empty() ? mlir::Value{} : x.getLBounds()[dim]; |
| 998 | }, |
| 999 | [&](const fir::MutableBoxValue &x) -> mlir::Value { |
| 1000 | return readLowerBound(builder, loc, |
| 1001 | fir::factory::genMutableBoxRead(builder, loc, x), |
| 1002 | dim, defaultValue); |
| 1003 | }, |
| 1004 | [&](const auto &) -> mlir::Value { |
| 1005 | fir::emitFatalError(loc, "lower bound inquiry on scalar" ); |
| 1006 | }); |
| 1007 | if (lb) |
| 1008 | return lb; |
| 1009 | return defaultValue; |
| 1010 | } |
| 1011 | |
| 1012 | llvm::SmallVector<mlir::Value> |
| 1013 | fir::factory::readExtents(fir::FirOpBuilder &builder, mlir::Location loc, |
| 1014 | const fir::BoxValue &box) { |
| 1015 | llvm::SmallVector<mlir::Value> result; |
| 1016 | auto explicitExtents = box.getExplicitExtents(); |
| 1017 | if (!explicitExtents.empty()) { |
| 1018 | result.append(explicitExtents.begin(), explicitExtents.end()); |
| 1019 | return result; |
| 1020 | } |
| 1021 | auto rank = box.rank(); |
| 1022 | auto idxTy = builder.getIndexType(); |
| 1023 | for (decltype(rank) dim = 0; dim < rank; ++dim) { |
| 1024 | auto dimVal = builder.createIntegerConstant(loc, idxTy, dim); |
| 1025 | auto dimInfo = builder.create<fir::BoxDimsOp>(loc, idxTy, idxTy, idxTy, |
| 1026 | box.getAddr(), dimVal); |
| 1027 | result.emplace_back(dimInfo.getResult(1)); |
| 1028 | } |
| 1029 | return result; |
| 1030 | } |
| 1031 | |
| 1032 | llvm::SmallVector<mlir::Value> |
| 1033 | fir::factory::getExtents(mlir::Location loc, fir::FirOpBuilder &builder, |
| 1034 | const fir::ExtendedValue &box) { |
| 1035 | return box.match( |
| 1036 | [&](const fir::ArrayBoxValue &x) -> llvm::SmallVector<mlir::Value> { |
| 1037 | return {x.getExtents().begin(), x.getExtents().end()}; |
| 1038 | }, |
| 1039 | [&](const fir::CharArrayBoxValue &x) -> llvm::SmallVector<mlir::Value> { |
| 1040 | return {x.getExtents().begin(), x.getExtents().end()}; |
| 1041 | }, |
| 1042 | [&](const fir::BoxValue &x) -> llvm::SmallVector<mlir::Value> { |
| 1043 | return fir::factory::readExtents(builder, loc, x); |
| 1044 | }, |
| 1045 | [&](const fir::MutableBoxValue &x) -> llvm::SmallVector<mlir::Value> { |
| 1046 | auto load = fir::factory::genMutableBoxRead(builder, loc, x); |
| 1047 | return fir::factory::getExtents(loc, builder, load); |
| 1048 | }, |
| 1049 | [&](const auto &) -> llvm::SmallVector<mlir::Value> { return {}; }); |
| 1050 | } |
| 1051 | |
| 1052 | fir::ExtendedValue fir::factory::readBoxValue(fir::FirOpBuilder &builder, |
| 1053 | mlir::Location loc, |
| 1054 | const fir::BoxValue &box) { |
| 1055 | assert(!box.hasAssumedRank() && |
| 1056 | "cannot read unlimited polymorphic or assumed rank fir.box" ); |
| 1057 | auto addr = |
| 1058 | builder.create<fir::BoxAddrOp>(loc, box.getMemTy(), box.getAddr()); |
| 1059 | if (box.isCharacter()) { |
| 1060 | auto len = fir::factory::readCharLen(builder, loc, box); |
| 1061 | if (box.rank() == 0) |
| 1062 | return fir::CharBoxValue(addr, len); |
| 1063 | return fir::CharArrayBoxValue(addr, len, |
| 1064 | fir::factory::readExtents(builder, loc, box), |
| 1065 | box.getLBounds()); |
| 1066 | } |
| 1067 | if (box.isDerivedWithLenParameters()) |
| 1068 | TODO(loc, "read fir.box with length parameters" ); |
| 1069 | mlir::Value sourceBox; |
| 1070 | if (box.isPolymorphic()) |
| 1071 | sourceBox = box.getAddr(); |
| 1072 | if (box.isPolymorphic() && box.rank() == 0) |
| 1073 | return fir::PolymorphicValue(addr, sourceBox); |
| 1074 | if (box.rank() == 0) |
| 1075 | return addr; |
| 1076 | return fir::ArrayBoxValue(addr, fir::factory::readExtents(builder, loc, box), |
| 1077 | box.getLBounds(), sourceBox); |
| 1078 | } |
| 1079 | |
| 1080 | llvm::SmallVector<mlir::Value> |
| 1081 | fir::factory::getNonDefaultLowerBounds(fir::FirOpBuilder &builder, |
| 1082 | mlir::Location loc, |
| 1083 | const fir::ExtendedValue &exv) { |
| 1084 | return exv.match( |
| 1085 | [&](const fir::ArrayBoxValue &array) -> llvm::SmallVector<mlir::Value> { |
| 1086 | return {array.getLBounds().begin(), array.getLBounds().end()}; |
| 1087 | }, |
| 1088 | [&](const fir::CharArrayBoxValue &array) |
| 1089 | -> llvm::SmallVector<mlir::Value> { |
| 1090 | return {array.getLBounds().begin(), array.getLBounds().end()}; |
| 1091 | }, |
| 1092 | [&](const fir::BoxValue &box) -> llvm::SmallVector<mlir::Value> { |
| 1093 | return {box.getLBounds().begin(), box.getLBounds().end()}; |
| 1094 | }, |
| 1095 | [&](const fir::MutableBoxValue &box) -> llvm::SmallVector<mlir::Value> { |
| 1096 | auto load = fir::factory::genMutableBoxRead(builder, loc, box); |
| 1097 | return fir::factory::getNonDefaultLowerBounds(builder, loc, load); |
| 1098 | }, |
| 1099 | [&](const auto &) -> llvm::SmallVector<mlir::Value> { return {}; }); |
| 1100 | } |
| 1101 | |
| 1102 | llvm::SmallVector<mlir::Value> |
| 1103 | fir::factory::getNonDeferredLenParams(const fir::ExtendedValue &exv) { |
| 1104 | return exv.match( |
| 1105 | [&](const fir::CharArrayBoxValue &character) |
| 1106 | -> llvm::SmallVector<mlir::Value> { return {character.getLen()}; }, |
| 1107 | [&](const fir::CharBoxValue &character) |
| 1108 | -> llvm::SmallVector<mlir::Value> { return {character.getLen()}; }, |
| 1109 | [&](const fir::MutableBoxValue &box) -> llvm::SmallVector<mlir::Value> { |
| 1110 | return {box.nonDeferredLenParams().begin(), |
| 1111 | box.nonDeferredLenParams().end()}; |
| 1112 | }, |
| 1113 | [&](const fir::BoxValue &box) -> llvm::SmallVector<mlir::Value> { |
| 1114 | return {box.getExplicitParameters().begin(), |
| 1115 | box.getExplicitParameters().end()}; |
| 1116 | }, |
| 1117 | [&](const auto &) -> llvm::SmallVector<mlir::Value> { return {}; }); |
| 1118 | } |
| 1119 | |
| 1120 | // If valTy is a box type, then we need to extract the type parameters from |
| 1121 | // the box value. |
| 1122 | static llvm::SmallVector<mlir::Value> getFromBox(mlir::Location loc, |
| 1123 | fir::FirOpBuilder &builder, |
| 1124 | mlir::Type valTy, |
| 1125 | mlir::Value boxVal) { |
| 1126 | if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(valTy)) { |
| 1127 | auto eleTy = fir::unwrapAllRefAndSeqType(boxTy.getEleTy()); |
| 1128 | if (auto recTy = mlir::dyn_cast<fir::RecordType>(eleTy)) { |
| 1129 | if (recTy.getNumLenParams() > 0) { |
| 1130 | // Walk each type parameter in the record and get the value. |
| 1131 | TODO(loc, "generate code to get LEN type parameters" ); |
| 1132 | } |
| 1133 | } else if (auto charTy = mlir::dyn_cast<fir::CharacterType>(eleTy)) { |
| 1134 | if (charTy.hasDynamicLen()) { |
| 1135 | auto idxTy = builder.getIndexType(); |
| 1136 | auto eleSz = builder.create<fir::BoxEleSizeOp>(loc, idxTy, boxVal); |
| 1137 | auto kindBytes = |
| 1138 | builder.getKindMap().getCharacterBitsize(charTy.getFKind()) / 8; |
| 1139 | mlir::Value charSz = |
| 1140 | builder.createIntegerConstant(loc, idxTy, kindBytes); |
| 1141 | mlir::Value len = |
| 1142 | builder.create<mlir::arith::DivSIOp>(loc, eleSz, charSz); |
| 1143 | return {len}; |
| 1144 | } |
| 1145 | } |
| 1146 | } |
| 1147 | return {}; |
| 1148 | } |
| 1149 | |
| 1150 | // fir::getTypeParams() will get the type parameters from the extended value. |
| 1151 | // When the extended value is a BoxValue or MutableBoxValue, it may be necessary |
| 1152 | // to generate code, so this factory function handles those cases. |
| 1153 | // TODO: fix the inverted type tests, etc. |
| 1154 | llvm::SmallVector<mlir::Value> |
| 1155 | fir::factory::getTypeParams(mlir::Location loc, fir::FirOpBuilder &builder, |
| 1156 | const fir::ExtendedValue &exv) { |
| 1157 | auto handleBoxed = [&](const auto &box) -> llvm::SmallVector<mlir::Value> { |
| 1158 | if (box.isCharacter()) |
| 1159 | return {fir::factory::readCharLen(builder, loc, exv)}; |
| 1160 | if (box.isDerivedWithLenParameters()) { |
| 1161 | // This should generate code to read the type parameters from the box. |
| 1162 | // This requires some consideration however as MutableBoxValues need to be |
| 1163 | // in a sane state to be provide the correct values. |
| 1164 | TODO(loc, "derived type with type parameters" ); |
| 1165 | } |
| 1166 | return {}; |
| 1167 | }; |
| 1168 | // Intentionally reuse the original code path to get type parameters for the |
| 1169 | // cases that were supported rather than introduce a new path. |
| 1170 | return exv.match( |
| 1171 | [&](const fir::BoxValue &box) { return handleBoxed(box); }, |
| 1172 | [&](const fir::MutableBoxValue &box) { return handleBoxed(box); }, |
| 1173 | [&](const auto &) { return fir::getTypeParams(exv); }); |
| 1174 | } |
| 1175 | |
| 1176 | llvm::SmallVector<mlir::Value> |
| 1177 | fir::factory::getTypeParams(mlir::Location loc, fir::FirOpBuilder &builder, |
| 1178 | fir::ArrayLoadOp load) { |
| 1179 | mlir::Type memTy = load.getMemref().getType(); |
| 1180 | if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(memTy)) |
| 1181 | return getFromBox(loc, builder, boxTy, load.getMemref()); |
| 1182 | return load.getTypeparams(); |
| 1183 | } |
| 1184 | |
| 1185 | std::string fir::factory::uniqueCGIdent(llvm::StringRef prefix, |
| 1186 | llvm::StringRef name) { |
| 1187 | // For "long" identifiers use a hash value |
| 1188 | if (name.size() > nameLengthHashSize) { |
| 1189 | llvm::MD5 hash; |
| 1190 | hash.update(name); |
| 1191 | llvm::MD5::MD5Result result; |
| 1192 | hash.final(result); |
| 1193 | llvm::SmallString<32> str; |
| 1194 | llvm::MD5::stringifyResult(result, str); |
| 1195 | std::string hashName = prefix.str(); |
| 1196 | hashName.append("X" ).append(str.c_str()); |
| 1197 | return fir::NameUniquer::doGenerated(hashName); |
| 1198 | } |
| 1199 | // "Short" identifiers use a reversible hex string |
| 1200 | std::string nm = prefix.str(); |
| 1201 | return fir::NameUniquer::doGenerated( |
| 1202 | nm.append("X" ).append(llvm::toHex(name))); |
| 1203 | } |
| 1204 | |
| 1205 | mlir::Value fir::factory::locationToFilename(fir::FirOpBuilder &builder, |
| 1206 | mlir::Location loc) { |
| 1207 | if (auto flc = mlir::dyn_cast<mlir::FileLineColLoc>(loc)) { |
| 1208 | // must be encoded as asciiz, C string |
| 1209 | auto fn = flc.getFilename().str() + '\0'; |
| 1210 | return fir::getBase(createStringLiteral(builder, loc, fn)); |
| 1211 | } |
| 1212 | return builder.createNullConstant(loc); |
| 1213 | } |
| 1214 | |
| 1215 | mlir::Value fir::factory::locationToLineNo(fir::FirOpBuilder &builder, |
| 1216 | mlir::Location loc, |
| 1217 | mlir::Type type) { |
| 1218 | if (auto flc = mlir::dyn_cast<mlir::FileLineColLoc>(loc)) |
| 1219 | return builder.createIntegerConstant(loc, type, flc.getLine()); |
| 1220 | return builder.createIntegerConstant(loc, type, 0); |
| 1221 | } |
| 1222 | |
| 1223 | fir::ExtendedValue fir::factory::createStringLiteral(fir::FirOpBuilder &builder, |
| 1224 | mlir::Location loc, |
| 1225 | llvm::StringRef str) { |
| 1226 | std::string globalName = fir::factory::uniqueCGIdent("cl" , str); |
| 1227 | auto type = fir::CharacterType::get(builder.getContext(), 1, str.size()); |
| 1228 | auto global = builder.getNamedGlobal(globalName); |
| 1229 | if (!global) |
| 1230 | global = builder.createGlobalConstant( |
| 1231 | loc, type, globalName, |
| 1232 | [&](fir::FirOpBuilder &builder) { |
| 1233 | auto stringLitOp = builder.createStringLitOp(loc, str); |
| 1234 | builder.create<fir::HasValueOp>(loc, stringLitOp); |
| 1235 | }, |
| 1236 | builder.createLinkOnceLinkage()); |
| 1237 | auto addr = builder.create<fir::AddrOfOp>(loc, global.resultType(), |
| 1238 | global.getSymbol()); |
| 1239 | auto len = builder.createIntegerConstant( |
| 1240 | loc, builder.getCharacterLengthType(), str.size()); |
| 1241 | return fir::CharBoxValue{addr, len}; |
| 1242 | } |
| 1243 | |
| 1244 | llvm::SmallVector<mlir::Value> |
| 1245 | fir::factory::createExtents(fir::FirOpBuilder &builder, mlir::Location loc, |
| 1246 | fir::SequenceType seqTy) { |
| 1247 | llvm::SmallVector<mlir::Value> extents; |
| 1248 | auto idxTy = builder.getIndexType(); |
| 1249 | for (auto ext : seqTy.getShape()) |
| 1250 | extents.emplace_back( |
| 1251 | ext == fir::SequenceType::getUnknownExtent() |
| 1252 | ? builder.create<fir::UndefOp>(loc, idxTy).getResult() |
| 1253 | : builder.createIntegerConstant(loc, idxTy, ext)); |
| 1254 | return extents; |
| 1255 | } |
| 1256 | |
| 1257 | // FIXME: This needs some work. To correctly determine the extended value of a |
| 1258 | // component, one needs the base object, its type, and its type parameters. (An |
| 1259 | // alternative would be to provide an already computed address of the final |
| 1260 | // component rather than the base object's address, the point being the result |
| 1261 | // will require the address of the final component to create the extended |
| 1262 | // value.) One further needs the full path of components being applied. One |
| 1263 | // needs to apply type-based expressions to type parameters along this said |
| 1264 | // path. (See applyPathToType for a type-only derivation.) Finally, one needs to |
| 1265 | // compose the extended value of the terminal component, including all of its |
| 1266 | // parameters: array lower bounds expressions, extents, type parameters, etc. |
| 1267 | // Any of these properties may be deferred until runtime in Fortran. This |
| 1268 | // operation may therefore generate a sizeable block of IR, including calls to |
| 1269 | // type-based helper functions, so caching the result of this operation in the |
| 1270 | // client would be advised as well. |
| 1271 | fir::ExtendedValue fir::factory::componentToExtendedValue( |
| 1272 | fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value component) { |
| 1273 | auto fieldTy = component.getType(); |
| 1274 | if (auto ty = fir::dyn_cast_ptrEleTy(fieldTy)) |
| 1275 | fieldTy = ty; |
| 1276 | if (mlir::isa<fir::BaseBoxType>(fieldTy)) { |
| 1277 | llvm::SmallVector<mlir::Value> nonDeferredTypeParams; |
| 1278 | auto eleTy = fir::unwrapSequenceType(fir::dyn_cast_ptrOrBoxEleTy(fieldTy)); |
| 1279 | if (auto charTy = mlir::dyn_cast<fir::CharacterType>(eleTy)) { |
| 1280 | auto lenTy = builder.getCharacterLengthType(); |
| 1281 | if (charTy.hasConstantLen()) |
| 1282 | nonDeferredTypeParams.emplace_back( |
| 1283 | builder.createIntegerConstant(loc, lenTy, charTy.getLen())); |
| 1284 | // TODO: Starting, F2003, the dynamic character length might be dependent |
| 1285 | // on a PDT length parameter. There is no way to make a difference with |
| 1286 | // deferred length here yet. |
| 1287 | } |
| 1288 | if (auto recTy = mlir::dyn_cast<fir::RecordType>(eleTy)) |
| 1289 | if (recTy.getNumLenParams() > 0) |
| 1290 | TODO(loc, "allocatable and pointer components non deferred length " |
| 1291 | "parameters" ); |
| 1292 | |
| 1293 | return fir::MutableBoxValue(component, nonDeferredTypeParams, |
| 1294 | /*mutableProperties=*/{}); |
| 1295 | } |
| 1296 | llvm::SmallVector<mlir::Value> extents; |
| 1297 | if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(fieldTy)) { |
| 1298 | fieldTy = seqTy.getEleTy(); |
| 1299 | auto idxTy = builder.getIndexType(); |
| 1300 | for (auto extent : seqTy.getShape()) { |
| 1301 | if (extent == fir::SequenceType::getUnknownExtent()) |
| 1302 | TODO(loc, "array component shape depending on length parameters" ); |
| 1303 | extents.emplace_back(builder.createIntegerConstant(loc, idxTy, extent)); |
| 1304 | } |
| 1305 | } |
| 1306 | if (auto charTy = mlir::dyn_cast<fir::CharacterType>(fieldTy)) { |
| 1307 | auto cstLen = charTy.getLen(); |
| 1308 | if (cstLen == fir::CharacterType::unknownLen()) |
| 1309 | TODO(loc, "get character component length from length type parameters" ); |
| 1310 | auto len = builder.createIntegerConstant( |
| 1311 | loc, builder.getCharacterLengthType(), cstLen); |
| 1312 | if (!extents.empty()) |
| 1313 | return fir::CharArrayBoxValue{component, len, extents}; |
| 1314 | return fir::CharBoxValue{component, len}; |
| 1315 | } |
| 1316 | if (auto recordTy = mlir::dyn_cast<fir::RecordType>(fieldTy)) |
| 1317 | if (recordTy.getNumLenParams() != 0) |
| 1318 | TODO(loc, |
| 1319 | "lower component ref that is a derived type with length parameter" ); |
| 1320 | if (!extents.empty()) |
| 1321 | return fir::ArrayBoxValue{component, extents}; |
| 1322 | return component; |
| 1323 | } |
| 1324 | |
| 1325 | fir::ExtendedValue fir::factory::arrayElementToExtendedValue( |
| 1326 | fir::FirOpBuilder &builder, mlir::Location loc, |
| 1327 | const fir::ExtendedValue &array, mlir::Value element) { |
| 1328 | return array.match( |
| 1329 | [&](const fir::CharBoxValue &cb) -> fir::ExtendedValue { |
| 1330 | return cb.clone(element); |
| 1331 | }, |
| 1332 | [&](const fir::CharArrayBoxValue &bv) -> fir::ExtendedValue { |
| 1333 | return bv.cloneElement(element); |
| 1334 | }, |
| 1335 | [&](const fir::BoxValue &box) -> fir::ExtendedValue { |
| 1336 | if (box.isCharacter()) { |
| 1337 | auto len = fir::factory::readCharLen(builder, loc, box); |
| 1338 | return fir::CharBoxValue{element, len}; |
| 1339 | } |
| 1340 | if (box.isDerivedWithLenParameters()) |
| 1341 | TODO(loc, "get length parameters from derived type BoxValue" ); |
| 1342 | if (box.isPolymorphic()) { |
| 1343 | return fir::PolymorphicValue(element, fir::getBase(box)); |
| 1344 | } |
| 1345 | return element; |
| 1346 | }, |
| 1347 | [&](const fir::ArrayBoxValue &box) -> fir::ExtendedValue { |
| 1348 | if (box.getSourceBox()) |
| 1349 | return fir::PolymorphicValue(element, box.getSourceBox()); |
| 1350 | return element; |
| 1351 | }, |
| 1352 | [&](const auto &) -> fir::ExtendedValue { return element; }); |
| 1353 | } |
| 1354 | |
| 1355 | fir::ExtendedValue fir::factory::arraySectionElementToExtendedValue( |
| 1356 | fir::FirOpBuilder &builder, mlir::Location loc, |
| 1357 | const fir::ExtendedValue &array, mlir::Value element, mlir::Value slice) { |
| 1358 | if (!slice) |
| 1359 | return arrayElementToExtendedValue(builder, loc, array, element); |
| 1360 | auto sliceOp = mlir::dyn_cast_or_null<fir::SliceOp>(slice.getDefiningOp()); |
| 1361 | assert(sliceOp && "slice must be a sliceOp" ); |
| 1362 | if (sliceOp.getFields().empty()) |
| 1363 | return arrayElementToExtendedValue(builder, loc, array, element); |
| 1364 | // For F95, using componentToExtendedValue will work, but when PDTs are |
| 1365 | // lowered. It will be required to go down the slice to propagate the length |
| 1366 | // parameters. |
| 1367 | return fir::factory::componentToExtendedValue(builder, loc, element); |
| 1368 | } |
| 1369 | |
| 1370 | void fir::factory::genScalarAssignment(fir::FirOpBuilder &builder, |
| 1371 | mlir::Location loc, |
| 1372 | const fir::ExtendedValue &lhs, |
| 1373 | const fir::ExtendedValue &rhs, |
| 1374 | bool needFinalization, |
| 1375 | bool isTemporaryLHS) { |
| 1376 | assert(lhs.rank() == 0 && rhs.rank() == 0 && "must be scalars" ); |
| 1377 | auto type = fir::unwrapSequenceType( |
| 1378 | fir::unwrapPassByRefType(fir::getBase(lhs).getType())); |
| 1379 | if (mlir::isa<fir::CharacterType>(type)) { |
| 1380 | const fir::CharBoxValue *toChar = lhs.getCharBox(); |
| 1381 | const fir::CharBoxValue *fromChar = rhs.getCharBox(); |
| 1382 | assert(toChar && fromChar); |
| 1383 | fir::factory::CharacterExprHelper helper{builder, loc}; |
| 1384 | helper.createAssign(fir::ExtendedValue{*toChar}, |
| 1385 | fir::ExtendedValue{*fromChar}); |
| 1386 | } else if (mlir::isa<fir::RecordType>(type)) { |
| 1387 | fir::factory::genRecordAssignment(builder, loc, lhs, rhs, needFinalization, |
| 1388 | isTemporaryLHS); |
| 1389 | } else { |
| 1390 | assert(!fir::hasDynamicSize(type)); |
| 1391 | auto rhsVal = fir::getBase(rhs); |
| 1392 | if (fir::isa_ref_type(rhsVal.getType())) |
| 1393 | rhsVal = builder.create<fir::LoadOp>(loc, rhsVal); |
| 1394 | mlir::Value lhsAddr = fir::getBase(lhs); |
| 1395 | rhsVal = builder.createConvert(loc, fir::unwrapRefType(lhsAddr.getType()), |
| 1396 | rhsVal); |
| 1397 | builder.create<fir::StoreOp>(loc, rhsVal, lhsAddr); |
| 1398 | } |
| 1399 | } |
| 1400 | |
| 1401 | static void genComponentByComponentAssignment(fir::FirOpBuilder &builder, |
| 1402 | mlir::Location loc, |
| 1403 | const fir::ExtendedValue &lhs, |
| 1404 | const fir::ExtendedValue &rhs, |
| 1405 | bool isTemporaryLHS) { |
| 1406 | auto lbaseType = fir::unwrapPassByRefType(fir::getBase(lhs).getType()); |
| 1407 | auto lhsType = mlir::dyn_cast<fir::RecordType>(lbaseType); |
| 1408 | assert(lhsType && "lhs must be a scalar record type" ); |
| 1409 | auto rbaseType = fir::unwrapPassByRefType(fir::getBase(rhs).getType()); |
| 1410 | auto rhsType = mlir::dyn_cast<fir::RecordType>(rbaseType); |
| 1411 | assert(rhsType && "rhs must be a scalar record type" ); |
| 1412 | auto fieldIndexType = fir::FieldType::get(lhsType.getContext()); |
| 1413 | for (auto [lhsPair, rhsPair] : |
| 1414 | llvm::zip(lhsType.getTypeList(), rhsType.getTypeList())) { |
| 1415 | auto &[lFieldName, lFieldTy] = lhsPair; |
| 1416 | auto &[rFieldName, rFieldTy] = rhsPair; |
| 1417 | assert(!fir::hasDynamicSize(lFieldTy) && !fir::hasDynamicSize(rFieldTy)); |
| 1418 | mlir::Value rField = builder.create<fir::FieldIndexOp>( |
| 1419 | loc, fieldIndexType, rFieldName, rhsType, fir::getTypeParams(rhs)); |
| 1420 | auto rFieldRefType = builder.getRefType(rFieldTy); |
| 1421 | mlir::Value fromCoor = builder.create<fir::CoordinateOp>( |
| 1422 | loc, rFieldRefType, fir::getBase(rhs), rField); |
| 1423 | mlir::Value field = builder.create<fir::FieldIndexOp>( |
| 1424 | loc, fieldIndexType, lFieldName, lhsType, fir::getTypeParams(lhs)); |
| 1425 | auto fieldRefType = builder.getRefType(lFieldTy); |
| 1426 | mlir::Value toCoor = builder.create<fir::CoordinateOp>( |
| 1427 | loc, fieldRefType, fir::getBase(lhs), field); |
| 1428 | std::optional<fir::DoLoopOp> outerLoop; |
| 1429 | if (auto sequenceType = mlir::dyn_cast<fir::SequenceType>(lFieldTy)) { |
| 1430 | // Create loops to assign array components elements by elements. |
| 1431 | // Note that, since these are components, they either do not overlap, |
| 1432 | // or are the same and exactly overlap. They also have compile time |
| 1433 | // constant shapes. |
| 1434 | mlir::Type idxTy = builder.getIndexType(); |
| 1435 | llvm::SmallVector<mlir::Value> indices; |
| 1436 | mlir::Value zero = builder.createIntegerConstant(loc, idxTy, 0); |
| 1437 | mlir::Value one = builder.createIntegerConstant(loc, idxTy, 1); |
| 1438 | for (auto extent : llvm::reverse(sequenceType.getShape())) { |
| 1439 | // TODO: add zero size test ! |
| 1440 | mlir::Value ub = builder.createIntegerConstant(loc, idxTy, extent - 1); |
| 1441 | auto loop = builder.create<fir::DoLoopOp>(loc, zero, ub, one); |
| 1442 | if (!outerLoop) |
| 1443 | outerLoop = loop; |
| 1444 | indices.push_back(loop.getInductionVar()); |
| 1445 | builder.setInsertionPointToStart(loop.getBody()); |
| 1446 | } |
| 1447 | // Set indices in column-major order. |
| 1448 | std::reverse(indices.begin(), indices.end()); |
| 1449 | auto elementRefType = builder.getRefType(sequenceType.getEleTy()); |
| 1450 | toCoor = builder.create<fir::CoordinateOp>(loc, elementRefType, toCoor, |
| 1451 | indices); |
| 1452 | fromCoor = builder.create<fir::CoordinateOp>(loc, elementRefType, |
| 1453 | fromCoor, indices); |
| 1454 | } |
| 1455 | if (auto fieldEleTy = fir::unwrapSequenceType(lFieldTy); |
| 1456 | mlir::isa<fir::BaseBoxType>(fieldEleTy)) { |
| 1457 | assert(mlir::isa<fir::PointerType>( |
| 1458 | mlir::cast<fir::BaseBoxType>(fieldEleTy).getEleTy()) && |
| 1459 | "allocatable members require deep copy" ); |
| 1460 | auto fromPointerValue = builder.create<fir::LoadOp>(loc, fromCoor); |
| 1461 | auto castTo = builder.createConvert(loc, fieldEleTy, fromPointerValue); |
| 1462 | builder.create<fir::StoreOp>(loc, castTo, toCoor); |
| 1463 | } else { |
| 1464 | auto from = |
| 1465 | fir::factory::componentToExtendedValue(builder, loc, fromCoor); |
| 1466 | auto to = fir::factory::componentToExtendedValue(builder, loc, toCoor); |
| 1467 | // If LHS finalization is needed it is expected to be done |
| 1468 | // for the parent record, so that component-by-component |
| 1469 | // assignments may avoid finalization calls. |
| 1470 | fir::factory::genScalarAssignment(builder, loc, to, from, |
| 1471 | /*needFinalization=*/false, |
| 1472 | isTemporaryLHS); |
| 1473 | } |
| 1474 | if (outerLoop) |
| 1475 | builder.setInsertionPointAfter(*outerLoop); |
| 1476 | } |
| 1477 | } |
| 1478 | |
| 1479 | /// Can the assignment of this record type be implement with a simple memory |
| 1480 | /// copy (it requires no deep copy or user defined assignment of components )? |
| 1481 | static bool recordTypeCanBeMemCopied(fir::RecordType recordType) { |
| 1482 | // c_devptr type is a special case. It has a nested c_ptr field but we know it |
| 1483 | // can be copied directly. |
| 1484 | if (fir::isa_builtin_c_devptr_type(recordType)) |
| 1485 | return true; |
| 1486 | if (fir::hasDynamicSize(recordType)) |
| 1487 | return false; |
| 1488 | for (auto [_, fieldType] : recordType.getTypeList()) { |
| 1489 | // Derived type component may have user assignment (so far, we cannot tell |
| 1490 | // in FIR, so assume it is always the case, TODO: get the actual info). |
| 1491 | if (mlir::isa<fir::RecordType>(fir::unwrapSequenceType(fieldType)) && |
| 1492 | !fir::isa_builtin_c_devptr_type(fir::unwrapSequenceType(fieldType))) |
| 1493 | return false; |
| 1494 | // Allocatable components need deep copy. |
| 1495 | if (auto boxType = mlir::dyn_cast<fir::BaseBoxType>(fieldType)) |
| 1496 | if (mlir::isa<fir::HeapType>(boxType.getEleTy())) |
| 1497 | return false; |
| 1498 | } |
| 1499 | // Constant size components without user defined assignment and pointers can |
| 1500 | // be memcopied. |
| 1501 | return true; |
| 1502 | } |
| 1503 | |
| 1504 | static bool mayHaveFinalizer(fir::RecordType recordType, |
| 1505 | fir::FirOpBuilder &builder) { |
| 1506 | if (auto typeInfo = builder.getModule().lookupSymbol<fir::TypeInfoOp>( |
| 1507 | recordType.getName())) |
| 1508 | return !typeInfo.getNoFinal(); |
| 1509 | // No info, be pessimistic. |
| 1510 | return true; |
| 1511 | } |
| 1512 | |
| 1513 | void fir::factory::genRecordAssignment(fir::FirOpBuilder &builder, |
| 1514 | mlir::Location loc, |
| 1515 | const fir::ExtendedValue &lhs, |
| 1516 | const fir::ExtendedValue &rhs, |
| 1517 | bool needFinalization, |
| 1518 | bool isTemporaryLHS) { |
| 1519 | assert(lhs.rank() == 0 && rhs.rank() == 0 && "assume scalar assignment" ); |
| 1520 | auto baseTy = fir::dyn_cast_ptrOrBoxEleTy(fir::getBase(lhs).getType()); |
| 1521 | assert(baseTy && "must be a memory type" ); |
| 1522 | // Box operands may be polymorphic, it is not entirely clear from 10.2.1.3 |
| 1523 | // if the assignment is performed on the dynamic of declared type. Use the |
| 1524 | // runtime assuming it is performed on the dynamic type. |
| 1525 | bool hasBoxOperands = |
| 1526 | mlir::isa<fir::BaseBoxType>(fir::getBase(lhs).getType()) || |
| 1527 | mlir::isa<fir::BaseBoxType>(fir::getBase(rhs).getType()); |
| 1528 | auto recTy = mlir::dyn_cast<fir::RecordType>(baseTy); |
| 1529 | assert(recTy && "must be a record type" ); |
| 1530 | if ((needFinalization && mayHaveFinalizer(recTy, builder)) || |
| 1531 | hasBoxOperands || !recordTypeCanBeMemCopied(recTy)) { |
| 1532 | auto to = fir::getBase(builder.createBox(loc, lhs)); |
| 1533 | auto from = fir::getBase(builder.createBox(loc, rhs)); |
| 1534 | // The runtime entry point may modify the LHS descriptor if it is |
| 1535 | // an allocatable. Allocatable assignment is handle elsewhere in lowering, |
| 1536 | // so just create a fir.ref<fir.box<>> from the fir.box to comply with the |
| 1537 | // runtime interface, but assume the fir.box is unchanged. |
| 1538 | // TODO: does this holds true with polymorphic entities ? |
| 1539 | auto toMutableBox = builder.createTemporary(loc, to.getType()); |
| 1540 | builder.create<fir::StoreOp>(loc, to, toMutableBox); |
| 1541 | if (isTemporaryLHS) |
| 1542 | fir::runtime::genAssignTemporary(builder, loc, toMutableBox, from); |
| 1543 | else |
| 1544 | fir::runtime::genAssign(builder, loc, toMutableBox, from); |
| 1545 | return; |
| 1546 | } |
| 1547 | |
| 1548 | // Otherwise, the derived type has compile time constant size and for which |
| 1549 | // the component by component assignment can be replaced by a memory copy. |
| 1550 | // Since we do not know the size of the derived type in lowering, do a |
| 1551 | // component by component assignment. Note that a single fir.load/fir.store |
| 1552 | // could be used on "small" record types, but as the type size grows, this |
| 1553 | // leads to issues in LLVM (long compile times, long IR files, and even |
| 1554 | // asserts at some point). Since there is no good size boundary, just always |
| 1555 | // use component by component assignment here. |
| 1556 | genComponentByComponentAssignment(builder, loc, lhs, rhs, isTemporaryLHS); |
| 1557 | } |
| 1558 | |
| 1559 | mlir::TupleType |
| 1560 | fir::factory::getRaggedArrayHeaderType(fir::FirOpBuilder &builder) { |
| 1561 | mlir::IntegerType i64Ty = builder.getIntegerType(64); |
| 1562 | auto arrTy = fir::SequenceType::get(builder.getIntegerType(8), 1); |
| 1563 | auto buffTy = fir::HeapType::get(arrTy); |
| 1564 | auto extTy = fir::SequenceType::get(i64Ty, 1); |
| 1565 | auto shTy = fir::HeapType::get(extTy); |
| 1566 | return mlir::TupleType::get(builder.getContext(), {i64Ty, buffTy, shTy}); |
| 1567 | } |
| 1568 | |
| 1569 | mlir::Value fir::factory::genLenOfCharacter( |
| 1570 | fir::FirOpBuilder &builder, mlir::Location loc, fir::ArrayLoadOp arrLoad, |
| 1571 | llvm::ArrayRef<mlir::Value> path, llvm::ArrayRef<mlir::Value> substring) { |
| 1572 | llvm::SmallVector<mlir::Value> typeParams(arrLoad.getTypeparams()); |
| 1573 | return genLenOfCharacter(builder, loc, |
| 1574 | mlir::cast<fir::SequenceType>(arrLoad.getType()), |
| 1575 | arrLoad.getMemref(), typeParams, path, substring); |
| 1576 | } |
| 1577 | |
| 1578 | mlir::Value fir::factory::genLenOfCharacter( |
| 1579 | fir::FirOpBuilder &builder, mlir::Location loc, fir::SequenceType seqTy, |
| 1580 | mlir::Value memref, llvm::ArrayRef<mlir::Value> typeParams, |
| 1581 | llvm::ArrayRef<mlir::Value> path, llvm::ArrayRef<mlir::Value> substring) { |
| 1582 | auto idxTy = builder.getIndexType(); |
| 1583 | auto zero = builder.createIntegerConstant(loc, idxTy, 0); |
| 1584 | auto saturatedDiff = [&](mlir::Value lower, mlir::Value upper) { |
| 1585 | auto diff = builder.create<mlir::arith::SubIOp>(loc, upper, lower); |
| 1586 | auto one = builder.createIntegerConstant(loc, idxTy, 1); |
| 1587 | auto size = builder.create<mlir::arith::AddIOp>(loc, diff, one); |
| 1588 | auto cmp = builder.create<mlir::arith::CmpIOp>( |
| 1589 | loc, mlir::arith::CmpIPredicate::sgt, size, zero); |
| 1590 | return builder.create<mlir::arith::SelectOp>(loc, cmp, size, zero); |
| 1591 | }; |
| 1592 | if (substring.size() == 2) { |
| 1593 | auto upper = builder.createConvert(loc, idxTy, substring.back()); |
| 1594 | auto lower = builder.createConvert(loc, idxTy, substring.front()); |
| 1595 | return saturatedDiff(lower, upper); |
| 1596 | } |
| 1597 | auto lower = zero; |
| 1598 | if (substring.size() == 1) |
| 1599 | lower = builder.createConvert(loc, idxTy, substring.front()); |
| 1600 | auto eleTy = fir::applyPathToType(seqTy, path); |
| 1601 | if (!fir::hasDynamicSize(eleTy)) { |
| 1602 | if (auto charTy = mlir::dyn_cast<fir::CharacterType>(eleTy)) { |
| 1603 | // Use LEN from the type. |
| 1604 | return builder.createIntegerConstant(loc, idxTy, charTy.getLen()); |
| 1605 | } |
| 1606 | // Do we need to support !fir.array<!fir.char<k,n>>? |
| 1607 | fir::emitFatalError(loc, |
| 1608 | "application of path did not result in a !fir.char" ); |
| 1609 | } |
| 1610 | if (fir::isa_box_type(memref.getType())) { |
| 1611 | if (mlir::isa<fir::BoxCharType>(memref.getType())) |
| 1612 | return builder.create<fir::BoxCharLenOp>(loc, idxTy, memref); |
| 1613 | if (mlir::isa<fir::BoxType>(memref.getType())) |
| 1614 | return CharacterExprHelper(builder, loc).readLengthFromBox(memref); |
| 1615 | fir::emitFatalError(loc, "memref has wrong type" ); |
| 1616 | } |
| 1617 | if (typeParams.empty()) { |
| 1618 | fir::emitFatalError(loc, "array_load must have typeparams" ); |
| 1619 | } |
| 1620 | if (fir::isa_char(seqTy.getEleTy())) { |
| 1621 | assert(typeParams.size() == 1 && "too many typeparams" ); |
| 1622 | return typeParams.front(); |
| 1623 | } |
| 1624 | TODO(loc, "LEN of character must be computed at runtime" ); |
| 1625 | } |
| 1626 | |
| 1627 | mlir::Value fir::factory::createZeroValue(fir::FirOpBuilder &builder, |
| 1628 | mlir::Location loc, mlir::Type type) { |
| 1629 | mlir::Type i1 = builder.getIntegerType(1); |
| 1630 | if (mlir::isa<fir::LogicalType>(type) || type == i1) |
| 1631 | return builder.createConvert(loc, type, builder.createBool(loc, false)); |
| 1632 | if (fir::isa_integer(type)) |
| 1633 | return builder.createIntegerConstant(loc, type, 0); |
| 1634 | if (fir::isa_real(type)) |
| 1635 | return builder.createRealZeroConstant(loc, type); |
| 1636 | if (fir::isa_complex(type)) { |
| 1637 | fir::factory::Complex complexHelper(builder, loc); |
| 1638 | mlir::Type partType = complexHelper.getComplexPartType(type); |
| 1639 | mlir::Value zeroPart = builder.createRealZeroConstant(loc, partType); |
| 1640 | return complexHelper.createComplex(type, zeroPart, zeroPart); |
| 1641 | } |
| 1642 | fir::emitFatalError(loc, "internal: trying to generate zero value of non " |
| 1643 | "numeric or logical type" ); |
| 1644 | } |
| 1645 | |
| 1646 | std::optional<std::int64_t> |
| 1647 | fir::factory::getExtentFromTriplet(mlir::Value lb, mlir::Value ub, |
| 1648 | mlir::Value stride) { |
| 1649 | std::function<std::optional<std::int64_t>(mlir::Value)> getConstantValue = |
| 1650 | [&](mlir::Value value) -> std::optional<std::int64_t> { |
| 1651 | if (auto valInt = fir::getIntIfConstant(value)) |
| 1652 | return *valInt; |
| 1653 | auto *definingOp = value.getDefiningOp(); |
| 1654 | if (mlir::isa_and_nonnull<fir::ConvertOp>(definingOp)) { |
| 1655 | auto valOp = mlir::dyn_cast<fir::ConvertOp>(definingOp); |
| 1656 | return getConstantValue(valOp.getValue()); |
| 1657 | } |
| 1658 | return {}; |
| 1659 | }; |
| 1660 | if (auto lbInt = getConstantValue(lb)) { |
| 1661 | if (auto ubInt = getConstantValue(ub)) { |
| 1662 | if (auto strideInt = getConstantValue(stride)) { |
| 1663 | if (*strideInt != 0) { |
| 1664 | std::int64_t extent = 1 + (*ubInt - *lbInt) / *strideInt; |
| 1665 | if (extent > 0) |
| 1666 | return extent; |
| 1667 | } |
| 1668 | } |
| 1669 | } |
| 1670 | } |
| 1671 | return {}; |
| 1672 | } |
| 1673 | |
| 1674 | mlir::Value fir::factory::genMaxWithZero(fir::FirOpBuilder &builder, |
| 1675 | mlir::Location loc, mlir::Value value, |
| 1676 | mlir::Value zero) { |
| 1677 | if (mlir::Operation *definingOp = value.getDefiningOp()) |
| 1678 | if (auto cst = mlir::dyn_cast<mlir::arith::ConstantOp>(definingOp)) |
| 1679 | if (auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>(cst.getValue())) |
| 1680 | return intAttr.getInt() > 0 ? value : zero; |
| 1681 | mlir::Value valueIsGreater = builder.create<mlir::arith::CmpIOp>( |
| 1682 | loc, mlir::arith::CmpIPredicate::sgt, value, zero); |
| 1683 | return builder.create<mlir::arith::SelectOp>(loc, valueIsGreater, value, |
| 1684 | zero); |
| 1685 | } |
| 1686 | |
| 1687 | mlir::Value fir::factory::genMaxWithZero(fir::FirOpBuilder &builder, |
| 1688 | mlir::Location loc, |
| 1689 | mlir::Value value) { |
| 1690 | mlir::Value zero = builder.createIntegerConstant(loc, value.getType(), 0); |
| 1691 | return genMaxWithZero(builder, loc, value, zero); |
| 1692 | } |
| 1693 | |
| 1694 | mlir::Value fir::factory::computeExtent(fir::FirOpBuilder &builder, |
| 1695 | mlir::Location loc, mlir::Value lb, |
| 1696 | mlir::Value ub, mlir::Value zero, |
| 1697 | mlir::Value one) { |
| 1698 | mlir::Type type = lb.getType(); |
| 1699 | // Let the folder deal with the common `ub - <const> + 1` case. |
| 1700 | auto diff = builder.create<mlir::arith::SubIOp>(loc, type, ub, lb); |
| 1701 | auto rawExtent = builder.create<mlir::arith::AddIOp>(loc, type, diff, one); |
| 1702 | return fir::factory::genMaxWithZero(builder, loc, rawExtent, zero); |
| 1703 | } |
| 1704 | mlir::Value fir::factory::computeExtent(fir::FirOpBuilder &builder, |
| 1705 | mlir::Location loc, mlir::Value lb, |
| 1706 | mlir::Value ub) { |
| 1707 | mlir::Type type = lb.getType(); |
| 1708 | mlir::Value one = builder.createIntegerConstant(loc, type, 1); |
| 1709 | mlir::Value zero = builder.createIntegerConstant(loc, type, 0); |
| 1710 | return computeExtent(builder, loc, lb, ub, zero, one); |
| 1711 | } |
| 1712 | |
| 1713 | static std::pair<mlir::Value, mlir::Type> |
| 1714 | genCPtrOrCFunptrFieldIndex(fir::FirOpBuilder &builder, mlir::Location loc, |
| 1715 | mlir::Type cptrTy) { |
| 1716 | auto recTy = mlir::cast<fir::RecordType>(cptrTy); |
| 1717 | assert(recTy.getTypeList().size() == 1); |
| 1718 | auto addrFieldName = recTy.getTypeList()[0].first; |
| 1719 | mlir::Type addrFieldTy = recTy.getTypeList()[0].second; |
| 1720 | auto fieldIndexType = fir::FieldType::get(cptrTy.getContext()); |
| 1721 | mlir::Value addrFieldIndex = builder.create<fir::FieldIndexOp>( |
| 1722 | loc, fieldIndexType, addrFieldName, recTy, |
| 1723 | /*typeParams=*/mlir::ValueRange{}); |
| 1724 | return {addrFieldIndex, addrFieldTy}; |
| 1725 | } |
| 1726 | |
| 1727 | mlir::Value fir::factory::genCPtrOrCFunptrAddr(fir::FirOpBuilder &builder, |
| 1728 | mlir::Location loc, |
| 1729 | mlir::Value cPtr, |
| 1730 | mlir::Type ty) { |
| 1731 | auto [addrFieldIndex, addrFieldTy] = |
| 1732 | genCPtrOrCFunptrFieldIndex(builder, loc, ty); |
| 1733 | return builder.create<fir::CoordinateOp>(loc, builder.getRefType(addrFieldTy), |
| 1734 | cPtr, addrFieldIndex); |
| 1735 | } |
| 1736 | |
| 1737 | mlir::Value fir::factory::genCDevPtrAddr(fir::FirOpBuilder &builder, |
| 1738 | mlir::Location loc, |
| 1739 | mlir::Value cDevPtr, mlir::Type ty) { |
| 1740 | auto recTy = mlir::cast<fir::RecordType>(ty); |
| 1741 | assert(recTy.getTypeList().size() == 1); |
| 1742 | auto cptrFieldName = recTy.getTypeList()[0].first; |
| 1743 | mlir::Type cptrFieldTy = recTy.getTypeList()[0].second; |
| 1744 | auto fieldIndexType = fir::FieldType::get(ty.getContext()); |
| 1745 | mlir::Value cptrFieldIndex = builder.create<fir::FieldIndexOp>( |
| 1746 | loc, fieldIndexType, cptrFieldName, recTy, |
| 1747 | /*typeParams=*/mlir::ValueRange{}); |
| 1748 | auto cptrCoord = builder.create<fir::CoordinateOp>( |
| 1749 | loc, builder.getRefType(cptrFieldTy), cDevPtr, cptrFieldIndex); |
| 1750 | auto [addrFieldIndex, addrFieldTy] = |
| 1751 | genCPtrOrCFunptrFieldIndex(builder, loc, cptrFieldTy); |
| 1752 | return builder.create<fir::CoordinateOp>(loc, builder.getRefType(addrFieldTy), |
| 1753 | cptrCoord, addrFieldIndex); |
| 1754 | } |
| 1755 | |
| 1756 | mlir::Value fir::factory::genCPtrOrCFunptrValue(fir::FirOpBuilder &builder, |
| 1757 | mlir::Location loc, |
| 1758 | mlir::Value cPtr) { |
| 1759 | mlir::Type cPtrTy = fir::unwrapRefType(cPtr.getType()); |
| 1760 | if (fir::isa_builtin_cdevptr_type(cPtrTy)) { |
| 1761 | // Unwrap c_ptr from c_devptr. |
| 1762 | auto [addrFieldIndex, addrFieldTy] = |
| 1763 | genCPtrOrCFunptrFieldIndex(builder, loc, cPtrTy); |
| 1764 | mlir::Value cPtrCoor; |
| 1765 | if (fir::isa_ref_type(cPtr.getType())) { |
| 1766 | cPtrCoor = builder.create<fir::CoordinateOp>( |
| 1767 | loc, builder.getRefType(addrFieldTy), cPtr, addrFieldIndex); |
| 1768 | } else { |
| 1769 | auto arrayAttr = builder.getArrayAttr( |
| 1770 | {builder.getIntegerAttr(builder.getIndexType(), 0)}); |
| 1771 | cPtrCoor = builder.create<fir::ExtractValueOp>(loc, addrFieldTy, cPtr, |
| 1772 | arrayAttr); |
| 1773 | } |
| 1774 | return genCPtrOrCFunptrValue(builder, loc, cPtrCoor); |
| 1775 | } |
| 1776 | |
| 1777 | if (fir::isa_ref_type(cPtr.getType())) { |
| 1778 | mlir::Value cPtrAddr = |
| 1779 | fir::factory::genCPtrOrCFunptrAddr(builder, loc, cPtr, cPtrTy); |
| 1780 | return builder.create<fir::LoadOp>(loc, cPtrAddr); |
| 1781 | } |
| 1782 | auto [addrFieldIndex, addrFieldTy] = |
| 1783 | genCPtrOrCFunptrFieldIndex(builder, loc, cPtrTy); |
| 1784 | auto arrayAttr = |
| 1785 | builder.getArrayAttr({builder.getIntegerAttr(builder.getIndexType(), 0)}); |
| 1786 | return builder.create<fir::ExtractValueOp>(loc, addrFieldTy, cPtr, arrayAttr); |
| 1787 | } |
| 1788 | |
| 1789 | fir::BoxValue fir::factory::createBoxValue(fir::FirOpBuilder &builder, |
| 1790 | mlir::Location loc, |
| 1791 | const fir::ExtendedValue &exv) { |
| 1792 | if (auto *boxValue = exv.getBoxOf<fir::BoxValue>()) |
| 1793 | return *boxValue; |
| 1794 | mlir::Value box = builder.createBox(loc, exv); |
| 1795 | llvm::SmallVector<mlir::Value> lbounds; |
| 1796 | llvm::SmallVector<mlir::Value> explicitTypeParams; |
| 1797 | exv.match( |
| 1798 | [&](const fir::ArrayBoxValue &box) { |
| 1799 | lbounds.append(box.getLBounds().begin(), box.getLBounds().end()); |
| 1800 | }, |
| 1801 | [&](const fir::CharArrayBoxValue &box) { |
| 1802 | lbounds.append(box.getLBounds().begin(), box.getLBounds().end()); |
| 1803 | explicitTypeParams.emplace_back(box.getLen()); |
| 1804 | }, |
| 1805 | [&](const fir::CharBoxValue &box) { |
| 1806 | explicitTypeParams.emplace_back(box.getLen()); |
| 1807 | }, |
| 1808 | [&](const fir::MutableBoxValue &x) { |
| 1809 | if (x.rank() > 0) { |
| 1810 | // The resulting box lbounds must be coming from the mutable box. |
| 1811 | fir::ExtendedValue boxVal = |
| 1812 | fir::factory::genMutableBoxRead(builder, loc, x); |
| 1813 | // Make sure we do not recurse infinitely. |
| 1814 | if (boxVal.getBoxOf<fir::MutableBoxValue>()) |
| 1815 | fir::emitFatalError(loc, "mutable box read cannot be mutable box" ); |
| 1816 | fir::BoxValue box = |
| 1817 | fir::factory::createBoxValue(builder, loc, boxVal); |
| 1818 | lbounds.append(box.getLBounds().begin(), box.getLBounds().end()); |
| 1819 | } |
| 1820 | explicitTypeParams.append(x.nonDeferredLenParams().begin(), |
| 1821 | x.nonDeferredLenParams().end()); |
| 1822 | }, |
| 1823 | [](const auto &) {}); |
| 1824 | return fir::BoxValue(box, lbounds, explicitTypeParams); |
| 1825 | } |
| 1826 | |
| 1827 | mlir::Value fir::factory::createNullBoxProc(fir::FirOpBuilder &builder, |
| 1828 | mlir::Location loc, |
| 1829 | mlir::Type boxType) { |
| 1830 | auto boxTy{mlir::dyn_cast<fir::BoxProcType>(boxType)}; |
| 1831 | if (!boxTy) |
| 1832 | fir::emitFatalError(loc, "Procedure pointer must be of BoxProcType" ); |
| 1833 | auto boxEleTy{fir::unwrapRefType(boxTy.getEleTy())}; |
| 1834 | mlir::Value initVal{builder.create<fir::ZeroOp>(loc, boxEleTy)}; |
| 1835 | return builder.create<fir::EmboxProcOp>(loc, boxTy, initVal); |
| 1836 | } |
| 1837 | |
| 1838 | void fir::factory::setInternalLinkage(mlir::func::FuncOp func) { |
| 1839 | auto internalLinkage = mlir::LLVM::linkage::Linkage::Internal; |
| 1840 | auto linkage = |
| 1841 | mlir::LLVM::LinkageAttr::get(func->getContext(), internalLinkage); |
| 1842 | func->setAttr("llvm.linkage" , linkage); |
| 1843 | } |
| 1844 | |
| 1845 | uint64_t |
| 1846 | fir::factory::getAllocaAddressSpace(const mlir::DataLayout *dataLayout) { |
| 1847 | if (dataLayout) |
| 1848 | if (mlir::Attribute addrSpace = dataLayout->getAllocaMemorySpace()) |
| 1849 | return mlir::cast<mlir::IntegerAttr>(addrSpace).getUInt(); |
| 1850 | return 0; |
| 1851 | } |
| 1852 | |
| 1853 | llvm::SmallVector<mlir::Value> |
| 1854 | fir::factory::deduceOptimalExtents(mlir::ValueRange extents1, |
| 1855 | mlir::ValueRange extents2) { |
| 1856 | llvm::SmallVector<mlir::Value> extents; |
| 1857 | extents.reserve(extents1.size()); |
| 1858 | for (auto [extent1, extent2] : llvm::zip(extents1, extents2)) { |
| 1859 | if (!fir::getIntIfConstant(extent1) && fir::getIntIfConstant(extent2)) |
| 1860 | extents.push_back(extent2); |
| 1861 | else |
| 1862 | extents.push_back(extent1); |
| 1863 | } |
| 1864 | return extents; |
| 1865 | } |
| 1866 | |
| 1867 | llvm::SmallVector<mlir::Value> fir::factory::updateRuntimeExtentsForEmptyArrays( |
| 1868 | fir::FirOpBuilder &builder, mlir::Location loc, mlir::ValueRange extents) { |
| 1869 | if (extents.size() <= 1) |
| 1870 | return extents; |
| 1871 | |
| 1872 | mlir::Type i1Type = builder.getI1Type(); |
| 1873 | mlir::Value isEmpty = createZeroValue(builder, loc, i1Type); |
| 1874 | |
| 1875 | llvm::SmallVector<mlir::Value, Fortran::common::maxRank> zeroes; |
| 1876 | for (mlir::Value extent : extents) { |
| 1877 | mlir::Type type = extent.getType(); |
| 1878 | mlir::Value zero = createZeroValue(builder, loc, type); |
| 1879 | zeroes.push_back(zero); |
| 1880 | mlir::Value isZero = builder.create<mlir::arith::CmpIOp>( |
| 1881 | loc, mlir::arith::CmpIPredicate::eq, extent, zero); |
| 1882 | isEmpty = builder.create<mlir::arith::OrIOp>(loc, isEmpty, isZero); |
| 1883 | } |
| 1884 | |
| 1885 | llvm::SmallVector<mlir::Value> newExtents; |
| 1886 | for (auto [zero, extent] : llvm::zip_equal(zeroes, extents)) { |
| 1887 | newExtents.push_back( |
| 1888 | builder.create<mlir::arith::SelectOp>(loc, isEmpty, zero, extent)); |
| 1889 | } |
| 1890 | return newExtents; |
| 1891 | } |
| 1892 | |
| 1893 | void fir::factory::genDimInfoFromBox( |
| 1894 | fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value box, |
| 1895 | llvm::SmallVectorImpl<mlir::Value> *lbounds, |
| 1896 | llvm::SmallVectorImpl<mlir::Value> *extents, |
| 1897 | llvm::SmallVectorImpl<mlir::Value> *strides) { |
| 1898 | auto boxType = mlir::dyn_cast<fir::BaseBoxType>(box.getType()); |
| 1899 | assert(boxType && "must be a box" ); |
| 1900 | if (!lbounds && !extents && !strides) |
| 1901 | return; |
| 1902 | |
| 1903 | unsigned rank = fir::getBoxRank(boxType); |
| 1904 | assert(rank != 0 && "must be an array of known rank" ); |
| 1905 | mlir::Type idxTy = builder.getIndexType(); |
| 1906 | for (unsigned i = 0; i < rank; ++i) { |
| 1907 | mlir::Value dim = builder.createIntegerConstant(loc, idxTy, i); |
| 1908 | auto dimInfo = |
| 1909 | builder.create<fir::BoxDimsOp>(loc, idxTy, idxTy, idxTy, box, dim); |
| 1910 | if (lbounds) |
| 1911 | lbounds->push_back(dimInfo.getLowerBound()); |
| 1912 | if (extents) |
| 1913 | extents->push_back(dimInfo.getExtent()); |
| 1914 | if (strides) |
| 1915 | strides->push_back(dimInfo.getByteStride()); |
| 1916 | } |
| 1917 | } |
| 1918 | |
| 1919 | mlir::Value fir::factory::genLifetimeStart(mlir::OpBuilder &builder, |
| 1920 | mlir::Location loc, |
| 1921 | fir::AllocaOp alloc, int64_t size, |
| 1922 | const mlir::DataLayout *dl) { |
| 1923 | mlir::Type ptrTy = mlir::LLVM::LLVMPointerType::get( |
| 1924 | alloc.getContext(), getAllocaAddressSpace(dl)); |
| 1925 | mlir::Value cast = |
| 1926 | builder.create<fir::ConvertOp>(loc, ptrTy, alloc.getResult()); |
| 1927 | builder.create<mlir::LLVM::LifetimeStartOp>(loc, size, cast); |
| 1928 | return cast; |
| 1929 | } |
| 1930 | |
| 1931 | void fir::factory::genLifetimeEnd(mlir::OpBuilder &builder, mlir::Location loc, |
| 1932 | mlir::Value cast, int64_t size) { |
| 1933 | builder.create<mlir::LLVM::LifetimeEndOp>(loc, size, cast); |
| 1934 | } |
| 1935 | |