1 //===-- CodeGen.cpp -- bridge to lower to LLVM ----------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "flang/Optimizer/CodeGen/CodeGen.h" 14 15 #include "flang/Optimizer/CodeGen/CGOps.h" 16 #include "flang/Optimizer/CodeGen/CodeGenOpenMP.h" 17 #include "flang/Optimizer/CodeGen/FIROpPatterns.h" 18 #include "flang/Optimizer/CodeGen/TypeConverter.h" 19 #include "flang/Optimizer/Dialect/FIRAttr.h" 20 #include "flang/Optimizer/Dialect/FIROps.h" 21 #include "flang/Optimizer/Dialect/FIRType.h" 22 #include "flang/Optimizer/Support/DataLayout.h" 23 #include "flang/Optimizer/Support/InternalNames.h" 24 #include "flang/Optimizer/Support/TypeCode.h" 25 #include "flang/Optimizer/Support/Utils.h" 26 #include "flang/Runtime/CUDA/descriptor.h" 27 #include "flang/Runtime/CUDA/memory.h" 28 #include "flang/Runtime/allocator-registry-consts.h" 29 #include "flang/Runtime/descriptor-consts.h" 30 #include "flang/Semantics/runtime-type-info.h" 31 #include "mlir/Conversion/ArithCommon/AttrToLLVMConverter.h" 32 #include "mlir/Conversion/ArithToLLVM/ArithToLLVM.h" 33 #include "mlir/Conversion/ComplexToLLVM/ComplexToLLVM.h" 34 #include "mlir/Conversion/ComplexToStandard/ComplexToStandard.h" 35 #include "mlir/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.h" 36 #include "mlir/Conversion/FuncToLLVM/ConvertFuncToLLVM.h" 37 #include "mlir/Conversion/LLVMCommon/Pattern.h" 38 #include "mlir/Conversion/MathToFuncs/MathToFuncs.h" 39 #include "mlir/Conversion/MathToLLVM/MathToLLVM.h" 40 #include "mlir/Conversion/MathToLibm/MathToLibm.h" 41 #include "mlir/Conversion/MathToROCDL/MathToROCDL.h" 42 #include "mlir/Conversion/OpenMPToLLVM/ConvertOpenMPToLLVM.h" 43 #include "mlir/Conversion/VectorToLLVM/ConvertVectorToLLVM.h" 44 #include "mlir/Dialect/Arith/IR/Arith.h" 45 #include "mlir/Dialect/DLTI/DLTI.h" 46 #include "mlir/Dialect/GPU/IR/GPUDialect.h" 47 #include "mlir/Dialect/LLVMIR/LLVMAttrs.h" 48 #include "mlir/Dialect/LLVMIR/LLVMDialect.h" 49 #include "mlir/Dialect/LLVMIR/Transforms/AddComdats.h" 50 #include "mlir/Dialect/OpenACC/OpenACC.h" 51 #include "mlir/Dialect/OpenMP/OpenMPDialect.h" 52 #include "mlir/IR/BuiltinTypes.h" 53 #include "mlir/IR/Matchers.h" 54 #include "mlir/Pass/Pass.h" 55 #include "mlir/Pass/PassManager.h" 56 #include "mlir/Target/LLVMIR/Import.h" 57 #include "mlir/Target/LLVMIR/ModuleTranslation.h" 58 #include "llvm/ADT/ArrayRef.h" 59 #include "llvm/ADT/TypeSwitch.h" 60 61 namespace fir { 62 #define GEN_PASS_DEF_FIRTOLLVMLOWERING 63 #include "flang/Optimizer/CodeGen/CGPasses.h.inc" 64 } // namespace fir 65 66 #define DEBUG_TYPE "flang-codegen" 67 68 // TODO: This should really be recovered from the specified target. 69 static constexpr unsigned defaultAlign = 8; 70 71 /// `fir.box` attribute values as defined for CFI_attribute_t in 72 /// flang/ISO_Fortran_binding.h. 73 static constexpr unsigned kAttrPointer = CFI_attribute_pointer; 74 static constexpr unsigned kAttrAllocatable = CFI_attribute_allocatable; 75 76 static inline mlir::Type getLlvmPtrType(mlir::MLIRContext *context, 77 unsigned addressSpace = 0) { 78 return mlir::LLVM::LLVMPointerType::get(context, addressSpace); 79 } 80 81 static inline mlir::Type getI8Type(mlir::MLIRContext *context) { 82 return mlir::IntegerType::get(context, 8); 83 } 84 85 static mlir::LLVM::ConstantOp 86 genConstantIndex(mlir::Location loc, mlir::Type ity, 87 mlir::ConversionPatternRewriter &rewriter, 88 std::int64_t offset) { 89 auto cattr = rewriter.getI64IntegerAttr(offset); 90 return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr); 91 } 92 93 static mlir::Block *createBlock(mlir::ConversionPatternRewriter &rewriter, 94 mlir::Block *insertBefore) { 95 assert(insertBefore && "expected valid insertion block"); 96 return rewriter.createBlock(insertBefore->getParent(), 97 mlir::Region::iterator(insertBefore)); 98 } 99 100 /// Extract constant from a value that must be the result of one of the 101 /// ConstantOp operations. 102 static int64_t getConstantIntValue(mlir::Value val) { 103 if (auto constVal = fir::getIntIfConstant(val)) 104 return *constVal; 105 fir::emitFatalError(val.getLoc(), "must be a constant"); 106 } 107 108 static unsigned getTypeDescFieldId(mlir::Type ty) { 109 auto isArray = mlir::isa<fir::SequenceType>(fir::dyn_cast_ptrOrBoxEleTy(ty)); 110 return isArray ? kOptTypePtrPosInBox : kDimsPosInBox; 111 } 112 static unsigned getLenParamFieldId(mlir::Type ty) { 113 return getTypeDescFieldId(ty) + 1; 114 } 115 116 static llvm::SmallVector<mlir::NamedAttribute> 117 addLLVMOpBundleAttrs(mlir::ConversionPatternRewriter &rewriter, 118 llvm::ArrayRef<mlir::NamedAttribute> attrs, 119 int32_t numCallOperands) { 120 llvm::SmallVector<mlir::NamedAttribute> newAttrs; 121 newAttrs.reserve(attrs.size() + 2); 122 123 for (mlir::NamedAttribute attr : attrs) { 124 if (attr.getName() != "operandSegmentSizes") 125 newAttrs.push_back(attr); 126 } 127 128 newAttrs.push_back(rewriter.getNamedAttr( 129 "operandSegmentSizes", 130 rewriter.getDenseI32ArrayAttr({numCallOperands, 0}))); 131 newAttrs.push_back(rewriter.getNamedAttr("op_bundle_sizes", 132 rewriter.getDenseI32ArrayAttr({}))); 133 return newAttrs; 134 } 135 136 namespace { 137 /// Lower `fir.address_of` operation to `llvm.address_of` operation. 138 struct AddrOfOpConversion : public fir::FIROpConversion<fir::AddrOfOp> { 139 using FIROpConversion::FIROpConversion; 140 141 llvm::LogicalResult 142 matchAndRewrite(fir::AddrOfOp addr, OpAdaptor adaptor, 143 mlir::ConversionPatternRewriter &rewriter) const override { 144 auto ty = convertType(addr.getType()); 145 rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>( 146 addr, ty, addr.getSymbol().getRootReference().getValue()); 147 return mlir::success(); 148 } 149 }; 150 } // namespace 151 152 /// Lookup the function to compute the memory size of this parametric derived 153 /// type. The size of the object may depend on the LEN type parameters of the 154 /// derived type. 155 static mlir::LLVM::LLVMFuncOp 156 getDependentTypeMemSizeFn(fir::RecordType recTy, fir::AllocaOp op, 157 mlir::ConversionPatternRewriter &rewriter) { 158 auto module = op->getParentOfType<mlir::ModuleOp>(); 159 std::string name = recTy.getName().str() + "P.mem.size"; 160 if (auto memSizeFunc = module.lookupSymbol<mlir::LLVM::LLVMFuncOp>(name)) 161 return memSizeFunc; 162 TODO(op.getLoc(), "did not find allocation function"); 163 } 164 165 // Compute the alloc scale size (constant factors encoded in the array type). 166 // We do this for arrays without a constant interior or arrays of character with 167 // dynamic length arrays, since those are the only ones that get decayed to a 168 // pointer to the element type. 169 template <typename OP> 170 static mlir::Value 171 genAllocationScaleSize(OP op, mlir::Type ity, 172 mlir::ConversionPatternRewriter &rewriter) { 173 mlir::Location loc = op.getLoc(); 174 mlir::Type dataTy = op.getInType(); 175 auto seqTy = mlir::dyn_cast<fir::SequenceType>(dataTy); 176 fir::SequenceType::Extent constSize = 1; 177 if (seqTy) { 178 int constRows = seqTy.getConstantRows(); 179 const fir::SequenceType::ShapeRef &shape = seqTy.getShape(); 180 if (constRows != static_cast<int>(shape.size())) { 181 for (auto extent : shape) { 182 if (constRows-- > 0) 183 continue; 184 if (extent != fir::SequenceType::getUnknownExtent()) 185 constSize *= extent; 186 } 187 } 188 } 189 190 if (constSize != 1) { 191 mlir::Value constVal{ 192 genConstantIndex(loc, ity, rewriter, constSize).getResult()}; 193 return constVal; 194 } 195 return nullptr; 196 } 197 198 namespace { 199 struct DeclareOpConversion : public fir::FIROpConversion<fir::cg::XDeclareOp> { 200 public: 201 using FIROpConversion::FIROpConversion; 202 llvm::LogicalResult 203 matchAndRewrite(fir::cg::XDeclareOp declareOp, OpAdaptor adaptor, 204 mlir::ConversionPatternRewriter &rewriter) const override { 205 auto memRef = adaptor.getOperands()[0]; 206 if (auto fusedLoc = mlir::dyn_cast<mlir::FusedLoc>(declareOp.getLoc())) { 207 if (auto varAttr = 208 mlir::dyn_cast_or_null<mlir::LLVM::DILocalVariableAttr>( 209 fusedLoc.getMetadata())) { 210 rewriter.create<mlir::LLVM::DbgDeclareOp>(memRef.getLoc(), memRef, 211 varAttr, nullptr); 212 } 213 } 214 rewriter.replaceOp(declareOp, memRef); 215 return mlir::success(); 216 } 217 }; 218 } // namespace 219 220 namespace { 221 /// convert to LLVM IR dialect `alloca` 222 struct AllocaOpConversion : public fir::FIROpConversion<fir::AllocaOp> { 223 using FIROpConversion::FIROpConversion; 224 225 llvm::LogicalResult 226 matchAndRewrite(fir::AllocaOp alloc, OpAdaptor adaptor, 227 mlir::ConversionPatternRewriter &rewriter) const override { 228 mlir::ValueRange operands = adaptor.getOperands(); 229 auto loc = alloc.getLoc(); 230 mlir::Type ity = lowerTy().indexType(); 231 unsigned i = 0; 232 mlir::Value size = genConstantIndex(loc, ity, rewriter, 1).getResult(); 233 mlir::Type firObjType = fir::unwrapRefType(alloc.getType()); 234 mlir::Type llvmObjectType = convertObjectType(firObjType); 235 if (alloc.hasLenParams()) { 236 unsigned end = alloc.numLenParams(); 237 llvm::SmallVector<mlir::Value> lenParams; 238 for (; i < end; ++i) 239 lenParams.push_back(operands[i]); 240 mlir::Type scalarType = fir::unwrapSequenceType(alloc.getInType()); 241 if (auto chrTy = mlir::dyn_cast<fir::CharacterType>(scalarType)) { 242 fir::CharacterType rawCharTy = fir::CharacterType::getUnknownLen( 243 chrTy.getContext(), chrTy.getFKind()); 244 llvmObjectType = convertType(rawCharTy); 245 assert(end == 1); 246 size = integerCast(loc, rewriter, ity, lenParams[0], /*fold=*/true); 247 } else if (auto recTy = mlir::dyn_cast<fir::RecordType>(scalarType)) { 248 mlir::LLVM::LLVMFuncOp memSizeFn = 249 getDependentTypeMemSizeFn(recTy, alloc, rewriter); 250 if (!memSizeFn) 251 emitError(loc, "did not find allocation function"); 252 mlir::NamedAttribute attr = rewriter.getNamedAttr( 253 "callee", mlir::SymbolRefAttr::get(memSizeFn)); 254 auto call = rewriter.create<mlir::LLVM::CallOp>( 255 loc, ity, lenParams, 256 addLLVMOpBundleAttrs(rewriter, {attr}, lenParams.size())); 257 size = call.getResult(); 258 llvmObjectType = ::getI8Type(alloc.getContext()); 259 } else { 260 return emitError(loc, "unexpected type ") 261 << scalarType << " with type parameters"; 262 } 263 } 264 if (auto scaleSize = genAllocationScaleSize(alloc, ity, rewriter)) 265 size = 266 rewriter.createOrFold<mlir::LLVM::MulOp>(loc, ity, size, scaleSize); 267 if (alloc.hasShapeOperands()) { 268 unsigned end = operands.size(); 269 for (; i < end; ++i) 270 size = rewriter.createOrFold<mlir::LLVM::MulOp>( 271 loc, ity, size, 272 integerCast(loc, rewriter, ity, operands[i], /*fold=*/true)); 273 } 274 275 unsigned allocaAs = getAllocaAddressSpace(rewriter); 276 unsigned programAs = getProgramAddressSpace(rewriter); 277 278 if (mlir::isa<mlir::LLVM::ConstantOp>(size.getDefiningOp())) { 279 // Set the Block in which the llvm alloca should be inserted. 280 mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp(); 281 mlir::Region *parentRegion = rewriter.getInsertionBlock()->getParent(); 282 mlir::Block *insertBlock = 283 getBlockForAllocaInsert(parentOp, parentRegion); 284 285 // The old size might have had multiple users, some at a broader scope 286 // than we can safely outline the alloca to. As it is only an 287 // llvm.constant operation, it is faster to clone it than to calculate the 288 // dominance to see if it really should be moved. 289 mlir::Operation *clonedSize = rewriter.clone(*size.getDefiningOp()); 290 size = clonedSize->getResult(0); 291 clonedSize->moveBefore(&insertBlock->front()); 292 rewriter.setInsertionPointAfter(size.getDefiningOp()); 293 } 294 295 // NOTE: we used to pass alloc->getAttrs() in the builder for non opaque 296 // pointers! Only propagate pinned and bindc_name to help debugging, but 297 // this should have no functional purpose (and passing the operand segment 298 // attribute like before is certainly bad). 299 auto llvmAlloc = rewriter.create<mlir::LLVM::AllocaOp>( 300 loc, ::getLlvmPtrType(alloc.getContext(), allocaAs), llvmObjectType, 301 size); 302 if (alloc.getPinned()) 303 llvmAlloc->setDiscardableAttr(alloc.getPinnedAttrName(), 304 alloc.getPinnedAttr()); 305 if (alloc.getBindcName()) 306 llvmAlloc->setDiscardableAttr(alloc.getBindcNameAttrName(), 307 alloc.getBindcNameAttr()); 308 if (allocaAs == programAs) { 309 rewriter.replaceOp(alloc, llvmAlloc); 310 } else { 311 // if our allocation address space, is not the same as the program address 312 // space, then we must emit a cast to the program address space before 313 // use. An example case would be on AMDGPU, where the allocation address 314 // space is the numeric value 5 (private), and the program address space 315 // is 0 (generic). 316 rewriter.replaceOpWithNewOp<mlir::LLVM::AddrSpaceCastOp>( 317 alloc, ::getLlvmPtrType(alloc.getContext(), programAs), llvmAlloc); 318 } 319 return mlir::success(); 320 } 321 }; 322 } // namespace 323 324 namespace { 325 /// Lower `fir.box_addr` to the sequence of operations to extract the first 326 /// element of the box. 327 struct BoxAddrOpConversion : public fir::FIROpConversion<fir::BoxAddrOp> { 328 using FIROpConversion::FIROpConversion; 329 330 llvm::LogicalResult 331 matchAndRewrite(fir::BoxAddrOp boxaddr, OpAdaptor adaptor, 332 mlir::ConversionPatternRewriter &rewriter) const override { 333 mlir::Value a = adaptor.getOperands()[0]; 334 auto loc = boxaddr.getLoc(); 335 if (auto argty = 336 mlir::dyn_cast<fir::BaseBoxType>(boxaddr.getVal().getType())) { 337 TypePair boxTyPair = getBoxTypePair(argty); 338 rewriter.replaceOp(boxaddr, 339 getBaseAddrFromBox(loc, boxTyPair, a, rewriter)); 340 } else { 341 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(boxaddr, a, 0); 342 } 343 return mlir::success(); 344 } 345 }; 346 347 /// Convert `!fir.boxchar_len` to `!llvm.extractvalue` for the 2nd part of the 348 /// boxchar. 349 struct BoxCharLenOpConversion : public fir::FIROpConversion<fir::BoxCharLenOp> { 350 using FIROpConversion::FIROpConversion; 351 352 llvm::LogicalResult 353 matchAndRewrite(fir::BoxCharLenOp boxCharLen, OpAdaptor adaptor, 354 mlir::ConversionPatternRewriter &rewriter) const override { 355 mlir::Value boxChar = adaptor.getOperands()[0]; 356 mlir::Location loc = boxChar.getLoc(); 357 mlir::Type returnValTy = boxCharLen.getResult().getType(); 358 359 constexpr int boxcharLenIdx = 1; 360 auto len = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, boxChar, 361 boxcharLenIdx); 362 mlir::Value lenAfterCast = integerCast(loc, rewriter, returnValTy, len); 363 rewriter.replaceOp(boxCharLen, lenAfterCast); 364 365 return mlir::success(); 366 } 367 }; 368 369 /// Lower `fir.box_dims` to a sequence of operations to extract the requested 370 /// dimension information from the boxed value. 371 /// Result in a triple set of GEPs and loads. 372 struct BoxDimsOpConversion : public fir::FIROpConversion<fir::BoxDimsOp> { 373 using FIROpConversion::FIROpConversion; 374 375 llvm::LogicalResult 376 matchAndRewrite(fir::BoxDimsOp boxdims, OpAdaptor adaptor, 377 mlir::ConversionPatternRewriter &rewriter) const override { 378 llvm::SmallVector<mlir::Type, 3> resultTypes = { 379 convertType(boxdims.getResult(0).getType()), 380 convertType(boxdims.getResult(1).getType()), 381 convertType(boxdims.getResult(2).getType()), 382 }; 383 TypePair boxTyPair = getBoxTypePair(boxdims.getVal().getType()); 384 auto results = getDimsFromBox(boxdims.getLoc(), resultTypes, boxTyPair, 385 adaptor.getOperands()[0], 386 adaptor.getOperands()[1], rewriter); 387 rewriter.replaceOp(boxdims, results); 388 return mlir::success(); 389 } 390 }; 391 392 /// Lower `fir.box_elesize` to a sequence of operations ro extract the size of 393 /// an element in the boxed value. 394 struct BoxEleSizeOpConversion : public fir::FIROpConversion<fir::BoxEleSizeOp> { 395 using FIROpConversion::FIROpConversion; 396 397 llvm::LogicalResult 398 matchAndRewrite(fir::BoxEleSizeOp boxelesz, OpAdaptor adaptor, 399 mlir::ConversionPatternRewriter &rewriter) const override { 400 mlir::Value box = adaptor.getOperands()[0]; 401 auto loc = boxelesz.getLoc(); 402 auto ty = convertType(boxelesz.getType()); 403 TypePair boxTyPair = getBoxTypePair(boxelesz.getVal().getType()); 404 auto elemSize = getElementSizeFromBox(loc, ty, boxTyPair, box, rewriter); 405 rewriter.replaceOp(boxelesz, elemSize); 406 return mlir::success(); 407 } 408 }; 409 410 /// Lower `fir.box_isalloc` to a sequence of operations to determine if the 411 /// boxed value was from an ALLOCATABLE entity. 412 struct BoxIsAllocOpConversion : public fir::FIROpConversion<fir::BoxIsAllocOp> { 413 using FIROpConversion::FIROpConversion; 414 415 llvm::LogicalResult 416 matchAndRewrite(fir::BoxIsAllocOp boxisalloc, OpAdaptor adaptor, 417 mlir::ConversionPatternRewriter &rewriter) const override { 418 mlir::Value box = adaptor.getOperands()[0]; 419 auto loc = boxisalloc.getLoc(); 420 TypePair boxTyPair = getBoxTypePair(boxisalloc.getVal().getType()); 421 mlir::Value check = 422 genBoxAttributeCheck(loc, boxTyPair, box, rewriter, kAttrAllocatable); 423 rewriter.replaceOp(boxisalloc, check); 424 return mlir::success(); 425 } 426 }; 427 428 /// Lower `fir.box_isarray` to a sequence of operations to determine if the 429 /// boxed is an array. 430 struct BoxIsArrayOpConversion : public fir::FIROpConversion<fir::BoxIsArrayOp> { 431 using FIROpConversion::FIROpConversion; 432 433 llvm::LogicalResult 434 matchAndRewrite(fir::BoxIsArrayOp boxisarray, OpAdaptor adaptor, 435 mlir::ConversionPatternRewriter &rewriter) const override { 436 mlir::Value a = adaptor.getOperands()[0]; 437 auto loc = boxisarray.getLoc(); 438 TypePair boxTyPair = getBoxTypePair(boxisarray.getVal().getType()); 439 mlir::Value rank = getRankFromBox(loc, boxTyPair, a, rewriter); 440 mlir::Value c0 = genConstantIndex(loc, rank.getType(), rewriter, 0); 441 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>( 442 boxisarray, mlir::LLVM::ICmpPredicate::ne, rank, c0); 443 return mlir::success(); 444 } 445 }; 446 447 /// Lower `fir.box_isptr` to a sequence of operations to determined if the 448 /// boxed value was from a POINTER entity. 449 struct BoxIsPtrOpConversion : public fir::FIROpConversion<fir::BoxIsPtrOp> { 450 using FIROpConversion::FIROpConversion; 451 452 llvm::LogicalResult 453 matchAndRewrite(fir::BoxIsPtrOp boxisptr, OpAdaptor adaptor, 454 mlir::ConversionPatternRewriter &rewriter) const override { 455 mlir::Value box = adaptor.getOperands()[0]; 456 auto loc = boxisptr.getLoc(); 457 TypePair boxTyPair = getBoxTypePair(boxisptr.getVal().getType()); 458 mlir::Value check = 459 genBoxAttributeCheck(loc, boxTyPair, box, rewriter, kAttrPointer); 460 rewriter.replaceOp(boxisptr, check); 461 return mlir::success(); 462 } 463 }; 464 465 /// Lower `fir.box_rank` to the sequence of operation to extract the rank from 466 /// the box. 467 struct BoxRankOpConversion : public fir::FIROpConversion<fir::BoxRankOp> { 468 using FIROpConversion::FIROpConversion; 469 470 llvm::LogicalResult 471 matchAndRewrite(fir::BoxRankOp boxrank, OpAdaptor adaptor, 472 mlir::ConversionPatternRewriter &rewriter) const override { 473 mlir::Value a = adaptor.getOperands()[0]; 474 auto loc = boxrank.getLoc(); 475 mlir::Type ty = convertType(boxrank.getType()); 476 TypePair boxTyPair = 477 getBoxTypePair(fir::unwrapRefType(boxrank.getBox().getType())); 478 mlir::Value rank = getRankFromBox(loc, boxTyPair, a, rewriter); 479 mlir::Value result = integerCast(loc, rewriter, ty, rank); 480 rewriter.replaceOp(boxrank, result); 481 return mlir::success(); 482 } 483 }; 484 485 /// Lower `fir.boxproc_host` operation. Extracts the host pointer from the 486 /// boxproc. 487 /// TODO: Part of supporting Fortran 2003 procedure pointers. 488 struct BoxProcHostOpConversion 489 : public fir::FIROpConversion<fir::BoxProcHostOp> { 490 using FIROpConversion::FIROpConversion; 491 492 llvm::LogicalResult 493 matchAndRewrite(fir::BoxProcHostOp boxprochost, OpAdaptor adaptor, 494 mlir::ConversionPatternRewriter &rewriter) const override { 495 TODO(boxprochost.getLoc(), "fir.boxproc_host codegen"); 496 return mlir::failure(); 497 } 498 }; 499 500 /// Lower `fir.box_tdesc` to the sequence of operations to extract the type 501 /// descriptor from the box. 502 struct BoxTypeDescOpConversion 503 : public fir::FIROpConversion<fir::BoxTypeDescOp> { 504 using FIROpConversion::FIROpConversion; 505 506 llvm::LogicalResult 507 matchAndRewrite(fir::BoxTypeDescOp boxtypedesc, OpAdaptor adaptor, 508 mlir::ConversionPatternRewriter &rewriter) const override { 509 mlir::Value box = adaptor.getOperands()[0]; 510 TypePair boxTyPair = getBoxTypePair(boxtypedesc.getBox().getType()); 511 auto typeDescAddr = 512 loadTypeDescAddress(boxtypedesc.getLoc(), boxTyPair, box, rewriter); 513 rewriter.replaceOp(boxtypedesc, typeDescAddr); 514 return mlir::success(); 515 } 516 }; 517 518 /// Lower `fir.box_typecode` to a sequence of operations to extract the type 519 /// code in the boxed value. 520 struct BoxTypeCodeOpConversion 521 : public fir::FIROpConversion<fir::BoxTypeCodeOp> { 522 using FIROpConversion::FIROpConversion; 523 524 llvm::LogicalResult 525 matchAndRewrite(fir::BoxTypeCodeOp op, OpAdaptor adaptor, 526 mlir::ConversionPatternRewriter &rewriter) const override { 527 mlir::Value box = adaptor.getOperands()[0]; 528 auto loc = box.getLoc(); 529 auto ty = convertType(op.getType()); 530 TypePair boxTyPair = getBoxTypePair(op.getBox().getType()); 531 auto typeCode = 532 getValueFromBox(loc, boxTyPair, box, ty, rewriter, kTypePosInBox); 533 rewriter.replaceOp(op, typeCode); 534 return mlir::success(); 535 } 536 }; 537 538 /// Lower `fir.string_lit` to LLVM IR dialect operation. 539 struct StringLitOpConversion : public fir::FIROpConversion<fir::StringLitOp> { 540 using FIROpConversion::FIROpConversion; 541 542 llvm::LogicalResult 543 matchAndRewrite(fir::StringLitOp constop, OpAdaptor adaptor, 544 mlir::ConversionPatternRewriter &rewriter) const override { 545 auto ty = convertType(constop.getType()); 546 auto attr = constop.getValue(); 547 if (mlir::isa<mlir::StringAttr>(attr)) { 548 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(constop, ty, attr); 549 return mlir::success(); 550 } 551 552 auto charTy = mlir::cast<fir::CharacterType>(constop.getType()); 553 unsigned bits = lowerTy().characterBitsize(charTy); 554 mlir::Type intTy = rewriter.getIntegerType(bits); 555 mlir::Location loc = constop.getLoc(); 556 mlir::Value cst = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 557 if (auto arr = mlir::dyn_cast<mlir::DenseElementsAttr>(attr)) { 558 cst = rewriter.create<mlir::LLVM::ConstantOp>(loc, ty, arr); 559 } else if (auto arr = mlir::dyn_cast<mlir::ArrayAttr>(attr)) { 560 for (auto a : llvm::enumerate(arr.getValue())) { 561 // convert each character to a precise bitsize 562 auto elemAttr = mlir::IntegerAttr::get( 563 intTy, 564 mlir::cast<mlir::IntegerAttr>(a.value()).getValue().zextOrTrunc( 565 bits)); 566 auto elemCst = 567 rewriter.create<mlir::LLVM::ConstantOp>(loc, intTy, elemAttr); 568 cst = rewriter.create<mlir::LLVM::InsertValueOp>(loc, cst, elemCst, 569 a.index()); 570 } 571 } else { 572 return mlir::failure(); 573 } 574 rewriter.replaceOp(constop, cst); 575 return mlir::success(); 576 } 577 }; 578 579 /// `fir.call` -> `llvm.call` 580 struct CallOpConversion : public fir::FIROpConversion<fir::CallOp> { 581 using FIROpConversion::FIROpConversion; 582 583 llvm::LogicalResult 584 matchAndRewrite(fir::CallOp call, OpAdaptor adaptor, 585 mlir::ConversionPatternRewriter &rewriter) const override { 586 llvm::SmallVector<mlir::Type> resultTys; 587 for (auto r : call.getResults()) 588 resultTys.push_back(convertType(r.getType())); 589 // Convert arith::FastMathFlagsAttr to LLVM::FastMathFlagsAttr. 590 mlir::arith::AttrConvertFastMathToLLVM<fir::CallOp, mlir::LLVM::CallOp> 591 attrConvert(call); 592 rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>( 593 call, resultTys, adaptor.getOperands(), 594 addLLVMOpBundleAttrs(rewriter, attrConvert.getAttrs(), 595 adaptor.getOperands().size())); 596 return mlir::success(); 597 } 598 }; 599 } // namespace 600 601 static mlir::Type getComplexEleTy(mlir::Type complex) { 602 return mlir::cast<mlir::ComplexType>(complex).getElementType(); 603 } 604 605 namespace { 606 /// Compare complex values 607 /// 608 /// Per 10.1, the only comparisons available are .EQ. (oeq) and .NE. (une). 609 /// 610 /// For completeness, all other comparison are done on the real component only. 611 struct CmpcOpConversion : public fir::FIROpConversion<fir::CmpcOp> { 612 using FIROpConversion::FIROpConversion; 613 614 llvm::LogicalResult 615 matchAndRewrite(fir::CmpcOp cmp, OpAdaptor adaptor, 616 mlir::ConversionPatternRewriter &rewriter) const override { 617 mlir::ValueRange operands = adaptor.getOperands(); 618 mlir::Type resTy = convertType(cmp.getType()); 619 mlir::Location loc = cmp.getLoc(); 620 mlir::LLVM::FastmathFlags fmf = 621 mlir::arith::convertArithFastMathFlagsToLLVM(cmp.getFastmath()); 622 mlir::LLVM::FCmpPredicate pred = 623 static_cast<mlir::LLVM::FCmpPredicate>(cmp.getPredicate()); 624 auto rcp = rewriter.create<mlir::LLVM::FCmpOp>( 625 loc, resTy, pred, 626 rewriter.create<mlir::LLVM::ExtractValueOp>(loc, operands[0], 0), 627 rewriter.create<mlir::LLVM::ExtractValueOp>(loc, operands[1], 0), fmf); 628 auto icp = rewriter.create<mlir::LLVM::FCmpOp>( 629 loc, resTy, pred, 630 rewriter.create<mlir::LLVM::ExtractValueOp>(loc, operands[0], 1), 631 rewriter.create<mlir::LLVM::ExtractValueOp>(loc, operands[1], 1), fmf); 632 llvm::SmallVector<mlir::Value, 2> cp = {rcp, icp}; 633 switch (cmp.getPredicate()) { 634 case mlir::arith::CmpFPredicate::OEQ: // .EQ. 635 rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(cmp, resTy, cp); 636 break; 637 case mlir::arith::CmpFPredicate::UNE: // .NE. 638 rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(cmp, resTy, cp); 639 break; 640 default: 641 rewriter.replaceOp(cmp, rcp.getResult()); 642 break; 643 } 644 return mlir::success(); 645 } 646 }; 647 648 /// convert value of from-type to value of to-type 649 struct ConvertOpConversion : public fir::FIROpConversion<fir::ConvertOp> { 650 using FIROpConversion::FIROpConversion; 651 652 static bool isFloatingPointTy(mlir::Type ty) { 653 return mlir::isa<mlir::FloatType>(ty); 654 } 655 656 llvm::LogicalResult 657 matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor, 658 mlir::ConversionPatternRewriter &rewriter) const override { 659 auto fromFirTy = convert.getValue().getType(); 660 auto toFirTy = convert.getRes().getType(); 661 auto fromTy = convertType(fromFirTy); 662 auto toTy = convertType(toFirTy); 663 mlir::Value op0 = adaptor.getOperands()[0]; 664 665 if (fromFirTy == toFirTy) { 666 rewriter.replaceOp(convert, op0); 667 return mlir::success(); 668 } 669 670 auto loc = convert.getLoc(); 671 auto i1Type = mlir::IntegerType::get(convert.getContext(), 1); 672 673 if (mlir::isa<fir::RecordType>(toFirTy)) { 674 // Convert to compatible BIND(C) record type. 675 // Double check that the record types are compatible (it should have 676 // already been checked by the verifier). 677 assert(mlir::cast<fir::RecordType>(fromFirTy).getTypeList() == 678 mlir::cast<fir::RecordType>(toFirTy).getTypeList() && 679 "incompatible record types"); 680 681 auto toStTy = mlir::cast<mlir::LLVM::LLVMStructType>(toTy); 682 mlir::Value val = rewriter.create<mlir::LLVM::UndefOp>(loc, toStTy); 683 auto indexTypeMap = toStTy.getSubelementIndexMap(); 684 assert(indexTypeMap.has_value() && "invalid record type"); 685 686 for (auto [attr, type] : indexTypeMap.value()) { 687 int64_t index = mlir::cast<mlir::IntegerAttr>(attr).getInt(); 688 auto extVal = 689 rewriter.create<mlir::LLVM::ExtractValueOp>(loc, op0, index); 690 val = 691 rewriter.create<mlir::LLVM::InsertValueOp>(loc, val, extVal, index); 692 } 693 694 rewriter.replaceOp(convert, val); 695 return mlir::success(); 696 } 697 698 if (mlir::isa<fir::LogicalType>(fromFirTy) || 699 mlir::isa<fir::LogicalType>(toFirTy)) { 700 // By specification fir::LogicalType value may be any number, 701 // where non-zero value represents .true. and zero value represents 702 // .false. 703 // 704 // integer<->logical conversion requires value normalization. 705 // Conversion from wide logical to narrow logical must set the result 706 // to non-zero iff the input is non-zero - the easiest way to implement 707 // it is to compare the input agains zero and set the result to 708 // the canonical 0/1. 709 // Conversion from narrow logical to wide logical may be implemented 710 // as a zero or sign extension of the input, but it may use value 711 // normalization as well. 712 if (!mlir::isa<mlir::IntegerType>(fromTy) || 713 !mlir::isa<mlir::IntegerType>(toTy)) 714 return mlir::emitError(loc) 715 << "unsupported types for logical conversion: " << fromTy 716 << " -> " << toTy; 717 718 // Do folding for constant inputs. 719 if (auto constVal = fir::getIntIfConstant(op0)) { 720 mlir::Value normVal = 721 genConstantIndex(loc, toTy, rewriter, *constVal ? 1 : 0); 722 rewriter.replaceOp(convert, normVal); 723 return mlir::success(); 724 } 725 726 // If the input is i1, then we can just zero extend it, and 727 // the result will be normalized. 728 if (fromTy == i1Type) { 729 rewriter.replaceOpWithNewOp<mlir::LLVM::ZExtOp>(convert, toTy, op0); 730 return mlir::success(); 731 } 732 733 // Compare the input with zero. 734 mlir::Value zero = genConstantIndex(loc, fromTy, rewriter, 0); 735 auto isTrue = rewriter.create<mlir::LLVM::ICmpOp>( 736 loc, mlir::LLVM::ICmpPredicate::ne, op0, zero); 737 738 // Zero extend the i1 isTrue result to the required type (unless it is i1 739 // itself). 740 if (toTy != i1Type) 741 rewriter.replaceOpWithNewOp<mlir::LLVM::ZExtOp>(convert, toTy, isTrue); 742 else 743 rewriter.replaceOp(convert, isTrue.getResult()); 744 745 return mlir::success(); 746 } 747 748 if (fromTy == toTy) { 749 rewriter.replaceOp(convert, op0); 750 return mlir::success(); 751 } 752 auto convertFpToFp = [&](mlir::Value val, unsigned fromBits, 753 unsigned toBits, mlir::Type toTy) -> mlir::Value { 754 if (fromBits == toBits) { 755 // TODO: Converting between two floating-point representations with the 756 // same bitwidth is not allowed for now. 757 mlir::emitError(loc, 758 "cannot implicitly convert between two floating-point " 759 "representations of the same bitwidth"); 760 return {}; 761 } 762 if (fromBits > toBits) 763 return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val); 764 return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val); 765 }; 766 // Complex to complex conversion. 767 if (fir::isa_complex(fromFirTy) && fir::isa_complex(toFirTy)) { 768 // Special case: handle the conversion of a complex such that both the 769 // real and imaginary parts are converted together. 770 auto ty = convertType(getComplexEleTy(convert.getValue().getType())); 771 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, op0, 0); 772 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, op0, 1); 773 auto nt = convertType(getComplexEleTy(convert.getRes().getType())); 774 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty); 775 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt); 776 auto rc = convertFpToFp(rp, fromBits, toBits, nt); 777 auto ic = convertFpToFp(ip, fromBits, toBits, nt); 778 auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy); 779 auto i1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, un, rc, 0); 780 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, i1, ic, 781 1); 782 return mlir::success(); 783 } 784 785 // Floating point to floating point conversion. 786 if (isFloatingPointTy(fromTy)) { 787 if (isFloatingPointTy(toTy)) { 788 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy); 789 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy); 790 auto v = convertFpToFp(op0, fromBits, toBits, toTy); 791 rewriter.replaceOp(convert, v); 792 return mlir::success(); 793 } 794 if (mlir::isa<mlir::IntegerType>(toTy)) { 795 if (toTy.isUnsignedInteger()) 796 rewriter.replaceOpWithNewOp<mlir::LLVM::FPToUIOp>(convert, toTy, op0); 797 else 798 rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0); 799 return mlir::success(); 800 } 801 } else if (mlir::isa<mlir::IntegerType>(fromTy)) { 802 // Integer to integer conversion. 803 if (mlir::isa<mlir::IntegerType>(toTy)) { 804 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy); 805 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy); 806 assert(fromBits != toBits); 807 if (fromBits > toBits) { 808 rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0); 809 return mlir::success(); 810 } 811 if (fromFirTy == i1Type || fromFirTy.isUnsignedInteger()) { 812 rewriter.replaceOpWithNewOp<mlir::LLVM::ZExtOp>(convert, toTy, op0); 813 return mlir::success(); 814 } 815 rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0); 816 return mlir::success(); 817 } 818 // Integer to floating point conversion. 819 if (isFloatingPointTy(toTy)) { 820 if (fromTy.isUnsignedInteger()) 821 rewriter.replaceOpWithNewOp<mlir::LLVM::UIToFPOp>(convert, toTy, op0); 822 else 823 rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0); 824 return mlir::success(); 825 } 826 // Integer to pointer conversion. 827 if (mlir::isa<mlir::LLVM::LLVMPointerType>(toTy)) { 828 rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0); 829 return mlir::success(); 830 } 831 } else if (mlir::isa<mlir::LLVM::LLVMPointerType>(fromTy)) { 832 // Pointer to integer conversion. 833 if (mlir::isa<mlir::IntegerType>(toTy)) { 834 rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0); 835 return mlir::success(); 836 } 837 // Pointer to pointer conversion. 838 if (mlir::isa<mlir::LLVM::LLVMPointerType>(toTy)) { 839 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0); 840 return mlir::success(); 841 } 842 } 843 return emitError(loc) << "cannot convert " << fromTy << " to " << toTy; 844 } 845 }; 846 847 /// `fir.type_info` operation has no specific CodeGen. The operation is 848 /// only used to carry information during FIR to FIR passes. It may be used 849 /// in the future to generate the runtime type info data structures instead 850 /// of generating them in lowering. 851 struct TypeInfoOpConversion : public fir::FIROpConversion<fir::TypeInfoOp> { 852 using FIROpConversion::FIROpConversion; 853 854 llvm::LogicalResult 855 matchAndRewrite(fir::TypeInfoOp op, OpAdaptor, 856 mlir::ConversionPatternRewriter &rewriter) const override { 857 rewriter.eraseOp(op); 858 return mlir::success(); 859 } 860 }; 861 862 /// `fir.dt_entry` operation has no specific CodeGen. The operation is only used 863 /// to carry information during FIR to FIR passes. 864 struct DTEntryOpConversion : public fir::FIROpConversion<fir::DTEntryOp> { 865 using FIROpConversion::FIROpConversion; 866 867 llvm::LogicalResult 868 matchAndRewrite(fir::DTEntryOp op, OpAdaptor, 869 mlir::ConversionPatternRewriter &rewriter) const override { 870 rewriter.eraseOp(op); 871 return mlir::success(); 872 } 873 }; 874 875 /// Lower `fir.global_len` operation. 876 struct GlobalLenOpConversion : public fir::FIROpConversion<fir::GlobalLenOp> { 877 using FIROpConversion::FIROpConversion; 878 879 llvm::LogicalResult 880 matchAndRewrite(fir::GlobalLenOp globalLen, OpAdaptor adaptor, 881 mlir::ConversionPatternRewriter &rewriter) const override { 882 TODO(globalLen.getLoc(), "fir.global_len codegen"); 883 return mlir::failure(); 884 } 885 }; 886 887 /// Lower fir.len_param_index 888 struct LenParamIndexOpConversion 889 : public fir::FIROpConversion<fir::LenParamIndexOp> { 890 using FIROpConversion::FIROpConversion; 891 892 // FIXME: this should be specialized by the runtime target 893 llvm::LogicalResult 894 matchAndRewrite(fir::LenParamIndexOp lenp, OpAdaptor, 895 mlir::ConversionPatternRewriter &rewriter) const override { 896 TODO(lenp.getLoc(), "fir.len_param_index codegen"); 897 } 898 }; 899 900 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of 901 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element 902 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd 903 /// element is the length of the character buffer (`#n`). 904 struct EmboxCharOpConversion : public fir::FIROpConversion<fir::EmboxCharOp> { 905 using FIROpConversion::FIROpConversion; 906 907 llvm::LogicalResult 908 matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor, 909 mlir::ConversionPatternRewriter &rewriter) const override { 910 mlir::ValueRange operands = adaptor.getOperands(); 911 912 mlir::Value charBuffer = operands[0]; 913 mlir::Value charBufferLen = operands[1]; 914 915 mlir::Location loc = emboxChar.getLoc(); 916 mlir::Type llvmStructTy = convertType(emboxChar.getType()); 917 auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy); 918 919 mlir::Type lenTy = 920 mlir::cast<mlir::LLVM::LLVMStructType>(llvmStructTy).getBody()[1]; 921 mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen); 922 923 mlir::Type addrTy = 924 mlir::cast<mlir::LLVM::LLVMStructType>(llvmStructTy).getBody()[0]; 925 if (addrTy != charBuffer.getType()) 926 charBuffer = 927 rewriter.create<mlir::LLVM::BitcastOp>(loc, addrTy, charBuffer); 928 929 auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>( 930 loc, llvmStruct, charBuffer, 0); 931 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 932 emboxChar, insertBufferOp, lenAfterCast, 1); 933 934 return mlir::success(); 935 } 936 }; 937 } // namespace 938 939 template <typename ModuleOp> 940 static mlir::SymbolRefAttr 941 getMallocInModule(ModuleOp mod, fir::AllocMemOp op, 942 mlir::ConversionPatternRewriter &rewriter) { 943 static constexpr char mallocName[] = "malloc"; 944 if (auto mallocFunc = 945 mod.template lookupSymbol<mlir::LLVM::LLVMFuncOp>(mallocName)) 946 return mlir::SymbolRefAttr::get(mallocFunc); 947 if (auto userMalloc = 948 mod.template lookupSymbol<mlir::func::FuncOp>(mallocName)) 949 return mlir::SymbolRefAttr::get(userMalloc); 950 951 mlir::OpBuilder moduleBuilder(mod.getBodyRegion()); 952 auto indexType = mlir::IntegerType::get(op.getContext(), 64); 953 auto mallocDecl = moduleBuilder.create<mlir::LLVM::LLVMFuncOp>( 954 op.getLoc(), mallocName, 955 mlir::LLVM::LLVMFunctionType::get(getLlvmPtrType(op.getContext()), 956 indexType, 957 /*isVarArg=*/false)); 958 return mlir::SymbolRefAttr::get(mallocDecl); 959 } 960 961 /// Return the LLVMFuncOp corresponding to the standard malloc call. 962 static mlir::SymbolRefAttr 963 getMalloc(fir::AllocMemOp op, mlir::ConversionPatternRewriter &rewriter) { 964 if (auto mod = op->getParentOfType<mlir::gpu::GPUModuleOp>()) 965 return getMallocInModule(mod, op, rewriter); 966 auto mod = op->getParentOfType<mlir::ModuleOp>(); 967 return getMallocInModule(mod, op, rewriter); 968 } 969 970 /// Helper function for generating the LLVM IR that computes the distance 971 /// in bytes between adjacent elements pointed to by a pointer 972 /// of type \p ptrTy. The result is returned as a value of \p idxTy integer 973 /// type. 974 static mlir::Value 975 computeElementDistance(mlir::Location loc, mlir::Type llvmObjectType, 976 mlir::Type idxTy, 977 mlir::ConversionPatternRewriter &rewriter) { 978 // Note that we cannot use something like 979 // mlir::LLVM::getPrimitiveTypeSizeInBits() for the element type here. For 980 // example, it returns 10 bytes for mlir::Float80Type for targets where it 981 // occupies 16 bytes. Proper solution is probably to use 982 // mlir::DataLayout::getTypeABIAlignment(), but DataLayout is not being set 983 // yet (see llvm-project#57230). For the time being use the '(intptr_t)((type 984 // *)0 + 1)' trick for all types. The generated instructions are optimized 985 // into constant by the first pass of InstCombine, so it should not be a 986 // performance issue. 987 auto llvmPtrTy = ::getLlvmPtrType(llvmObjectType.getContext()); 988 auto nullPtr = rewriter.create<mlir::LLVM::ZeroOp>(loc, llvmPtrTy); 989 auto gep = rewriter.create<mlir::LLVM::GEPOp>( 990 loc, llvmPtrTy, llvmObjectType, nullPtr, 991 llvm::ArrayRef<mlir::LLVM::GEPArg>{1}); 992 return rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, gep); 993 } 994 995 /// Return value of the stride in bytes between adjacent elements 996 /// of LLVM type \p llTy. The result is returned as a value of 997 /// \p idxTy integer type. 998 static mlir::Value 999 genTypeStrideInBytes(mlir::Location loc, mlir::Type idxTy, 1000 mlir::ConversionPatternRewriter &rewriter, 1001 mlir::Type llTy) { 1002 // Create a pointer type and use computeElementDistance(). 1003 return computeElementDistance(loc, llTy, idxTy, rewriter); 1004 } 1005 1006 namespace { 1007 /// Lower a `fir.allocmem` instruction into `llvm.call @malloc` 1008 struct AllocMemOpConversion : public fir::FIROpConversion<fir::AllocMemOp> { 1009 using FIROpConversion::FIROpConversion; 1010 1011 llvm::LogicalResult 1012 matchAndRewrite(fir::AllocMemOp heap, OpAdaptor adaptor, 1013 mlir::ConversionPatternRewriter &rewriter) const override { 1014 mlir::Type heapTy = heap.getType(); 1015 mlir::Location loc = heap.getLoc(); 1016 auto ity = lowerTy().indexType(); 1017 mlir::Type dataTy = fir::unwrapRefType(heapTy); 1018 mlir::Type llvmObjectTy = convertObjectType(dataTy); 1019 if (fir::isRecordWithTypeParameters(fir::unwrapSequenceType(dataTy))) 1020 TODO(loc, "fir.allocmem codegen of derived type with length parameters"); 1021 mlir::Value size = genTypeSizeInBytes(loc, ity, rewriter, llvmObjectTy); 1022 if (auto scaleSize = genAllocationScaleSize(heap, ity, rewriter)) 1023 size = rewriter.create<mlir::LLVM::MulOp>(loc, ity, size, scaleSize); 1024 for (mlir::Value opnd : adaptor.getOperands()) 1025 size = rewriter.create<mlir::LLVM::MulOp>( 1026 loc, ity, size, integerCast(loc, rewriter, ity, opnd)); 1027 heap->setAttr("callee", getMalloc(heap, rewriter)); 1028 rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>( 1029 heap, ::getLlvmPtrType(heap.getContext()), size, 1030 addLLVMOpBundleAttrs(rewriter, heap->getAttrs(), 1)); 1031 return mlir::success(); 1032 } 1033 1034 /// Compute the allocation size in bytes of the element type of 1035 /// \p llTy pointer type. The result is returned as a value of \p idxTy 1036 /// integer type. 1037 mlir::Value genTypeSizeInBytes(mlir::Location loc, mlir::Type idxTy, 1038 mlir::ConversionPatternRewriter &rewriter, 1039 mlir::Type llTy) const { 1040 return computeElementDistance(loc, llTy, idxTy, rewriter); 1041 } 1042 }; 1043 } // namespace 1044 1045 /// Return the LLVMFuncOp corresponding to the standard free call. 1046 template <typename ModuleOp> 1047 static mlir::SymbolRefAttr 1048 getFreeInModule(ModuleOp mod, fir::FreeMemOp op, 1049 mlir::ConversionPatternRewriter &rewriter) { 1050 static constexpr char freeName[] = "free"; 1051 // Check if free already defined in the module. 1052 if (auto freeFunc = 1053 mod.template lookupSymbol<mlir::LLVM::LLVMFuncOp>(freeName)) 1054 return mlir::SymbolRefAttr::get(freeFunc); 1055 if (auto freeDefinedByUser = 1056 mod.template lookupSymbol<mlir::func::FuncOp>(freeName)) 1057 return mlir::SymbolRefAttr::get(freeDefinedByUser); 1058 // Create llvm declaration for free. 1059 mlir::OpBuilder moduleBuilder(mod.getBodyRegion()); 1060 auto voidType = mlir::LLVM::LLVMVoidType::get(op.getContext()); 1061 auto freeDecl = moduleBuilder.create<mlir::LLVM::LLVMFuncOp>( 1062 rewriter.getUnknownLoc(), freeName, 1063 mlir::LLVM::LLVMFunctionType::get(voidType, 1064 getLlvmPtrType(op.getContext()), 1065 /*isVarArg=*/false)); 1066 return mlir::SymbolRefAttr::get(freeDecl); 1067 } 1068 1069 static mlir::SymbolRefAttr getFree(fir::FreeMemOp op, 1070 mlir::ConversionPatternRewriter &rewriter) { 1071 if (auto mod = op->getParentOfType<mlir::gpu::GPUModuleOp>()) 1072 return getFreeInModule(mod, op, rewriter); 1073 auto mod = op->getParentOfType<mlir::ModuleOp>(); 1074 return getFreeInModule(mod, op, rewriter); 1075 } 1076 1077 static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) { 1078 unsigned result = 1; 1079 for (auto eleTy = 1080 mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(ty.getElementType()); 1081 eleTy; eleTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>( 1082 eleTy.getElementType())) 1083 ++result; 1084 return result; 1085 } 1086 1087 namespace { 1088 /// Lower a `fir.freemem` instruction into `llvm.call @free` 1089 struct FreeMemOpConversion : public fir::FIROpConversion<fir::FreeMemOp> { 1090 using FIROpConversion::FIROpConversion; 1091 1092 llvm::LogicalResult 1093 matchAndRewrite(fir::FreeMemOp freemem, OpAdaptor adaptor, 1094 mlir::ConversionPatternRewriter &rewriter) const override { 1095 mlir::Location loc = freemem.getLoc(); 1096 freemem->setAttr("callee", getFree(freemem, rewriter)); 1097 rewriter.create<mlir::LLVM::CallOp>( 1098 loc, mlir::TypeRange{}, mlir::ValueRange{adaptor.getHeapref()}, 1099 addLLVMOpBundleAttrs(rewriter, freemem->getAttrs(), 1)); 1100 rewriter.eraseOp(freemem); 1101 return mlir::success(); 1102 } 1103 }; 1104 } // namespace 1105 1106 // Convert subcomponent array indices from column-major to row-major ordering. 1107 static llvm::SmallVector<mlir::Value> 1108 convertSubcomponentIndices(mlir::Location loc, mlir::Type eleTy, 1109 mlir::ValueRange indices, 1110 mlir::Type *retTy = nullptr) { 1111 llvm::SmallVector<mlir::Value> result; 1112 llvm::SmallVector<mlir::Value> arrayIndices; 1113 1114 auto appendArrayIndices = [&] { 1115 if (arrayIndices.empty()) 1116 return; 1117 std::reverse(arrayIndices.begin(), arrayIndices.end()); 1118 result.append(arrayIndices.begin(), arrayIndices.end()); 1119 arrayIndices.clear(); 1120 }; 1121 1122 for (mlir::Value index : indices) { 1123 // Component indices can be field index to select a component, or array 1124 // index, to select an element in an array component. 1125 if (auto structTy = mlir::dyn_cast<mlir::LLVM::LLVMStructType>(eleTy)) { 1126 std::int64_t cstIndex = getConstantIntValue(index); 1127 assert(cstIndex < (int64_t)structTy.getBody().size() && 1128 "out-of-bounds struct field index"); 1129 eleTy = structTy.getBody()[cstIndex]; 1130 appendArrayIndices(); 1131 result.push_back(index); 1132 } else if (auto arrayTy = 1133 mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(eleTy)) { 1134 eleTy = arrayTy.getElementType(); 1135 arrayIndices.push_back(index); 1136 } else 1137 fir::emitFatalError(loc, "Unexpected subcomponent type"); 1138 } 1139 appendArrayIndices(); 1140 if (retTy) 1141 *retTy = eleTy; 1142 return result; 1143 } 1144 1145 static mlir::Value genSourceFile(mlir::Location loc, mlir::ModuleOp mod, 1146 mlir::ConversionPatternRewriter &rewriter) { 1147 auto ptrTy = mlir::LLVM::LLVMPointerType::get(rewriter.getContext()); 1148 if (auto flc = mlir::dyn_cast<mlir::FileLineColLoc>(loc)) { 1149 auto fn = flc.getFilename().str() + '\0'; 1150 std::string globalName = fir::factory::uniqueCGIdent("cl", fn); 1151 1152 if (auto g = mod.lookupSymbol<fir::GlobalOp>(globalName)) { 1153 return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ptrTy, g.getName()); 1154 } else if (auto g = mod.lookupSymbol<mlir::LLVM::GlobalOp>(globalName)) { 1155 return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ptrTy, g.getName()); 1156 } 1157 1158 auto crtInsPt = rewriter.saveInsertionPoint(); 1159 rewriter.setInsertionPoint(mod.getBody(), mod.getBody()->end()); 1160 auto arrayTy = mlir::LLVM::LLVMArrayType::get( 1161 mlir::IntegerType::get(rewriter.getContext(), 8), fn.size()); 1162 mlir::LLVM::GlobalOp globalOp = rewriter.create<mlir::LLVM::GlobalOp>( 1163 loc, arrayTy, /*constant=*/true, mlir::LLVM::Linkage::Linkonce, 1164 globalName, mlir::Attribute()); 1165 1166 mlir::Region ®ion = globalOp.getInitializerRegion(); 1167 mlir::Block *block = rewriter.createBlock(®ion); 1168 rewriter.setInsertionPoint(block, block->begin()); 1169 mlir::Value constValue = rewriter.create<mlir::LLVM::ConstantOp>( 1170 loc, arrayTy, rewriter.getStringAttr(fn)); 1171 rewriter.create<mlir::LLVM::ReturnOp>(loc, constValue); 1172 rewriter.restoreInsertionPoint(crtInsPt); 1173 return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ptrTy, 1174 globalOp.getName()); 1175 } 1176 return rewriter.create<mlir::LLVM::ZeroOp>(loc, ptrTy); 1177 } 1178 1179 static mlir::Value genSourceLine(mlir::Location loc, 1180 mlir::ConversionPatternRewriter &rewriter) { 1181 if (auto flc = mlir::dyn_cast<mlir::FileLineColLoc>(loc)) 1182 return rewriter.create<mlir::LLVM::ConstantOp>(loc, rewriter.getI32Type(), 1183 flc.getLine()); 1184 return rewriter.create<mlir::LLVM::ConstantOp>(loc, rewriter.getI32Type(), 0); 1185 } 1186 1187 static mlir::Value 1188 genCUFAllocDescriptor(mlir::Location loc, 1189 mlir::ConversionPatternRewriter &rewriter, 1190 mlir::ModuleOp mod, fir::BaseBoxType boxTy, 1191 const fir::LLVMTypeConverter &typeConverter) { 1192 std::optional<mlir::DataLayout> dl = 1193 fir::support::getOrSetDataLayout(mod, /*allowDefaultLayout=*/true); 1194 if (!dl) 1195 mlir::emitError(mod.getLoc(), 1196 "module operation must carry a data layout attribute " 1197 "to generate llvm IR from FIR"); 1198 1199 mlir::Value sourceFile = genSourceFile(loc, mod, rewriter); 1200 mlir::Value sourceLine = genSourceLine(loc, rewriter); 1201 1202 mlir::MLIRContext *ctx = mod.getContext(); 1203 1204 mlir::LLVM::LLVMPointerType llvmPointerType = 1205 mlir::LLVM::LLVMPointerType::get(ctx); 1206 mlir::Type llvmInt32Type = mlir::IntegerType::get(ctx, 32); 1207 mlir::Type llvmIntPtrType = 1208 mlir::IntegerType::get(ctx, typeConverter.getPointerBitwidth(0)); 1209 auto fctTy = mlir::LLVM::LLVMFunctionType::get( 1210 llvmPointerType, {llvmIntPtrType, llvmPointerType, llvmInt32Type}); 1211 1212 auto llvmFunc = mod.lookupSymbol<mlir::LLVM::LLVMFuncOp>( 1213 RTNAME_STRING(CUFAllocDescriptor)); 1214 auto funcFunc = 1215 mod.lookupSymbol<mlir::func::FuncOp>(RTNAME_STRING(CUFAllocDescriptor)); 1216 if (!llvmFunc && !funcFunc) 1217 mlir::OpBuilder::atBlockEnd(mod.getBody()) 1218 .create<mlir::LLVM::LLVMFuncOp>(loc, RTNAME_STRING(CUFAllocDescriptor), 1219 fctTy); 1220 1221 mlir::Type structTy = typeConverter.convertBoxTypeAsStruct(boxTy); 1222 std::size_t boxSize = dl->getTypeSizeInBits(structTy) / 8; 1223 mlir::Value sizeInBytes = 1224 genConstantIndex(loc, llvmIntPtrType, rewriter, boxSize); 1225 llvm::SmallVector args = {sizeInBytes, sourceFile, sourceLine}; 1226 return rewriter 1227 .create<mlir::LLVM::CallOp>(loc, fctTy, RTNAME_STRING(CUFAllocDescriptor), 1228 args) 1229 .getResult(); 1230 } 1231 1232 /// Common base class for embox to descriptor conversion. 1233 template <typename OP> 1234 struct EmboxCommonConversion : public fir::FIROpConversion<OP> { 1235 using fir::FIROpConversion<OP>::FIROpConversion; 1236 using TypePair = typename fir::FIROpConversion<OP>::TypePair; 1237 1238 static int getCFIAttr(fir::BaseBoxType boxTy) { 1239 auto eleTy = boxTy.getEleTy(); 1240 if (mlir::isa<fir::PointerType>(eleTy)) 1241 return CFI_attribute_pointer; 1242 if (mlir::isa<fir::HeapType>(eleTy)) 1243 return CFI_attribute_allocatable; 1244 return CFI_attribute_other; 1245 } 1246 1247 mlir::Value getCharacterByteSize(mlir::Location loc, 1248 mlir::ConversionPatternRewriter &rewriter, 1249 fir::CharacterType charTy, 1250 mlir::ValueRange lenParams) const { 1251 auto i64Ty = mlir::IntegerType::get(rewriter.getContext(), 64); 1252 mlir::Value size = 1253 genTypeStrideInBytes(loc, i64Ty, rewriter, this->convertType(charTy)); 1254 if (charTy.hasConstantLen()) 1255 return size; // Length accounted for in the genTypeStrideInBytes GEP. 1256 // Otherwise, multiply the single character size by the length. 1257 assert(!lenParams.empty()); 1258 auto len64 = fir::FIROpConversion<OP>::integerCast(loc, rewriter, i64Ty, 1259 lenParams.back()); 1260 return rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, size, len64); 1261 } 1262 1263 // Get the element size and CFI type code of the boxed value. 1264 std::tuple<mlir::Value, mlir::Value> getSizeAndTypeCode( 1265 mlir::Location loc, mlir::ConversionPatternRewriter &rewriter, 1266 mlir::Type boxEleTy, mlir::ValueRange lenParams = {}) const { 1267 auto i64Ty = mlir::IntegerType::get(rewriter.getContext(), 64); 1268 if (auto eleTy = fir::dyn_cast_ptrEleTy(boxEleTy)) 1269 boxEleTy = eleTy; 1270 if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(boxEleTy)) 1271 return getSizeAndTypeCode(loc, rewriter, seqTy.getEleTy(), lenParams); 1272 if (mlir::isa<mlir::NoneType>( 1273 boxEleTy)) // unlimited polymorphic or assumed type 1274 return {rewriter.create<mlir::LLVM::ConstantOp>(loc, i64Ty, 0), 1275 this->genConstantOffset(loc, rewriter, CFI_type_other)}; 1276 mlir::Value typeCodeVal = this->genConstantOffset( 1277 loc, rewriter, 1278 fir::getTypeCode(boxEleTy, this->lowerTy().getKindMap())); 1279 if (fir::isa_integer(boxEleTy) || 1280 mlir::dyn_cast<fir::LogicalType>(boxEleTy) || fir::isa_real(boxEleTy) || 1281 fir::isa_complex(boxEleTy)) 1282 return {genTypeStrideInBytes(loc, i64Ty, rewriter, 1283 this->convertType(boxEleTy)), 1284 typeCodeVal}; 1285 if (auto charTy = mlir::dyn_cast<fir::CharacterType>(boxEleTy)) 1286 return {getCharacterByteSize(loc, rewriter, charTy, lenParams), 1287 typeCodeVal}; 1288 if (fir::isa_ref_type(boxEleTy)) { 1289 auto ptrTy = ::getLlvmPtrType(rewriter.getContext()); 1290 return {genTypeStrideInBytes(loc, i64Ty, rewriter, ptrTy), typeCodeVal}; 1291 } 1292 if (mlir::isa<fir::RecordType>(boxEleTy)) 1293 return {genTypeStrideInBytes(loc, i64Ty, rewriter, 1294 this->convertType(boxEleTy)), 1295 typeCodeVal}; 1296 fir::emitFatalError(loc, "unhandled type in fir.box code generation"); 1297 } 1298 1299 /// Basic pattern to write a field in the descriptor 1300 mlir::Value insertField(mlir::ConversionPatternRewriter &rewriter, 1301 mlir::Location loc, mlir::Value dest, 1302 llvm::ArrayRef<std::int64_t> fldIndexes, 1303 mlir::Value value, bool bitcast = false) const { 1304 auto boxTy = dest.getType(); 1305 auto fldTy = this->getBoxEleTy(boxTy, fldIndexes); 1306 if (!bitcast) 1307 value = this->integerCast(loc, rewriter, fldTy, value); 1308 // bitcast are no-ops with LLVM opaque pointers. 1309 return rewriter.create<mlir::LLVM::InsertValueOp>(loc, dest, value, 1310 fldIndexes); 1311 } 1312 1313 inline mlir::Value 1314 insertBaseAddress(mlir::ConversionPatternRewriter &rewriter, 1315 mlir::Location loc, mlir::Value dest, 1316 mlir::Value base) const { 1317 return insertField(rewriter, loc, dest, {kAddrPosInBox}, base, 1318 /*bitCast=*/true); 1319 } 1320 1321 inline mlir::Value insertLowerBound(mlir::ConversionPatternRewriter &rewriter, 1322 mlir::Location loc, mlir::Value dest, 1323 unsigned dim, mlir::Value lb) const { 1324 return insertField(rewriter, loc, dest, 1325 {kDimsPosInBox, dim, kDimLowerBoundPos}, lb); 1326 } 1327 1328 inline mlir::Value insertExtent(mlir::ConversionPatternRewriter &rewriter, 1329 mlir::Location loc, mlir::Value dest, 1330 unsigned dim, mlir::Value extent) const { 1331 return insertField(rewriter, loc, dest, {kDimsPosInBox, dim, kDimExtentPos}, 1332 extent); 1333 } 1334 1335 inline mlir::Value insertStride(mlir::ConversionPatternRewriter &rewriter, 1336 mlir::Location loc, mlir::Value dest, 1337 unsigned dim, mlir::Value stride) const { 1338 return insertField(rewriter, loc, dest, {kDimsPosInBox, dim, kDimStridePos}, 1339 stride); 1340 } 1341 1342 /// Get the address of the type descriptor global variable that was created by 1343 /// lowering for derived type \p recType. 1344 template <typename ModOpTy> 1345 mlir::Value 1346 getTypeDescriptor(ModOpTy mod, mlir::ConversionPatternRewriter &rewriter, 1347 mlir::Location loc, fir::RecordType recType) const { 1348 std::string name = 1349 this->options.typeDescriptorsRenamedForAssembly 1350 ? fir::NameUniquer::getTypeDescriptorAssemblyName(recType.getName()) 1351 : fir::NameUniquer::getTypeDescriptorName(recType.getName()); 1352 mlir::Type llvmPtrTy = ::getLlvmPtrType(mod.getContext()); 1353 if (auto global = mod.template lookupSymbol<fir::GlobalOp>(name)) { 1354 return rewriter.create<mlir::LLVM::AddressOfOp>(loc, llvmPtrTy, 1355 global.getSymName()); 1356 } 1357 if (auto global = mod.template lookupSymbol<mlir::LLVM::GlobalOp>(name)) { 1358 // The global may have already been translated to LLVM. 1359 return rewriter.create<mlir::LLVM::AddressOfOp>(loc, llvmPtrTy, 1360 global.getSymName()); 1361 } 1362 // Type info derived types do not have type descriptors since they are the 1363 // types defining type descriptors. 1364 if (!this->options.ignoreMissingTypeDescriptors && 1365 !fir::NameUniquer::belongsToModule( 1366 name, Fortran::semantics::typeInfoBuiltinModule)) 1367 fir::emitFatalError( 1368 loc, "runtime derived type info descriptor was not generated"); 1369 return rewriter.create<mlir::LLVM::ZeroOp>(loc, llvmPtrTy); 1370 } 1371 1372 template <typename ModOpTy> 1373 mlir::Value populateDescriptor(mlir::Location loc, ModOpTy mod, 1374 fir::BaseBoxType boxTy, mlir::Type inputType, 1375 mlir::ConversionPatternRewriter &rewriter, 1376 unsigned rank, mlir::Value eleSize, 1377 mlir::Value cfiTy, mlir::Value typeDesc, 1378 int allocatorIdx = kDefaultAllocator, 1379 mlir::Value extraField = {}) const { 1380 auto llvmBoxTy = this->lowerTy().convertBoxTypeAsStruct(boxTy, rank); 1381 bool isUnlimitedPolymorphic = fir::isUnlimitedPolymorphicType(boxTy); 1382 bool useInputType = fir::isPolymorphicType(boxTy) || isUnlimitedPolymorphic; 1383 mlir::Value descriptor = 1384 rewriter.create<mlir::LLVM::UndefOp>(loc, llvmBoxTy); 1385 descriptor = 1386 insertField(rewriter, loc, descriptor, {kElemLenPosInBox}, eleSize); 1387 descriptor = insertField(rewriter, loc, descriptor, {kVersionPosInBox}, 1388 this->genI32Constant(loc, rewriter, CFI_VERSION)); 1389 descriptor = insertField(rewriter, loc, descriptor, {kRankPosInBox}, 1390 this->genI32Constant(loc, rewriter, rank)); 1391 descriptor = insertField(rewriter, loc, descriptor, {kTypePosInBox}, cfiTy); 1392 descriptor = 1393 insertField(rewriter, loc, descriptor, {kAttributePosInBox}, 1394 this->genI32Constant(loc, rewriter, getCFIAttr(boxTy))); 1395 1396 const bool hasAddendum = fir::boxHasAddendum(boxTy); 1397 1398 if (extraField) { 1399 // Make sure to set the addendum presence flag according to the 1400 // destination box. 1401 if (hasAddendum) { 1402 auto maskAttr = mlir::IntegerAttr::get( 1403 rewriter.getIntegerType(8, /*isSigned=*/false), 1404 llvm::APInt(8, (uint64_t)_CFI_ADDENDUM_FLAG, /*isSigned=*/false)); 1405 mlir::LLVM::ConstantOp mask = rewriter.create<mlir::LLVM::ConstantOp>( 1406 loc, rewriter.getI8Type(), maskAttr); 1407 extraField = rewriter.create<mlir::LLVM::OrOp>(loc, extraField, mask); 1408 } else { 1409 auto maskAttr = mlir::IntegerAttr::get( 1410 rewriter.getIntegerType(8, /*isSigned=*/false), 1411 llvm::APInt(8, (uint64_t)~_CFI_ADDENDUM_FLAG, /*isSigned=*/true)); 1412 mlir::LLVM::ConstantOp mask = rewriter.create<mlir::LLVM::ConstantOp>( 1413 loc, rewriter.getI8Type(), maskAttr); 1414 extraField = rewriter.create<mlir::LLVM::AndOp>(loc, extraField, mask); 1415 } 1416 // Extra field value is provided so just use it. 1417 descriptor = 1418 insertField(rewriter, loc, descriptor, {kExtraPosInBox}, extraField); 1419 } else { 1420 // Compute the value of the extra field based on allocator_idx and 1421 // addendum present. 1422 unsigned extra = allocatorIdx << _CFI_ALLOCATOR_IDX_SHIFT; 1423 if (hasAddendum) 1424 extra |= _CFI_ADDENDUM_FLAG; 1425 descriptor = insertField(rewriter, loc, descriptor, {kExtraPosInBox}, 1426 this->genI32Constant(loc, rewriter, extra)); 1427 } 1428 1429 if (hasAddendum) { 1430 unsigned typeDescFieldId = getTypeDescFieldId(boxTy); 1431 if (!typeDesc) { 1432 if (useInputType) { 1433 mlir::Type innerType = fir::unwrapInnerType(inputType); 1434 if (innerType && mlir::isa<fir::RecordType>(innerType)) { 1435 auto recTy = mlir::dyn_cast<fir::RecordType>(innerType); 1436 typeDesc = getTypeDescriptor(mod, rewriter, loc, recTy); 1437 } else { 1438 // Unlimited polymorphic type descriptor with no record type. Set 1439 // type descriptor address to a clean state. 1440 typeDesc = rewriter.create<mlir::LLVM::ZeroOp>( 1441 loc, ::getLlvmPtrType(mod.getContext())); 1442 } 1443 } else { 1444 typeDesc = getTypeDescriptor(mod, rewriter, loc, 1445 fir::unwrapIfDerived(boxTy)); 1446 } 1447 } 1448 if (typeDesc) 1449 descriptor = 1450 insertField(rewriter, loc, descriptor, {typeDescFieldId}, typeDesc, 1451 /*bitCast=*/true); 1452 // Always initialize the length parameter field to zero to avoid issues 1453 // with uninitialized values in Fortran code trying to compare physical 1454 // representation of derived types with pointer/allocatable components. 1455 // This has been seen in hashing algorithms using TRANSFER. 1456 mlir::Value zero = 1457 genConstantIndex(loc, rewriter.getI64Type(), rewriter, 0); 1458 descriptor = insertField(rewriter, loc, descriptor, 1459 {getLenParamFieldId(boxTy), 0}, zero); 1460 } 1461 return descriptor; 1462 } 1463 1464 // Template used for fir::EmboxOp and fir::cg::XEmboxOp 1465 template <typename BOX> 1466 std::tuple<fir::BaseBoxType, mlir::Value, mlir::Value> 1467 consDescriptorPrefix(BOX box, mlir::Type inputType, 1468 mlir::ConversionPatternRewriter &rewriter, unsigned rank, 1469 [[maybe_unused]] mlir::ValueRange substrParams, 1470 mlir::ValueRange lenParams, mlir::Value sourceBox = {}, 1471 mlir::Type sourceBoxType = {}) const { 1472 auto loc = box.getLoc(); 1473 auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(box.getType()); 1474 bool useInputType = fir::isPolymorphicType(boxTy) && 1475 !fir::isUnlimitedPolymorphicType(inputType); 1476 llvm::SmallVector<mlir::Value> typeparams = lenParams; 1477 if constexpr (!std::is_same_v<BOX, fir::EmboxOp>) { 1478 if (!box.getSubstr().empty() && fir::hasDynamicSize(boxTy.getEleTy())) 1479 typeparams.push_back(substrParams[1]); 1480 } 1481 1482 int allocatorIdx = 0; 1483 if constexpr (std::is_same_v<BOX, fir::EmboxOp> || 1484 std::is_same_v<BOX, fir::cg::XEmboxOp>) { 1485 if (box.getAllocatorIdx()) 1486 allocatorIdx = *box.getAllocatorIdx(); 1487 } 1488 1489 // Write each of the fields with the appropriate values. 1490 // When emboxing an element to a polymorphic descriptor, use the 1491 // input type since the destination descriptor type has not the exact 1492 // information. 1493 auto [eleSize, cfiTy] = getSizeAndTypeCode( 1494 loc, rewriter, useInputType ? inputType : boxTy.getEleTy(), typeparams); 1495 1496 mlir::Value typeDesc; 1497 mlir::Value extraField; 1498 // When emboxing to a polymorphic box, get the type descriptor, type code 1499 // and element size from the source box if any. 1500 if (fir::isPolymorphicType(boxTy) && sourceBox) { 1501 TypePair sourceBoxTyPair = this->getBoxTypePair(sourceBoxType); 1502 typeDesc = 1503 this->loadTypeDescAddress(loc, sourceBoxTyPair, sourceBox, rewriter); 1504 mlir::Type idxTy = this->lowerTy().indexType(); 1505 eleSize = this->getElementSizeFromBox(loc, idxTy, sourceBoxTyPair, 1506 sourceBox, rewriter); 1507 cfiTy = this->getValueFromBox(loc, sourceBoxTyPair, sourceBox, 1508 cfiTy.getType(), rewriter, kTypePosInBox); 1509 extraField = 1510 this->getExtraFromBox(loc, sourceBoxTyPair, sourceBox, rewriter); 1511 } 1512 1513 mlir::Value descriptor; 1514 if (auto gpuMod = box->template getParentOfType<mlir::gpu::GPUModuleOp>()) 1515 descriptor = populateDescriptor(loc, gpuMod, boxTy, inputType, rewriter, 1516 rank, eleSize, cfiTy, typeDesc, 1517 allocatorIdx, extraField); 1518 else if (auto mod = box->template getParentOfType<mlir::ModuleOp>()) 1519 descriptor = populateDescriptor(loc, mod, boxTy, inputType, rewriter, 1520 rank, eleSize, cfiTy, typeDesc, 1521 allocatorIdx, extraField); 1522 1523 return {boxTy, descriptor, eleSize}; 1524 } 1525 1526 std::tuple<fir::BaseBoxType, mlir::Value, mlir::Value> 1527 consDescriptorPrefix(fir::cg::XReboxOp box, mlir::Value loweredBox, 1528 mlir::ConversionPatternRewriter &rewriter, unsigned rank, 1529 mlir::ValueRange substrParams, 1530 mlir::ValueRange lenParams, 1531 mlir::Value typeDesc = {}) const { 1532 auto loc = box.getLoc(); 1533 auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(box.getType()); 1534 auto inputBoxTy = mlir::dyn_cast<fir::BaseBoxType>(box.getBox().getType()); 1535 auto inputBoxTyPair = this->getBoxTypePair(inputBoxTy); 1536 llvm::SmallVector<mlir::Value> typeparams = lenParams; 1537 if (!box.getSubstr().empty() && fir::hasDynamicSize(boxTy.getEleTy())) 1538 typeparams.push_back(substrParams[1]); 1539 1540 auto [eleSize, cfiTy] = 1541 getSizeAndTypeCode(loc, rewriter, boxTy.getEleTy(), typeparams); 1542 1543 // Reboxing to a polymorphic entity. eleSize and type code need to 1544 // be retrieved from the initial box and propagated to the new box. 1545 // If the initial box has an addendum, the type desc must be propagated as 1546 // well. 1547 if (fir::isPolymorphicType(boxTy)) { 1548 mlir::Type idxTy = this->lowerTy().indexType(); 1549 eleSize = this->getElementSizeFromBox(loc, idxTy, inputBoxTyPair, 1550 loweredBox, rewriter); 1551 cfiTy = this->getValueFromBox(loc, inputBoxTyPair, loweredBox, 1552 cfiTy.getType(), rewriter, kTypePosInBox); 1553 // TODO: For initial box that are unlimited polymorphic entities, this 1554 // code must be made conditional because unlimited polymorphic entities 1555 // with intrinsic type spec does not have addendum. 1556 if (fir::boxHasAddendum(inputBoxTy)) 1557 typeDesc = this->loadTypeDescAddress(loc, inputBoxTyPair, loweredBox, 1558 rewriter); 1559 } 1560 1561 mlir::Value extraField = 1562 this->getExtraFromBox(loc, inputBoxTyPair, loweredBox, rewriter); 1563 1564 mlir::Value descriptor; 1565 if (auto gpuMod = box->template getParentOfType<mlir::gpu::GPUModuleOp>()) 1566 descriptor = 1567 populateDescriptor(loc, gpuMod, boxTy, box.getBox().getType(), 1568 rewriter, rank, eleSize, cfiTy, typeDesc, 1569 /*allocatorIdx=*/kDefaultAllocator, extraField); 1570 else if (auto mod = box->template getParentOfType<mlir::ModuleOp>()) 1571 descriptor = 1572 populateDescriptor(loc, mod, boxTy, box.getBox().getType(), rewriter, 1573 rank, eleSize, cfiTy, typeDesc, 1574 /*allocatorIdx=*/kDefaultAllocator, extraField); 1575 1576 return {boxTy, descriptor, eleSize}; 1577 } 1578 1579 // Compute the base address of a fir.box given the indices from the slice. 1580 // The indices from the "outer" dimensions (every dimension after the first 1581 // one (included) that is not a compile time constant) must have been 1582 // multiplied with the related extents and added together into \p outerOffset. 1583 mlir::Value 1584 genBoxOffsetGep(mlir::ConversionPatternRewriter &rewriter, mlir::Location loc, 1585 mlir::Value base, mlir::Type llvmBaseObjectType, 1586 mlir::Value outerOffset, mlir::ValueRange cstInteriorIndices, 1587 mlir::ValueRange componentIndices, 1588 std::optional<mlir::Value> substringOffset) const { 1589 llvm::SmallVector<mlir::LLVM::GEPArg> gepArgs{outerOffset}; 1590 mlir::Type resultTy = llvmBaseObjectType; 1591 // Fortran is column major, llvm GEP is row major: reverse the indices here. 1592 for (mlir::Value interiorIndex : llvm::reverse(cstInteriorIndices)) { 1593 auto arrayTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(resultTy); 1594 if (!arrayTy) 1595 fir::emitFatalError( 1596 loc, 1597 "corrupted GEP generated being generated in fir.embox/fir.rebox"); 1598 resultTy = arrayTy.getElementType(); 1599 gepArgs.push_back(interiorIndex); 1600 } 1601 llvm::SmallVector<mlir::Value> gepIndices = 1602 convertSubcomponentIndices(loc, resultTy, componentIndices, &resultTy); 1603 gepArgs.append(gepIndices.begin(), gepIndices.end()); 1604 if (substringOffset) { 1605 if (auto arrayTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(resultTy)) { 1606 gepArgs.push_back(*substringOffset); 1607 resultTy = arrayTy.getElementType(); 1608 } else { 1609 // If the CHARACTER length is dynamic, the whole base type should have 1610 // degenerated to an llvm.ptr<i[width]>, and there should not be any 1611 // cstInteriorIndices/componentIndices. The substring offset can be 1612 // added to the outterOffset since it applies on the same LLVM type. 1613 if (gepArgs.size() != 1) 1614 fir::emitFatalError(loc, 1615 "corrupted substring GEP in fir.embox/fir.rebox"); 1616 mlir::Type outterOffsetTy = 1617 llvm::cast<mlir::Value>(gepArgs[0]).getType(); 1618 mlir::Value cast = 1619 this->integerCast(loc, rewriter, outterOffsetTy, *substringOffset); 1620 1621 gepArgs[0] = rewriter.create<mlir::LLVM::AddOp>( 1622 loc, outterOffsetTy, llvm::cast<mlir::Value>(gepArgs[0]), cast); 1623 } 1624 } 1625 mlir::Type llvmPtrTy = ::getLlvmPtrType(resultTy.getContext()); 1626 return rewriter.create<mlir::LLVM::GEPOp>( 1627 loc, llvmPtrTy, llvmBaseObjectType, base, gepArgs); 1628 } 1629 1630 template <typename BOX> 1631 void 1632 getSubcomponentIndices(BOX xbox, mlir::Value memref, 1633 mlir::ValueRange operands, 1634 mlir::SmallVectorImpl<mlir::Value> &indices) const { 1635 // For each field in the path add the offset to base via the args list. 1636 // In the most general case, some offsets must be computed since 1637 // they are not be known until runtime. 1638 if (fir::hasDynamicSize(fir::unwrapSequenceType( 1639 fir::unwrapPassByRefType(memref.getType())))) 1640 TODO(xbox.getLoc(), 1641 "fir.embox codegen dynamic size component in derived type"); 1642 indices.append(operands.begin() + xbox.getSubcomponentOperandIndex(), 1643 operands.begin() + xbox.getSubcomponentOperandIndex() + 1644 xbox.getSubcomponent().size()); 1645 } 1646 1647 static bool isInGlobalOp(mlir::ConversionPatternRewriter &rewriter) { 1648 auto *thisBlock = rewriter.getInsertionBlock(); 1649 return thisBlock && 1650 mlir::isa<mlir::LLVM::GlobalOp>(thisBlock->getParentOp()); 1651 } 1652 1653 /// If the embox is not in a globalOp body, allocate storage for the box; 1654 /// store the value inside and return the generated alloca. Return the input 1655 /// value otherwise. 1656 mlir::Value 1657 placeInMemoryIfNotGlobalInit(mlir::ConversionPatternRewriter &rewriter, 1658 mlir::Location loc, mlir::Type boxTy, 1659 mlir::Value boxValue, 1660 bool needDeviceAllocation = false) const { 1661 if (isInGlobalOp(rewriter)) 1662 return boxValue; 1663 mlir::Type llvmBoxTy = boxValue.getType(); 1664 mlir::Value storage; 1665 if (needDeviceAllocation) { 1666 auto mod = boxValue.getDefiningOp()->getParentOfType<mlir::ModuleOp>(); 1667 auto baseBoxTy = mlir::dyn_cast<fir::BaseBoxType>(boxTy); 1668 storage = 1669 genCUFAllocDescriptor(loc, rewriter, mod, baseBoxTy, this->lowerTy()); 1670 } else { 1671 storage = this->genAllocaAndAddrCastWithType(loc, llvmBoxTy, defaultAlign, 1672 rewriter); 1673 } 1674 auto storeOp = rewriter.create<mlir::LLVM::StoreOp>(loc, boxValue, storage); 1675 this->attachTBAATag(storeOp, boxTy, boxTy, nullptr); 1676 return storage; 1677 } 1678 1679 /// Compute the extent of a triplet slice (lb:ub:step). 1680 mlir::Value computeTripletExtent(mlir::ConversionPatternRewriter &rewriter, 1681 mlir::Location loc, mlir::Value lb, 1682 mlir::Value ub, mlir::Value step, 1683 mlir::Value zero, mlir::Type type) const { 1684 lb = this->integerCast(loc, rewriter, type, lb); 1685 ub = this->integerCast(loc, rewriter, type, ub); 1686 step = this->integerCast(loc, rewriter, type, step); 1687 zero = this->integerCast(loc, rewriter, type, zero); 1688 mlir::Value extent = rewriter.create<mlir::LLVM::SubOp>(loc, type, ub, lb); 1689 extent = rewriter.create<mlir::LLVM::AddOp>(loc, type, extent, step); 1690 extent = rewriter.create<mlir::LLVM::SDivOp>(loc, type, extent, step); 1691 // If the resulting extent is negative (`ub-lb` and `step` have different 1692 // signs), zero must be returned instead. 1693 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 1694 loc, mlir::LLVM::ICmpPredicate::sgt, extent, zero); 1695 return rewriter.create<mlir::LLVM::SelectOp>(loc, cmp, extent, zero); 1696 } 1697 }; 1698 1699 /// Create a generic box on a memory reference. This conversions lowers the 1700 /// abstract box to the appropriate, initialized descriptor. 1701 struct EmboxOpConversion : public EmboxCommonConversion<fir::EmboxOp> { 1702 using EmboxCommonConversion::EmboxCommonConversion; 1703 1704 llvm::LogicalResult 1705 matchAndRewrite(fir::EmboxOp embox, OpAdaptor adaptor, 1706 mlir::ConversionPatternRewriter &rewriter) const override { 1707 mlir::ValueRange operands = adaptor.getOperands(); 1708 mlir::Value sourceBox; 1709 mlir::Type sourceBoxType; 1710 if (embox.getSourceBox()) { 1711 sourceBox = operands[embox.getSourceBoxOperandIndex()]; 1712 sourceBoxType = embox.getSourceBox().getType(); 1713 } 1714 assert(!embox.getShape() && "There should be no dims on this embox op"); 1715 auto [boxTy, dest, eleSize] = consDescriptorPrefix( 1716 embox, fir::unwrapRefType(embox.getMemref().getType()), rewriter, 1717 /*rank=*/0, /*substrParams=*/mlir::ValueRange{}, 1718 adaptor.getTypeparams(), sourceBox, sourceBoxType); 1719 dest = insertBaseAddress(rewriter, embox.getLoc(), dest, operands[0]); 1720 if (fir::isDerivedTypeWithLenParams(boxTy)) { 1721 TODO(embox.getLoc(), 1722 "fir.embox codegen of derived with length parameters"); 1723 return mlir::failure(); 1724 } 1725 auto result = 1726 placeInMemoryIfNotGlobalInit(rewriter, embox.getLoc(), boxTy, dest); 1727 rewriter.replaceOp(embox, result); 1728 return mlir::success(); 1729 } 1730 }; 1731 1732 static bool isDeviceAllocation(mlir::Value val, mlir::Value adaptorVal) { 1733 if (auto loadOp = mlir::dyn_cast_or_null<fir::LoadOp>(val.getDefiningOp())) 1734 return isDeviceAllocation(loadOp.getMemref(), {}); 1735 if (auto boxAddrOp = 1736 mlir::dyn_cast_or_null<fir::BoxAddrOp>(val.getDefiningOp())) 1737 return isDeviceAllocation(boxAddrOp.getVal(), {}); 1738 if (auto convertOp = 1739 mlir::dyn_cast_or_null<fir::ConvertOp>(val.getDefiningOp())) 1740 return isDeviceAllocation(convertOp.getValue(), {}); 1741 if (!val.getDefiningOp() && adaptorVal) { 1742 if (auto blockArg = llvm::cast<mlir::BlockArgument>(adaptorVal)) { 1743 if (blockArg.getOwner() && blockArg.getOwner()->getParentOp() && 1744 blockArg.getOwner()->isEntryBlock()) { 1745 if (auto func = mlir::dyn_cast_or_null<mlir::FunctionOpInterface>( 1746 *blockArg.getOwner()->getParentOp())) { 1747 auto argAttrs = func.getArgAttrs(blockArg.getArgNumber()); 1748 for (auto attr : argAttrs) { 1749 if (attr.getName().getValue().ends_with(cuf::getDataAttrName())) { 1750 auto dataAttr = 1751 mlir::dyn_cast<cuf::DataAttributeAttr>(attr.getValue()); 1752 if (dataAttr.getValue() != cuf::DataAttribute::Pinned && 1753 dataAttr.getValue() != cuf::DataAttribute::Unified) 1754 return true; 1755 } 1756 } 1757 } 1758 } 1759 } 1760 } 1761 if (auto callOp = mlir::dyn_cast_or_null<fir::CallOp>(val.getDefiningOp())) 1762 if (callOp.getCallee() && 1763 (callOp.getCallee().value().getRootReference().getValue().starts_with( 1764 RTNAME_STRING(CUFMemAlloc)) || 1765 callOp.getCallee().value().getRootReference().getValue().starts_with( 1766 RTNAME_STRING(CUFAllocDescriptor)))) 1767 return true; 1768 return false; 1769 } 1770 1771 /// Create a generic box on a memory reference. 1772 struct XEmboxOpConversion : public EmboxCommonConversion<fir::cg::XEmboxOp> { 1773 using EmboxCommonConversion::EmboxCommonConversion; 1774 1775 llvm::LogicalResult 1776 matchAndRewrite(fir::cg::XEmboxOp xbox, OpAdaptor adaptor, 1777 mlir::ConversionPatternRewriter &rewriter) const override { 1778 mlir::ValueRange operands = adaptor.getOperands(); 1779 mlir::Value sourceBox; 1780 mlir::Type sourceBoxType; 1781 if (xbox.getSourceBox()) { 1782 sourceBox = operands[xbox.getSourceBoxOperandIndex()]; 1783 sourceBoxType = xbox.getSourceBox().getType(); 1784 } 1785 auto [boxTy, dest, resultEleSize] = consDescriptorPrefix( 1786 xbox, fir::unwrapRefType(xbox.getMemref().getType()), rewriter, 1787 xbox.getOutRank(), adaptor.getSubstr(), adaptor.getLenParams(), 1788 sourceBox, sourceBoxType); 1789 // Generate the triples in the dims field of the descriptor 1790 auto i64Ty = mlir::IntegerType::get(xbox.getContext(), 64); 1791 assert(!xbox.getShape().empty() && "must have a shape"); 1792 unsigned shapeOffset = xbox.getShapeOperandIndex(); 1793 bool hasShift = !xbox.getShift().empty(); 1794 unsigned shiftOffset = xbox.getShiftOperandIndex(); 1795 bool hasSlice = !xbox.getSlice().empty(); 1796 unsigned sliceOffset = xbox.getSliceOperandIndex(); 1797 mlir::Location loc = xbox.getLoc(); 1798 mlir::Value zero = genConstantIndex(loc, i64Ty, rewriter, 0); 1799 mlir::Value one = genConstantIndex(loc, i64Ty, rewriter, 1); 1800 mlir::Value prevPtrOff = one; 1801 mlir::Type eleTy = boxTy.getEleTy(); 1802 const unsigned rank = xbox.getRank(); 1803 llvm::SmallVector<mlir::Value> cstInteriorIndices; 1804 unsigned constRows = 0; 1805 mlir::Value ptrOffset = zero; 1806 mlir::Type memEleTy = fir::dyn_cast_ptrEleTy(xbox.getMemref().getType()); 1807 assert(mlir::isa<fir::SequenceType>(memEleTy)); 1808 auto seqTy = mlir::cast<fir::SequenceType>(memEleTy); 1809 mlir::Type seqEleTy = seqTy.getEleTy(); 1810 // Adjust the element scaling factor if the element is a dependent type. 1811 if (fir::hasDynamicSize(seqEleTy)) { 1812 if (auto charTy = mlir::dyn_cast<fir::CharacterType>(seqEleTy)) { 1813 // The GEP pointer type decays to llvm.ptr<i[width]>. 1814 // The scaling factor is the runtime value of the length. 1815 assert(!adaptor.getLenParams().empty()); 1816 prevPtrOff = FIROpConversion::integerCast( 1817 loc, rewriter, i64Ty, adaptor.getLenParams().back()); 1818 } else if (mlir::isa<fir::RecordType>(seqEleTy)) { 1819 // prevPtrOff = ; 1820 TODO(loc, "generate call to calculate size of PDT"); 1821 } else { 1822 fir::emitFatalError(loc, "unexpected dynamic type"); 1823 } 1824 } else { 1825 constRows = seqTy.getConstantRows(); 1826 } 1827 1828 const auto hasSubcomp = !xbox.getSubcomponent().empty(); 1829 const bool hasSubstr = !xbox.getSubstr().empty(); 1830 // Initial element stride that will be use to compute the step in 1831 // each dimension. Initially, this is the size of the input element. 1832 // Note that when there are no components/substring, the resultEleSize 1833 // that was previously computed matches the input element size. 1834 mlir::Value prevDimByteStride = resultEleSize; 1835 if (hasSubcomp) { 1836 // We have a subcomponent. The step value needs to be the number of 1837 // bytes per element (which is a derived type). 1838 prevDimByteStride = 1839 genTypeStrideInBytes(loc, i64Ty, rewriter, convertType(seqEleTy)); 1840 } else if (hasSubstr) { 1841 // We have a substring. The step value needs to be the number of bytes 1842 // per CHARACTER element. 1843 auto charTy = mlir::cast<fir::CharacterType>(seqEleTy); 1844 if (fir::hasDynamicSize(charTy)) { 1845 prevDimByteStride = 1846 getCharacterByteSize(loc, rewriter, charTy, adaptor.getLenParams()); 1847 } else { 1848 prevDimByteStride = genConstantIndex( 1849 loc, i64Ty, rewriter, 1850 charTy.getLen() * lowerTy().characterBitsize(charTy) / 8); 1851 } 1852 } 1853 1854 // Process the array subspace arguments (shape, shift, etc.), if any, 1855 // translating everything to values in the descriptor wherever the entity 1856 // has a dynamic array dimension. 1857 for (unsigned di = 0, descIdx = 0; di < rank; ++di) { 1858 mlir::Value extent = 1859 integerCast(loc, rewriter, i64Ty, operands[shapeOffset]); 1860 mlir::Value outerExtent = extent; 1861 bool skipNext = false; 1862 if (hasSlice) { 1863 mlir::Value off = 1864 integerCast(loc, rewriter, i64Ty, operands[sliceOffset]); 1865 mlir::Value adj = one; 1866 if (hasShift) 1867 adj = integerCast(loc, rewriter, i64Ty, operands[shiftOffset]); 1868 auto ao = rewriter.create<mlir::LLVM::SubOp>(loc, i64Ty, off, adj); 1869 if (constRows > 0) { 1870 cstInteriorIndices.push_back(ao); 1871 } else { 1872 auto dimOff = 1873 rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, ao, prevPtrOff); 1874 ptrOffset = 1875 rewriter.create<mlir::LLVM::AddOp>(loc, i64Ty, dimOff, ptrOffset); 1876 } 1877 if (mlir::isa_and_nonnull<fir::UndefOp>( 1878 xbox.getSlice()[3 * di + 1].getDefiningOp())) { 1879 // This dimension contains a scalar expression in the array slice op. 1880 // The dimension is loop invariant, will be dropped, and will not 1881 // appear in the descriptor. 1882 skipNext = true; 1883 } 1884 } 1885 if (!skipNext) { 1886 // store extent 1887 if (hasSlice) 1888 extent = computeTripletExtent(rewriter, loc, operands[sliceOffset], 1889 operands[sliceOffset + 1], 1890 operands[sliceOffset + 2], zero, i64Ty); 1891 // Lower bound is normalized to 0 for BIND(C) interoperability. 1892 mlir::Value lb = zero; 1893 const bool isaPointerOrAllocatable = 1894 mlir::isa<fir::PointerType, fir::HeapType>(eleTy); 1895 // Lower bound is defaults to 1 for POINTER, ALLOCATABLE, and 1896 // denormalized descriptors. 1897 if (isaPointerOrAllocatable || !normalizedLowerBound(xbox)) 1898 lb = one; 1899 // If there is a shifted origin, and no fir.slice, and this is not 1900 // a normalized descriptor then use the value from the shift op as 1901 // the lower bound. 1902 if (hasShift && !(hasSlice || hasSubcomp || hasSubstr) && 1903 (isaPointerOrAllocatable || !normalizedLowerBound(xbox))) { 1904 lb = integerCast(loc, rewriter, i64Ty, operands[shiftOffset]); 1905 auto extentIsEmpty = rewriter.create<mlir::LLVM::ICmpOp>( 1906 loc, mlir::LLVM::ICmpPredicate::eq, extent, zero); 1907 lb = rewriter.create<mlir::LLVM::SelectOp>(loc, extentIsEmpty, one, 1908 lb); 1909 } 1910 dest = insertLowerBound(rewriter, loc, dest, descIdx, lb); 1911 1912 dest = insertExtent(rewriter, loc, dest, descIdx, extent); 1913 1914 // store step (scaled by shaped extent) 1915 mlir::Value step = prevDimByteStride; 1916 if (hasSlice) { 1917 mlir::Value sliceStep = 1918 integerCast(loc, rewriter, i64Ty, operands[sliceOffset + 2]); 1919 step = 1920 rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, step, sliceStep); 1921 } 1922 dest = insertStride(rewriter, loc, dest, descIdx, step); 1923 ++descIdx; 1924 } 1925 1926 // compute the stride and offset for the next natural dimension 1927 prevDimByteStride = rewriter.create<mlir::LLVM::MulOp>( 1928 loc, i64Ty, prevDimByteStride, outerExtent); 1929 if (constRows == 0) 1930 prevPtrOff = rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, prevPtrOff, 1931 outerExtent); 1932 else 1933 --constRows; 1934 1935 // increment iterators 1936 ++shapeOffset; 1937 if (hasShift) 1938 ++shiftOffset; 1939 if (hasSlice) 1940 sliceOffset += 3; 1941 } 1942 mlir::Value base = adaptor.getMemref(); 1943 if (hasSlice || hasSubcomp || hasSubstr) { 1944 // Shift the base address. 1945 llvm::SmallVector<mlir::Value> fieldIndices; 1946 std::optional<mlir::Value> substringOffset; 1947 if (hasSubcomp) 1948 getSubcomponentIndices(xbox, xbox.getMemref(), operands, fieldIndices); 1949 if (hasSubstr) 1950 substringOffset = operands[xbox.getSubstrOperandIndex()]; 1951 mlir::Type llvmBaseType = 1952 convertType(fir::unwrapRefType(xbox.getMemref().getType())); 1953 base = genBoxOffsetGep(rewriter, loc, base, llvmBaseType, ptrOffset, 1954 cstInteriorIndices, fieldIndices, substringOffset); 1955 } 1956 dest = insertBaseAddress(rewriter, loc, dest, base); 1957 if (fir::isDerivedTypeWithLenParams(boxTy)) 1958 TODO(loc, "fir.embox codegen of derived with length parameters"); 1959 mlir::Value result = placeInMemoryIfNotGlobalInit( 1960 rewriter, loc, boxTy, dest, 1961 isDeviceAllocation(xbox.getMemref(), adaptor.getMemref())); 1962 rewriter.replaceOp(xbox, result); 1963 return mlir::success(); 1964 } 1965 1966 /// Return true if `xbox` has a normalized lower bounds attribute. A box value 1967 /// that is neither a POINTER nor an ALLOCATABLE should be normalized to a 1968 /// zero origin lower bound for interoperability with BIND(C). 1969 inline static bool normalizedLowerBound(fir::cg::XEmboxOp xbox) { 1970 return xbox->hasAttr(fir::getNormalizedLowerBoundAttrName()); 1971 } 1972 }; 1973 1974 /// Create a new box given a box reference. 1975 struct XReboxOpConversion : public EmboxCommonConversion<fir::cg::XReboxOp> { 1976 using EmboxCommonConversion::EmboxCommonConversion; 1977 1978 llvm::LogicalResult 1979 matchAndRewrite(fir::cg::XReboxOp rebox, OpAdaptor adaptor, 1980 mlir::ConversionPatternRewriter &rewriter) const override { 1981 mlir::Location loc = rebox.getLoc(); 1982 mlir::Type idxTy = lowerTy().indexType(); 1983 mlir::Value loweredBox = adaptor.getOperands()[0]; 1984 mlir::ValueRange operands = adaptor.getOperands(); 1985 1986 // Inside a fir.global, the input box was produced as an llvm.struct<> 1987 // because objects cannot be handled in memory inside a fir.global body that 1988 // must be constant foldable. However, the type translation are not 1989 // contextual, so the fir.box<T> type of the operation that produced the 1990 // fir.box was translated to an llvm.ptr<llvm.struct<>> and the MLIR pass 1991 // manager inserted a builtin.unrealized_conversion_cast that was inserted 1992 // and needs to be removed here. 1993 if (isInGlobalOp(rewriter)) 1994 if (auto unrealizedCast = 1995 loweredBox.getDefiningOp<mlir::UnrealizedConversionCastOp>()) 1996 loweredBox = unrealizedCast.getInputs()[0]; 1997 1998 TypePair inputBoxTyPair = getBoxTypePair(rebox.getBox().getType()); 1999 2000 // Create new descriptor and fill its non-shape related data. 2001 llvm::SmallVector<mlir::Value, 2> lenParams; 2002 mlir::Type inputEleTy = getInputEleTy(rebox); 2003 if (auto charTy = mlir::dyn_cast<fir::CharacterType>(inputEleTy)) { 2004 if (charTy.hasConstantLen()) { 2005 mlir::Value len = 2006 genConstantIndex(loc, idxTy, rewriter, charTy.getLen()); 2007 lenParams.emplace_back(len); 2008 } else { 2009 mlir::Value len = getElementSizeFromBox(loc, idxTy, inputBoxTyPair, 2010 loweredBox, rewriter); 2011 if (charTy.getFKind() != 1) { 2012 assert(!isInGlobalOp(rewriter) && 2013 "character target in global op must have constant length"); 2014 mlir::Value width = 2015 genConstantIndex(loc, idxTy, rewriter, charTy.getFKind()); 2016 len = rewriter.create<mlir::LLVM::SDivOp>(loc, idxTy, len, width); 2017 } 2018 lenParams.emplace_back(len); 2019 } 2020 } else if (auto recTy = mlir::dyn_cast<fir::RecordType>(inputEleTy)) { 2021 if (recTy.getNumLenParams() != 0) 2022 TODO(loc, "reboxing descriptor of derived type with length parameters"); 2023 } 2024 2025 // Rebox on polymorphic entities needs to carry over the dynamic type. 2026 mlir::Value typeDescAddr; 2027 if (mlir::isa<fir::ClassType>(inputBoxTyPair.fir) && 2028 mlir::isa<fir::ClassType>(rebox.getType())) 2029 typeDescAddr = 2030 loadTypeDescAddress(loc, inputBoxTyPair, loweredBox, rewriter); 2031 2032 auto [boxTy, dest, eleSize] = 2033 consDescriptorPrefix(rebox, loweredBox, rewriter, rebox.getOutRank(), 2034 adaptor.getSubstr(), lenParams, typeDescAddr); 2035 2036 // Read input extents, strides, and base address 2037 llvm::SmallVector<mlir::Value> inputExtents; 2038 llvm::SmallVector<mlir::Value> inputStrides; 2039 const unsigned inputRank = rebox.getRank(); 2040 for (unsigned dim = 0; dim < inputRank; ++dim) { 2041 llvm::SmallVector<mlir::Value, 3> dimInfo = 2042 getDimsFromBox(loc, {idxTy, idxTy, idxTy}, inputBoxTyPair, loweredBox, 2043 dim, rewriter); 2044 inputExtents.emplace_back(dimInfo[1]); 2045 inputStrides.emplace_back(dimInfo[2]); 2046 } 2047 2048 mlir::Value baseAddr = 2049 getBaseAddrFromBox(loc, inputBoxTyPair, loweredBox, rewriter); 2050 2051 if (!rebox.getSlice().empty() || !rebox.getSubcomponent().empty()) 2052 return sliceBox(rebox, adaptor, boxTy, dest, baseAddr, inputExtents, 2053 inputStrides, operands, rewriter); 2054 return reshapeBox(rebox, adaptor, boxTy, dest, baseAddr, inputExtents, 2055 inputStrides, operands, rewriter); 2056 } 2057 2058 private: 2059 /// Write resulting shape and base address in descriptor, and replace rebox 2060 /// op. 2061 llvm::LogicalResult 2062 finalizeRebox(fir::cg::XReboxOp rebox, OpAdaptor adaptor, 2063 mlir::Type destBoxTy, mlir::Value dest, mlir::Value base, 2064 mlir::ValueRange lbounds, mlir::ValueRange extents, 2065 mlir::ValueRange strides, 2066 mlir::ConversionPatternRewriter &rewriter) const { 2067 mlir::Location loc = rebox.getLoc(); 2068 mlir::Value zero = 2069 genConstantIndex(loc, lowerTy().indexType(), rewriter, 0); 2070 mlir::Value one = genConstantIndex(loc, lowerTy().indexType(), rewriter, 1); 2071 for (auto iter : llvm::enumerate(llvm::zip(extents, strides))) { 2072 mlir::Value extent = std::get<0>(iter.value()); 2073 unsigned dim = iter.index(); 2074 mlir::Value lb = one; 2075 if (!lbounds.empty()) { 2076 lb = lbounds[dim]; 2077 auto extentIsEmpty = rewriter.create<mlir::LLVM::ICmpOp>( 2078 loc, mlir::LLVM::ICmpPredicate::eq, extent, zero); 2079 lb = rewriter.create<mlir::LLVM::SelectOp>(loc, extentIsEmpty, one, lb); 2080 }; 2081 dest = insertLowerBound(rewriter, loc, dest, dim, lb); 2082 dest = insertExtent(rewriter, loc, dest, dim, extent); 2083 dest = insertStride(rewriter, loc, dest, dim, std::get<1>(iter.value())); 2084 } 2085 dest = insertBaseAddress(rewriter, loc, dest, base); 2086 mlir::Value result = placeInMemoryIfNotGlobalInit( 2087 rewriter, rebox.getLoc(), destBoxTy, dest, 2088 isDeviceAllocation(rebox.getBox(), adaptor.getBox())); 2089 rewriter.replaceOp(rebox, result); 2090 return mlir::success(); 2091 } 2092 2093 // Apply slice given the base address, extents and strides of the input box. 2094 llvm::LogicalResult 2095 sliceBox(fir::cg::XReboxOp rebox, OpAdaptor adaptor, mlir::Type destBoxTy, 2096 mlir::Value dest, mlir::Value base, mlir::ValueRange inputExtents, 2097 mlir::ValueRange inputStrides, mlir::ValueRange operands, 2098 mlir::ConversionPatternRewriter &rewriter) const { 2099 mlir::Location loc = rebox.getLoc(); 2100 mlir::Type byteTy = ::getI8Type(rebox.getContext()); 2101 mlir::Type idxTy = lowerTy().indexType(); 2102 mlir::Value zero = genConstantIndex(loc, idxTy, rewriter, 0); 2103 // Apply subcomponent and substring shift on base address. 2104 if (!rebox.getSubcomponent().empty() || !rebox.getSubstr().empty()) { 2105 // Cast to inputEleTy* so that a GEP can be used. 2106 mlir::Type inputEleTy = getInputEleTy(rebox); 2107 mlir::Type llvmBaseObjectType = convertType(inputEleTy); 2108 llvm::SmallVector<mlir::Value> fieldIndices; 2109 std::optional<mlir::Value> substringOffset; 2110 if (!rebox.getSubcomponent().empty()) 2111 getSubcomponentIndices(rebox, rebox.getBox(), operands, fieldIndices); 2112 if (!rebox.getSubstr().empty()) 2113 substringOffset = operands[rebox.getSubstrOperandIndex()]; 2114 base = genBoxOffsetGep(rewriter, loc, base, llvmBaseObjectType, zero, 2115 /*cstInteriorIndices=*/std::nullopt, fieldIndices, 2116 substringOffset); 2117 } 2118 2119 if (rebox.getSlice().empty()) 2120 // The array section is of the form array[%component][substring], keep 2121 // the input array extents and strides. 2122 return finalizeRebox(rebox, adaptor, destBoxTy, dest, base, 2123 /*lbounds*/ std::nullopt, inputExtents, inputStrides, 2124 rewriter); 2125 2126 // The slice is of the form array(i:j:k)[%component]. Compute new extents 2127 // and strides. 2128 llvm::SmallVector<mlir::Value> slicedExtents; 2129 llvm::SmallVector<mlir::Value> slicedStrides; 2130 mlir::Value one = genConstantIndex(loc, idxTy, rewriter, 1); 2131 const bool sliceHasOrigins = !rebox.getShift().empty(); 2132 unsigned sliceOps = rebox.getSliceOperandIndex(); 2133 unsigned shiftOps = rebox.getShiftOperandIndex(); 2134 auto strideOps = inputStrides.begin(); 2135 const unsigned inputRank = inputStrides.size(); 2136 for (unsigned i = 0; i < inputRank; 2137 ++i, ++strideOps, ++shiftOps, sliceOps += 3) { 2138 mlir::Value sliceLb = 2139 integerCast(loc, rewriter, idxTy, operands[sliceOps]); 2140 mlir::Value inputStride = *strideOps; // already idxTy 2141 // Apply origin shift: base += (lb-shift)*input_stride 2142 mlir::Value sliceOrigin = 2143 sliceHasOrigins 2144 ? integerCast(loc, rewriter, idxTy, operands[shiftOps]) 2145 : one; 2146 mlir::Value diff = 2147 rewriter.create<mlir::LLVM::SubOp>(loc, idxTy, sliceLb, sliceOrigin); 2148 mlir::Value offset = 2149 rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, diff, inputStride); 2150 // Strides from the fir.box are in bytes. 2151 base = genGEP(loc, byteTy, rewriter, base, offset); 2152 // Apply upper bound and step if this is a triplet. Otherwise, the 2153 // dimension is dropped and no extents/strides are computed. 2154 mlir::Value upper = operands[sliceOps + 1]; 2155 const bool isTripletSlice = 2156 !mlir::isa_and_nonnull<mlir::LLVM::UndefOp>(upper.getDefiningOp()); 2157 if (isTripletSlice) { 2158 mlir::Value step = 2159 integerCast(loc, rewriter, idxTy, operands[sliceOps + 2]); 2160 // extent = ub-lb+step/step 2161 mlir::Value sliceUb = integerCast(loc, rewriter, idxTy, upper); 2162 mlir::Value extent = computeTripletExtent(rewriter, loc, sliceLb, 2163 sliceUb, step, zero, idxTy); 2164 slicedExtents.emplace_back(extent); 2165 // stride = step*input_stride 2166 mlir::Value stride = 2167 rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, step, inputStride); 2168 slicedStrides.emplace_back(stride); 2169 } 2170 } 2171 return finalizeRebox(rebox, adaptor, destBoxTy, dest, base, 2172 /*lbounds*/ std::nullopt, slicedExtents, slicedStrides, 2173 rewriter); 2174 } 2175 2176 /// Apply a new shape to the data described by a box given the base address, 2177 /// extents and strides of the box. 2178 llvm::LogicalResult 2179 reshapeBox(fir::cg::XReboxOp rebox, OpAdaptor adaptor, mlir::Type destBoxTy, 2180 mlir::Value dest, mlir::Value base, mlir::ValueRange inputExtents, 2181 mlir::ValueRange inputStrides, mlir::ValueRange operands, 2182 mlir::ConversionPatternRewriter &rewriter) const { 2183 mlir::ValueRange reboxShifts{ 2184 operands.begin() + rebox.getShiftOperandIndex(), 2185 operands.begin() + rebox.getShiftOperandIndex() + 2186 rebox.getShift().size()}; 2187 if (rebox.getShape().empty()) { 2188 // Only setting new lower bounds. 2189 return finalizeRebox(rebox, adaptor, destBoxTy, dest, base, reboxShifts, 2190 inputExtents, inputStrides, rewriter); 2191 } 2192 2193 mlir::Location loc = rebox.getLoc(); 2194 2195 llvm::SmallVector<mlir::Value> newStrides; 2196 llvm::SmallVector<mlir::Value> newExtents; 2197 mlir::Type idxTy = lowerTy().indexType(); 2198 // First stride from input box is kept. The rest is assumed contiguous 2199 // (it is not possible to reshape otherwise). If the input is scalar, 2200 // which may be OK if all new extents are ones, the stride does not 2201 // matter, use one. 2202 mlir::Value stride = inputStrides.empty() 2203 ? genConstantIndex(loc, idxTy, rewriter, 1) 2204 : inputStrides[0]; 2205 for (unsigned i = 0; i < rebox.getShape().size(); ++i) { 2206 mlir::Value rawExtent = operands[rebox.getShapeOperandIndex() + i]; 2207 mlir::Value extent = integerCast(loc, rewriter, idxTy, rawExtent); 2208 newExtents.emplace_back(extent); 2209 newStrides.emplace_back(stride); 2210 // nextStride = extent * stride; 2211 stride = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, extent, stride); 2212 } 2213 return finalizeRebox(rebox, adaptor, destBoxTy, dest, base, reboxShifts, 2214 newExtents, newStrides, rewriter); 2215 } 2216 2217 /// Return scalar element type of the input box. 2218 static mlir::Type getInputEleTy(fir::cg::XReboxOp rebox) { 2219 auto ty = fir::dyn_cast_ptrOrBoxEleTy(rebox.getBox().getType()); 2220 if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(ty)) 2221 return seqTy.getEleTy(); 2222 return ty; 2223 } 2224 }; 2225 2226 /// Lower `fir.emboxproc` operation. Creates a procedure box. 2227 /// TODO: Part of supporting Fortran 2003 procedure pointers. 2228 struct EmboxProcOpConversion : public fir::FIROpConversion<fir::EmboxProcOp> { 2229 using FIROpConversion::FIROpConversion; 2230 2231 llvm::LogicalResult 2232 matchAndRewrite(fir::EmboxProcOp emboxproc, OpAdaptor adaptor, 2233 mlir::ConversionPatternRewriter &rewriter) const override { 2234 TODO(emboxproc.getLoc(), "fir.emboxproc codegen"); 2235 return mlir::failure(); 2236 } 2237 }; 2238 2239 // Code shared between insert_value and extract_value Ops. 2240 struct ValueOpCommon { 2241 // Translate the arguments pertaining to any multidimensional array to 2242 // row-major order for LLVM-IR. 2243 static void toRowMajor(llvm::SmallVectorImpl<int64_t> &indices, 2244 mlir::Type ty) { 2245 assert(ty && "type is null"); 2246 const auto end = indices.size(); 2247 for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) { 2248 if (auto seq = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(ty)) { 2249 const auto dim = getDimension(seq); 2250 if (dim > 1) { 2251 auto ub = std::min(i + dim, end); 2252 std::reverse(indices.begin() + i, indices.begin() + ub); 2253 i += dim - 1; 2254 } 2255 ty = getArrayElementType(seq); 2256 } else if (auto st = mlir::dyn_cast<mlir::LLVM::LLVMStructType>(ty)) { 2257 ty = st.getBody()[indices[i]]; 2258 } else { 2259 llvm_unreachable("index into invalid type"); 2260 } 2261 } 2262 } 2263 2264 static llvm::SmallVector<int64_t> 2265 collectIndices(mlir::ConversionPatternRewriter &rewriter, 2266 mlir::ArrayAttr arrAttr) { 2267 llvm::SmallVector<int64_t> indices; 2268 for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) { 2269 if (auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>(*i)) { 2270 indices.push_back(intAttr.getInt()); 2271 } else { 2272 auto fieldName = mlir::cast<mlir::StringAttr>(*i).getValue(); 2273 ++i; 2274 auto ty = mlir::cast<mlir::TypeAttr>(*i).getValue(); 2275 auto index = mlir::cast<fir::RecordType>(ty).getFieldIndex(fieldName); 2276 indices.push_back(index); 2277 } 2278 } 2279 return indices; 2280 } 2281 2282 private: 2283 static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) { 2284 auto eleTy = ty.getElementType(); 2285 while (auto arrTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(eleTy)) 2286 eleTy = arrTy.getElementType(); 2287 return eleTy; 2288 } 2289 }; 2290 2291 namespace { 2292 /// Extract a subobject value from an ssa-value of aggregate type 2293 struct ExtractValueOpConversion 2294 : public fir::FIROpAndTypeConversion<fir::ExtractValueOp>, 2295 public ValueOpCommon { 2296 using FIROpAndTypeConversion::FIROpAndTypeConversion; 2297 2298 llvm::LogicalResult 2299 doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor, 2300 mlir::ConversionPatternRewriter &rewriter) const override { 2301 mlir::ValueRange operands = adaptor.getOperands(); 2302 auto indices = collectIndices(rewriter, extractVal.getCoor()); 2303 toRowMajor(indices, operands[0].getType()); 2304 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>( 2305 extractVal, operands[0], indices); 2306 return mlir::success(); 2307 } 2308 }; 2309 2310 /// InsertValue is the generalized instruction for the composition of new 2311 /// aggregate type values. 2312 struct InsertValueOpConversion 2313 : public mlir::OpConversionPattern<fir::InsertValueOp>, 2314 public ValueOpCommon { 2315 using OpConversionPattern::OpConversionPattern; 2316 2317 llvm::LogicalResult 2318 matchAndRewrite(fir::InsertValueOp insertVal, OpAdaptor adaptor, 2319 mlir::ConversionPatternRewriter &rewriter) const override { 2320 mlir::ValueRange operands = adaptor.getOperands(); 2321 auto indices = collectIndices(rewriter, insertVal.getCoor()); 2322 toRowMajor(indices, operands[0].getType()); 2323 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 2324 insertVal, operands[0], operands[1], indices); 2325 return mlir::success(); 2326 } 2327 }; 2328 2329 /// InsertOnRange inserts a value into a sequence over a range of offsets. 2330 struct InsertOnRangeOpConversion 2331 : public fir::FIROpAndTypeConversion<fir::InsertOnRangeOp> { 2332 using FIROpAndTypeConversion::FIROpAndTypeConversion; 2333 2334 // Increments an array of subscripts in a row major fasion. 2335 void incrementSubscripts(llvm::ArrayRef<int64_t> dims, 2336 llvm::SmallVectorImpl<int64_t> &subscripts) const { 2337 for (size_t i = dims.size(); i > 0; --i) { 2338 if (++subscripts[i - 1] < dims[i - 1]) { 2339 return; 2340 } 2341 subscripts[i - 1] = 0; 2342 } 2343 } 2344 2345 llvm::LogicalResult 2346 doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor, 2347 mlir::ConversionPatternRewriter &rewriter) const override { 2348 2349 llvm::SmallVector<std::int64_t> dims; 2350 auto type = adaptor.getOperands()[0].getType(); 2351 2352 // Iteratively extract the array dimensions from the type. 2353 while (auto t = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(type)) { 2354 dims.push_back(t.getNumElements()); 2355 type = t.getElementType(); 2356 } 2357 2358 llvm::SmallVector<std::int64_t> lBounds; 2359 llvm::SmallVector<std::int64_t> uBounds; 2360 2361 // Unzip the upper and lower bound and convert to a row major format. 2362 mlir::DenseIntElementsAttr coor = range.getCoor(); 2363 auto reversedCoor = llvm::reverse(coor.getValues<int64_t>()); 2364 for (auto i = reversedCoor.begin(), e = reversedCoor.end(); i != e; ++i) { 2365 uBounds.push_back(*i++); 2366 lBounds.push_back(*i); 2367 } 2368 2369 auto &subscripts = lBounds; 2370 auto loc = range.getLoc(); 2371 mlir::Value lastOp = adaptor.getOperands()[0]; 2372 mlir::Value insertVal = adaptor.getOperands()[1]; 2373 2374 while (subscripts != uBounds) { 2375 lastOp = rewriter.create<mlir::LLVM::InsertValueOp>( 2376 loc, lastOp, insertVal, subscripts); 2377 2378 incrementSubscripts(dims, subscripts); 2379 } 2380 2381 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 2382 range, lastOp, insertVal, subscripts); 2383 2384 return mlir::success(); 2385 } 2386 }; 2387 } // namespace 2388 2389 namespace { 2390 /// XArrayCoor is the address arithmetic on a dynamically shaped, sliced, 2391 /// shifted etc. array. 2392 /// (See the static restriction on coordinate_of.) array_coor determines the 2393 /// coordinate (location) of a specific element. 2394 struct XArrayCoorOpConversion 2395 : public fir::FIROpAndTypeConversion<fir::cg::XArrayCoorOp> { 2396 using FIROpAndTypeConversion::FIROpAndTypeConversion; 2397 2398 llvm::LogicalResult 2399 doRewrite(fir::cg::XArrayCoorOp coor, mlir::Type llvmPtrTy, OpAdaptor adaptor, 2400 mlir::ConversionPatternRewriter &rewriter) const override { 2401 auto loc = coor.getLoc(); 2402 mlir::ValueRange operands = adaptor.getOperands(); 2403 unsigned rank = coor.getRank(); 2404 assert(coor.getIndices().size() == rank); 2405 assert(coor.getShape().empty() || coor.getShape().size() == rank); 2406 assert(coor.getShift().empty() || coor.getShift().size() == rank); 2407 assert(coor.getSlice().empty() || coor.getSlice().size() == 3 * rank); 2408 mlir::Type idxTy = lowerTy().indexType(); 2409 unsigned indexOffset = coor.getIndicesOperandIndex(); 2410 unsigned shapeOffset = coor.getShapeOperandIndex(); 2411 unsigned shiftOffset = coor.getShiftOperandIndex(); 2412 unsigned sliceOffset = coor.getSliceOperandIndex(); 2413 auto sliceOps = coor.getSlice().begin(); 2414 mlir::Value one = genConstantIndex(loc, idxTy, rewriter, 1); 2415 mlir::Value prevExt = one; 2416 mlir::Value offset = genConstantIndex(loc, idxTy, rewriter, 0); 2417 const bool isShifted = !coor.getShift().empty(); 2418 const bool isSliced = !coor.getSlice().empty(); 2419 const bool baseIsBoxed = 2420 mlir::isa<fir::BaseBoxType>(coor.getMemref().getType()); 2421 TypePair baseBoxTyPair = 2422 baseIsBoxed ? getBoxTypePair(coor.getMemref().getType()) : TypePair{}; 2423 mlir::LLVM::IntegerOverflowFlags nsw = 2424 mlir::LLVM::IntegerOverflowFlags::nsw; 2425 2426 // For each dimension of the array, generate the offset calculation. 2427 for (unsigned i = 0; i < rank; ++i, ++indexOffset, ++shapeOffset, 2428 ++shiftOffset, sliceOffset += 3, sliceOps += 3) { 2429 mlir::Value index = 2430 integerCast(loc, rewriter, idxTy, operands[indexOffset]); 2431 mlir::Value lb = 2432 isShifted ? integerCast(loc, rewriter, idxTy, operands[shiftOffset]) 2433 : one; 2434 mlir::Value step = one; 2435 bool normalSlice = isSliced; 2436 // Compute zero based index in dimension i of the element, applying 2437 // potential triplets and lower bounds. 2438 if (isSliced) { 2439 mlir::Value originalUb = *(sliceOps + 1); 2440 normalSlice = 2441 !mlir::isa_and_nonnull<fir::UndefOp>(originalUb.getDefiningOp()); 2442 if (normalSlice) 2443 step = integerCast(loc, rewriter, idxTy, operands[sliceOffset + 2]); 2444 } 2445 auto idx = rewriter.create<mlir::LLVM::SubOp>(loc, idxTy, index, lb, nsw); 2446 mlir::Value diff = 2447 rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, idx, step, nsw); 2448 if (normalSlice) { 2449 mlir::Value sliceLb = 2450 integerCast(loc, rewriter, idxTy, operands[sliceOffset]); 2451 auto adj = 2452 rewriter.create<mlir::LLVM::SubOp>(loc, idxTy, sliceLb, lb, nsw); 2453 diff = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, diff, adj, nsw); 2454 } 2455 // Update the offset given the stride and the zero based index `diff` 2456 // that was just computed. 2457 if (baseIsBoxed) { 2458 // Use stride in bytes from the descriptor. 2459 mlir::Value stride = 2460 getStrideFromBox(loc, baseBoxTyPair, operands[0], i, rewriter); 2461 auto sc = 2462 rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, diff, stride, nsw); 2463 offset = 2464 rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, offset, nsw); 2465 } else { 2466 // Use stride computed at last iteration. 2467 auto sc = 2468 rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, diff, prevExt, nsw); 2469 offset = 2470 rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, offset, nsw); 2471 // Compute next stride assuming contiguity of the base array 2472 // (in element number). 2473 auto nextExt = integerCast(loc, rewriter, idxTy, operands[shapeOffset]); 2474 prevExt = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, prevExt, 2475 nextExt, nsw); 2476 } 2477 } 2478 2479 // Add computed offset to the base address. 2480 if (baseIsBoxed) { 2481 // Working with byte offsets. The base address is read from the fir.box. 2482 // and used in i8* GEP to do the pointer arithmetic. 2483 mlir::Type byteTy = ::getI8Type(coor.getContext()); 2484 mlir::Value base = 2485 getBaseAddrFromBox(loc, baseBoxTyPair, operands[0], rewriter); 2486 llvm::SmallVector<mlir::LLVM::GEPArg> args{offset}; 2487 auto addr = rewriter.create<mlir::LLVM::GEPOp>(loc, llvmPtrTy, byteTy, 2488 base, args); 2489 if (coor.getSubcomponent().empty()) { 2490 rewriter.replaceOp(coor, addr); 2491 return mlir::success(); 2492 } 2493 // Cast the element address from void* to the derived type so that the 2494 // derived type members can be addresses via a GEP using the index of 2495 // components. 2496 mlir::Type elementType = 2497 getLlvmObjectTypeFromBoxType(coor.getMemref().getType()); 2498 while (auto arrayTy = 2499 mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(elementType)) 2500 elementType = arrayTy.getElementType(); 2501 args.clear(); 2502 args.push_back(0); 2503 if (!coor.getLenParams().empty()) { 2504 // If type parameters are present, then we don't want to use a GEPOp 2505 // as below, as the LLVM struct type cannot be statically defined. 2506 TODO(loc, "derived type with type parameters"); 2507 } 2508 llvm::SmallVector<mlir::Value> indices = convertSubcomponentIndices( 2509 loc, elementType, 2510 operands.slice(coor.getSubcomponentOperandIndex(), 2511 coor.getSubcomponent().size())); 2512 args.append(indices.begin(), indices.end()); 2513 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(coor, llvmPtrTy, 2514 elementType, addr, args); 2515 return mlir::success(); 2516 } 2517 2518 // The array was not boxed, so it must be contiguous. offset is therefore an 2519 // element offset and the base type is kept in the GEP unless the element 2520 // type size is itself dynamic. 2521 mlir::Type objectTy = fir::unwrapRefType(coor.getMemref().getType()); 2522 mlir::Type eleType = fir::unwrapSequenceType(objectTy); 2523 mlir::Type gepObjectType = convertType(eleType); 2524 llvm::SmallVector<mlir::LLVM::GEPArg> args; 2525 if (coor.getSubcomponent().empty()) { 2526 // No subcomponent. 2527 if (!coor.getLenParams().empty()) { 2528 // Type parameters. Adjust element size explicitly. 2529 auto eleTy = fir::dyn_cast_ptrEleTy(coor.getType()); 2530 assert(eleTy && "result must be a reference-like type"); 2531 if (fir::characterWithDynamicLen(eleTy)) { 2532 assert(coor.getLenParams().size() == 1); 2533 auto length = integerCast(loc, rewriter, idxTy, 2534 operands[coor.getLenParamsOperandIndex()]); 2535 offset = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, offset, 2536 length, nsw); 2537 } else { 2538 TODO(loc, "compute size of derived type with type parameters"); 2539 } 2540 } 2541 args.push_back(offset); 2542 } else { 2543 // There are subcomponents. 2544 args.push_back(offset); 2545 llvm::SmallVector<mlir::Value> indices = convertSubcomponentIndices( 2546 loc, gepObjectType, 2547 operands.slice(coor.getSubcomponentOperandIndex(), 2548 coor.getSubcomponent().size())); 2549 args.append(indices.begin(), indices.end()); 2550 } 2551 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>( 2552 coor, llvmPtrTy, gepObjectType, adaptor.getMemref(), args); 2553 return mlir::success(); 2554 } 2555 }; 2556 } // namespace 2557 2558 /// Convert to (memory) reference to a reference to a subobject. 2559 /// The coordinate_of op is a Swiss army knife operation that can be used on 2560 /// (memory) references to records, arrays, complex, etc. as well as boxes. 2561 /// With unboxed arrays, there is the restriction that the array have a static 2562 /// shape in all but the last column. 2563 struct CoordinateOpConversion 2564 : public fir::FIROpAndTypeConversion<fir::CoordinateOp> { 2565 using FIROpAndTypeConversion::FIROpAndTypeConversion; 2566 2567 llvm::LogicalResult 2568 doRewrite(fir::CoordinateOp coor, mlir::Type ty, OpAdaptor adaptor, 2569 mlir::ConversionPatternRewriter &rewriter) const override { 2570 mlir::ValueRange operands = adaptor.getOperands(); 2571 2572 mlir::Location loc = coor.getLoc(); 2573 mlir::Value base = operands[0]; 2574 mlir::Type baseObjectTy = coor.getBaseType(); 2575 mlir::Type objectTy = fir::dyn_cast_ptrOrBoxEleTy(baseObjectTy); 2576 assert(objectTy && "fir.coordinate_of expects a reference type"); 2577 mlir::Type llvmObjectTy = convertType(objectTy); 2578 2579 // Complex type - basically, extract the real or imaginary part 2580 // FIXME: double check why this is done before the fir.box case below. 2581 if (fir::isa_complex(objectTy)) { 2582 mlir::Value gep = 2583 genGEP(loc, llvmObjectTy, rewriter, base, 0, operands[1]); 2584 rewriter.replaceOp(coor, gep); 2585 return mlir::success(); 2586 } 2587 2588 // Boxed type - get the base pointer from the box 2589 if (mlir::dyn_cast<fir::BaseBoxType>(baseObjectTy)) 2590 return doRewriteBox(coor, operands, loc, rewriter); 2591 2592 // Reference, pointer or a heap type 2593 if (mlir::isa<fir::ReferenceType, fir::PointerType, fir::HeapType>( 2594 baseObjectTy)) 2595 return doRewriteRefOrPtr(coor, llvmObjectTy, operands, loc, rewriter); 2596 2597 return rewriter.notifyMatchFailure( 2598 coor, "fir.coordinate_of base operand has unsupported type"); 2599 } 2600 2601 static unsigned getFieldNumber(fir::RecordType ty, mlir::Value op) { 2602 return fir::hasDynamicSize(ty) 2603 ? op.getDefiningOp() 2604 ->getAttrOfType<mlir::IntegerAttr>("field") 2605 .getInt() 2606 : getConstantIntValue(op); 2607 } 2608 2609 static bool hasSubDimensions(mlir::Type type) { 2610 return mlir::isa<fir::SequenceType, fir::RecordType, mlir::TupleType>(type); 2611 } 2612 2613 /// Check whether this form of `!fir.coordinate_of` is supported. These 2614 /// additional checks are required, because we are not yet able to convert 2615 /// all valid forms of `!fir.coordinate_of`. 2616 /// TODO: Either implement the unsupported cases or extend the verifier 2617 /// in FIROps.cpp instead. 2618 static bool supportedCoordinate(mlir::Type type, mlir::ValueRange coors) { 2619 const std::size_t numOfCoors = coors.size(); 2620 std::size_t i = 0; 2621 bool subEle = false; 2622 bool ptrEle = false; 2623 for (; i < numOfCoors; ++i) { 2624 mlir::Value nxtOpnd = coors[i]; 2625 if (auto arrTy = mlir::dyn_cast<fir::SequenceType>(type)) { 2626 subEle = true; 2627 i += arrTy.getDimension() - 1; 2628 type = arrTy.getEleTy(); 2629 } else if (auto recTy = mlir::dyn_cast<fir::RecordType>(type)) { 2630 subEle = true; 2631 type = recTy.getType(getFieldNumber(recTy, nxtOpnd)); 2632 } else if (auto tupTy = mlir::dyn_cast<mlir::TupleType>(type)) { 2633 subEle = true; 2634 type = tupTy.getType(getConstantIntValue(nxtOpnd)); 2635 } else { 2636 ptrEle = true; 2637 } 2638 } 2639 if (ptrEle) 2640 return (!subEle) && (numOfCoors == 1); 2641 return subEle && (i >= numOfCoors); 2642 } 2643 2644 /// Walk the abstract memory layout and determine if the path traverses any 2645 /// array types with unknown shape. Return true iff all the array types have a 2646 /// constant shape along the path. 2647 static bool arraysHaveKnownShape(mlir::Type type, mlir::ValueRange coors) { 2648 for (std::size_t i = 0, sz = coors.size(); i < sz; ++i) { 2649 mlir::Value nxtOpnd = coors[i]; 2650 if (auto arrTy = mlir::dyn_cast<fir::SequenceType>(type)) { 2651 if (fir::sequenceWithNonConstantShape(arrTy)) 2652 return false; 2653 i += arrTy.getDimension() - 1; 2654 type = arrTy.getEleTy(); 2655 } else if (auto strTy = mlir::dyn_cast<fir::RecordType>(type)) { 2656 type = strTy.getType(getFieldNumber(strTy, nxtOpnd)); 2657 } else if (auto strTy = mlir::dyn_cast<mlir::TupleType>(type)) { 2658 type = strTy.getType(getConstantIntValue(nxtOpnd)); 2659 } else { 2660 return true; 2661 } 2662 } 2663 return true; 2664 } 2665 2666 private: 2667 llvm::LogicalResult 2668 doRewriteBox(fir::CoordinateOp coor, mlir::ValueRange operands, 2669 mlir::Location loc, 2670 mlir::ConversionPatternRewriter &rewriter) const { 2671 mlir::Type boxObjTy = coor.getBaseType(); 2672 assert(mlir::dyn_cast<fir::BaseBoxType>(boxObjTy) && 2673 "This is not a `fir.box`"); 2674 TypePair boxTyPair = getBoxTypePair(boxObjTy); 2675 2676 mlir::Value boxBaseAddr = operands[0]; 2677 2678 // 1. SPECIAL CASE (uses `fir.len_param_index`): 2679 // %box = ... : !fir.box<!fir.type<derived{len1:i32}>> 2680 // %lenp = fir.len_param_index len1, !fir.type<derived{len1:i32}> 2681 // %addr = coordinate_of %box, %lenp 2682 if (coor.getNumOperands() == 2) { 2683 mlir::Operation *coordinateDef = 2684 (*coor.getCoor().begin()).getDefiningOp(); 2685 if (mlir::isa_and_nonnull<fir::LenParamIndexOp>(coordinateDef)) 2686 TODO(loc, 2687 "fir.coordinate_of - fir.len_param_index is not supported yet"); 2688 } 2689 2690 // 2. GENERAL CASE: 2691 // 2.1. (`fir.array`) 2692 // %box = ... : !fix.box<!fir.array<?xU>> 2693 // %idx = ... : index 2694 // %resultAddr = coordinate_of %box, %idx : !fir.ref<U> 2695 // 2.2 (`fir.derived`) 2696 // %box = ... : !fix.box<!fir.type<derived_type{field_1:i32}>> 2697 // %idx = ... : i32 2698 // %resultAddr = coordinate_of %box, %idx : !fir.ref<i32> 2699 // 2.3 (`fir.derived` inside `fir.array`) 2700 // %box = ... : !fir.box<!fir.array<10 x !fir.type<derived_1{field_1:f32, 2701 // field_2:f32}>>> %idx1 = ... : index %idx2 = ... : i32 %resultAddr = 2702 // coordinate_of %box, %idx1, %idx2 : !fir.ref<f32> 2703 // 2.4. TODO: Either document or disable any other case that the following 2704 // implementation might convert. 2705 mlir::Value resultAddr = 2706 getBaseAddrFromBox(loc, boxTyPair, boxBaseAddr, rewriter); 2707 // Component Type 2708 auto cpnTy = fir::dyn_cast_ptrOrBoxEleTy(boxObjTy); 2709 mlir::Type llvmPtrTy = ::getLlvmPtrType(coor.getContext()); 2710 mlir::Type byteTy = ::getI8Type(coor.getContext()); 2711 mlir::LLVM::IntegerOverflowFlags nsw = 2712 mlir::LLVM::IntegerOverflowFlags::nsw; 2713 2714 for (unsigned i = 1, last = operands.size(); i < last; ++i) { 2715 if (auto arrTy = mlir::dyn_cast<fir::SequenceType>(cpnTy)) { 2716 if (i != 1) 2717 TODO(loc, "fir.array nested inside other array and/or derived type"); 2718 // Applies byte strides from the box. Ignore lower bound from box 2719 // since fir.coordinate_of indexes are zero based. Lowering takes care 2720 // of lower bound aspects. This both accounts for dynamically sized 2721 // types and non contiguous arrays. 2722 auto idxTy = lowerTy().indexType(); 2723 mlir::Value off = genConstantIndex(loc, idxTy, rewriter, 0); 2724 for (unsigned index = i, lastIndex = i + arrTy.getDimension(); 2725 index < lastIndex; ++index) { 2726 mlir::Value stride = getStrideFromBox(loc, boxTyPair, operands[0], 2727 index - i, rewriter); 2728 auto sc = rewriter.create<mlir::LLVM::MulOp>( 2729 loc, idxTy, operands[index], stride, nsw); 2730 off = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, off, nsw); 2731 } 2732 resultAddr = rewriter.create<mlir::LLVM::GEPOp>( 2733 loc, llvmPtrTy, byteTy, resultAddr, 2734 llvm::ArrayRef<mlir::LLVM::GEPArg>{off}); 2735 i += arrTy.getDimension() - 1; 2736 cpnTy = arrTy.getEleTy(); 2737 } else if (auto recTy = mlir::dyn_cast<fir::RecordType>(cpnTy)) { 2738 mlir::Value nxtOpnd = operands[i]; 2739 cpnTy = recTy.getType(getFieldNumber(recTy, nxtOpnd)); 2740 auto llvmRecTy = lowerTy().convertType(recTy); 2741 resultAddr = rewriter.create<mlir::LLVM::GEPOp>( 2742 loc, llvmPtrTy, llvmRecTy, resultAddr, 2743 llvm::ArrayRef<mlir::LLVM::GEPArg>{0, nxtOpnd}); 2744 } else { 2745 fir::emitFatalError(loc, "unexpected type in coordinate_of"); 2746 } 2747 } 2748 2749 rewriter.replaceOp(coor, resultAddr); 2750 return mlir::success(); 2751 } 2752 2753 llvm::LogicalResult 2754 doRewriteRefOrPtr(fir::CoordinateOp coor, mlir::Type llvmObjectTy, 2755 mlir::ValueRange operands, mlir::Location loc, 2756 mlir::ConversionPatternRewriter &rewriter) const { 2757 mlir::Type baseObjectTy = coor.getBaseType(); 2758 2759 // Component Type 2760 mlir::Type cpnTy = fir::dyn_cast_ptrOrBoxEleTy(baseObjectTy); 2761 bool hasSubdimension = hasSubDimensions(cpnTy); 2762 bool columnIsDeferred = !hasSubdimension; 2763 2764 if (!supportedCoordinate(cpnTy, operands.drop_front(1))) 2765 TODO(loc, "unsupported combination of coordinate operands"); 2766 2767 const bool hasKnownShape = 2768 arraysHaveKnownShape(cpnTy, operands.drop_front(1)); 2769 2770 // If only the column is `?`, then we can simply place the column value in 2771 // the 0-th GEP position. 2772 if (auto arrTy = mlir::dyn_cast<fir::SequenceType>(cpnTy)) { 2773 if (!hasKnownShape) { 2774 const unsigned sz = arrTy.getDimension(); 2775 if (arraysHaveKnownShape(arrTy.getEleTy(), 2776 operands.drop_front(1 + sz))) { 2777 fir::SequenceType::ShapeRef shape = arrTy.getShape(); 2778 bool allConst = true; 2779 for (unsigned i = 0; i < sz - 1; ++i) { 2780 if (shape[i] < 0) { 2781 allConst = false; 2782 break; 2783 } 2784 } 2785 if (allConst) 2786 columnIsDeferred = true; 2787 } 2788 } 2789 } 2790 2791 if (fir::hasDynamicSize(fir::unwrapSequenceType(cpnTy))) 2792 return mlir::emitError( 2793 loc, "fir.coordinate_of with a dynamic element size is unsupported"); 2794 2795 if (hasKnownShape || columnIsDeferred) { 2796 llvm::SmallVector<mlir::LLVM::GEPArg> offs; 2797 if (hasKnownShape && hasSubdimension) { 2798 offs.push_back(0); 2799 } 2800 std::optional<int> dims; 2801 llvm::SmallVector<mlir::Value> arrIdx; 2802 for (std::size_t i = 1, sz = operands.size(); i < sz; ++i) { 2803 mlir::Value nxtOpnd = operands[i]; 2804 2805 if (!cpnTy) 2806 return mlir::emitError(loc, "invalid coordinate/check failed"); 2807 2808 // check if the i-th coordinate relates to an array 2809 if (dims) { 2810 arrIdx.push_back(nxtOpnd); 2811 int dimsLeft = *dims; 2812 if (dimsLeft > 1) { 2813 dims = dimsLeft - 1; 2814 continue; 2815 } 2816 cpnTy = mlir::cast<fir::SequenceType>(cpnTy).getElementType(); 2817 // append array range in reverse (FIR arrays are column-major) 2818 offs.append(arrIdx.rbegin(), arrIdx.rend()); 2819 arrIdx.clear(); 2820 dims.reset(); 2821 continue; 2822 } 2823 if (auto arrTy = mlir::dyn_cast<fir::SequenceType>(cpnTy)) { 2824 int d = arrTy.getDimension() - 1; 2825 if (d > 0) { 2826 dims = d; 2827 arrIdx.push_back(nxtOpnd); 2828 continue; 2829 } 2830 cpnTy = mlir::cast<fir::SequenceType>(cpnTy).getElementType(); 2831 offs.push_back(nxtOpnd); 2832 continue; 2833 } 2834 2835 // check if the i-th coordinate relates to a field 2836 if (auto recTy = mlir::dyn_cast<fir::RecordType>(cpnTy)) 2837 cpnTy = recTy.getType(getFieldNumber(recTy, nxtOpnd)); 2838 else if (auto tupTy = mlir::dyn_cast<mlir::TupleType>(cpnTy)) 2839 cpnTy = tupTy.getType(getConstantIntValue(nxtOpnd)); 2840 else 2841 cpnTy = nullptr; 2842 2843 offs.push_back(nxtOpnd); 2844 } 2845 if (dims) 2846 offs.append(arrIdx.rbegin(), arrIdx.rend()); 2847 mlir::Value base = operands[0]; 2848 mlir::Value retval = genGEP(loc, llvmObjectTy, rewriter, base, offs); 2849 rewriter.replaceOp(coor, retval); 2850 return mlir::success(); 2851 } 2852 2853 return mlir::emitError( 2854 loc, "fir.coordinate_of base operand has unsupported type"); 2855 } 2856 }; 2857 2858 /// Convert `fir.field_index`. The conversion depends on whether the size of 2859 /// the record is static or dynamic. 2860 struct FieldIndexOpConversion : public fir::FIROpConversion<fir::FieldIndexOp> { 2861 using FIROpConversion::FIROpConversion; 2862 2863 // NB: most field references should be resolved by this point 2864 llvm::LogicalResult 2865 matchAndRewrite(fir::FieldIndexOp field, OpAdaptor adaptor, 2866 mlir::ConversionPatternRewriter &rewriter) const override { 2867 auto recTy = mlir::cast<fir::RecordType>(field.getOnType()); 2868 unsigned index = recTy.getFieldIndex(field.getFieldId()); 2869 2870 if (!fir::hasDynamicSize(recTy)) { 2871 // Derived type has compile-time constant layout. Return index of the 2872 // component type in the parent type (to be used in GEP). 2873 rewriter.replaceOp(field, mlir::ValueRange{genConstantOffset( 2874 field.getLoc(), rewriter, index)}); 2875 return mlir::success(); 2876 } 2877 2878 // Derived type has compile-time constant layout. Call the compiler 2879 // generated function to determine the byte offset of the field at runtime. 2880 // This returns a non-constant. 2881 mlir::FlatSymbolRefAttr symAttr = mlir::SymbolRefAttr::get( 2882 field.getContext(), getOffsetMethodName(recTy, field.getFieldId())); 2883 mlir::NamedAttribute callAttr = rewriter.getNamedAttr("callee", symAttr); 2884 mlir::NamedAttribute fieldAttr = rewriter.getNamedAttr( 2885 "field", mlir::IntegerAttr::get(lowerTy().indexType(), index)); 2886 rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>( 2887 field, lowerTy().offsetType(), adaptor.getOperands(), 2888 addLLVMOpBundleAttrs(rewriter, {callAttr, fieldAttr}, 2889 adaptor.getOperands().size())); 2890 return mlir::success(); 2891 } 2892 2893 // Re-Construct the name of the compiler generated method that calculates the 2894 // offset 2895 inline static std::string getOffsetMethodName(fir::RecordType recTy, 2896 llvm::StringRef field) { 2897 return recTy.getName().str() + "P." + field.str() + ".offset"; 2898 } 2899 }; 2900 2901 /// Convert `fir.end` 2902 struct FirEndOpConversion : public fir::FIROpConversion<fir::FirEndOp> { 2903 using FIROpConversion::FIROpConversion; 2904 2905 llvm::LogicalResult 2906 matchAndRewrite(fir::FirEndOp firEnd, OpAdaptor, 2907 mlir::ConversionPatternRewriter &rewriter) const override { 2908 TODO(firEnd.getLoc(), "fir.end codegen"); 2909 return mlir::failure(); 2910 } 2911 }; 2912 2913 /// Lower `fir.type_desc` to a global addr. 2914 struct TypeDescOpConversion : public fir::FIROpConversion<fir::TypeDescOp> { 2915 using FIROpConversion::FIROpConversion; 2916 2917 llvm::LogicalResult 2918 matchAndRewrite(fir::TypeDescOp typeDescOp, OpAdaptor adaptor, 2919 mlir::ConversionPatternRewriter &rewriter) const override { 2920 mlir::Type inTy = typeDescOp.getInType(); 2921 assert(mlir::isa<fir::RecordType>(inTy) && "expecting fir.type"); 2922 auto recordType = mlir::dyn_cast<fir::RecordType>(inTy); 2923 auto module = typeDescOp.getOperation()->getParentOfType<mlir::ModuleOp>(); 2924 std::string typeDescName = 2925 this->options.typeDescriptorsRenamedForAssembly 2926 ? fir::NameUniquer::getTypeDescriptorAssemblyName( 2927 recordType.getName()) 2928 : fir::NameUniquer::getTypeDescriptorName(recordType.getName()); 2929 auto llvmPtrTy = ::getLlvmPtrType(typeDescOp.getContext()); 2930 if (auto global = module.lookupSymbol<mlir::LLVM::GlobalOp>(typeDescName)) { 2931 rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>( 2932 typeDescOp, llvmPtrTy, global.getSymName()); 2933 return mlir::success(); 2934 } else if (auto global = module.lookupSymbol<fir::GlobalOp>(typeDescName)) { 2935 rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>( 2936 typeDescOp, llvmPtrTy, global.getSymName()); 2937 return mlir::success(); 2938 } 2939 return mlir::failure(); 2940 } 2941 }; 2942 2943 /// Lower `fir.has_value` operation to `llvm.return` operation. 2944 struct HasValueOpConversion 2945 : public mlir::OpConversionPattern<fir::HasValueOp> { 2946 using OpConversionPattern::OpConversionPattern; 2947 2948 llvm::LogicalResult 2949 matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor, 2950 mlir::ConversionPatternRewriter &rewriter) const override { 2951 rewriter.replaceOpWithNewOp<mlir::LLVM::ReturnOp>(op, 2952 adaptor.getOperands()); 2953 return mlir::success(); 2954 } 2955 }; 2956 2957 #ifndef NDEBUG 2958 // Check if attr's type is compatible with ty. 2959 // 2960 // This is done by comparing attr's element type, converted to LLVM type, 2961 // with ty's element type. 2962 // 2963 // Only integer and floating point (including complex) attributes are 2964 // supported. Also, attr is expected to have a TensorType and ty is expected 2965 // to be of LLVMArrayType. If any of the previous conditions is false, then 2966 // the specified attr and ty are not supported by this function and are 2967 // assumed to be compatible. 2968 static inline bool attributeTypeIsCompatible(mlir::MLIRContext *ctx, 2969 mlir::Attribute attr, 2970 mlir::Type ty) { 2971 // Get attr's LLVM element type. 2972 if (!attr) 2973 return true; 2974 auto intOrFpEleAttr = mlir::dyn_cast<mlir::DenseIntOrFPElementsAttr>(attr); 2975 if (!intOrFpEleAttr) 2976 return true; 2977 auto tensorTy = mlir::dyn_cast<mlir::TensorType>(intOrFpEleAttr.getType()); 2978 if (!tensorTy) 2979 return true; 2980 mlir::Type attrEleTy = 2981 mlir::LLVMTypeConverter(ctx).convertType(tensorTy.getElementType()); 2982 2983 // Get ty's element type. 2984 auto arrTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(ty); 2985 if (!arrTy) 2986 return true; 2987 mlir::Type eleTy = arrTy.getElementType(); 2988 while ((arrTy = mlir::dyn_cast<mlir::LLVM::LLVMArrayType>(eleTy))) 2989 eleTy = arrTy.getElementType(); 2990 2991 return attrEleTy == eleTy; 2992 } 2993 #endif 2994 2995 /// Lower `fir.global` operation to `llvm.global` operation. 2996 /// `fir.insert_on_range` operations are replaced with constant dense attribute 2997 /// if they are applied on the full range. 2998 struct GlobalOpConversion : public fir::FIROpConversion<fir::GlobalOp> { 2999 using FIROpConversion::FIROpConversion; 3000 3001 llvm::LogicalResult 3002 matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor, 3003 mlir::ConversionPatternRewriter &rewriter) const override { 3004 3005 llvm::SmallVector<mlir::Attribute> dbgExprs; 3006 3007 if (auto fusedLoc = mlir::dyn_cast<mlir::FusedLoc>(global.getLoc())) { 3008 if (auto gvExprAttr = mlir::dyn_cast_if_present<mlir::ArrayAttr>( 3009 fusedLoc.getMetadata())) { 3010 for (auto attr : gvExprAttr.getAsRange<mlir::Attribute>()) 3011 if (auto dbgAttr = 3012 mlir::dyn_cast<mlir::LLVM::DIGlobalVariableExpressionAttr>( 3013 attr)) 3014 dbgExprs.push_back(dbgAttr); 3015 } 3016 } 3017 3018 auto tyAttr = convertType(global.getType()); 3019 if (auto boxType = mlir::dyn_cast<fir::BaseBoxType>(global.getType())) 3020 tyAttr = this->lowerTy().convertBoxTypeAsStruct(boxType); 3021 auto loc = global.getLoc(); 3022 mlir::Attribute initAttr = global.getInitVal().value_or(mlir::Attribute()); 3023 assert(attributeTypeIsCompatible(global.getContext(), initAttr, tyAttr)); 3024 auto linkage = convertLinkage(global.getLinkName()); 3025 auto isConst = global.getConstant().has_value(); 3026 mlir::SymbolRefAttr comdat; 3027 llvm::ArrayRef<mlir::NamedAttribute> attrs; 3028 auto g = rewriter.create<mlir::LLVM::GlobalOp>( 3029 loc, tyAttr, isConst, linkage, global.getSymName(), initAttr, 0, 0, 3030 false, false, comdat, attrs, dbgExprs); 3031 3032 if (global.getAlignment() && *global.getAlignment() > 0) 3033 g.setAlignment(*global.getAlignment()); 3034 3035 auto module = global->getParentOfType<mlir::ModuleOp>(); 3036 auto gpuMod = global->getParentOfType<mlir::gpu::GPUModuleOp>(); 3037 // Add comdat if necessary 3038 if (fir::getTargetTriple(module).supportsCOMDAT() && 3039 (linkage == mlir::LLVM::Linkage::Linkonce || 3040 linkage == mlir::LLVM::Linkage::LinkonceODR) && 3041 !gpuMod) { 3042 addComdat(g, rewriter, module); 3043 } 3044 3045 // Apply all non-Fir::GlobalOp attributes to the LLVM::GlobalOp, preserving 3046 // them; whilst taking care not to apply attributes that are lowered in 3047 // other ways. 3048 llvm::SmallDenseSet<llvm::StringRef> elidedAttrsSet( 3049 global.getAttributeNames().begin(), global.getAttributeNames().end()); 3050 for (auto &attr : global->getAttrs()) 3051 if (!elidedAttrsSet.contains(attr.getName().strref())) 3052 g->setAttr(attr.getName(), attr.getValue()); 3053 3054 auto &gr = g.getInitializerRegion(); 3055 rewriter.inlineRegionBefore(global.getRegion(), gr, gr.end()); 3056 if (!gr.empty()) { 3057 // Replace insert_on_range with a constant dense attribute if the 3058 // initialization is on the full range. 3059 auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>(); 3060 for (auto insertOp : insertOnRangeOps) { 3061 if (isFullRange(insertOp.getCoor(), insertOp.getType())) { 3062 auto seqTyAttr = convertType(insertOp.getType()); 3063 auto *op = insertOp.getVal().getDefiningOp(); 3064 auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op); 3065 if (!constant) { 3066 auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op); 3067 if (!convertOp) 3068 continue; 3069 constant = mlir::cast<mlir::arith::ConstantOp>( 3070 convertOp.getValue().getDefiningOp()); 3071 } 3072 mlir::Type vecType = mlir::VectorType::get( 3073 insertOp.getType().getShape(), constant.getType()); 3074 auto denseAttr = mlir::DenseElementsAttr::get( 3075 mlir::cast<mlir::ShapedType>(vecType), constant.getValue()); 3076 rewriter.setInsertionPointAfter(insertOp); 3077 rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>( 3078 insertOp, seqTyAttr, denseAttr); 3079 } 3080 } 3081 } 3082 rewriter.eraseOp(global); 3083 return mlir::success(); 3084 } 3085 3086 bool isFullRange(mlir::DenseIntElementsAttr indexes, 3087 fir::SequenceType seqTy) const { 3088 auto extents = seqTy.getShape(); 3089 if (indexes.size() / 2 != static_cast<int64_t>(extents.size())) 3090 return false; 3091 auto cur_index = indexes.value_begin<int64_t>(); 3092 for (unsigned i = 0; i < indexes.size(); i += 2) { 3093 if (*(cur_index++) != 0) 3094 return false; 3095 if (*(cur_index++) != extents[i / 2] - 1) 3096 return false; 3097 } 3098 return true; 3099 } 3100 3101 // TODO: String comparaison should be avoided. Replace linkName with an 3102 // enumeration. 3103 mlir::LLVM::Linkage 3104 convertLinkage(std::optional<llvm::StringRef> optLinkage) const { 3105 if (optLinkage) { 3106 auto name = *optLinkage; 3107 if (name == "internal") 3108 return mlir::LLVM::Linkage::Internal; 3109 if (name == "linkonce") 3110 return mlir::LLVM::Linkage::Linkonce; 3111 if (name == "linkonce_odr") 3112 return mlir::LLVM::Linkage::LinkonceODR; 3113 if (name == "common") 3114 return mlir::LLVM::Linkage::Common; 3115 if (name == "weak") 3116 return mlir::LLVM::Linkage::Weak; 3117 } 3118 return mlir::LLVM::Linkage::External; 3119 } 3120 3121 private: 3122 static void addComdat(mlir::LLVM::GlobalOp &global, 3123 mlir::ConversionPatternRewriter &rewriter, 3124 mlir::ModuleOp module) { 3125 const char *comdatName = "__llvm_comdat"; 3126 mlir::LLVM::ComdatOp comdatOp = 3127 module.lookupSymbol<mlir::LLVM::ComdatOp>(comdatName); 3128 if (!comdatOp) { 3129 comdatOp = 3130 rewriter.create<mlir::LLVM::ComdatOp>(module.getLoc(), comdatName); 3131 } 3132 if (auto select = comdatOp.lookupSymbol<mlir::LLVM::ComdatSelectorOp>( 3133 global.getSymName())) 3134 return; 3135 mlir::OpBuilder::InsertionGuard guard(rewriter); 3136 rewriter.setInsertionPointToEnd(&comdatOp.getBody().back()); 3137 auto selectorOp = rewriter.create<mlir::LLVM::ComdatSelectorOp>( 3138 comdatOp.getLoc(), global.getSymName(), 3139 mlir::LLVM::comdat::Comdat::Any); 3140 global.setComdatAttr(mlir::SymbolRefAttr::get( 3141 rewriter.getContext(), comdatName, 3142 mlir::FlatSymbolRefAttr::get(selectorOp.getSymNameAttr()))); 3143 } 3144 }; 3145 3146 /// `fir.load` --> `llvm.load` 3147 struct LoadOpConversion : public fir::FIROpConversion<fir::LoadOp> { 3148 using FIROpConversion::FIROpConversion; 3149 3150 llvm::LogicalResult 3151 matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor, 3152 mlir::ConversionPatternRewriter &rewriter) const override { 3153 3154 mlir::Type llvmLoadTy = convertObjectType(load.getType()); 3155 if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(load.getType())) { 3156 // fir.box is a special case because it is considered an ssa value in 3157 // fir, but it is lowered as a pointer to a descriptor. So 3158 // fir.ref<fir.box> and fir.box end up being the same llvm types and 3159 // loading a fir.ref<fir.box> is implemented as taking a snapshot of the 3160 // descriptor value into a new descriptor temp. 3161 auto inputBoxStorage = adaptor.getOperands()[0]; 3162 mlir::Value newBoxStorage; 3163 mlir::Location loc = load.getLoc(); 3164 if (auto callOp = mlir::dyn_cast_or_null<mlir::LLVM::CallOp>( 3165 inputBoxStorage.getDefiningOp())) { 3166 if (callOp.getCallee() && 3167 (*callOp.getCallee()) 3168 .starts_with(RTNAME_STRING(CUFAllocDescriptor))) { 3169 // CUDA Fortran local descriptor are allocated in managed memory. So 3170 // new storage must be allocated the same way. 3171 auto mod = load->getParentOfType<mlir::ModuleOp>(); 3172 newBoxStorage = 3173 genCUFAllocDescriptor(loc, rewriter, mod, boxTy, lowerTy()); 3174 } 3175 } 3176 if (!newBoxStorage) 3177 newBoxStorage = genAllocaAndAddrCastWithType(loc, llvmLoadTy, 3178 defaultAlign, rewriter); 3179 3180 TypePair boxTypePair{boxTy, llvmLoadTy}; 3181 mlir::Value boxSize = 3182 computeBoxSize(loc, boxTypePair, inputBoxStorage, rewriter); 3183 auto memcpy = rewriter.create<mlir::LLVM::MemcpyOp>( 3184 loc, newBoxStorage, inputBoxStorage, boxSize, /*isVolatile=*/false); 3185 3186 if (std::optional<mlir::ArrayAttr> optionalTag = load.getTbaa()) 3187 memcpy.setTBAATags(*optionalTag); 3188 else 3189 attachTBAATag(memcpy, boxTy, boxTy, nullptr); 3190 rewriter.replaceOp(load, newBoxStorage); 3191 } else { 3192 auto loadOp = rewriter.create<mlir::LLVM::LoadOp>( 3193 load.getLoc(), llvmLoadTy, adaptor.getOperands(), load->getAttrs()); 3194 if (std::optional<mlir::ArrayAttr> optionalTag = load.getTbaa()) 3195 loadOp.setTBAATags(*optionalTag); 3196 else 3197 attachTBAATag(loadOp, load.getType(), load.getType(), nullptr); 3198 rewriter.replaceOp(load, loadOp.getResult()); 3199 } 3200 return mlir::success(); 3201 } 3202 }; 3203 3204 /// Lower `fir.no_reassoc` to LLVM IR dialect. 3205 /// TODO: how do we want to enforce this in LLVM-IR? Can we manipulate the fast 3206 /// math flags? 3207 struct NoReassocOpConversion : public fir::FIROpConversion<fir::NoReassocOp> { 3208 using FIROpConversion::FIROpConversion; 3209 3210 llvm::LogicalResult 3211 matchAndRewrite(fir::NoReassocOp noreassoc, OpAdaptor adaptor, 3212 mlir::ConversionPatternRewriter &rewriter) const override { 3213 rewriter.replaceOp(noreassoc, adaptor.getOperands()[0]); 3214 return mlir::success(); 3215 } 3216 }; 3217 3218 static void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest, 3219 std::optional<mlir::ValueRange> destOps, 3220 mlir::ConversionPatternRewriter &rewriter, 3221 mlir::Block *newBlock) { 3222 if (destOps) 3223 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, *destOps, newBlock, 3224 mlir::ValueRange()); 3225 else 3226 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock); 3227 } 3228 3229 template <typename A, typename B> 3230 static void genBrOp(A caseOp, mlir::Block *dest, std::optional<B> destOps, 3231 mlir::ConversionPatternRewriter &rewriter) { 3232 if (destOps) 3233 rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, *destOps, dest); 3234 else 3235 rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, std::nullopt, dest); 3236 } 3237 3238 static void genCaseLadderStep(mlir::Location loc, mlir::Value cmp, 3239 mlir::Block *dest, 3240 std::optional<mlir::ValueRange> destOps, 3241 mlir::ConversionPatternRewriter &rewriter) { 3242 auto *thisBlock = rewriter.getInsertionBlock(); 3243 auto *newBlock = createBlock(rewriter, dest); 3244 rewriter.setInsertionPointToEnd(thisBlock); 3245 genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock); 3246 rewriter.setInsertionPointToEnd(newBlock); 3247 } 3248 3249 /// Conversion of `fir.select_case` 3250 /// 3251 /// The `fir.select_case` operation is converted to a if-then-else ladder. 3252 /// Depending on the case condition type, one or several comparison and 3253 /// conditional branching can be generated. 3254 /// 3255 /// A point value case such as `case(4)`, a lower bound case such as 3256 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a 3257 /// simple comparison between the selector value and the constant value in the 3258 /// case. The block associated with the case condition is then executed if 3259 /// the comparison succeed otherwise it branch to the next block with the 3260 /// comparison for the next case conditon. 3261 /// 3262 /// A closed interval case condition such as `case(7:10)` is converted with a 3263 /// first comparison and conditional branching for the lower bound. If 3264 /// successful, it branch to a second block with the comparison for the 3265 /// upper bound in the same case condition. 3266 /// 3267 /// TODO: lowering of CHARACTER type cases is not handled yet. 3268 struct SelectCaseOpConversion : public fir::FIROpConversion<fir::SelectCaseOp> { 3269 using FIROpConversion::FIROpConversion; 3270 3271 llvm::LogicalResult 3272 matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor, 3273 mlir::ConversionPatternRewriter &rewriter) const override { 3274 unsigned conds = caseOp.getNumConditions(); 3275 llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue(); 3276 // Type can be CHARACTER, INTEGER, or LOGICAL (C1145) 3277 auto ty = caseOp.getSelector().getType(); 3278 if (mlir::isa<fir::CharacterType>(ty)) { 3279 TODO(caseOp.getLoc(), "fir.select_case codegen with character type"); 3280 return mlir::failure(); 3281 } 3282 mlir::Value selector = caseOp.getSelector(adaptor.getOperands()); 3283 auto loc = caseOp.getLoc(); 3284 for (unsigned t = 0; t != conds; ++t) { 3285 mlir::Block *dest = caseOp.getSuccessor(t); 3286 std::optional<mlir::ValueRange> destOps = 3287 caseOp.getSuccessorOperands(adaptor.getOperands(), t); 3288 std::optional<mlir::ValueRange> cmpOps = 3289 *caseOp.getCompareOperands(adaptor.getOperands(), t); 3290 mlir::Attribute attr = cases[t]; 3291 assert(mlir::isa<mlir::UnitAttr>(attr) || cmpOps.has_value()); 3292 if (mlir::isa<fir::PointIntervalAttr>(attr)) { 3293 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 3294 loc, mlir::LLVM::ICmpPredicate::eq, selector, cmpOps->front()); 3295 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 3296 continue; 3297 } 3298 if (mlir::isa<fir::LowerBoundAttr>(attr)) { 3299 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 3300 loc, mlir::LLVM::ICmpPredicate::sle, cmpOps->front(), selector); 3301 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 3302 continue; 3303 } 3304 if (mlir::isa<fir::UpperBoundAttr>(attr)) { 3305 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 3306 loc, mlir::LLVM::ICmpPredicate::sle, selector, cmpOps->front()); 3307 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 3308 continue; 3309 } 3310 if (mlir::isa<fir::ClosedIntervalAttr>(attr)) { 3311 mlir::Value caseArg0 = *cmpOps->begin(); 3312 auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>( 3313 loc, mlir::LLVM::ICmpPredicate::sle, caseArg0, selector); 3314 auto *thisBlock = rewriter.getInsertionBlock(); 3315 auto *newBlock1 = createBlock(rewriter, dest); 3316 auto *newBlock2 = createBlock(rewriter, dest); 3317 rewriter.setInsertionPointToEnd(thisBlock); 3318 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp0, newBlock1, newBlock2); 3319 rewriter.setInsertionPointToEnd(newBlock1); 3320 mlir::Value caseArg1 = *(cmpOps->begin() + 1); 3321 auto cmp1 = rewriter.create<mlir::LLVM::ICmpOp>( 3322 loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg1); 3323 genCondBrOp(loc, cmp1, dest, destOps, rewriter, newBlock2); 3324 rewriter.setInsertionPointToEnd(newBlock2); 3325 continue; 3326 } 3327 assert(mlir::isa<mlir::UnitAttr>(attr)); 3328 assert((t + 1 == conds) && "unit must be last"); 3329 genBrOp(caseOp, dest, destOps, rewriter); 3330 } 3331 return mlir::success(); 3332 } 3333 }; 3334 3335 /// Helper function for converting select ops. This function converts the 3336 /// signature of the given block. If the new block signature is different from 3337 /// `expectedTypes`, returns "failure". 3338 static llvm::FailureOr<mlir::Block *> 3339 getConvertedBlock(mlir::ConversionPatternRewriter &rewriter, 3340 const mlir::TypeConverter *converter, 3341 mlir::Operation *branchOp, mlir::Block *block, 3342 mlir::TypeRange expectedTypes) { 3343 assert(converter && "expected non-null type converter"); 3344 assert(!block->isEntryBlock() && "entry blocks have no predecessors"); 3345 3346 // There is nothing to do if the types already match. 3347 if (block->getArgumentTypes() == expectedTypes) 3348 return block; 3349 3350 // Compute the new block argument types and convert the block. 3351 std::optional<mlir::TypeConverter::SignatureConversion> conversion = 3352 converter->convertBlockSignature(block); 3353 if (!conversion) 3354 return rewriter.notifyMatchFailure(branchOp, 3355 "could not compute block signature"); 3356 if (expectedTypes != conversion->getConvertedTypes()) 3357 return rewriter.notifyMatchFailure( 3358 branchOp, 3359 "mismatch between adaptor operand types and computed block signature"); 3360 return rewriter.applySignatureConversion(block, *conversion, converter); 3361 } 3362 3363 template <typename OP> 3364 static llvm::LogicalResult 3365 selectMatchAndRewrite(const fir::LLVMTypeConverter &lowering, OP select, 3366 typename OP::Adaptor adaptor, 3367 mlir::ConversionPatternRewriter &rewriter, 3368 const mlir::TypeConverter *converter) { 3369 unsigned conds = select.getNumConditions(); 3370 auto cases = select.getCases().getValue(); 3371 mlir::Value selector = adaptor.getSelector(); 3372 auto loc = select.getLoc(); 3373 assert(conds > 0 && "select must have cases"); 3374 3375 llvm::SmallVector<mlir::Block *> destinations; 3376 llvm::SmallVector<mlir::ValueRange> destinationsOperands; 3377 mlir::Block *defaultDestination; 3378 mlir::ValueRange defaultOperands; 3379 llvm::SmallVector<int32_t> caseValues; 3380 3381 for (unsigned t = 0; t != conds; ++t) { 3382 mlir::Block *dest = select.getSuccessor(t); 3383 auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t); 3384 const mlir::Attribute &attr = cases[t]; 3385 if (auto intAttr = mlir::dyn_cast<mlir::IntegerAttr>(attr)) { 3386 destinationsOperands.push_back(destOps ? *destOps : mlir::ValueRange{}); 3387 auto convertedBlock = 3388 getConvertedBlock(rewriter, converter, select, dest, 3389 mlir::TypeRange(destinationsOperands.back())); 3390 if (mlir::failed(convertedBlock)) 3391 return mlir::failure(); 3392 destinations.push_back(*convertedBlock); 3393 caseValues.push_back(intAttr.getInt()); 3394 continue; 3395 } 3396 assert(mlir::dyn_cast_or_null<mlir::UnitAttr>(attr)); 3397 assert((t + 1 == conds) && "unit must be last"); 3398 defaultOperands = destOps ? *destOps : mlir::ValueRange{}; 3399 auto convertedBlock = getConvertedBlock(rewriter, converter, select, dest, 3400 mlir::TypeRange(defaultOperands)); 3401 if (mlir::failed(convertedBlock)) 3402 return mlir::failure(); 3403 defaultDestination = *convertedBlock; 3404 } 3405 3406 // LLVM::SwitchOp takes a i32 type for the selector. 3407 if (select.getSelector().getType() != rewriter.getI32Type()) 3408 selector = rewriter.create<mlir::LLVM::TruncOp>(loc, rewriter.getI32Type(), 3409 selector); 3410 3411 rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>( 3412 select, selector, 3413 /*defaultDestination=*/defaultDestination, 3414 /*defaultOperands=*/defaultOperands, 3415 /*caseValues=*/caseValues, 3416 /*caseDestinations=*/destinations, 3417 /*caseOperands=*/destinationsOperands, 3418 /*branchWeights=*/llvm::ArrayRef<std::int32_t>()); 3419 return mlir::success(); 3420 } 3421 3422 /// conversion of fir::SelectOp to an if-then-else ladder 3423 struct SelectOpConversion : public fir::FIROpConversion<fir::SelectOp> { 3424 using FIROpConversion::FIROpConversion; 3425 3426 llvm::LogicalResult 3427 matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor, 3428 mlir::ConversionPatternRewriter &rewriter) const override { 3429 return selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, 3430 rewriter, getTypeConverter()); 3431 } 3432 }; 3433 3434 /// conversion of fir::SelectRankOp to an if-then-else ladder 3435 struct SelectRankOpConversion : public fir::FIROpConversion<fir::SelectRankOp> { 3436 using FIROpConversion::FIROpConversion; 3437 3438 llvm::LogicalResult 3439 matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor, 3440 mlir::ConversionPatternRewriter &rewriter) const override { 3441 return selectMatchAndRewrite<fir::SelectRankOp>( 3442 lowerTy(), op, adaptor, rewriter, getTypeConverter()); 3443 } 3444 }; 3445 3446 /// Lower `fir.select_type` to LLVM IR dialect. 3447 struct SelectTypeOpConversion : public fir::FIROpConversion<fir::SelectTypeOp> { 3448 using FIROpConversion::FIROpConversion; 3449 3450 llvm::LogicalResult 3451 matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor, 3452 mlir::ConversionPatternRewriter &rewriter) const override { 3453 mlir::emitError(select.getLoc(), 3454 "fir.select_type should have already been converted"); 3455 return mlir::failure(); 3456 } 3457 }; 3458 3459 /// `fir.store` --> `llvm.store` 3460 struct StoreOpConversion : public fir::FIROpConversion<fir::StoreOp> { 3461 using FIROpConversion::FIROpConversion; 3462 3463 llvm::LogicalResult 3464 matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor, 3465 mlir::ConversionPatternRewriter &rewriter) const override { 3466 mlir::Location loc = store.getLoc(); 3467 mlir::Type storeTy = store.getValue().getType(); 3468 mlir::Value llvmValue = adaptor.getValue(); 3469 mlir::Value llvmMemref = adaptor.getMemref(); 3470 mlir::LLVM::AliasAnalysisOpInterface newOp; 3471 if (auto boxTy = mlir::dyn_cast<fir::BaseBoxType>(storeTy)) { 3472 mlir::Type llvmBoxTy = lowerTy().convertBoxTypeAsStruct(boxTy); 3473 // Always use memcpy because LLVM is not as effective at optimizing 3474 // aggregate loads/stores as it is optimizing memcpy. 3475 TypePair boxTypePair{boxTy, llvmBoxTy}; 3476 mlir::Value boxSize = 3477 computeBoxSize(loc, boxTypePair, llvmValue, rewriter); 3478 newOp = rewriter.create<mlir::LLVM::MemcpyOp>( 3479 loc, llvmMemref, llvmValue, boxSize, /*isVolatile=*/false); 3480 } else { 3481 newOp = rewriter.create<mlir::LLVM::StoreOp>(loc, llvmValue, llvmMemref); 3482 } 3483 if (std::optional<mlir::ArrayAttr> optionalTag = store.getTbaa()) 3484 newOp.setTBAATags(*optionalTag); 3485 else 3486 attachTBAATag(newOp, storeTy, storeTy, nullptr); 3487 rewriter.eraseOp(store); 3488 return mlir::success(); 3489 } 3490 }; 3491 3492 namespace { 3493 3494 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for 3495 /// the character buffer and one for the buffer length. 3496 struct UnboxCharOpConversion : public fir::FIROpConversion<fir::UnboxCharOp> { 3497 using FIROpConversion::FIROpConversion; 3498 3499 llvm::LogicalResult 3500 matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor, 3501 mlir::ConversionPatternRewriter &rewriter) const override { 3502 mlir::Type lenTy = convertType(unboxchar.getType(1)); 3503 mlir::Value tuple = adaptor.getOperands()[0]; 3504 3505 mlir::Location loc = unboxchar.getLoc(); 3506 mlir::Value ptrToBuffer = 3507 rewriter.create<mlir::LLVM::ExtractValueOp>(loc, tuple, 0); 3508 3509 auto len = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, tuple, 1); 3510 mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len); 3511 3512 rewriter.replaceOp(unboxchar, 3513 llvm::ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast}); 3514 return mlir::success(); 3515 } 3516 }; 3517 3518 /// Lower `fir.unboxproc` operation. Unbox a procedure box value, yielding its 3519 /// components. 3520 /// TODO: Part of supporting Fortran 2003 procedure pointers. 3521 struct UnboxProcOpConversion : public fir::FIROpConversion<fir::UnboxProcOp> { 3522 using FIROpConversion::FIROpConversion; 3523 3524 llvm::LogicalResult 3525 matchAndRewrite(fir::UnboxProcOp unboxproc, OpAdaptor adaptor, 3526 mlir::ConversionPatternRewriter &rewriter) const override { 3527 TODO(unboxproc.getLoc(), "fir.unboxproc codegen"); 3528 return mlir::failure(); 3529 } 3530 }; 3531 3532 /// convert to LLVM IR dialect `undef` 3533 struct UndefOpConversion : public fir::FIROpConversion<fir::UndefOp> { 3534 using FIROpConversion::FIROpConversion; 3535 3536 llvm::LogicalResult 3537 matchAndRewrite(fir::UndefOp undef, OpAdaptor, 3538 mlir::ConversionPatternRewriter &rewriter) const override { 3539 rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>( 3540 undef, convertType(undef.getType())); 3541 return mlir::success(); 3542 } 3543 }; 3544 3545 struct ZeroOpConversion : public fir::FIROpConversion<fir::ZeroOp> { 3546 using FIROpConversion::FIROpConversion; 3547 3548 llvm::LogicalResult 3549 matchAndRewrite(fir::ZeroOp zero, OpAdaptor, 3550 mlir::ConversionPatternRewriter &rewriter) const override { 3551 mlir::Type ty = convertType(zero.getType()); 3552 rewriter.replaceOpWithNewOp<mlir::LLVM::ZeroOp>(zero, ty); 3553 return mlir::success(); 3554 } 3555 }; 3556 3557 /// `fir.unreachable` --> `llvm.unreachable` 3558 struct UnreachableOpConversion 3559 : public fir::FIROpConversion<fir::UnreachableOp> { 3560 using FIROpConversion::FIROpConversion; 3561 3562 llvm::LogicalResult 3563 matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor, 3564 mlir::ConversionPatternRewriter &rewriter) const override { 3565 rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach); 3566 return mlir::success(); 3567 } 3568 }; 3569 3570 /// `fir.is_present` --> 3571 /// ``` 3572 /// %0 = llvm.mlir.constant(0 : i64) 3573 /// %1 = llvm.ptrtoint %0 3574 /// %2 = llvm.icmp "ne" %1, %0 : i64 3575 /// ``` 3576 struct IsPresentOpConversion : public fir::FIROpConversion<fir::IsPresentOp> { 3577 using FIROpConversion::FIROpConversion; 3578 3579 llvm::LogicalResult 3580 matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor, 3581 mlir::ConversionPatternRewriter &rewriter) const override { 3582 mlir::Type idxTy = lowerTy().indexType(); 3583 mlir::Location loc = isPresent.getLoc(); 3584 auto ptr = adaptor.getOperands()[0]; 3585 3586 if (mlir::isa<fir::BoxCharType>(isPresent.getVal().getType())) { 3587 [[maybe_unused]] auto structTy = 3588 mlir::cast<mlir::LLVM::LLVMStructType>(ptr.getType()); 3589 assert(!structTy.isOpaque() && !structTy.getBody().empty()); 3590 3591 ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ptr, 0); 3592 } 3593 mlir::LLVM::ConstantOp c0 = 3594 genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0); 3595 auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr); 3596 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>( 3597 isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0); 3598 3599 return mlir::success(); 3600 } 3601 }; 3602 3603 /// Create value signaling an absent optional argument in a call, e.g. 3604 /// `fir.absent !fir.ref<i64>` --> `llvm.mlir.zero : !llvm.ptr<i64>` 3605 struct AbsentOpConversion : public fir::FIROpConversion<fir::AbsentOp> { 3606 using FIROpConversion::FIROpConversion; 3607 3608 llvm::LogicalResult 3609 matchAndRewrite(fir::AbsentOp absent, OpAdaptor, 3610 mlir::ConversionPatternRewriter &rewriter) const override { 3611 mlir::Type ty = convertType(absent.getType()); 3612 rewriter.replaceOpWithNewOp<mlir::LLVM::ZeroOp>(absent, ty); 3613 return mlir::success(); 3614 } 3615 }; 3616 3617 // 3618 // Primitive operations on Complex types 3619 // 3620 3621 template <typename OPTY> 3622 static inline mlir::LLVM::FastmathFlagsAttr getLLVMFMFAttr(OPTY op) { 3623 return mlir::LLVM::FastmathFlagsAttr::get( 3624 op.getContext(), 3625 mlir::arith::convertArithFastMathFlagsToLLVM(op.getFastmath())); 3626 } 3627 3628 /// Generate inline code for complex addition/subtraction 3629 template <typename LLVMOP, typename OPTY> 3630 static mlir::LLVM::InsertValueOp 3631 complexSum(OPTY sumop, mlir::ValueRange opnds, 3632 mlir::ConversionPatternRewriter &rewriter, 3633 const fir::LLVMTypeConverter &lowering) { 3634 mlir::LLVM::FastmathFlagsAttr fmf = getLLVMFMFAttr(sumop); 3635 mlir::Value a = opnds[0]; 3636 mlir::Value b = opnds[1]; 3637 auto loc = sumop.getLoc(); 3638 mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType())); 3639 mlir::Type ty = lowering.convertType(sumop.getType()); 3640 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, a, 0); 3641 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, a, 1); 3642 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, b, 0); 3643 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, b, 1); 3644 auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1, fmf); 3645 auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1, fmf); 3646 auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 3647 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, r0, rx, 0); 3648 return rewriter.create<mlir::LLVM::InsertValueOp>(loc, r1, ry, 1); 3649 } 3650 } // namespace 3651 3652 namespace { 3653 struct AddcOpConversion : public fir::FIROpConversion<fir::AddcOp> { 3654 using FIROpConversion::FIROpConversion; 3655 3656 llvm::LogicalResult 3657 matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor, 3658 mlir::ConversionPatternRewriter &rewriter) const override { 3659 // given: (x + iy) + (x' + iy') 3660 // result: (x + x') + i(y + y') 3661 auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(), 3662 rewriter, lowerTy()); 3663 rewriter.replaceOp(addc, r.getResult()); 3664 return mlir::success(); 3665 } 3666 }; 3667 3668 struct SubcOpConversion : public fir::FIROpConversion<fir::SubcOp> { 3669 using FIROpConversion::FIROpConversion; 3670 3671 llvm::LogicalResult 3672 matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor, 3673 mlir::ConversionPatternRewriter &rewriter) const override { 3674 // given: (x + iy) - (x' + iy') 3675 // result: (x - x') + i(y - y') 3676 auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(), 3677 rewriter, lowerTy()); 3678 rewriter.replaceOp(subc, r.getResult()); 3679 return mlir::success(); 3680 } 3681 }; 3682 3683 /// Inlined complex multiply 3684 struct MulcOpConversion : public fir::FIROpConversion<fir::MulcOp> { 3685 using FIROpConversion::FIROpConversion; 3686 3687 llvm::LogicalResult 3688 matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor, 3689 mlir::ConversionPatternRewriter &rewriter) const override { 3690 // TODO: Can we use a call to __muldc3 ? 3691 // given: (x + iy) * (x' + iy') 3692 // result: (xx'-yy')+i(xy'+yx') 3693 mlir::LLVM::FastmathFlagsAttr fmf = getLLVMFMFAttr(mulc); 3694 mlir::Value a = adaptor.getOperands()[0]; 3695 mlir::Value b = adaptor.getOperands()[1]; 3696 auto loc = mulc.getLoc(); 3697 mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType())); 3698 mlir::Type ty = convertType(mulc.getType()); 3699 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, a, 0); 3700 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, a, 1); 3701 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, b, 0); 3702 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, b, 1); 3703 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1, fmf); 3704 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1, fmf); 3705 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1, fmf); 3706 auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx, fmf); 3707 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1, fmf); 3708 auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy, fmf); 3709 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 3710 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ra, rr, 0); 3711 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, r1, ri, 1); 3712 rewriter.replaceOp(mulc, r0.getResult()); 3713 return mlir::success(); 3714 } 3715 }; 3716 3717 /// Inlined complex division 3718 struct DivcOpConversion : public fir::FIROpConversion<fir::DivcOp> { 3719 using FIROpConversion::FIROpConversion; 3720 3721 llvm::LogicalResult 3722 matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor, 3723 mlir::ConversionPatternRewriter &rewriter) const override { 3724 // TODO: Can we use a call to __divdc3 instead? 3725 // Just generate inline code for now. 3726 // given: (x + iy) / (x' + iy') 3727 // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y' 3728 mlir::LLVM::FastmathFlagsAttr fmf = getLLVMFMFAttr(divc); 3729 mlir::Value a = adaptor.getOperands()[0]; 3730 mlir::Value b = adaptor.getOperands()[1]; 3731 auto loc = divc.getLoc(); 3732 mlir::Type eleTy = convertType(getComplexEleTy(divc.getType())); 3733 mlir::Type ty = convertType(divc.getType()); 3734 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, a, 0); 3735 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, a, 1); 3736 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, b, 0); 3737 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, b, 1); 3738 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1, fmf); 3739 auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1, fmf); 3740 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1, fmf); 3741 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1, fmf); 3742 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1, fmf); 3743 auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1, fmf); 3744 auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1, fmf); 3745 auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy, fmf); 3746 auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy, fmf); 3747 auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d, fmf); 3748 auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d, fmf); 3749 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 3750 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ra, rr, 0); 3751 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, r1, ri, 1); 3752 rewriter.replaceOp(divc, r0.getResult()); 3753 return mlir::success(); 3754 } 3755 }; 3756 3757 /// Inlined complex negation 3758 struct NegcOpConversion : public fir::FIROpConversion<fir::NegcOp> { 3759 using FIROpConversion::FIROpConversion; 3760 3761 llvm::LogicalResult 3762 matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor, 3763 mlir::ConversionPatternRewriter &rewriter) const override { 3764 // given: -(x + iy) 3765 // result: -x - iy 3766 auto eleTy = convertType(getComplexEleTy(neg.getType())); 3767 auto loc = neg.getLoc(); 3768 mlir::Value o0 = adaptor.getOperands()[0]; 3769 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, o0, 0); 3770 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, o0, 1); 3771 auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp); 3772 auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip); 3773 auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, o0, nrp, 0); 3774 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, r, nip, 1); 3775 return mlir::success(); 3776 } 3777 }; 3778 3779 struct BoxOffsetOpConversion : public fir::FIROpConversion<fir::BoxOffsetOp> { 3780 using FIROpConversion::FIROpConversion; 3781 3782 llvm::LogicalResult 3783 matchAndRewrite(fir::BoxOffsetOp boxOffset, OpAdaptor adaptor, 3784 mlir::ConversionPatternRewriter &rewriter) const override { 3785 3786 mlir::Type pty = ::getLlvmPtrType(boxOffset.getContext()); 3787 mlir::Type boxType = fir::unwrapRefType(boxOffset.getBoxRef().getType()); 3788 mlir::Type llvmBoxTy = 3789 lowerTy().convertBoxTypeAsStruct(mlir::cast<fir::BaseBoxType>(boxType)); 3790 int fieldId = boxOffset.getField() == fir::BoxFieldAttr::derived_type 3791 ? getTypeDescFieldId(boxType) 3792 : kAddrPosInBox; 3793 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>( 3794 boxOffset, pty, llvmBoxTy, adaptor.getBoxRef(), 3795 llvm::ArrayRef<mlir::LLVM::GEPArg>{0, fieldId}); 3796 return mlir::success(); 3797 } 3798 }; 3799 3800 /// Conversion pattern for operation that must be dead. The information in these 3801 /// operations is used by other operation. At this point they should not have 3802 /// anymore uses. 3803 /// These operations are normally dead after the pre-codegen pass. 3804 template <typename FromOp> 3805 struct MustBeDeadConversion : public fir::FIROpConversion<FromOp> { 3806 explicit MustBeDeadConversion(const fir::LLVMTypeConverter &lowering, 3807 const fir::FIRToLLVMPassOptions &options) 3808 : fir::FIROpConversion<FromOp>(lowering, options) {} 3809 using OpAdaptor = typename FromOp::Adaptor; 3810 3811 llvm::LogicalResult 3812 matchAndRewrite(FromOp op, OpAdaptor adaptor, 3813 mlir::ConversionPatternRewriter &rewriter) const final { 3814 if (!op->getUses().empty()) 3815 return rewriter.notifyMatchFailure(op, "op must be dead"); 3816 rewriter.eraseOp(op); 3817 return mlir::success(); 3818 } 3819 }; 3820 3821 struct ShapeOpConversion : public MustBeDeadConversion<fir::ShapeOp> { 3822 using MustBeDeadConversion::MustBeDeadConversion; 3823 }; 3824 3825 struct ShapeShiftOpConversion : public MustBeDeadConversion<fir::ShapeShiftOp> { 3826 using MustBeDeadConversion::MustBeDeadConversion; 3827 }; 3828 3829 struct ShiftOpConversion : public MustBeDeadConversion<fir::ShiftOp> { 3830 using MustBeDeadConversion::MustBeDeadConversion; 3831 }; 3832 3833 struct SliceOpConversion : public MustBeDeadConversion<fir::SliceOp> { 3834 using MustBeDeadConversion::MustBeDeadConversion; 3835 }; 3836 3837 } // namespace 3838 3839 namespace { 3840 class RenameMSVCLibmCallees 3841 : public mlir::OpRewritePattern<mlir::LLVM::CallOp> { 3842 public: 3843 using OpRewritePattern::OpRewritePattern; 3844 3845 llvm::LogicalResult 3846 matchAndRewrite(mlir::LLVM::CallOp op, 3847 mlir::PatternRewriter &rewriter) const override { 3848 rewriter.startOpModification(op); 3849 auto callee = op.getCallee(); 3850 if (callee) 3851 if (*callee == "hypotf") 3852 op.setCalleeAttr(mlir::SymbolRefAttr::get(op.getContext(), "_hypotf")); 3853 3854 rewriter.finalizeOpModification(op); 3855 return mlir::success(); 3856 } 3857 }; 3858 3859 class RenameMSVCLibmFuncs 3860 : public mlir::OpRewritePattern<mlir::LLVM::LLVMFuncOp> { 3861 public: 3862 using OpRewritePattern::OpRewritePattern; 3863 3864 llvm::LogicalResult 3865 matchAndRewrite(mlir::LLVM::LLVMFuncOp op, 3866 mlir::PatternRewriter &rewriter) const override { 3867 rewriter.startOpModification(op); 3868 if (op.getSymName() == "hypotf") 3869 op.setSymNameAttr(rewriter.getStringAttr("_hypotf")); 3870 rewriter.finalizeOpModification(op); 3871 return mlir::success(); 3872 } 3873 }; 3874 } // namespace 3875 3876 namespace { 3877 /// Convert FIR dialect to LLVM dialect 3878 /// 3879 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An 3880 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect. 3881 class FIRToLLVMLowering 3882 : public fir::impl::FIRToLLVMLoweringBase<FIRToLLVMLowering> { 3883 public: 3884 FIRToLLVMLowering() = default; 3885 FIRToLLVMLowering(fir::FIRToLLVMPassOptions options) : options{options} {} 3886 mlir::ModuleOp getModule() { return getOperation(); } 3887 3888 void runOnOperation() override final { 3889 auto mod = getModule(); 3890 if (!forcedTargetTriple.empty()) 3891 fir::setTargetTriple(mod, forcedTargetTriple); 3892 3893 if (!forcedDataLayout.empty()) { 3894 llvm::DataLayout dl(forcedDataLayout); 3895 fir::support::setMLIRDataLayout(mod, dl); 3896 } 3897 3898 if (!forcedTargetCPU.empty()) 3899 fir::setTargetCPU(mod, forcedTargetCPU); 3900 3901 if (!forcedTuneCPU.empty()) 3902 fir::setTuneCPU(mod, forcedTuneCPU); 3903 3904 if (!forcedTargetFeatures.empty()) 3905 fir::setTargetFeatures(mod, forcedTargetFeatures); 3906 3907 if (typeDescriptorsRenamedForAssembly) 3908 options.typeDescriptorsRenamedForAssembly = 3909 typeDescriptorsRenamedForAssembly; 3910 3911 // Run dynamic pass pipeline for converting Math dialect 3912 // operations into other dialects (llvm, func, etc.). 3913 // Some conversions of Math operations cannot be done 3914 // by just using conversion patterns. This is true for 3915 // conversions that affect the ModuleOp, e.g. create new 3916 // function operations in it. We have to run such conversions 3917 // as passes here. 3918 mlir::OpPassManager mathConvertionPM("builtin.module"); 3919 3920 bool isAMDGCN = fir::getTargetTriple(mod).isAMDGCN(); 3921 // If compiling for AMD target some math operations must be lowered to AMD 3922 // GPU library calls, the rest can be converted to LLVM intrinsics, which 3923 // is handled in the mathToLLVM conversion. The lowering to libm calls is 3924 // not needed since all math operations are handled this way. 3925 if (isAMDGCN) 3926 mathConvertionPM.addPass(mlir::createConvertMathToROCDL()); 3927 3928 // Convert math::FPowI operations to inline implementation 3929 // only if the exponent's width is greater than 32, otherwise, 3930 // it will be lowered to LLVM intrinsic operation by a later conversion. 3931 mlir::ConvertMathToFuncsOptions mathToFuncsOptions{}; 3932 mathToFuncsOptions.minWidthOfFPowIExponent = 33; 3933 mathConvertionPM.addPass( 3934 mlir::createConvertMathToFuncs(mathToFuncsOptions)); 3935 mathConvertionPM.addPass(mlir::createConvertComplexToStandardPass()); 3936 // Convert Math dialect operations into LLVM dialect operations. 3937 // There is no way to prefer MathToLLVM patterns over MathToLibm 3938 // patterns (applied below), so we have to run MathToLLVM conversion here. 3939 mathConvertionPM.addNestedPass<mlir::func::FuncOp>( 3940 mlir::createConvertMathToLLVMPass()); 3941 if (mlir::failed(runPipeline(mathConvertionPM, mod))) 3942 return signalPassFailure(); 3943 3944 std::optional<mlir::DataLayout> dl = 3945 fir::support::getOrSetDataLayout(mod, /*allowDefaultLayout=*/true); 3946 if (!dl) { 3947 mlir::emitError(mod.getLoc(), 3948 "module operation must carry a data layout attribute " 3949 "to generate llvm IR from FIR"); 3950 signalPassFailure(); 3951 return; 3952 } 3953 3954 auto *context = getModule().getContext(); 3955 fir::LLVMTypeConverter typeConverter{getModule(), 3956 options.applyTBAA || applyTBAA, 3957 options.forceUnifiedTBAATree, *dl}; 3958 mlir::RewritePatternSet pattern(context); 3959 fir::populateFIRToLLVMConversionPatterns(typeConverter, pattern, options); 3960 mlir::populateFuncToLLVMConversionPatterns(typeConverter, pattern); 3961 mlir::populateOpenMPToLLVMConversionPatterns(typeConverter, pattern); 3962 mlir::arith::populateArithToLLVMConversionPatterns(typeConverter, pattern); 3963 mlir::cf::populateControlFlowToLLVMConversionPatterns(typeConverter, 3964 pattern); 3965 mlir::cf::populateAssertToLLVMConversionPattern(typeConverter, pattern); 3966 // Math operations that have not been converted yet must be converted 3967 // to Libm. 3968 if (!isAMDGCN) 3969 mlir::populateMathToLibmConversionPatterns(pattern); 3970 mlir::populateComplexToLLVMConversionPatterns(typeConverter, pattern); 3971 mlir::populateVectorToLLVMConversionPatterns(typeConverter, pattern); 3972 3973 // Flang specific overloads for OpenMP operations, to allow for special 3974 // handling of things like Box types. 3975 fir::populateOpenMPFIRToLLVMConversionPatterns(typeConverter, pattern); 3976 3977 mlir::ConversionTarget target{*context}; 3978 target.addLegalDialect<mlir::LLVM::LLVMDialect>(); 3979 // The OpenMP dialect is legal for Operations without regions, for those 3980 // which contains regions it is legal if the region contains only the 3981 // LLVM dialect. Add OpenMP dialect as a legal dialect for conversion and 3982 // legalize conversion of OpenMP operations without regions. 3983 mlir::configureOpenMPToLLVMConversionLegality(target, typeConverter); 3984 target.addLegalDialect<mlir::omp::OpenMPDialect>(); 3985 target.addLegalDialect<mlir::acc::OpenACCDialect>(); 3986 target.addLegalDialect<mlir::gpu::GPUDialect>(); 3987 3988 // required NOPs for applying a full conversion 3989 target.addLegalOp<mlir::ModuleOp>(); 3990 3991 // If we're on Windows, we might need to rename some libm calls. 3992 bool isMSVC = fir::getTargetTriple(mod).isOSMSVCRT(); 3993 if (isMSVC) { 3994 pattern.insert<RenameMSVCLibmCallees, RenameMSVCLibmFuncs>(context); 3995 3996 target.addDynamicallyLegalOp<mlir::LLVM::CallOp>( 3997 [](mlir::LLVM::CallOp op) { 3998 auto callee = op.getCallee(); 3999 if (!callee) 4000 return true; 4001 return *callee != "hypotf"; 4002 }); 4003 target.addDynamicallyLegalOp<mlir::LLVM::LLVMFuncOp>( 4004 [](mlir::LLVM::LLVMFuncOp op) { 4005 return op.getSymName() != "hypotf"; 4006 }); 4007 } 4008 4009 // apply the patterns 4010 if (mlir::failed(mlir::applyFullConversion(getModule(), target, 4011 std::move(pattern)))) { 4012 signalPassFailure(); 4013 } 4014 4015 // Run pass to add comdats to functions that have weak linkage on relevant 4016 // platforms 4017 if (fir::getTargetTriple(mod).supportsCOMDAT()) { 4018 mlir::OpPassManager comdatPM("builtin.module"); 4019 comdatPM.addPass(mlir::LLVM::createLLVMAddComdats()); 4020 if (mlir::failed(runPipeline(comdatPM, mod))) 4021 return signalPassFailure(); 4022 } 4023 } 4024 4025 private: 4026 fir::FIRToLLVMPassOptions options; 4027 }; 4028 4029 /// Lower from LLVM IR dialect to proper LLVM-IR and dump the module 4030 struct LLVMIRLoweringPass 4031 : public mlir::PassWrapper<LLVMIRLoweringPass, 4032 mlir::OperationPass<mlir::ModuleOp>> { 4033 MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(LLVMIRLoweringPass) 4034 4035 LLVMIRLoweringPass(llvm::raw_ostream &output, fir::LLVMIRLoweringPrinter p) 4036 : output{output}, printer{p} {} 4037 4038 mlir::ModuleOp getModule() { return getOperation(); } 4039 4040 void runOnOperation() override final { 4041 auto *ctx = getModule().getContext(); 4042 auto optName = getModule().getName(); 4043 llvm::LLVMContext llvmCtx; 4044 if (auto llvmModule = mlir::translateModuleToLLVMIR( 4045 getModule(), llvmCtx, optName ? *optName : "FIRModule")) { 4046 printer(*llvmModule, output); 4047 return; 4048 } 4049 4050 mlir::emitError(mlir::UnknownLoc::get(ctx), "could not emit LLVM-IR\n"); 4051 signalPassFailure(); 4052 } 4053 4054 private: 4055 llvm::raw_ostream &output; 4056 fir::LLVMIRLoweringPrinter printer; 4057 }; 4058 4059 } // namespace 4060 4061 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() { 4062 return std::make_unique<FIRToLLVMLowering>(); 4063 } 4064 4065 std::unique_ptr<mlir::Pass> 4066 fir::createFIRToLLVMPass(fir::FIRToLLVMPassOptions options) { 4067 return std::make_unique<FIRToLLVMLowering>(options); 4068 } 4069 4070 std::unique_ptr<mlir::Pass> 4071 fir::createLLVMDialectToLLVMPass(llvm::raw_ostream &output, 4072 fir::LLVMIRLoweringPrinter printer) { 4073 return std::make_unique<LLVMIRLoweringPass>(output, printer); 4074 } 4075 4076 void fir::populateFIRToLLVMConversionPatterns( 4077 const fir::LLVMTypeConverter &converter, mlir::RewritePatternSet &patterns, 4078 fir::FIRToLLVMPassOptions &options) { 4079 patterns.insert< 4080 AbsentOpConversion, AddcOpConversion, AddrOfOpConversion, 4081 AllocaOpConversion, AllocMemOpConversion, BoxAddrOpConversion, 4082 BoxCharLenOpConversion, BoxDimsOpConversion, BoxEleSizeOpConversion, 4083 BoxIsAllocOpConversion, BoxIsArrayOpConversion, BoxIsPtrOpConversion, 4084 BoxOffsetOpConversion, BoxProcHostOpConversion, BoxRankOpConversion, 4085 BoxTypeCodeOpConversion, BoxTypeDescOpConversion, CallOpConversion, 4086 CmpcOpConversion, ConvertOpConversion, CoordinateOpConversion, 4087 DTEntryOpConversion, DeclareOpConversion, DivcOpConversion, 4088 EmboxOpConversion, EmboxCharOpConversion, EmboxProcOpConversion, 4089 ExtractValueOpConversion, FieldIndexOpConversion, FirEndOpConversion, 4090 FreeMemOpConversion, GlobalLenOpConversion, GlobalOpConversion, 4091 InsertOnRangeOpConversion, IsPresentOpConversion, 4092 LenParamIndexOpConversion, LoadOpConversion, MulcOpConversion, 4093 NegcOpConversion, NoReassocOpConversion, SelectCaseOpConversion, 4094 SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion, 4095 ShapeOpConversion, ShapeShiftOpConversion, ShiftOpConversion, 4096 SliceOpConversion, StoreOpConversion, StringLitOpConversion, 4097 SubcOpConversion, TypeDescOpConversion, TypeInfoOpConversion, 4098 UnboxCharOpConversion, UnboxProcOpConversion, UndefOpConversion, 4099 UnreachableOpConversion, XArrayCoorOpConversion, XEmboxOpConversion, 4100 XReboxOpConversion, ZeroOpConversion>(converter, options); 4101 4102 // Patterns that are populated without a type converter do not trigger 4103 // target materializations for the operands of the root op. 4104 patterns.insert<HasValueOpConversion, InsertValueOpConversion>( 4105 patterns.getContext()); 4106 } 4107