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 #include "CGOps.h" 15 #include "PassDetail.h" 16 #include "flang/ISO_Fortran_binding.h" 17 #include "flang/Optimizer/Dialect/FIRAttr.h" 18 #include "flang/Optimizer/Dialect/FIROps.h" 19 #include "flang/Optimizer/Support/InternalNames.h" 20 #include "flang/Optimizer/Support/TypeCode.h" 21 #include "flang/Semantics/runtime-type-info.h" 22 #include "mlir/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.h" 23 #include "mlir/Conversion/ControlFlowToLLVM/ControlFlowToLLVM.h" 24 #include "mlir/Conversion/FuncToLLVM/ConvertFuncToLLVM.h" 25 #include "mlir/Conversion/LLVMCommon/Pattern.h" 26 #include "mlir/Conversion/OpenMPToLLVM/ConvertOpenMPToLLVM.h" 27 #include "mlir/IR/BuiltinTypes.h" 28 #include "mlir/IR/Matchers.h" 29 #include "mlir/Pass/Pass.h" 30 #include "mlir/Target/LLVMIR/ModuleTranslation.h" 31 #include "llvm/ADT/ArrayRef.h" 32 33 #define DEBUG_TYPE "flang-codegen" 34 35 using namespace mlir; 36 37 // fir::LLVMTypeConverter for converting to LLVM IR dialect types. 38 #include "TypeConverter.h" 39 40 // TODO: This should really be recovered from the specified target. 41 static constexpr unsigned defaultAlign = 8; 42 43 /// `fir.box` attribute values as defined for CFI_attribute_t in 44 /// flang/ISO_Fortran_binding.h. 45 static constexpr unsigned kAttrPointer = CFI_attribute_pointer; 46 static constexpr unsigned kAttrAllocatable = CFI_attribute_allocatable; 47 48 static inline mlir::Type getVoidPtrType(mlir::MLIRContext *context) { 49 return mlir::LLVM::LLVMPointerType::get(mlir::IntegerType::get(context, 8)); 50 } 51 52 static mlir::LLVM::ConstantOp 53 genConstantIndex(mlir::Location loc, mlir::Type ity, 54 mlir::ConversionPatternRewriter &rewriter, 55 std::int64_t offset) { 56 auto cattr = rewriter.getI64IntegerAttr(offset); 57 return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr); 58 } 59 60 static Block *createBlock(mlir::ConversionPatternRewriter &rewriter, 61 mlir::Block *insertBefore) { 62 assert(insertBefore && "expected valid insertion block"); 63 return rewriter.createBlock(insertBefore->getParent(), 64 mlir::Region::iterator(insertBefore)); 65 } 66 67 namespace { 68 /// FIR conversion pattern template 69 template <typename FromOp> 70 class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> { 71 public: 72 explicit FIROpConversion(fir::LLVMTypeConverter &lowering, 73 const fir::FIRToLLVMPassOptions &options) 74 : mlir::ConvertOpToLLVMPattern<FromOp>(lowering), options(options) {} 75 76 protected: 77 mlir::Type convertType(mlir::Type ty) const { 78 return lowerTy().convertType(ty); 79 } 80 mlir::Type voidPtrTy() const { return getVoidPtrType(); } 81 82 mlir::Type getVoidPtrType() const { 83 return mlir::LLVM::LLVMPointerType::get( 84 mlir::IntegerType::get(&lowerTy().getContext(), 8)); 85 } 86 87 mlir::LLVM::ConstantOp 88 genI32Constant(mlir::Location loc, mlir::ConversionPatternRewriter &rewriter, 89 int value) const { 90 mlir::Type i32Ty = rewriter.getI32Type(); 91 mlir::IntegerAttr attr = rewriter.getI32IntegerAttr(value); 92 return rewriter.create<mlir::LLVM::ConstantOp>(loc, i32Ty, attr); 93 } 94 95 mlir::LLVM::ConstantOp 96 genConstantOffset(mlir::Location loc, 97 mlir::ConversionPatternRewriter &rewriter, 98 int offset) const { 99 mlir::Type ity = lowerTy().offsetType(); 100 mlir::IntegerAttr cattr = rewriter.getI32IntegerAttr(offset); 101 return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr); 102 } 103 104 /// Perform an extension or truncation as needed on an integer value. Lowering 105 /// to the specific target may involve some sign-extending or truncation of 106 /// values, particularly to fit them from abstract box types to the 107 /// appropriate reified structures. 108 mlir::Value integerCast(mlir::Location loc, 109 mlir::ConversionPatternRewriter &rewriter, 110 mlir::Type ty, mlir::Value val) const { 111 auto valTy = val.getType(); 112 // If the value was not yet lowered, lower its type so that it can 113 // be used in getPrimitiveTypeSizeInBits. 114 if (!valTy.isa<mlir::IntegerType>()) 115 valTy = convertType(valTy); 116 auto toSize = mlir::LLVM::getPrimitiveTypeSizeInBits(ty); 117 auto fromSize = mlir::LLVM::getPrimitiveTypeSizeInBits(valTy); 118 if (toSize < fromSize) 119 return rewriter.create<mlir::LLVM::TruncOp>(loc, ty, val); 120 if (toSize > fromSize) 121 return rewriter.create<mlir::LLVM::SExtOp>(loc, ty, val); 122 return val; 123 } 124 125 /// Construct code sequence to extract the specifc value from a `fir.box`. 126 mlir::Value getValueFromBox(mlir::Location loc, mlir::Value box, 127 mlir::Type resultTy, 128 mlir::ConversionPatternRewriter &rewriter, 129 unsigned boxValue) const { 130 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 131 mlir::LLVM::ConstantOp cValuePos = 132 genConstantOffset(loc, rewriter, boxValue); 133 auto pty = mlir::LLVM::LLVMPointerType::get(resultTy); 134 auto p = rewriter.create<mlir::LLVM::GEPOp>( 135 loc, pty, box, mlir::ValueRange{c0, cValuePos}); 136 return rewriter.create<mlir::LLVM::LoadOp>(loc, resultTy, p); 137 } 138 139 /// Method to construct code sequence to get the triple for dimension `dim` 140 /// from a box. 141 SmallVector<mlir::Value, 3> 142 getDimsFromBox(mlir::Location loc, ArrayRef<mlir::Type> retTys, 143 mlir::Value box, mlir::Value dim, 144 mlir::ConversionPatternRewriter &rewriter) const { 145 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 146 mlir::LLVM::ConstantOp cDims = 147 genConstantOffset(loc, rewriter, kDimsPosInBox); 148 mlir::LLVM::LoadOp l0 = 149 loadFromOffset(loc, box, c0, cDims, dim, 0, retTys[0], rewriter); 150 mlir::LLVM::LoadOp l1 = 151 loadFromOffset(loc, box, c0, cDims, dim, 1, retTys[1], rewriter); 152 mlir::LLVM::LoadOp l2 = 153 loadFromOffset(loc, box, c0, cDims, dim, 2, retTys[2], rewriter); 154 return {l0.getResult(), l1.getResult(), l2.getResult()}; 155 } 156 157 mlir::LLVM::LoadOp 158 loadFromOffset(mlir::Location loc, mlir::Value a, mlir::LLVM::ConstantOp c0, 159 mlir::LLVM::ConstantOp cDims, mlir::Value dim, int off, 160 mlir::Type ty, 161 mlir::ConversionPatternRewriter &rewriter) const { 162 auto pty = mlir::LLVM::LLVMPointerType::get(ty); 163 mlir::LLVM::ConstantOp c = genConstantOffset(loc, rewriter, off); 164 mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, a, c0, cDims, dim, c); 165 return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p); 166 } 167 168 mlir::Value 169 loadStrideFromBox(mlir::Location loc, mlir::Value box, unsigned dim, 170 mlir::ConversionPatternRewriter &rewriter) const { 171 auto idxTy = lowerTy().indexType(); 172 auto c0 = genConstantOffset(loc, rewriter, 0); 173 auto cDims = genConstantOffset(loc, rewriter, kDimsPosInBox); 174 auto dimValue = genConstantIndex(loc, idxTy, rewriter, dim); 175 return loadFromOffset(loc, box, c0, cDims, dimValue, kDimStridePos, idxTy, 176 rewriter); 177 } 178 179 /// Read base address from a fir.box. Returned address has type ty. 180 mlir::Value 181 loadBaseAddrFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box, 182 mlir::ConversionPatternRewriter &rewriter) const { 183 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 184 mlir::LLVM::ConstantOp cAddr = 185 genConstantOffset(loc, rewriter, kAddrPosInBox); 186 auto pty = mlir::LLVM::LLVMPointerType::get(ty); 187 mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cAddr); 188 return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p); 189 } 190 191 mlir::Value 192 loadElementSizeFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box, 193 mlir::ConversionPatternRewriter &rewriter) const { 194 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 195 mlir::LLVM::ConstantOp cElemLen = 196 genConstantOffset(loc, rewriter, kElemLenPosInBox); 197 auto pty = mlir::LLVM::LLVMPointerType::get(ty); 198 mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cElemLen); 199 return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p); 200 } 201 202 // Get the element type given an LLVM type that is of the form 203 // [llvm.ptr](array|struct|vector)+ and the provided indexes. 204 static mlir::Type getBoxEleTy(mlir::Type type, 205 llvm::ArrayRef<unsigned> indexes) { 206 if (auto t = type.dyn_cast<mlir::LLVM::LLVMPointerType>()) 207 type = t.getElementType(); 208 for (auto i : indexes) { 209 if (auto t = type.dyn_cast<mlir::LLVM::LLVMStructType>()) { 210 assert(!t.isOpaque() && i < t.getBody().size()); 211 type = t.getBody()[i]; 212 } else if (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 213 type = t.getElementType(); 214 } else if (auto t = type.dyn_cast<mlir::VectorType>()) { 215 type = t.getElementType(); 216 } else { 217 fir::emitFatalError(mlir::UnknownLoc::get(type.getContext()), 218 "request for invalid box element type"); 219 } 220 } 221 return type; 222 } 223 224 // Return LLVM type of the base address given the LLVM type 225 // of the related descriptor (lowered fir.box type). 226 static mlir::Type getBaseAddrTypeFromBox(mlir::Type type) { 227 return getBoxEleTy(type, {kAddrPosInBox}); 228 } 229 230 // Load the attribute from the \p box and perform a check against \p maskValue 231 // The final comparison is implemented as `(attribute & maskValue) != 0`. 232 mlir::Value genBoxAttributeCheck(mlir::Location loc, mlir::Value box, 233 mlir::ConversionPatternRewriter &rewriter, 234 unsigned maskValue) const { 235 mlir::Type attrTy = rewriter.getI32Type(); 236 mlir::Value attribute = 237 getValueFromBox(loc, box, attrTy, rewriter, kAttributePosInBox); 238 mlir::LLVM::ConstantOp attrMask = 239 genConstantOffset(loc, rewriter, maskValue); 240 auto maskRes = 241 rewriter.create<mlir::LLVM::AndOp>(loc, attrTy, attribute, attrMask); 242 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 243 return rewriter.create<mlir::LLVM::ICmpOp>( 244 loc, mlir::LLVM::ICmpPredicate::ne, maskRes, c0); 245 } 246 247 template <typename... ARGS> 248 mlir::LLVM::GEPOp genGEP(mlir::Location loc, mlir::Type ty, 249 mlir::ConversionPatternRewriter &rewriter, 250 mlir::Value base, ARGS... args) const { 251 SmallVector<mlir::Value> cv{args...}; 252 return rewriter.create<mlir::LLVM::GEPOp>(loc, ty, base, cv); 253 } 254 255 fir::LLVMTypeConverter &lowerTy() const { 256 return *static_cast<fir::LLVMTypeConverter *>(this->getTypeConverter()); 257 } 258 259 const fir::FIRToLLVMPassOptions &options; 260 }; 261 262 /// FIR conversion pattern template 263 template <typename FromOp> 264 class FIROpAndTypeConversion : public FIROpConversion<FromOp> { 265 public: 266 using FIROpConversion<FromOp>::FIROpConversion; 267 using OpAdaptor = typename FromOp::Adaptor; 268 269 mlir::LogicalResult 270 matchAndRewrite(FromOp op, OpAdaptor adaptor, 271 mlir::ConversionPatternRewriter &rewriter) const final { 272 mlir::Type ty = this->convertType(op.getType()); 273 return doRewrite(op, ty, adaptor, rewriter); 274 } 275 276 virtual mlir::LogicalResult 277 doRewrite(FromOp addr, mlir::Type ty, OpAdaptor adaptor, 278 mlir::ConversionPatternRewriter &rewriter) const = 0; 279 }; 280 281 // Lower `fir.address_of` operation to `llvm.address_of` operation. 282 struct AddrOfOpConversion : public FIROpConversion<fir::AddrOfOp> { 283 using FIROpConversion::FIROpConversion; 284 285 mlir::LogicalResult 286 matchAndRewrite(fir::AddrOfOp addr, OpAdaptor adaptor, 287 mlir::ConversionPatternRewriter &rewriter) const override { 288 auto ty = convertType(addr.getType()); 289 rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>( 290 addr, ty, addr.getSymbol().getRootReference().getValue()); 291 return success(); 292 } 293 }; 294 } // namespace 295 296 /// Lookup the function to compute the memory size of this parametric derived 297 /// type. The size of the object may depend on the LEN type parameters of the 298 /// derived type. 299 static mlir::LLVM::LLVMFuncOp 300 getDependentTypeMemSizeFn(fir::RecordType recTy, fir::AllocaOp op, 301 mlir::ConversionPatternRewriter &rewriter) { 302 auto module = op->getParentOfType<mlir::ModuleOp>(); 303 std::string name = recTy.getName().str() + "P.mem.size"; 304 return module.lookupSymbol<mlir::LLVM::LLVMFuncOp>(name); 305 } 306 307 namespace { 308 /// convert to LLVM IR dialect `alloca` 309 struct AllocaOpConversion : public FIROpConversion<fir::AllocaOp> { 310 using FIROpConversion::FIROpConversion; 311 312 mlir::LogicalResult 313 matchAndRewrite(fir::AllocaOp alloc, OpAdaptor adaptor, 314 mlir::ConversionPatternRewriter &rewriter) const override { 315 mlir::ValueRange operands = adaptor.getOperands(); 316 auto loc = alloc.getLoc(); 317 mlir::Type ity = lowerTy().indexType(); 318 unsigned i = 0; 319 mlir::Value size = genConstantIndex(loc, ity, rewriter, 1).getResult(); 320 mlir::Type ty = convertType(alloc.getType()); 321 mlir::Type resultTy = ty; 322 if (alloc.hasLenParams()) { 323 unsigned end = alloc.numLenParams(); 324 llvm::SmallVector<mlir::Value> lenParams; 325 for (; i < end; ++i) 326 lenParams.push_back(operands[i]); 327 mlir::Type scalarType = fir::unwrapSequenceType(alloc.getInType()); 328 if (auto chrTy = scalarType.dyn_cast<fir::CharacterType>()) { 329 fir::CharacterType rawCharTy = fir::CharacterType::getUnknownLen( 330 chrTy.getContext(), chrTy.getFKind()); 331 ty = mlir::LLVM::LLVMPointerType::get(convertType(rawCharTy)); 332 assert(end == 1); 333 size = integerCast(loc, rewriter, ity, lenParams[0]); 334 } else if (auto recTy = scalarType.dyn_cast<fir::RecordType>()) { 335 mlir::LLVM::LLVMFuncOp memSizeFn = 336 getDependentTypeMemSizeFn(recTy, alloc, rewriter); 337 if (!memSizeFn) 338 emitError(loc, "did not find allocation function"); 339 mlir::NamedAttribute attr = rewriter.getNamedAttr( 340 "callee", mlir::SymbolRefAttr::get(memSizeFn)); 341 auto call = rewriter.create<mlir::LLVM::CallOp>( 342 loc, ity, lenParams, llvm::ArrayRef<mlir::NamedAttribute>{attr}); 343 size = call.getResult(0); 344 ty = mlir::LLVM::LLVMPointerType::get( 345 mlir::IntegerType::get(alloc.getContext(), 8)); 346 } else { 347 return emitError(loc, "unexpected type ") 348 << scalarType << " with type parameters"; 349 } 350 } 351 if (alloc.hasShapeOperands()) { 352 mlir::Type allocEleTy = fir::unwrapRefType(alloc.getType()); 353 // Scale the size by constant factors encoded in the array type. 354 // We only do this for arrays that don't have a constant interior, since 355 // those are the only ones that get decayed to a pointer to the element 356 // type. 357 if (auto seqTy = allocEleTy.dyn_cast<fir::SequenceType>()) { 358 if (!seqTy.hasConstantInterior()) { 359 fir::SequenceType::Extent constSize = 1; 360 for (auto extent : seqTy.getShape()) 361 if (extent != fir::SequenceType::getUnknownExtent()) 362 constSize *= extent; 363 mlir::Value constVal{ 364 genConstantIndex(loc, ity, rewriter, constSize).getResult()}; 365 size = rewriter.create<mlir::LLVM::MulOp>(loc, ity, size, constVal); 366 } 367 } 368 unsigned end = operands.size(); 369 for (; i < end; ++i) 370 size = rewriter.create<mlir::LLVM::MulOp>( 371 loc, ity, size, integerCast(loc, rewriter, ity, operands[i])); 372 } 373 if (ty == resultTy) { 374 // Do not emit the bitcast if ty and resultTy are the same. 375 rewriter.replaceOpWithNewOp<mlir::LLVM::AllocaOp>(alloc, ty, size, 376 alloc->getAttrs()); 377 } else { 378 auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, ty, size, 379 alloc->getAttrs()); 380 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(alloc, resultTy, al); 381 } 382 return success(); 383 } 384 }; 385 } // namespace 386 387 /// Construct an `llvm.extractvalue` instruction. It will return value at 388 /// element \p x from \p tuple. 389 static mlir::LLVM::ExtractValueOp 390 genExtractValueWithIndex(mlir::Location loc, mlir::Value tuple, mlir::Type ty, 391 mlir::ConversionPatternRewriter &rewriter, 392 mlir::MLIRContext *ctx, int x) { 393 auto cx = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(x)); 394 auto xty = ty.cast<mlir::LLVM::LLVMStructType>().getBody()[x]; 395 return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, xty, tuple, cx); 396 } 397 398 namespace { 399 /// Lower `fir.box_addr` to the sequence of operations to extract the first 400 /// element of the box. 401 struct BoxAddrOpConversion : public FIROpConversion<fir::BoxAddrOp> { 402 using FIROpConversion::FIROpConversion; 403 404 mlir::LogicalResult 405 matchAndRewrite(fir::BoxAddrOp boxaddr, OpAdaptor adaptor, 406 mlir::ConversionPatternRewriter &rewriter) const override { 407 mlir::Value a = adaptor.getOperands()[0]; 408 auto loc = boxaddr.getLoc(); 409 mlir::Type ty = convertType(boxaddr.getType()); 410 if (auto argty = boxaddr.getVal().getType().dyn_cast<fir::BoxType>()) { 411 rewriter.replaceOp(boxaddr, loadBaseAddrFromBox(loc, ty, a, rewriter)); 412 } else { 413 auto c0attr = rewriter.getI32IntegerAttr(0); 414 auto c0 = mlir::ArrayAttr::get(boxaddr.getContext(), c0attr); 415 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(boxaddr, ty, a, 416 c0); 417 } 418 return success(); 419 } 420 }; 421 422 /// Convert `!fir.boxchar_len` to `!llvm.extractvalue` for the 2nd part of the 423 /// boxchar. 424 struct BoxCharLenOpConversion : public FIROpConversion<fir::BoxCharLenOp> { 425 using FIROpConversion::FIROpConversion; 426 427 mlir::LogicalResult 428 matchAndRewrite(fir::BoxCharLenOp boxCharLen, OpAdaptor adaptor, 429 mlir::ConversionPatternRewriter &rewriter) const override { 430 mlir::Value boxChar = adaptor.getOperands()[0]; 431 mlir::Location loc = boxChar.getLoc(); 432 mlir::MLIRContext *ctx = boxChar.getContext(); 433 mlir::Type returnValTy = boxCharLen.getResult().getType(); 434 435 constexpr int boxcharLenIdx = 1; 436 mlir::LLVM::ExtractValueOp len = genExtractValueWithIndex( 437 loc, boxChar, boxChar.getType(), rewriter, ctx, boxcharLenIdx); 438 mlir::Value lenAfterCast = integerCast(loc, rewriter, returnValTy, len); 439 rewriter.replaceOp(boxCharLen, lenAfterCast); 440 441 return success(); 442 } 443 }; 444 445 /// Lower `fir.box_dims` to a sequence of operations to extract the requested 446 /// dimension infomartion from the boxed value. 447 /// Result in a triple set of GEPs and loads. 448 struct BoxDimsOpConversion : public FIROpConversion<fir::BoxDimsOp> { 449 using FIROpConversion::FIROpConversion; 450 451 mlir::LogicalResult 452 matchAndRewrite(fir::BoxDimsOp boxdims, OpAdaptor adaptor, 453 mlir::ConversionPatternRewriter &rewriter) const override { 454 SmallVector<mlir::Type, 3> resultTypes = { 455 convertType(boxdims.getResult(0).getType()), 456 convertType(boxdims.getResult(1).getType()), 457 convertType(boxdims.getResult(2).getType()), 458 }; 459 auto results = 460 getDimsFromBox(boxdims.getLoc(), resultTypes, adaptor.getOperands()[0], 461 adaptor.getOperands()[1], rewriter); 462 rewriter.replaceOp(boxdims, results); 463 return success(); 464 } 465 }; 466 467 /// Lower `fir.box_elesize` to a sequence of operations ro extract the size of 468 /// an element in the boxed value. 469 struct BoxEleSizeOpConversion : public FIROpConversion<fir::BoxEleSizeOp> { 470 using FIROpConversion::FIROpConversion; 471 472 mlir::LogicalResult 473 matchAndRewrite(fir::BoxEleSizeOp boxelesz, OpAdaptor adaptor, 474 mlir::ConversionPatternRewriter &rewriter) const override { 475 mlir::Value a = adaptor.getOperands()[0]; 476 auto loc = boxelesz.getLoc(); 477 auto ty = convertType(boxelesz.getType()); 478 auto elemSize = getValueFromBox(loc, a, ty, rewriter, kElemLenPosInBox); 479 rewriter.replaceOp(boxelesz, elemSize); 480 return success(); 481 } 482 }; 483 484 /// Lower `fir.box_isalloc` to a sequence of operations to determine if the 485 /// boxed value was from an ALLOCATABLE entity. 486 struct BoxIsAllocOpConversion : public FIROpConversion<fir::BoxIsAllocOp> { 487 using FIROpConversion::FIROpConversion; 488 489 mlir::LogicalResult 490 matchAndRewrite(fir::BoxIsAllocOp boxisalloc, OpAdaptor adaptor, 491 mlir::ConversionPatternRewriter &rewriter) const override { 492 mlir::Value box = adaptor.getOperands()[0]; 493 auto loc = boxisalloc.getLoc(); 494 mlir::Value check = 495 genBoxAttributeCheck(loc, box, rewriter, kAttrAllocatable); 496 rewriter.replaceOp(boxisalloc, check); 497 return success(); 498 } 499 }; 500 501 /// Lower `fir.box_isarray` to a sequence of operations to determine if the 502 /// boxed is an array. 503 struct BoxIsArrayOpConversion : public FIROpConversion<fir::BoxIsArrayOp> { 504 using FIROpConversion::FIROpConversion; 505 506 mlir::LogicalResult 507 matchAndRewrite(fir::BoxIsArrayOp boxisarray, OpAdaptor adaptor, 508 mlir::ConversionPatternRewriter &rewriter) const override { 509 mlir::Value a = adaptor.getOperands()[0]; 510 auto loc = boxisarray.getLoc(); 511 auto rank = 512 getValueFromBox(loc, a, rewriter.getI32Type(), rewriter, kRankPosInBox); 513 auto c0 = genConstantOffset(loc, rewriter, 0); 514 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>( 515 boxisarray, mlir::LLVM::ICmpPredicate::ne, rank, c0); 516 return success(); 517 } 518 }; 519 520 /// Lower `fir.box_isptr` to a sequence of operations to determined if the 521 /// boxed value was from a POINTER entity. 522 struct BoxIsPtrOpConversion : public FIROpConversion<fir::BoxIsPtrOp> { 523 using FIROpConversion::FIROpConversion; 524 525 mlir::LogicalResult 526 matchAndRewrite(fir::BoxIsPtrOp boxisptr, OpAdaptor adaptor, 527 mlir::ConversionPatternRewriter &rewriter) const override { 528 mlir::Value box = adaptor.getOperands()[0]; 529 auto loc = boxisptr.getLoc(); 530 mlir::Value check = genBoxAttributeCheck(loc, box, rewriter, kAttrPointer); 531 rewriter.replaceOp(boxisptr, check); 532 return success(); 533 } 534 }; 535 536 /// Lower `fir.box_rank` to the sequence of operation to extract the rank from 537 /// the box. 538 struct BoxRankOpConversion : public FIROpConversion<fir::BoxRankOp> { 539 using FIROpConversion::FIROpConversion; 540 541 mlir::LogicalResult 542 matchAndRewrite(fir::BoxRankOp boxrank, OpAdaptor adaptor, 543 mlir::ConversionPatternRewriter &rewriter) const override { 544 mlir::Value a = adaptor.getOperands()[0]; 545 auto loc = boxrank.getLoc(); 546 mlir::Type ty = convertType(boxrank.getType()); 547 auto result = getValueFromBox(loc, a, ty, rewriter, kRankPosInBox); 548 rewriter.replaceOp(boxrank, result); 549 return success(); 550 } 551 }; 552 553 /// Lower `fir.boxproc_host` operation. Extracts the host pointer from the 554 /// boxproc. 555 /// TODO: Part of supporting Fortran 2003 procedure pointers. 556 struct BoxProcHostOpConversion : public FIROpConversion<fir::BoxProcHostOp> { 557 using FIROpConversion::FIROpConversion; 558 559 mlir::LogicalResult 560 matchAndRewrite(fir::BoxProcHostOp boxprochost, OpAdaptor adaptor, 561 mlir::ConversionPatternRewriter &rewriter) const override { 562 TODO(boxprochost.getLoc(), "fir.boxproc_host codegen"); 563 return failure(); 564 } 565 }; 566 567 /// Lower `fir.box_tdesc` to the sequence of operations to extract the type 568 /// descriptor from the box. 569 struct BoxTypeDescOpConversion : public FIROpConversion<fir::BoxTypeDescOp> { 570 using FIROpConversion::FIROpConversion; 571 572 mlir::LogicalResult 573 matchAndRewrite(fir::BoxTypeDescOp boxtypedesc, OpAdaptor adaptor, 574 mlir::ConversionPatternRewriter &rewriter) const override { 575 mlir::Value box = adaptor.getOperands()[0]; 576 auto loc = boxtypedesc.getLoc(); 577 mlir::Type typeTy = 578 fir::getDescFieldTypeModel<kTypePosInBox>()(boxtypedesc.getContext()); 579 auto result = getValueFromBox(loc, box, typeTy, rewriter, kTypePosInBox); 580 auto typePtrTy = mlir::LLVM::LLVMPointerType::get(typeTy); 581 rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(boxtypedesc, typePtrTy, 582 result); 583 return success(); 584 } 585 }; 586 587 /// Lower `fir.string_lit` to LLVM IR dialect operation. 588 struct StringLitOpConversion : public FIROpConversion<fir::StringLitOp> { 589 using FIROpConversion::FIROpConversion; 590 591 mlir::LogicalResult 592 matchAndRewrite(fir::StringLitOp constop, OpAdaptor adaptor, 593 mlir::ConversionPatternRewriter &rewriter) const override { 594 auto ty = convertType(constop.getType()); 595 auto attr = constop.getValue(); 596 if (attr.isa<mlir::StringAttr>()) { 597 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(constop, ty, attr); 598 return success(); 599 } 600 601 auto arr = attr.cast<mlir::ArrayAttr>(); 602 auto charTy = constop.getType().cast<fir::CharacterType>(); 603 unsigned bits = lowerTy().characterBitsize(charTy); 604 mlir::Type intTy = rewriter.getIntegerType(bits); 605 auto attrs = llvm::map_range( 606 arr.getValue(), [intTy, bits](mlir::Attribute attr) -> Attribute { 607 return mlir::IntegerAttr::get( 608 intTy, 609 attr.cast<mlir::IntegerAttr>().getValue().sextOrTrunc(bits)); 610 }); 611 mlir::Type vecType = mlir::VectorType::get(arr.size(), intTy); 612 auto denseAttr = mlir::DenseElementsAttr::get( 613 vecType.cast<mlir::ShapedType>(), llvm::to_vector<8>(attrs)); 614 rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(constop, ty, 615 denseAttr); 616 return success(); 617 } 618 }; 619 620 // `fir.call` -> `llvm.call` 621 struct CallOpConversion : public FIROpConversion<fir::CallOp> { 622 using FIROpConversion::FIROpConversion; 623 624 mlir::LogicalResult 625 matchAndRewrite(fir::CallOp call, OpAdaptor adaptor, 626 mlir::ConversionPatternRewriter &rewriter) const override { 627 SmallVector<mlir::Type> resultTys; 628 for (auto r : call.getResults()) 629 resultTys.push_back(convertType(r.getType())); 630 rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>( 631 call, resultTys, adaptor.getOperands(), call->getAttrs()); 632 return success(); 633 } 634 }; 635 } // namespace 636 637 static mlir::Type getComplexEleTy(mlir::Type complex) { 638 if (auto cc = complex.dyn_cast<mlir::ComplexType>()) 639 return cc.getElementType(); 640 return complex.cast<fir::ComplexType>().getElementType(); 641 } 642 643 namespace { 644 /// Compare complex values 645 /// 646 /// Per 10.1, the only comparisons available are .EQ. (oeq) and .NE. (une). 647 /// 648 /// For completeness, all other comparison are done on the real component only. 649 struct CmpcOpConversion : public FIROpConversion<fir::CmpcOp> { 650 using FIROpConversion::FIROpConversion; 651 652 mlir::LogicalResult 653 matchAndRewrite(fir::CmpcOp cmp, OpAdaptor adaptor, 654 mlir::ConversionPatternRewriter &rewriter) const override { 655 mlir::ValueRange operands = adaptor.getOperands(); 656 mlir::MLIRContext *ctxt = cmp.getContext(); 657 mlir::Type eleTy = convertType(getComplexEleTy(cmp.getLhs().getType())); 658 mlir::Type resTy = convertType(cmp.getType()); 659 mlir::Location loc = cmp.getLoc(); 660 auto pos0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0)); 661 SmallVector<mlir::Value, 2> rp{rewriter.create<mlir::LLVM::ExtractValueOp>( 662 loc, eleTy, operands[0], pos0), 663 rewriter.create<mlir::LLVM::ExtractValueOp>( 664 loc, eleTy, operands[1], pos0)}; 665 auto rcp = 666 rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, rp, cmp->getAttrs()); 667 auto pos1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1)); 668 SmallVector<mlir::Value, 2> ip{rewriter.create<mlir::LLVM::ExtractValueOp>( 669 loc, eleTy, operands[0], pos1), 670 rewriter.create<mlir::LLVM::ExtractValueOp>( 671 loc, eleTy, operands[1], pos1)}; 672 auto icp = 673 rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, ip, cmp->getAttrs()); 674 SmallVector<mlir::Value, 2> cp{rcp, icp}; 675 switch (cmp.getPredicate()) { 676 case mlir::arith::CmpFPredicate::OEQ: // .EQ. 677 rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(cmp, resTy, cp); 678 break; 679 case mlir::arith::CmpFPredicate::UNE: // .NE. 680 rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(cmp, resTy, cp); 681 break; 682 default: 683 rewriter.replaceOp(cmp, rcp.getResult()); 684 break; 685 } 686 return success(); 687 } 688 }; 689 690 /// Lower complex constants 691 struct ConstcOpConversion : public FIROpConversion<fir::ConstcOp> { 692 using FIROpConversion::FIROpConversion; 693 694 mlir::LogicalResult 695 matchAndRewrite(fir::ConstcOp conc, OpAdaptor, 696 mlir::ConversionPatternRewriter &rewriter) const override { 697 mlir::Location loc = conc.getLoc(); 698 mlir::MLIRContext *ctx = conc.getContext(); 699 mlir::Type ty = convertType(conc.getType()); 700 mlir::Type ety = convertType(getComplexEleTy(conc.getType())); 701 auto realFloatAttr = mlir::FloatAttr::get(ety, getValue(conc.getReal())); 702 auto realPart = 703 rewriter.create<mlir::LLVM::ConstantOp>(loc, ety, realFloatAttr); 704 auto imFloatAttr = mlir::FloatAttr::get(ety, getValue(conc.getImaginary())); 705 auto imPart = 706 rewriter.create<mlir::LLVM::ConstantOp>(loc, ety, imFloatAttr); 707 auto realIndex = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 708 auto imIndex = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 709 auto undef = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 710 auto setReal = rewriter.create<mlir::LLVM::InsertValueOp>( 711 loc, ty, undef, realPart, realIndex); 712 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(conc, ty, setReal, 713 imPart, imIndex); 714 return success(); 715 } 716 717 inline APFloat getValue(mlir::Attribute attr) const { 718 return attr.cast<fir::RealAttr>().getValue(); 719 } 720 }; 721 722 /// convert value of from-type to value of to-type 723 struct ConvertOpConversion : public FIROpConversion<fir::ConvertOp> { 724 using FIROpConversion::FIROpConversion; 725 726 static bool isFloatingPointTy(mlir::Type ty) { 727 return ty.isa<mlir::FloatType>(); 728 } 729 730 mlir::LogicalResult 731 matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor, 732 mlir::ConversionPatternRewriter &rewriter) const override { 733 auto fromFirTy = convert.getValue().getType(); 734 auto toFirTy = convert.getRes().getType(); 735 auto fromTy = convertType(fromFirTy); 736 auto toTy = convertType(toFirTy); 737 mlir::Value op0 = adaptor.getOperands()[0]; 738 if (fromTy == toTy) { 739 rewriter.replaceOp(convert, op0); 740 return success(); 741 } 742 auto loc = convert.getLoc(); 743 auto convertFpToFp = [&](mlir::Value val, unsigned fromBits, 744 unsigned toBits, mlir::Type toTy) -> mlir::Value { 745 if (fromBits == toBits) { 746 // TODO: Converting between two floating-point representations with the 747 // same bitwidth is not allowed for now. 748 mlir::emitError(loc, 749 "cannot implicitly convert between two floating-point " 750 "representations of the same bitwidth"); 751 return {}; 752 } 753 if (fromBits > toBits) 754 return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val); 755 return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val); 756 }; 757 // Complex to complex conversion. 758 if (fir::isa_complex(fromFirTy) && fir::isa_complex(toFirTy)) { 759 // Special case: handle the conversion of a complex such that both the 760 // real and imaginary parts are converted together. 761 auto zero = mlir::ArrayAttr::get(convert.getContext(), 762 rewriter.getI32IntegerAttr(0)); 763 auto one = mlir::ArrayAttr::get(convert.getContext(), 764 rewriter.getI32IntegerAttr(1)); 765 auto ty = convertType(getComplexEleTy(convert.getValue().getType())); 766 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, zero); 767 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, one); 768 auto nt = convertType(getComplexEleTy(convert.getRes().getType())); 769 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty); 770 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt); 771 auto rc = convertFpToFp(rp, fromBits, toBits, nt); 772 auto ic = convertFpToFp(ip, fromBits, toBits, nt); 773 auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy); 774 auto i1 = 775 rewriter.create<mlir::LLVM::InsertValueOp>(loc, toTy, un, rc, zero); 776 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, toTy, i1, 777 ic, one); 778 return mlir::success(); 779 } 780 781 // Follow UNIX F77 convention for logicals: 782 // 1. underlying integer is not zero => logical is .TRUE. 783 // 2. logical is .TRUE. => set underlying integer to 1. 784 auto i1Type = mlir::IntegerType::get(convert.getContext(), 1); 785 if (fromFirTy.isa<fir::LogicalType>() && toFirTy == i1Type) { 786 mlir::Value zero = genConstantIndex(loc, fromTy, rewriter, 0); 787 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>( 788 convert, mlir::LLVM::ICmpPredicate::ne, op0, zero); 789 return mlir::success(); 790 } 791 if (fromFirTy == i1Type && toFirTy.isa<fir::LogicalType>()) { 792 rewriter.replaceOpWithNewOp<mlir::LLVM::ZExtOp>(convert, toTy, op0); 793 return mlir::success(); 794 } 795 796 // Floating point to floating point conversion. 797 if (isFloatingPointTy(fromTy)) { 798 if (isFloatingPointTy(toTy)) { 799 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy); 800 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy); 801 auto v = convertFpToFp(op0, fromBits, toBits, toTy); 802 rewriter.replaceOp(convert, v); 803 return mlir::success(); 804 } 805 if (toTy.isa<mlir::IntegerType>()) { 806 rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0); 807 return mlir::success(); 808 } 809 } else if (fromTy.isa<mlir::IntegerType>()) { 810 // Integer to integer conversion. 811 if (toTy.isa<mlir::IntegerType>()) { 812 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy); 813 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy); 814 assert(fromBits != toBits); 815 if (fromBits > toBits) { 816 rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0); 817 return mlir::success(); 818 } 819 rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0); 820 return mlir::success(); 821 } 822 // Integer to floating point conversion. 823 if (isFloatingPointTy(toTy)) { 824 rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0); 825 return mlir::success(); 826 } 827 // Integer to pointer conversion. 828 if (toTy.isa<mlir::LLVM::LLVMPointerType>()) { 829 rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0); 830 return mlir::success(); 831 } 832 } else if (fromTy.isa<mlir::LLVM::LLVMPointerType>()) { 833 // Pointer to integer conversion. 834 if (toTy.isa<mlir::IntegerType>()) { 835 rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0); 836 return mlir::success(); 837 } 838 // Pointer to pointer conversion. 839 if (toTy.isa<mlir::LLVM::LLVMPointerType>()) { 840 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0); 841 return mlir::success(); 842 } 843 } 844 return emitError(loc) << "cannot convert " << fromTy << " to " << toTy; 845 } 846 }; 847 848 /// Lower `fir.dispatch` operation. A virtual call to a method in a dispatch 849 /// table. 850 struct DispatchOpConversion : public FIROpConversion<fir::DispatchOp> { 851 using FIROpConversion::FIROpConversion; 852 853 mlir::LogicalResult 854 matchAndRewrite(fir::DispatchOp dispatch, OpAdaptor adaptor, 855 mlir::ConversionPatternRewriter &rewriter) const override { 856 TODO(dispatch.getLoc(), "fir.dispatch codegen"); 857 return failure(); 858 } 859 }; 860 861 /// Lower `fir.dispatch_table` operation. The dispatch table for a Fortran 862 /// derived type. 863 struct DispatchTableOpConversion 864 : public FIROpConversion<fir::DispatchTableOp> { 865 using FIROpConversion::FIROpConversion; 866 867 mlir::LogicalResult 868 matchAndRewrite(fir::DispatchTableOp dispTab, OpAdaptor adaptor, 869 mlir::ConversionPatternRewriter &rewriter) const override { 870 TODO(dispTab.getLoc(), "fir.dispatch_table codegen"); 871 return failure(); 872 } 873 }; 874 875 /// Lower `fir.dt_entry` operation. An entry in a dispatch table; binds a 876 /// method-name to a function. 877 struct DTEntryOpConversion : public FIROpConversion<fir::DTEntryOp> { 878 using FIROpConversion::FIROpConversion; 879 880 mlir::LogicalResult 881 matchAndRewrite(fir::DTEntryOp dtEnt, OpAdaptor adaptor, 882 mlir::ConversionPatternRewriter &rewriter) const override { 883 TODO(dtEnt.getLoc(), "fir.dt_entry codegen"); 884 return failure(); 885 } 886 }; 887 888 /// Lower `fir.global_len` operation. 889 struct GlobalLenOpConversion : public FIROpConversion<fir::GlobalLenOp> { 890 using FIROpConversion::FIROpConversion; 891 892 mlir::LogicalResult 893 matchAndRewrite(fir::GlobalLenOp globalLen, OpAdaptor adaptor, 894 mlir::ConversionPatternRewriter &rewriter) const override { 895 TODO(globalLen.getLoc(), "fir.global_len codegen"); 896 return failure(); 897 } 898 }; 899 900 /// Lower fir.len_param_index 901 struct LenParamIndexOpConversion 902 : public FIROpConversion<fir::LenParamIndexOp> { 903 using FIROpConversion::FIROpConversion; 904 905 // FIXME: this should be specialized by the runtime target 906 mlir::LogicalResult 907 matchAndRewrite(fir::LenParamIndexOp lenp, OpAdaptor, 908 mlir::ConversionPatternRewriter &rewriter) const override { 909 TODO(lenp.getLoc(), "fir.len_param_index codegen"); 910 } 911 }; 912 913 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of 914 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element 915 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd 916 /// element is the length of the character buffer (`#n`). 917 struct EmboxCharOpConversion : public FIROpConversion<fir::EmboxCharOp> { 918 using FIROpConversion::FIROpConversion; 919 920 mlir::LogicalResult 921 matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor, 922 mlir::ConversionPatternRewriter &rewriter) const override { 923 mlir::ValueRange operands = adaptor.getOperands(); 924 MLIRContext *ctx = emboxChar.getContext(); 925 926 mlir::Value charBuffer = operands[0]; 927 mlir::Value charBufferLen = operands[1]; 928 929 mlir::Location loc = emboxChar.getLoc(); 930 mlir::Type llvmStructTy = convertType(emboxChar.getType()); 931 auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy); 932 933 mlir::Type lenTy = 934 llvmStructTy.cast<mlir::LLVM::LLVMStructType>().getBody()[1]; 935 mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen); 936 937 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 938 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 939 auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>( 940 loc, llvmStructTy, llvmStruct, charBuffer, c0); 941 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 942 emboxChar, llvmStructTy, insertBufferOp, lenAfterCast, c1); 943 944 return success(); 945 } 946 }; 947 } // namespace 948 949 /// Return the LLVMFuncOp corresponding to the standard malloc call. 950 static mlir::LLVM::LLVMFuncOp 951 getMalloc(fir::AllocMemOp op, mlir::ConversionPatternRewriter &rewriter) { 952 auto module = op->getParentOfType<mlir::ModuleOp>(); 953 if (mlir::LLVM::LLVMFuncOp mallocFunc = 954 module.lookupSymbol<mlir::LLVM::LLVMFuncOp>("malloc")) 955 return mallocFunc; 956 mlir::OpBuilder moduleBuilder( 957 op->getParentOfType<mlir::ModuleOp>().getBodyRegion()); 958 auto indexType = mlir::IntegerType::get(op.getContext(), 64); 959 return moduleBuilder.create<mlir::LLVM::LLVMFuncOp>( 960 rewriter.getUnknownLoc(), "malloc", 961 mlir::LLVM::LLVMFunctionType::get(getVoidPtrType(op.getContext()), 962 indexType, 963 /*isVarArg=*/false)); 964 } 965 966 /// Helper function for generating the LLVM IR that computes the size 967 /// in bytes for a derived type. 968 static mlir::Value 969 computeDerivedTypeSize(mlir::Location loc, mlir::Type ptrTy, mlir::Type idxTy, 970 mlir::ConversionPatternRewriter &rewriter) { 971 auto nullPtr = rewriter.create<mlir::LLVM::NullOp>(loc, ptrTy); 972 mlir::Value one = genConstantIndex(loc, idxTy, rewriter, 1); 973 llvm::SmallVector<mlir::Value> args{one}; 974 auto gep = rewriter.create<mlir::LLVM::GEPOp>(loc, ptrTy, nullPtr, args); 975 return rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, gep); 976 } 977 978 namespace { 979 /// Lower a `fir.allocmem` instruction into `llvm.call @malloc` 980 struct AllocMemOpConversion : public FIROpConversion<fir::AllocMemOp> { 981 using FIROpConversion::FIROpConversion; 982 983 mlir::LogicalResult 984 matchAndRewrite(fir::AllocMemOp heap, OpAdaptor adaptor, 985 mlir::ConversionPatternRewriter &rewriter) const override { 986 auto heapTy = heap.getType(); 987 auto ty = convertType(heapTy); 988 mlir::LLVM::LLVMFuncOp mallocFunc = getMalloc(heap, rewriter); 989 mlir::Location loc = heap.getLoc(); 990 auto ity = lowerTy().indexType(); 991 auto dataTy = fir::unwrapRefType(heapTy); 992 if (fir::isRecordWithTypeParameters(fir::unwrapSequenceType(dataTy))) 993 TODO(loc, "fir.allocmem codegen of derived type with length parameters"); 994 mlir::Value size = genTypeSizeInBytes(loc, ity, rewriter, ty); 995 // !fir.array<NxMx!fir.char<K,?>> sets `size` to the width of !fir.char<K>. 996 // So multiply the constant dimensions here. 997 if (fir::hasDynamicSize(dataTy)) 998 if (auto seqTy = dataTy.dyn_cast<fir::SequenceType>()) 999 if (fir::characterWithDynamicLen(seqTy.getEleTy())) { 1000 fir::SequenceType::Extent arrSize = 1; 1001 for (auto d : seqTy.getShape()) 1002 if (d != fir::SequenceType::getUnknownExtent()) 1003 arrSize *= d; 1004 size = rewriter.create<mlir::LLVM::MulOp>( 1005 loc, ity, size, genConstantIndex(loc, ity, rewriter, arrSize)); 1006 } 1007 for (mlir::Value opnd : adaptor.getOperands()) 1008 size = rewriter.create<mlir::LLVM::MulOp>( 1009 loc, ity, size, integerCast(loc, rewriter, ity, opnd)); 1010 heap->setAttr("callee", mlir::SymbolRefAttr::get(mallocFunc)); 1011 auto malloc = rewriter.create<mlir::LLVM::CallOp>( 1012 loc, ::getVoidPtrType(heap.getContext()), size, heap->getAttrs()); 1013 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(heap, ty, 1014 malloc.getResult(0)); 1015 return success(); 1016 } 1017 1018 // Compute the (allocation) size of the allocmem type in bytes. 1019 mlir::Value genTypeSizeInBytes(mlir::Location loc, mlir::Type idxTy, 1020 mlir::ConversionPatternRewriter &rewriter, 1021 mlir::Type llTy) const { 1022 // Use the primitive size, if available. 1023 auto ptrTy = llTy.dyn_cast<mlir::LLVM::LLVMPointerType>(); 1024 if (auto size = 1025 mlir::LLVM::getPrimitiveTypeSizeInBits(ptrTy.getElementType())) 1026 return genConstantIndex(loc, idxTy, rewriter, size / 8); 1027 1028 // Otherwise, generate the GEP trick in LLVM IR to compute the size. 1029 return computeDerivedTypeSize(loc, ptrTy, idxTy, rewriter); 1030 } 1031 }; 1032 } // namespace 1033 1034 /// Return the LLVMFuncOp corresponding to the standard free call. 1035 static mlir::LLVM::LLVMFuncOp 1036 getFree(fir::FreeMemOp op, mlir::ConversionPatternRewriter &rewriter) { 1037 auto module = op->getParentOfType<mlir::ModuleOp>(); 1038 if (mlir::LLVM::LLVMFuncOp freeFunc = 1039 module.lookupSymbol<mlir::LLVM::LLVMFuncOp>("free")) 1040 return freeFunc; 1041 mlir::OpBuilder moduleBuilder(module.getBodyRegion()); 1042 auto voidType = mlir::LLVM::LLVMVoidType::get(op.getContext()); 1043 return moduleBuilder.create<mlir::LLVM::LLVMFuncOp>( 1044 rewriter.getUnknownLoc(), "free", 1045 mlir::LLVM::LLVMFunctionType::get(voidType, 1046 getVoidPtrType(op.getContext()), 1047 /*isVarArg=*/false)); 1048 } 1049 1050 namespace { 1051 /// Lower a `fir.freemem` instruction into `llvm.call @free` 1052 struct FreeMemOpConversion : public FIROpConversion<fir::FreeMemOp> { 1053 using FIROpConversion::FIROpConversion; 1054 1055 mlir::LogicalResult 1056 matchAndRewrite(fir::FreeMemOp freemem, OpAdaptor adaptor, 1057 mlir::ConversionPatternRewriter &rewriter) const override { 1058 mlir::LLVM::LLVMFuncOp freeFunc = getFree(freemem, rewriter); 1059 mlir::Location loc = freemem.getLoc(); 1060 auto bitcast = rewriter.create<mlir::LLVM::BitcastOp>( 1061 freemem.getLoc(), voidPtrTy(), adaptor.getOperands()[0]); 1062 freemem->setAttr("callee", mlir::SymbolRefAttr::get(freeFunc)); 1063 rewriter.create<mlir::LLVM::CallOp>( 1064 loc, mlir::TypeRange{}, mlir::ValueRange{bitcast}, freemem->getAttrs()); 1065 rewriter.eraseOp(freemem); 1066 return success(); 1067 } 1068 }; 1069 } // namespace 1070 1071 namespace {} // namespace 1072 1073 /// Common base class for embox to descriptor conversion. 1074 template <typename OP> 1075 struct EmboxCommonConversion : public FIROpConversion<OP> { 1076 using FIROpConversion<OP>::FIROpConversion; 1077 1078 // Find the LLVMFuncOp in whose entry block the alloca should be inserted. 1079 // The order to find the LLVMFuncOp is as follows: 1080 // 1. The parent operation of the current block if it is a LLVMFuncOp. 1081 // 2. The first ancestor that is a LLVMFuncOp. 1082 mlir::LLVM::LLVMFuncOp 1083 getFuncForAllocaInsert(mlir::ConversionPatternRewriter &rewriter) const { 1084 mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp(); 1085 return mlir::isa<mlir::LLVM::LLVMFuncOp>(parentOp) 1086 ? mlir::cast<mlir::LLVM::LLVMFuncOp>(parentOp) 1087 : parentOp->getParentOfType<mlir::LLVM::LLVMFuncOp>(); 1088 } 1089 1090 // Generate an alloca of size 1 and type \p toTy. 1091 mlir::LLVM::AllocaOp 1092 genAllocaWithType(mlir::Location loc, mlir::Type toTy, unsigned alignment, 1093 mlir::ConversionPatternRewriter &rewriter) const { 1094 auto thisPt = rewriter.saveInsertionPoint(); 1095 mlir::LLVM::LLVMFuncOp func = getFuncForAllocaInsert(rewriter); 1096 rewriter.setInsertionPointToStart(&func.front()); 1097 auto size = this->genI32Constant(loc, rewriter, 1); 1098 auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, toTy, size, alignment); 1099 rewriter.restoreInsertionPoint(thisPt); 1100 return al; 1101 } 1102 1103 static int getCFIAttr(fir::BoxType boxTy) { 1104 auto eleTy = boxTy.getEleTy(); 1105 if (eleTy.isa<fir::PointerType>()) 1106 return CFI_attribute_pointer; 1107 if (eleTy.isa<fir::HeapType>()) 1108 return CFI_attribute_allocatable; 1109 return CFI_attribute_other; 1110 } 1111 1112 static fir::RecordType unwrapIfDerived(fir::BoxType boxTy) { 1113 return fir::unwrapSequenceType(fir::dyn_cast_ptrOrBoxEleTy(boxTy)) 1114 .template dyn_cast<fir::RecordType>(); 1115 } 1116 static bool isDerivedTypeWithLenParams(fir::BoxType boxTy) { 1117 auto recTy = unwrapIfDerived(boxTy); 1118 return recTy && recTy.getNumLenParams() > 0; 1119 } 1120 static bool isDerivedType(fir::BoxType boxTy) { 1121 return unwrapIfDerived(boxTy) != nullptr; 1122 } 1123 1124 // Get the element size and CFI type code of the boxed value. 1125 std::tuple<mlir::Value, mlir::Value> getSizeAndTypeCode( 1126 mlir::Location loc, mlir::ConversionPatternRewriter &rewriter, 1127 mlir::Type boxEleTy, mlir::ValueRange lenParams = {}) const { 1128 auto doInteger = 1129 [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> { 1130 int typeCode = fir::integerBitsToTypeCode(width); 1131 return {this->genConstantOffset(loc, rewriter, width / 8), 1132 this->genConstantOffset(loc, rewriter, typeCode)}; 1133 }; 1134 auto doLogical = 1135 [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> { 1136 int typeCode = fir::logicalBitsToTypeCode(width); 1137 return {this->genConstantOffset(loc, rewriter, width / 8), 1138 this->genConstantOffset(loc, rewriter, typeCode)}; 1139 }; 1140 auto doFloat = [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> { 1141 int typeCode = fir::realBitsToTypeCode(width); 1142 return {this->genConstantOffset(loc, rewriter, width / 8), 1143 this->genConstantOffset(loc, rewriter, typeCode)}; 1144 }; 1145 auto doComplex = 1146 [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> { 1147 auto typeCode = fir::complexBitsToTypeCode(width); 1148 return {this->genConstantOffset(loc, rewriter, width / 8 * 2), 1149 this->genConstantOffset(loc, rewriter, typeCode)}; 1150 }; 1151 auto doCharacter = 1152 [&](unsigned width, 1153 mlir::Value len) -> std::tuple<mlir::Value, mlir::Value> { 1154 auto typeCode = fir::characterBitsToTypeCode(width); 1155 auto typeCodeVal = this->genConstantOffset(loc, rewriter, typeCode); 1156 if (width == 8) 1157 return {len, typeCodeVal}; 1158 auto byteWidth = this->genConstantOffset(loc, rewriter, width / 8); 1159 auto i64Ty = mlir::IntegerType::get(&this->lowerTy().getContext(), 64); 1160 auto size = 1161 rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, byteWidth, len); 1162 return {size, typeCodeVal}; 1163 }; 1164 auto getKindMap = [&]() -> fir::KindMapping & { 1165 return this->lowerTy().getKindMap(); 1166 }; 1167 // Pointer-like types. 1168 if (auto eleTy = fir::dyn_cast_ptrEleTy(boxEleTy)) 1169 boxEleTy = eleTy; 1170 // Integer types. 1171 if (fir::isa_integer(boxEleTy)) { 1172 if (auto ty = boxEleTy.dyn_cast<mlir::IntegerType>()) 1173 return doInteger(ty.getWidth()); 1174 auto ty = boxEleTy.cast<fir::IntegerType>(); 1175 return doInteger(getKindMap().getIntegerBitsize(ty.getFKind())); 1176 } 1177 // Floating point types. 1178 if (fir::isa_real(boxEleTy)) { 1179 if (auto ty = boxEleTy.dyn_cast<mlir::FloatType>()) 1180 return doFloat(ty.getWidth()); 1181 auto ty = boxEleTy.cast<fir::RealType>(); 1182 return doFloat(getKindMap().getRealBitsize(ty.getFKind())); 1183 } 1184 // Complex types. 1185 if (fir::isa_complex(boxEleTy)) { 1186 if (auto ty = boxEleTy.dyn_cast<mlir::ComplexType>()) 1187 return doComplex( 1188 ty.getElementType().cast<mlir::FloatType>().getWidth()); 1189 auto ty = boxEleTy.cast<fir::ComplexType>(); 1190 return doComplex(getKindMap().getRealBitsize(ty.getFKind())); 1191 } 1192 // Character types. 1193 if (auto ty = boxEleTy.dyn_cast<fir::CharacterType>()) { 1194 auto charWidth = getKindMap().getCharacterBitsize(ty.getFKind()); 1195 if (ty.getLen() != fir::CharacterType::unknownLen()) { 1196 auto len = this->genConstantOffset(loc, rewriter, ty.getLen()); 1197 return doCharacter(charWidth, len); 1198 } 1199 assert(!lenParams.empty()); 1200 return doCharacter(charWidth, lenParams.back()); 1201 } 1202 // Logical type. 1203 if (auto ty = boxEleTy.dyn_cast<fir::LogicalType>()) 1204 return doLogical(getKindMap().getLogicalBitsize(ty.getFKind())); 1205 // Array types. 1206 if (auto seqTy = boxEleTy.dyn_cast<fir::SequenceType>()) 1207 return getSizeAndTypeCode(loc, rewriter, seqTy.getEleTy(), lenParams); 1208 // Derived-type types. 1209 if (boxEleTy.isa<fir::RecordType>()) { 1210 auto ptrTy = mlir::LLVM::LLVMPointerType::get( 1211 this->lowerTy().convertType(boxEleTy)); 1212 auto nullPtr = rewriter.create<mlir::LLVM::NullOp>(loc, ptrTy); 1213 auto one = 1214 genConstantIndex(loc, this->lowerTy().offsetType(), rewriter, 1); 1215 auto gep = rewriter.create<mlir::LLVM::GEPOp>(loc, ptrTy, nullPtr, 1216 mlir::ValueRange{one}); 1217 auto eleSize = rewriter.create<mlir::LLVM::PtrToIntOp>( 1218 loc, this->lowerTy().indexType(), gep); 1219 return {eleSize, 1220 this->genConstantOffset(loc, rewriter, fir::derivedToTypeCode())}; 1221 } 1222 // Reference type. 1223 if (fir::isa_ref_type(boxEleTy)) { 1224 // FIXME: use the target pointer size rather than sizeof(void*) 1225 return {this->genConstantOffset(loc, rewriter, sizeof(void *)), 1226 this->genConstantOffset(loc, rewriter, CFI_type_cptr)}; 1227 } 1228 fir::emitFatalError(loc, "unhandled type in fir.box code generation"); 1229 } 1230 1231 /// Basic pattern to write a field in the descriptor 1232 mlir::Value insertField(mlir::ConversionPatternRewriter &rewriter, 1233 mlir::Location loc, mlir::Value dest, 1234 ArrayRef<unsigned> fldIndexes, mlir::Value value, 1235 bool bitcast = false) const { 1236 auto boxTy = dest.getType(); 1237 auto fldTy = this->getBoxEleTy(boxTy, fldIndexes); 1238 if (bitcast) 1239 value = rewriter.create<mlir::LLVM::BitcastOp>(loc, fldTy, value); 1240 else 1241 value = this->integerCast(loc, rewriter, fldTy, value); 1242 SmallVector<mlir::Attribute, 2> attrs; 1243 for (auto i : fldIndexes) 1244 attrs.push_back(rewriter.getI32IntegerAttr(i)); 1245 auto indexesAttr = mlir::ArrayAttr::get(rewriter.getContext(), attrs); 1246 return rewriter.create<mlir::LLVM::InsertValueOp>(loc, boxTy, dest, value, 1247 indexesAttr); 1248 } 1249 1250 inline mlir::Value 1251 insertBaseAddress(mlir::ConversionPatternRewriter &rewriter, 1252 mlir::Location loc, mlir::Value dest, 1253 mlir::Value base) const { 1254 return insertField(rewriter, loc, dest, {kAddrPosInBox}, base, 1255 /*bitCast=*/true); 1256 } 1257 1258 inline mlir::Value insertLowerBound(mlir::ConversionPatternRewriter &rewriter, 1259 mlir::Location loc, mlir::Value dest, 1260 unsigned dim, mlir::Value lb) const { 1261 return insertField(rewriter, loc, dest, 1262 {kDimsPosInBox, dim, kDimLowerBoundPos}, lb); 1263 } 1264 1265 inline mlir::Value insertExtent(mlir::ConversionPatternRewriter &rewriter, 1266 mlir::Location loc, mlir::Value dest, 1267 unsigned dim, mlir::Value extent) const { 1268 return insertField(rewriter, loc, dest, {kDimsPosInBox, dim, kDimExtentPos}, 1269 extent); 1270 } 1271 1272 inline mlir::Value insertStride(mlir::ConversionPatternRewriter &rewriter, 1273 mlir::Location loc, mlir::Value dest, 1274 unsigned dim, mlir::Value stride) const { 1275 return insertField(rewriter, loc, dest, {kDimsPosInBox, dim, kDimStridePos}, 1276 stride); 1277 } 1278 1279 /// Get the address of the type descriptor global variable that was created by 1280 /// lowering for derived type \p recType. 1281 template <typename BOX> 1282 mlir::Value 1283 getTypeDescriptor(BOX box, mlir::ConversionPatternRewriter &rewriter, 1284 mlir::Location loc, fir::RecordType recType) const { 1285 std::string name = 1286 fir::NameUniquer::getTypeDescriptorName(recType.getName()); 1287 auto module = box->template getParentOfType<mlir::ModuleOp>(); 1288 if (auto global = module.template lookupSymbol<fir::GlobalOp>(name)) { 1289 auto ty = mlir::LLVM::LLVMPointerType::get( 1290 this->lowerTy().convertType(global.getType())); 1291 return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty, 1292 global.getSymName()); 1293 } 1294 if (auto global = 1295 module.template lookupSymbol<mlir::LLVM::GlobalOp>(name)) { 1296 // The global may have already been translated to LLVM. 1297 auto ty = mlir::LLVM::LLVMPointerType::get(global.getType()); 1298 return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty, 1299 global.getSymName()); 1300 } 1301 // Type info derived types do not have type descriptors since they are the 1302 // types defining type descriptors. 1303 if (!this->options.ignoreMissingTypeDescriptors && 1304 !fir::NameUniquer::belongsToModule( 1305 name, Fortran::semantics::typeInfoBuiltinModule)) 1306 fir::emitFatalError( 1307 loc, "runtime derived type info descriptor was not generated"); 1308 return rewriter.create<mlir::LLVM::NullOp>( 1309 loc, ::getVoidPtrType(box.getContext())); 1310 } 1311 1312 template <typename BOX> 1313 std::tuple<fir::BoxType, mlir::Value, mlir::Value> 1314 consDescriptorPrefix(BOX box, mlir::ConversionPatternRewriter &rewriter, 1315 unsigned rank, mlir::ValueRange lenParams) const { 1316 auto loc = box.getLoc(); 1317 auto boxTy = box.getType().template dyn_cast<fir::BoxType>(); 1318 auto convTy = this->lowerTy().convertBoxType(boxTy, rank); 1319 auto llvmBoxPtrTy = convTy.template cast<mlir::LLVM::LLVMPointerType>(); 1320 auto llvmBoxTy = llvmBoxPtrTy.getElementType(); 1321 mlir::Value descriptor = 1322 rewriter.create<mlir::LLVM::UndefOp>(loc, llvmBoxTy); 1323 1324 llvm::SmallVector<mlir::Value> typeparams = lenParams; 1325 if constexpr (!std::is_same_v<BOX, fir::EmboxOp>) { 1326 if (!box.substr().empty() && fir::hasDynamicSize(boxTy.getEleTy())) 1327 typeparams.push_back(box.substr()[1]); 1328 } 1329 1330 // Write each of the fields with the appropriate values 1331 auto [eleSize, cfiTy] = 1332 getSizeAndTypeCode(loc, rewriter, boxTy.getEleTy(), typeparams); 1333 descriptor = 1334 insertField(rewriter, loc, descriptor, {kElemLenPosInBox}, eleSize); 1335 descriptor = insertField(rewriter, loc, descriptor, {kVersionPosInBox}, 1336 this->genI32Constant(loc, rewriter, CFI_VERSION)); 1337 descriptor = insertField(rewriter, loc, descriptor, {kRankPosInBox}, 1338 this->genI32Constant(loc, rewriter, rank)); 1339 descriptor = insertField(rewriter, loc, descriptor, {kTypePosInBox}, cfiTy); 1340 descriptor = 1341 insertField(rewriter, loc, descriptor, {kAttributePosInBox}, 1342 this->genI32Constant(loc, rewriter, getCFIAttr(boxTy))); 1343 const bool hasAddendum = isDerivedType(boxTy); 1344 descriptor = 1345 insertField(rewriter, loc, descriptor, {kF18AddendumPosInBox}, 1346 this->genI32Constant(loc, rewriter, hasAddendum ? 1 : 0)); 1347 1348 if (hasAddendum) { 1349 auto isArray = 1350 fir::dyn_cast_ptrOrBoxEleTy(boxTy).template isa<fir::SequenceType>(); 1351 unsigned typeDescFieldId = isArray ? kOptTypePtrPosInBox : kDimsPosInBox; 1352 auto typeDesc = 1353 getTypeDescriptor(box, rewriter, loc, unwrapIfDerived(boxTy)); 1354 descriptor = 1355 insertField(rewriter, loc, descriptor, {typeDescFieldId}, typeDesc, 1356 /*bitCast=*/true); 1357 } 1358 1359 return {boxTy, descriptor, eleSize}; 1360 } 1361 1362 /// Compute the base address of a substring given the base address of a scalar 1363 /// string and the zero based string lower bound. 1364 mlir::Value shiftSubstringBase(mlir::ConversionPatternRewriter &rewriter, 1365 mlir::Location loc, mlir::Value base, 1366 mlir::Value lowerBound) const { 1367 llvm::SmallVector<mlir::Value> gepOperands; 1368 auto baseType = 1369 base.getType().cast<mlir::LLVM::LLVMPointerType>().getElementType(); 1370 if (baseType.isa<mlir::LLVM::LLVMArrayType>()) { 1371 auto idxTy = this->lowerTy().indexType(); 1372 mlir::Value zero = genConstantIndex(loc, idxTy, rewriter, 0); 1373 gepOperands.push_back(zero); 1374 } 1375 gepOperands.push_back(lowerBound); 1376 return this->genGEP(loc, base.getType(), rewriter, base, gepOperands); 1377 } 1378 1379 /// If the embox is not in a globalOp body, allocate storage for the box; 1380 /// store the value inside and return the generated alloca. Return the input 1381 /// value otherwise. 1382 mlir::Value 1383 placeInMemoryIfNotGlobalInit(mlir::ConversionPatternRewriter &rewriter, 1384 mlir::Location loc, mlir::Value boxValue) const { 1385 auto *thisBlock = rewriter.getInsertionBlock(); 1386 if (thisBlock && mlir::isa<mlir::LLVM::GlobalOp>(thisBlock->getParentOp())) 1387 return boxValue; 1388 auto boxPtrTy = mlir::LLVM::LLVMPointerType::get(boxValue.getType()); 1389 auto alloca = genAllocaWithType(loc, boxPtrTy, defaultAlign, rewriter); 1390 rewriter.create<mlir::LLVM::StoreOp>(loc, boxValue, alloca); 1391 return alloca; 1392 } 1393 }; 1394 1395 /// Compute the extent of a triplet slice (lb:ub:step). 1396 static mlir::Value 1397 computeTripletExtent(mlir::ConversionPatternRewriter &rewriter, 1398 mlir::Location loc, mlir::Value lb, mlir::Value ub, 1399 mlir::Value step, mlir::Value zero, mlir::Type type) { 1400 mlir::Value extent = rewriter.create<mlir::LLVM::SubOp>(loc, type, ub, lb); 1401 extent = rewriter.create<mlir::LLVM::AddOp>(loc, type, extent, step); 1402 extent = rewriter.create<mlir::LLVM::SDivOp>(loc, type, extent, step); 1403 // If the resulting extent is negative (`ub-lb` and `step` have different 1404 // signs), zero must be returned instead. 1405 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 1406 loc, mlir::LLVM::ICmpPredicate::sgt, extent, zero); 1407 return rewriter.create<mlir::LLVM::SelectOp>(loc, cmp, extent, zero); 1408 } 1409 1410 /// Create a generic box on a memory reference. This conversions lowers the 1411 /// abstract box to the appropriate, initialized descriptor. 1412 struct EmboxOpConversion : public EmboxCommonConversion<fir::EmboxOp> { 1413 using EmboxCommonConversion::EmboxCommonConversion; 1414 1415 mlir::LogicalResult 1416 matchAndRewrite(fir::EmboxOp embox, OpAdaptor adaptor, 1417 mlir::ConversionPatternRewriter &rewriter) const override { 1418 assert(!embox.getShape() && "There should be no dims on this embox op"); 1419 auto [boxTy, dest, eleSize] = 1420 consDescriptorPrefix(embox, rewriter, /*rank=*/0, 1421 /*lenParams=*/adaptor.getOperands().drop_front(1)); 1422 dest = insertBaseAddress(rewriter, embox.getLoc(), dest, 1423 adaptor.getOperands()[0]); 1424 if (isDerivedTypeWithLenParams(boxTy)) { 1425 TODO(embox.getLoc(), 1426 "fir.embox codegen of derived with length parameters"); 1427 return failure(); 1428 } 1429 auto result = placeInMemoryIfNotGlobalInit(rewriter, embox.getLoc(), dest); 1430 rewriter.replaceOp(embox, result); 1431 return success(); 1432 } 1433 }; 1434 1435 /// Create a generic box on a memory reference. 1436 struct XEmboxOpConversion : public EmboxCommonConversion<fir::cg::XEmboxOp> { 1437 using EmboxCommonConversion::EmboxCommonConversion; 1438 1439 mlir::LogicalResult 1440 matchAndRewrite(fir::cg::XEmboxOp xbox, OpAdaptor adaptor, 1441 mlir::ConversionPatternRewriter &rewriter) const override { 1442 auto [boxTy, dest, eleSize] = consDescriptorPrefix( 1443 xbox, rewriter, xbox.getOutRank(), 1444 adaptor.getOperands().drop_front(xbox.lenParamOffset())); 1445 // Generate the triples in the dims field of the descriptor 1446 mlir::ValueRange operands = adaptor.getOperands(); 1447 auto i64Ty = mlir::IntegerType::get(xbox.getContext(), 64); 1448 mlir::Value base = operands[0]; 1449 assert(!xbox.shape().empty() && "must have a shape"); 1450 unsigned shapeOffset = xbox.shapeOffset(); 1451 bool hasShift = !xbox.shift().empty(); 1452 unsigned shiftOffset = xbox.shiftOffset(); 1453 bool hasSlice = !xbox.slice().empty(); 1454 unsigned sliceOffset = xbox.sliceOffset(); 1455 mlir::Location loc = xbox.getLoc(); 1456 mlir::Value zero = genConstantIndex(loc, i64Ty, rewriter, 0); 1457 mlir::Value one = genConstantIndex(loc, i64Ty, rewriter, 1); 1458 mlir::Value prevDim = integerCast(loc, rewriter, i64Ty, eleSize); 1459 mlir::Value prevPtrOff = one; 1460 mlir::Type eleTy = boxTy.getEleTy(); 1461 const unsigned rank = xbox.getRank(); 1462 llvm::SmallVector<mlir::Value> gepArgs; 1463 unsigned constRows = 0; 1464 mlir::Value ptrOffset = zero; 1465 if (auto memEleTy = fir::dyn_cast_ptrEleTy(xbox.memref().getType())) 1466 if (auto seqTy = memEleTy.dyn_cast<fir::SequenceType>()) { 1467 mlir::Type seqEleTy = seqTy.getEleTy(); 1468 // Adjust the element scaling factor if the element is a dependent type. 1469 if (fir::hasDynamicSize(seqEleTy)) { 1470 if (fir::isa_char(seqEleTy)) { 1471 assert(xbox.lenParams().size() == 1); 1472 prevPtrOff = integerCast(loc, rewriter, i64Ty, 1473 operands[xbox.lenParamOffset()]); 1474 } else if (seqEleTy.isa<fir::RecordType>()) { 1475 TODO(loc, "generate call to calculate size of PDT"); 1476 } else { 1477 return rewriter.notifyMatchFailure(xbox, "unexpected dynamic type"); 1478 } 1479 } else { 1480 constRows = seqTy.getConstantRows(); 1481 } 1482 } 1483 1484 bool hasSubcomp = !xbox.subcomponent().empty(); 1485 mlir::Value stepExpr; 1486 if (hasSubcomp) { 1487 // We have a subcomponent. The step value needs to be the number of 1488 // bytes per element (which is a derived type). 1489 mlir::Type ty0 = base.getType(); 1490 [[maybe_unused]] auto ptrTy = ty0.dyn_cast<mlir::LLVM::LLVMPointerType>(); 1491 assert(ptrTy && "expected pointer type"); 1492 mlir::Type memEleTy = fir::dyn_cast_ptrEleTy(xbox.memref().getType()); 1493 assert(memEleTy && "expected fir pointer type"); 1494 auto seqTy = memEleTy.dyn_cast<fir::SequenceType>(); 1495 assert(seqTy && "expected sequence type"); 1496 mlir::Type seqEleTy = seqTy.getEleTy(); 1497 auto eleTy = mlir::LLVM::LLVMPointerType::get(convertType(seqEleTy)); 1498 stepExpr = computeDerivedTypeSize(loc, eleTy, i64Ty, rewriter); 1499 } 1500 1501 // Process the array subspace arguments (shape, shift, etc.), if any, 1502 // translating everything to values in the descriptor wherever the entity 1503 // has a dynamic array dimension. 1504 for (unsigned di = 0, descIdx = 0; di < rank; ++di) { 1505 mlir::Value extent = operands[shapeOffset]; 1506 mlir::Value outerExtent = extent; 1507 bool skipNext = false; 1508 if (hasSlice) { 1509 mlir::Value off = operands[sliceOffset]; 1510 mlir::Value adj = one; 1511 if (hasShift) 1512 adj = operands[shiftOffset]; 1513 auto ao = rewriter.create<mlir::LLVM::SubOp>(loc, i64Ty, off, adj); 1514 if (constRows > 0) { 1515 gepArgs.push_back(ao); 1516 --constRows; 1517 } else { 1518 auto dimOff = 1519 rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, ao, prevPtrOff); 1520 ptrOffset = 1521 rewriter.create<mlir::LLVM::AddOp>(loc, i64Ty, dimOff, ptrOffset); 1522 } 1523 if (mlir::isa_and_nonnull<fir::UndefOp>( 1524 xbox.slice()[3 * di + 1].getDefiningOp())) { 1525 // This dimension contains a scalar expression in the array slice op. 1526 // The dimension is loop invariant, will be dropped, and will not 1527 // appear in the descriptor. 1528 skipNext = true; 1529 } 1530 } 1531 if (!skipNext) { 1532 // store lower bound (normally 0) 1533 mlir::Value lb = zero; 1534 if (eleTy.isa<fir::PointerType>() || eleTy.isa<fir::HeapType>()) { 1535 lb = one; 1536 if (hasShift) 1537 lb = operands[shiftOffset]; 1538 } 1539 dest = insertLowerBound(rewriter, loc, dest, descIdx, lb); 1540 1541 // store extent 1542 if (hasSlice) 1543 extent = computeTripletExtent(rewriter, loc, operands[sliceOffset], 1544 operands[sliceOffset + 1], 1545 operands[sliceOffset + 2], zero, i64Ty); 1546 dest = insertExtent(rewriter, loc, dest, descIdx, extent); 1547 1548 // store step (scaled by shaped extent) 1549 1550 mlir::Value step = hasSubcomp ? stepExpr : prevDim; 1551 if (hasSlice) 1552 step = rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, step, 1553 operands[sliceOffset + 2]); 1554 dest = insertStride(rewriter, loc, dest, descIdx, step); 1555 ++descIdx; 1556 } 1557 1558 // compute the stride and offset for the next natural dimension 1559 prevDim = 1560 rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, prevDim, outerExtent); 1561 if (constRows == 0) 1562 prevPtrOff = rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, prevPtrOff, 1563 outerExtent); 1564 1565 // increment iterators 1566 ++shapeOffset; 1567 if (hasShift) 1568 ++shiftOffset; 1569 if (hasSlice) 1570 sliceOffset += 3; 1571 } 1572 if (hasSlice || hasSubcomp || !xbox.substr().empty()) { 1573 llvm::SmallVector<mlir::Value> args = {ptrOffset}; 1574 args.append(gepArgs.rbegin(), gepArgs.rend()); 1575 if (hasSubcomp) { 1576 // For each field in the path add the offset to base via the args list. 1577 // In the most general case, some offsets must be computed since 1578 // they are not be known until runtime. 1579 if (fir::hasDynamicSize(fir::unwrapSequenceType( 1580 fir::unwrapPassByRefType(xbox.memref().getType())))) 1581 TODO(loc, "fir.embox codegen dynamic size component in derived type"); 1582 args.append(operands.begin() + xbox.subcomponentOffset(), 1583 operands.begin() + xbox.subcomponentOffset() + 1584 xbox.subcomponent().size()); 1585 } 1586 base = 1587 rewriter.create<mlir::LLVM::GEPOp>(loc, base.getType(), base, args); 1588 if (!xbox.substr().empty()) 1589 base = shiftSubstringBase(rewriter, loc, base, 1590 operands[xbox.substrOffset()]); 1591 } 1592 dest = insertBaseAddress(rewriter, loc, dest, base); 1593 if (isDerivedTypeWithLenParams(boxTy)) 1594 TODO(loc, "fir.embox codegen of derived with length parameters"); 1595 1596 mlir::Value result = placeInMemoryIfNotGlobalInit(rewriter, loc, dest); 1597 rewriter.replaceOp(xbox, result); 1598 return success(); 1599 } 1600 }; 1601 1602 /// Create a new box given a box reference. 1603 struct XReboxOpConversion : public EmboxCommonConversion<fir::cg::XReboxOp> { 1604 using EmboxCommonConversion::EmboxCommonConversion; 1605 1606 mlir::LogicalResult 1607 matchAndRewrite(fir::cg::XReboxOp rebox, OpAdaptor adaptor, 1608 mlir::ConversionPatternRewriter &rewriter) const override { 1609 mlir::Location loc = rebox.getLoc(); 1610 mlir::Type idxTy = lowerTy().indexType(); 1611 mlir::Value loweredBox = adaptor.getOperands()[0]; 1612 mlir::ValueRange operands = adaptor.getOperands(); 1613 1614 // Create new descriptor and fill its non-shape related data. 1615 llvm::SmallVector<mlir::Value, 2> lenParams; 1616 mlir::Type inputEleTy = getInputEleTy(rebox); 1617 if (auto charTy = inputEleTy.dyn_cast<fir::CharacterType>()) { 1618 mlir::Value len = 1619 loadElementSizeFromBox(loc, idxTy, loweredBox, rewriter); 1620 if (charTy.getFKind() != 1) { 1621 mlir::Value width = 1622 genConstantIndex(loc, idxTy, rewriter, charTy.getFKind()); 1623 len = rewriter.create<mlir::LLVM::SDivOp>(loc, idxTy, len, width); 1624 } 1625 lenParams.emplace_back(len); 1626 } else if (auto recTy = inputEleTy.dyn_cast<fir::RecordType>()) { 1627 if (recTy.getNumLenParams() != 0) 1628 TODO(loc, "reboxing descriptor of derived type with length parameters"); 1629 } 1630 auto [boxTy, dest, eleSize] = 1631 consDescriptorPrefix(rebox, rewriter, rebox.getOutRank(), lenParams); 1632 1633 // Read input extents, strides, and base address 1634 llvm::SmallVector<mlir::Value> inputExtents; 1635 llvm::SmallVector<mlir::Value> inputStrides; 1636 const unsigned inputRank = rebox.getRank(); 1637 for (unsigned i = 0; i < inputRank; ++i) { 1638 mlir::Value dim = genConstantIndex(loc, idxTy, rewriter, i); 1639 SmallVector<mlir::Value, 3> dimInfo = 1640 getDimsFromBox(loc, {idxTy, idxTy, idxTy}, loweredBox, dim, rewriter); 1641 inputExtents.emplace_back(dimInfo[1]); 1642 inputStrides.emplace_back(dimInfo[2]); 1643 } 1644 1645 mlir::Type baseTy = getBaseAddrTypeFromBox(loweredBox.getType()); 1646 mlir::Value baseAddr = 1647 loadBaseAddrFromBox(loc, baseTy, loweredBox, rewriter); 1648 1649 if (!rebox.slice().empty() || !rebox.subcomponent().empty()) 1650 return sliceBox(rebox, dest, baseAddr, inputExtents, inputStrides, 1651 operands, rewriter); 1652 return reshapeBox(rebox, dest, baseAddr, inputExtents, inputStrides, 1653 operands, rewriter); 1654 } 1655 1656 private: 1657 /// Write resulting shape and base address in descriptor, and replace rebox 1658 /// op. 1659 mlir::LogicalResult 1660 finalizeRebox(fir::cg::XReboxOp rebox, mlir::Value dest, mlir::Value base, 1661 mlir::ValueRange lbounds, mlir::ValueRange extents, 1662 mlir::ValueRange strides, 1663 mlir::ConversionPatternRewriter &rewriter) const { 1664 mlir::Location loc = rebox.getLoc(); 1665 mlir::Value one = genConstantIndex(loc, lowerTy().indexType(), rewriter, 1); 1666 for (auto iter : llvm::enumerate(llvm::zip(extents, strides))) { 1667 unsigned dim = iter.index(); 1668 mlir::Value lb = lbounds.empty() ? one : lbounds[dim]; 1669 dest = insertLowerBound(rewriter, loc, dest, dim, lb); 1670 dest = insertExtent(rewriter, loc, dest, dim, std::get<0>(iter.value())); 1671 dest = insertStride(rewriter, loc, dest, dim, std::get<1>(iter.value())); 1672 } 1673 dest = insertBaseAddress(rewriter, loc, dest, base); 1674 mlir::Value result = 1675 placeInMemoryIfNotGlobalInit(rewriter, rebox.getLoc(), dest); 1676 rewriter.replaceOp(rebox, result); 1677 return success(); 1678 } 1679 1680 // Apply slice given the base address, extents and strides of the input box. 1681 mlir::LogicalResult 1682 sliceBox(fir::cg::XReboxOp rebox, mlir::Value dest, mlir::Value base, 1683 mlir::ValueRange inputExtents, mlir::ValueRange inputStrides, 1684 mlir::ValueRange operands, 1685 mlir::ConversionPatternRewriter &rewriter) const { 1686 mlir::Location loc = rebox.getLoc(); 1687 mlir::Type voidPtrTy = ::getVoidPtrType(rebox.getContext()); 1688 mlir::Type idxTy = lowerTy().indexType(); 1689 mlir::Value zero = genConstantIndex(loc, idxTy, rewriter, 0); 1690 // Apply subcomponent and substring shift on base address. 1691 if (!rebox.subcomponent().empty() || !rebox.substr().empty()) { 1692 // Cast to inputEleTy* so that a GEP can be used. 1693 mlir::Type inputEleTy = getInputEleTy(rebox); 1694 auto llvmElePtrTy = 1695 mlir::LLVM::LLVMPointerType::get(convertType(inputEleTy)); 1696 base = rewriter.create<mlir::LLVM::BitcastOp>(loc, llvmElePtrTy, base); 1697 1698 if (!rebox.subcomponent().empty()) { 1699 llvm::SmallVector<mlir::Value> gepOperands = {zero}; 1700 for (unsigned i = 0; i < rebox.subcomponent().size(); ++i) 1701 gepOperands.push_back(operands[rebox.subcomponentOffset() + i]); 1702 base = genGEP(loc, llvmElePtrTy, rewriter, base, gepOperands); 1703 } 1704 if (!rebox.substr().empty()) 1705 base = shiftSubstringBase(rewriter, loc, base, 1706 operands[rebox.substrOffset()]); 1707 } 1708 1709 if (rebox.slice().empty()) 1710 // The array section is of the form array[%component][substring], keep 1711 // the input array extents and strides. 1712 return finalizeRebox(rebox, dest, base, /*lbounds*/ llvm::None, 1713 inputExtents, inputStrides, rewriter); 1714 1715 // Strides from the fir.box are in bytes. 1716 base = rewriter.create<mlir::LLVM::BitcastOp>(loc, voidPtrTy, base); 1717 1718 // The slice is of the form array(i:j:k)[%component]. Compute new extents 1719 // and strides. 1720 llvm::SmallVector<mlir::Value> slicedExtents; 1721 llvm::SmallVector<mlir::Value> slicedStrides; 1722 mlir::Value one = genConstantIndex(loc, idxTy, rewriter, 1); 1723 const bool sliceHasOrigins = !rebox.shift().empty(); 1724 unsigned sliceOps = rebox.sliceOffset(); 1725 unsigned shiftOps = rebox.shiftOffset(); 1726 auto strideOps = inputStrides.begin(); 1727 const unsigned inputRank = inputStrides.size(); 1728 for (unsigned i = 0; i < inputRank; 1729 ++i, ++strideOps, ++shiftOps, sliceOps += 3) { 1730 mlir::Value sliceLb = 1731 integerCast(loc, rewriter, idxTy, operands[sliceOps]); 1732 mlir::Value inputStride = *strideOps; // already idxTy 1733 // Apply origin shift: base += (lb-shift)*input_stride 1734 mlir::Value sliceOrigin = 1735 sliceHasOrigins 1736 ? integerCast(loc, rewriter, idxTy, operands[shiftOps]) 1737 : one; 1738 mlir::Value diff = 1739 rewriter.create<mlir::LLVM::SubOp>(loc, idxTy, sliceLb, sliceOrigin); 1740 mlir::Value offset = 1741 rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, diff, inputStride); 1742 base = genGEP(loc, voidPtrTy, rewriter, base, offset); 1743 // Apply upper bound and step if this is a triplet. Otherwise, the 1744 // dimension is dropped and no extents/strides are computed. 1745 mlir::Value upper = operands[sliceOps + 1]; 1746 const bool isTripletSlice = 1747 !mlir::isa_and_nonnull<mlir::LLVM::UndefOp>(upper.getDefiningOp()); 1748 if (isTripletSlice) { 1749 mlir::Value step = 1750 integerCast(loc, rewriter, idxTy, operands[sliceOps + 2]); 1751 // extent = ub-lb+step/step 1752 mlir::Value sliceUb = integerCast(loc, rewriter, idxTy, upper); 1753 mlir::Value extent = computeTripletExtent(rewriter, loc, sliceLb, 1754 sliceUb, step, zero, idxTy); 1755 slicedExtents.emplace_back(extent); 1756 // stride = step*input_stride 1757 mlir::Value stride = 1758 rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, step, inputStride); 1759 slicedStrides.emplace_back(stride); 1760 } 1761 } 1762 return finalizeRebox(rebox, dest, base, /*lbounds*/ llvm::None, 1763 slicedExtents, slicedStrides, rewriter); 1764 } 1765 1766 /// Apply a new shape to the data described by a box given the base address, 1767 /// extents and strides of the box. 1768 mlir::LogicalResult 1769 reshapeBox(fir::cg::XReboxOp rebox, mlir::Value dest, mlir::Value base, 1770 mlir::ValueRange inputExtents, mlir::ValueRange inputStrides, 1771 mlir::ValueRange operands, 1772 mlir::ConversionPatternRewriter &rewriter) const { 1773 mlir::ValueRange reboxShifts{operands.begin() + rebox.shiftOffset(), 1774 operands.begin() + rebox.shiftOffset() + 1775 rebox.shift().size()}; 1776 if (rebox.shape().empty()) { 1777 // Only setting new lower bounds. 1778 return finalizeRebox(rebox, dest, base, reboxShifts, inputExtents, 1779 inputStrides, rewriter); 1780 } 1781 1782 mlir::Location loc = rebox.getLoc(); 1783 // Strides from the fir.box are in bytes. 1784 mlir::Type voidPtrTy = ::getVoidPtrType(rebox.getContext()); 1785 base = rewriter.create<mlir::LLVM::BitcastOp>(loc, voidPtrTy, base); 1786 1787 llvm::SmallVector<mlir::Value> newStrides; 1788 llvm::SmallVector<mlir::Value> newExtents; 1789 mlir::Type idxTy = lowerTy().indexType(); 1790 // First stride from input box is kept. The rest is assumed contiguous 1791 // (it is not possible to reshape otherwise). If the input is scalar, 1792 // which may be OK if all new extents are ones, the stride does not 1793 // matter, use one. 1794 mlir::Value stride = inputStrides.empty() 1795 ? genConstantIndex(loc, idxTy, rewriter, 1) 1796 : inputStrides[0]; 1797 for (unsigned i = 0; i < rebox.shape().size(); ++i) { 1798 mlir::Value rawExtent = operands[rebox.shapeOffset() + i]; 1799 mlir::Value extent = integerCast(loc, rewriter, idxTy, rawExtent); 1800 newExtents.emplace_back(extent); 1801 newStrides.emplace_back(stride); 1802 // nextStride = extent * stride; 1803 stride = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, extent, stride); 1804 } 1805 return finalizeRebox(rebox, dest, base, reboxShifts, newExtents, newStrides, 1806 rewriter); 1807 } 1808 1809 /// Return scalar element type of the input box. 1810 static mlir::Type getInputEleTy(fir::cg::XReboxOp rebox) { 1811 auto ty = fir::dyn_cast_ptrOrBoxEleTy(rebox.box().getType()); 1812 if (auto seqTy = ty.dyn_cast<fir::SequenceType>()) 1813 return seqTy.getEleTy(); 1814 return ty; 1815 } 1816 }; 1817 1818 /// Lower `fir.emboxproc` operation. Creates a procedure box. 1819 /// TODO: Part of supporting Fortran 2003 procedure pointers. 1820 struct EmboxProcOpConversion : public FIROpConversion<fir::EmboxProcOp> { 1821 using FIROpConversion::FIROpConversion; 1822 1823 mlir::LogicalResult 1824 matchAndRewrite(fir::EmboxProcOp emboxproc, OpAdaptor adaptor, 1825 mlir::ConversionPatternRewriter &rewriter) const override { 1826 TODO(emboxproc.getLoc(), "fir.emboxproc codegen"); 1827 return failure(); 1828 } 1829 }; 1830 1831 // Code shared between insert_value and extract_value Ops. 1832 struct ValueOpCommon { 1833 // Translate the arguments pertaining to any multidimensional array to 1834 // row-major order for LLVM-IR. 1835 static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs, 1836 mlir::Type ty) { 1837 assert(ty && "type is null"); 1838 const auto end = attrs.size(); 1839 for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) { 1840 if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 1841 const auto dim = getDimension(seq); 1842 if (dim > 1) { 1843 auto ub = std::min(i + dim, end); 1844 std::reverse(attrs.begin() + i, attrs.begin() + ub); 1845 i += dim - 1; 1846 } 1847 ty = getArrayElementType(seq); 1848 } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) { 1849 ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()]; 1850 } else { 1851 llvm_unreachable("index into invalid type"); 1852 } 1853 } 1854 } 1855 1856 static llvm::SmallVector<mlir::Attribute> 1857 collectIndices(mlir::ConversionPatternRewriter &rewriter, 1858 mlir::ArrayAttr arrAttr) { 1859 llvm::SmallVector<mlir::Attribute> attrs; 1860 for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) { 1861 if (i->isa<mlir::IntegerAttr>()) { 1862 attrs.push_back(*i); 1863 } else { 1864 auto fieldName = i->cast<mlir::StringAttr>().getValue(); 1865 ++i; 1866 auto ty = i->cast<mlir::TypeAttr>().getValue(); 1867 auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName); 1868 attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index)); 1869 } 1870 } 1871 return attrs; 1872 } 1873 1874 private: 1875 static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) { 1876 unsigned result = 1; 1877 for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>(); 1878 eleTy; 1879 eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>()) 1880 ++result; 1881 return result; 1882 } 1883 1884 static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) { 1885 auto eleTy = ty.getElementType(); 1886 while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>()) 1887 eleTy = arrTy.getElementType(); 1888 return eleTy; 1889 } 1890 }; 1891 1892 namespace { 1893 /// Extract a subobject value from an ssa-value of aggregate type 1894 struct ExtractValueOpConversion 1895 : public FIROpAndTypeConversion<fir::ExtractValueOp>, 1896 public ValueOpCommon { 1897 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1898 1899 mlir::LogicalResult 1900 doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor, 1901 mlir::ConversionPatternRewriter &rewriter) const override { 1902 auto attrs = collectIndices(rewriter, extractVal.getCoor()); 1903 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 1904 auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs); 1905 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>( 1906 extractVal, ty, adaptor.getOperands()[0], position); 1907 return success(); 1908 } 1909 }; 1910 1911 /// InsertValue is the generalized instruction for the composition of new 1912 /// aggregate type values. 1913 struct InsertValueOpConversion 1914 : public FIROpAndTypeConversion<fir::InsertValueOp>, 1915 public ValueOpCommon { 1916 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1917 1918 mlir::LogicalResult 1919 doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor, 1920 mlir::ConversionPatternRewriter &rewriter) const override { 1921 auto attrs = collectIndices(rewriter, insertVal.getCoor()); 1922 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 1923 auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs); 1924 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1925 insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1], 1926 position); 1927 return success(); 1928 } 1929 }; 1930 1931 /// InsertOnRange inserts a value into a sequence over a range of offsets. 1932 struct InsertOnRangeOpConversion 1933 : public FIROpAndTypeConversion<fir::InsertOnRangeOp> { 1934 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1935 1936 // Increments an array of subscripts in a row major fasion. 1937 void incrementSubscripts(const SmallVector<uint64_t> &dims, 1938 SmallVector<uint64_t> &subscripts) const { 1939 for (size_t i = dims.size(); i > 0; --i) { 1940 if (++subscripts[i - 1] < dims[i - 1]) { 1941 return; 1942 } 1943 subscripts[i - 1] = 0; 1944 } 1945 } 1946 1947 mlir::LogicalResult 1948 doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor, 1949 mlir::ConversionPatternRewriter &rewriter) const override { 1950 1951 llvm::SmallVector<uint64_t> dims; 1952 auto type = adaptor.getOperands()[0].getType(); 1953 1954 // Iteratively extract the array dimensions from the type. 1955 while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 1956 dims.push_back(t.getNumElements()); 1957 type = t.getElementType(); 1958 } 1959 1960 SmallVector<uint64_t> lBounds; 1961 SmallVector<uint64_t> uBounds; 1962 1963 // Unzip the upper and lower bound and convert to a row major format. 1964 mlir::DenseIntElementsAttr coor = range.getCoor(); 1965 auto reversedCoor = llvm::reverse(coor.getValues<int64_t>()); 1966 for (auto i = reversedCoor.begin(), e = reversedCoor.end(); i != e; ++i) { 1967 uBounds.push_back(*i++); 1968 lBounds.push_back(*i); 1969 } 1970 1971 auto &subscripts = lBounds; 1972 auto loc = range.getLoc(); 1973 mlir::Value lastOp = adaptor.getOperands()[0]; 1974 mlir::Value insertVal = adaptor.getOperands()[1]; 1975 1976 auto i64Ty = rewriter.getI64Type(); 1977 while (subscripts != uBounds) { 1978 // Convert uint64_t's to Attribute's. 1979 SmallVector<mlir::Attribute> subscriptAttrs; 1980 for (const auto &subscript : subscripts) 1981 subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript)); 1982 lastOp = rewriter.create<mlir::LLVM::InsertValueOp>( 1983 loc, ty, lastOp, insertVal, 1984 ArrayAttr::get(range.getContext(), subscriptAttrs)); 1985 1986 incrementSubscripts(dims, subscripts); 1987 } 1988 1989 // Convert uint64_t's to Attribute's. 1990 SmallVector<mlir::Attribute> subscriptAttrs; 1991 for (const auto &subscript : subscripts) 1992 subscriptAttrs.push_back( 1993 IntegerAttr::get(rewriter.getI64Type(), subscript)); 1994 mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs); 1995 1996 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1997 range, ty, lastOp, insertVal, 1998 ArrayAttr::get(range.getContext(), arrayRef)); 1999 2000 return success(); 2001 } 2002 }; 2003 } // namespace 2004 2005 namespace { 2006 /// XArrayCoor is the address arithmetic on a dynamically shaped, sliced, 2007 /// shifted etc. array. 2008 /// (See the static restriction on coordinate_of.) array_coor determines the 2009 /// coordinate (location) of a specific element. 2010 struct XArrayCoorOpConversion 2011 : public FIROpAndTypeConversion<fir::cg::XArrayCoorOp> { 2012 using FIROpAndTypeConversion::FIROpAndTypeConversion; 2013 2014 mlir::LogicalResult 2015 doRewrite(fir::cg::XArrayCoorOp coor, mlir::Type ty, OpAdaptor adaptor, 2016 mlir::ConversionPatternRewriter &rewriter) const override { 2017 auto loc = coor.getLoc(); 2018 mlir::ValueRange operands = adaptor.getOperands(); 2019 unsigned rank = coor.getRank(); 2020 assert(coor.indices().size() == rank); 2021 assert(coor.shape().empty() || coor.shape().size() == rank); 2022 assert(coor.shift().empty() || coor.shift().size() == rank); 2023 assert(coor.slice().empty() || coor.slice().size() == 3 * rank); 2024 mlir::Type idxTy = lowerTy().indexType(); 2025 mlir::Value one = genConstantIndex(loc, idxTy, rewriter, 1); 2026 mlir::Value prevExt = one; 2027 mlir::Value zero = genConstantIndex(loc, idxTy, rewriter, 0); 2028 mlir::Value offset = zero; 2029 const bool isShifted = !coor.shift().empty(); 2030 const bool isSliced = !coor.slice().empty(); 2031 const bool baseIsBoxed = coor.memref().getType().isa<fir::BoxType>(); 2032 2033 auto indexOps = coor.indices().begin(); 2034 auto shapeOps = coor.shape().begin(); 2035 auto shiftOps = coor.shift().begin(); 2036 auto sliceOps = coor.slice().begin(); 2037 // For each dimension of the array, generate the offset calculation. 2038 for (unsigned i = 0; i < rank; 2039 ++i, ++indexOps, ++shapeOps, ++shiftOps, sliceOps += 3) { 2040 mlir::Value index = 2041 integerCast(loc, rewriter, idxTy, operands[coor.indicesOffset() + i]); 2042 mlir::Value lb = isShifted ? integerCast(loc, rewriter, idxTy, 2043 operands[coor.shiftOffset() + i]) 2044 : one; 2045 mlir::Value step = one; 2046 bool normalSlice = isSliced; 2047 // Compute zero based index in dimension i of the element, applying 2048 // potential triplets and lower bounds. 2049 if (isSliced) { 2050 mlir::Value ub = *(sliceOps + 1); 2051 normalSlice = !mlir::isa_and_nonnull<fir::UndefOp>(ub.getDefiningOp()); 2052 if (normalSlice) 2053 step = integerCast(loc, rewriter, idxTy, *(sliceOps + 2)); 2054 } 2055 auto idx = rewriter.create<mlir::LLVM::SubOp>(loc, idxTy, index, lb); 2056 mlir::Value diff = 2057 rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, idx, step); 2058 if (normalSlice) { 2059 mlir::Value sliceLb = 2060 integerCast(loc, rewriter, idxTy, operands[coor.sliceOffset() + i]); 2061 auto adj = rewriter.create<mlir::LLVM::SubOp>(loc, idxTy, sliceLb, lb); 2062 diff = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, diff, adj); 2063 } 2064 // Update the offset given the stride and the zero based index `diff` 2065 // that was just computed. 2066 if (baseIsBoxed) { 2067 // Use stride in bytes from the descriptor. 2068 mlir::Value stride = 2069 loadStrideFromBox(loc, adaptor.getOperands()[0], i, rewriter); 2070 auto sc = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, diff, stride); 2071 offset = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, offset); 2072 } else { 2073 // Use stride computed at last iteration. 2074 auto sc = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, diff, prevExt); 2075 offset = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, offset); 2076 // Compute next stride assuming contiguity of the base array 2077 // (in element number). 2078 auto nextExt = 2079 integerCast(loc, rewriter, idxTy, operands[coor.shapeOffset() + i]); 2080 prevExt = 2081 rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, prevExt, nextExt); 2082 } 2083 } 2084 2085 // Add computed offset to the base address. 2086 if (baseIsBoxed) { 2087 // Working with byte offsets. The base address is read from the fir.box. 2088 // and need to be casted to i8* to do the pointer arithmetic. 2089 mlir::Type baseTy = 2090 getBaseAddrTypeFromBox(adaptor.getOperands()[0].getType()); 2091 mlir::Value base = 2092 loadBaseAddrFromBox(loc, baseTy, adaptor.getOperands()[0], rewriter); 2093 mlir::Type voidPtrTy = getVoidPtrType(); 2094 base = rewriter.create<mlir::LLVM::BitcastOp>(loc, voidPtrTy, base); 2095 llvm::SmallVector<mlir::Value> args{offset}; 2096 auto addr = 2097 rewriter.create<mlir::LLVM::GEPOp>(loc, voidPtrTy, base, args); 2098 if (coor.subcomponent().empty()) { 2099 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(coor, baseTy, addr); 2100 return success(); 2101 } 2102 auto casted = rewriter.create<mlir::LLVM::BitcastOp>(loc, baseTy, addr); 2103 args.clear(); 2104 args.push_back(zero); 2105 if (!coor.lenParams().empty()) { 2106 // If type parameters are present, then we don't want to use a GEPOp 2107 // as below, as the LLVM struct type cannot be statically defined. 2108 TODO(loc, "derived type with type parameters"); 2109 } 2110 // TODO: array offset subcomponents must be converted to LLVM's 2111 // row-major layout here. 2112 for (auto i = coor.subcomponentOffset(); i != coor.indicesOffset(); ++i) 2113 args.push_back(operands[i]); 2114 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(coor, baseTy, casted, 2115 args); 2116 return success(); 2117 } 2118 2119 // The array was not boxed, so it must be contiguous. offset is therefore an 2120 // element offset and the base type is kept in the GEP unless the element 2121 // type size is itself dynamic. 2122 mlir::Value base; 2123 if (coor.subcomponent().empty()) { 2124 // No subcomponent. 2125 if (!coor.lenParams().empty()) { 2126 // Type parameters. Adjust element size explicitly. 2127 auto eleTy = fir::dyn_cast_ptrEleTy(coor.getType()); 2128 assert(eleTy && "result must be a reference-like type"); 2129 if (fir::characterWithDynamicLen(eleTy)) { 2130 assert(coor.lenParams().size() == 1); 2131 auto bitsInChar = lowerTy().getKindMap().getCharacterBitsize( 2132 eleTy.cast<fir::CharacterType>().getFKind()); 2133 auto scaling = genConstantIndex(loc, idxTy, rewriter, bitsInChar / 8); 2134 auto scaledBySize = 2135 rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, offset, scaling); 2136 auto length = 2137 integerCast(loc, rewriter, idxTy, 2138 adaptor.getOperands()[coor.lenParamsOffset()]); 2139 offset = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, scaledBySize, 2140 length); 2141 } else { 2142 TODO(loc, "compute size of derived type with type parameters"); 2143 } 2144 } 2145 // Cast the base address to a pointer to T. 2146 base = rewriter.create<mlir::LLVM::BitcastOp>(loc, ty, 2147 adaptor.getOperands()[0]); 2148 } else { 2149 // Operand #0 must have a pointer type. For subcomponent slicing, we 2150 // want to cast away the array type and have a plain struct type. 2151 mlir::Type ty0 = adaptor.getOperands()[0].getType(); 2152 auto ptrTy = ty0.dyn_cast<mlir::LLVM::LLVMPointerType>(); 2153 assert(ptrTy && "expected pointer type"); 2154 mlir::Type eleTy = ptrTy.getElementType(); 2155 while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>()) 2156 eleTy = arrTy.getElementType(); 2157 auto newTy = mlir::LLVM::LLVMPointerType::get(eleTy); 2158 base = rewriter.create<mlir::LLVM::BitcastOp>(loc, newTy, 2159 adaptor.getOperands()[0]); 2160 } 2161 SmallVector<mlir::Value> args = {offset}; 2162 for (auto i = coor.subcomponentOffset(); i != coor.indicesOffset(); ++i) 2163 args.push_back(operands[i]); 2164 rewriter.replaceOpWithNewOp<mlir::LLVM::GEPOp>(coor, ty, base, args); 2165 return success(); 2166 } 2167 }; 2168 } // namespace 2169 2170 /// Convert to (memory) reference to a reference to a subobject. 2171 /// The coordinate_of op is a Swiss army knife operation that can be used on 2172 /// (memory) references to records, arrays, complex, etc. as well as boxes. 2173 /// With unboxed arrays, there is the restriction that the array have a static 2174 /// shape in all but the last column. 2175 struct CoordinateOpConversion 2176 : public FIROpAndTypeConversion<fir::CoordinateOp> { 2177 using FIROpAndTypeConversion::FIROpAndTypeConversion; 2178 2179 mlir::LogicalResult 2180 doRewrite(fir::CoordinateOp coor, mlir::Type ty, OpAdaptor adaptor, 2181 mlir::ConversionPatternRewriter &rewriter) const override { 2182 mlir::ValueRange operands = adaptor.getOperands(); 2183 2184 mlir::Location loc = coor.getLoc(); 2185 mlir::Value base = operands[0]; 2186 mlir::Type baseObjectTy = coor.getBaseType(); 2187 mlir::Type objectTy = fir::dyn_cast_ptrOrBoxEleTy(baseObjectTy); 2188 assert(objectTy && "fir.coordinate_of expects a reference type"); 2189 2190 // Complex type - basically, extract the real or imaginary part 2191 if (fir::isa_complex(objectTy)) { 2192 mlir::LLVM::ConstantOp c0 = 2193 genConstantIndex(loc, lowerTy().indexType(), rewriter, 0); 2194 SmallVector<mlir::Value> offs = {c0, operands[1]}; 2195 mlir::Value gep = genGEP(loc, ty, rewriter, base, offs); 2196 rewriter.replaceOp(coor, gep); 2197 return success(); 2198 } 2199 2200 // Boxed type - get the base pointer from the box 2201 if (baseObjectTy.dyn_cast<fir::BoxType>()) 2202 return doRewriteBox(coor, ty, operands, loc, rewriter); 2203 2204 // Reference or pointer type 2205 if (baseObjectTy.isa<fir::ReferenceType, fir::PointerType>()) 2206 return doRewriteRefOrPtr(coor, ty, operands, loc, rewriter); 2207 2208 return rewriter.notifyMatchFailure( 2209 coor, "fir.coordinate_of base operand has unsupported type"); 2210 } 2211 2212 unsigned getFieldNumber(fir::RecordType ty, mlir::Value op) const { 2213 return fir::hasDynamicSize(ty) 2214 ? op.getDefiningOp() 2215 ->getAttrOfType<mlir::IntegerAttr>("field") 2216 .getInt() 2217 : getIntValue(op); 2218 } 2219 2220 int64_t getIntValue(mlir::Value val) const { 2221 assert(val && val.dyn_cast<mlir::OpResult>() && "must not be null value"); 2222 mlir::Operation *defop = val.getDefiningOp(); 2223 2224 if (auto constOp = dyn_cast<mlir::arith::ConstantIntOp>(defop)) 2225 return constOp.value(); 2226 if (auto llConstOp = dyn_cast<mlir::LLVM::ConstantOp>(defop)) 2227 if (auto attr = llConstOp.getValue().dyn_cast<mlir::IntegerAttr>()) 2228 return attr.getValue().getSExtValue(); 2229 fir::emitFatalError(val.getLoc(), "must be a constant"); 2230 } 2231 2232 bool hasSubDimensions(mlir::Type type) const { 2233 return type.isa<fir::SequenceType, fir::RecordType, mlir::TupleType>(); 2234 } 2235 2236 /// Check whether this form of `!fir.coordinate_of` is supported. These 2237 /// additional checks are required, because we are not yet able to convert 2238 /// all valid forms of `!fir.coordinate_of`. 2239 /// TODO: Either implement the unsupported cases or extend the verifier 2240 /// in FIROps.cpp instead. 2241 bool supportedCoordinate(mlir::Type type, mlir::ValueRange coors) const { 2242 const std::size_t numOfCoors = coors.size(); 2243 std::size_t i = 0; 2244 bool subEle = false; 2245 bool ptrEle = false; 2246 for (; i < numOfCoors; ++i) { 2247 mlir::Value nxtOpnd = coors[i]; 2248 if (auto arrTy = type.dyn_cast<fir::SequenceType>()) { 2249 subEle = true; 2250 i += arrTy.getDimension() - 1; 2251 type = arrTy.getEleTy(); 2252 } else if (auto recTy = type.dyn_cast<fir::RecordType>()) { 2253 subEle = true; 2254 type = recTy.getType(getFieldNumber(recTy, nxtOpnd)); 2255 } else if (auto tupTy = type.dyn_cast<mlir::TupleType>()) { 2256 subEle = true; 2257 type = tupTy.getType(getIntValue(nxtOpnd)); 2258 } else { 2259 ptrEle = true; 2260 } 2261 } 2262 if (ptrEle) 2263 return (!subEle) && (numOfCoors == 1); 2264 return subEle && (i >= numOfCoors); 2265 } 2266 2267 /// Walk the abstract memory layout and determine if the path traverses any 2268 /// array types with unknown shape. Return true iff all the array types have a 2269 /// constant shape along the path. 2270 bool arraysHaveKnownShape(mlir::Type type, mlir::ValueRange coors) const { 2271 const std::size_t sz = coors.size(); 2272 std::size_t i = 0; 2273 for (; i < sz; ++i) { 2274 mlir::Value nxtOpnd = coors[i]; 2275 if (auto arrTy = type.dyn_cast<fir::SequenceType>()) { 2276 if (fir::sequenceWithNonConstantShape(arrTy)) 2277 return false; 2278 i += arrTy.getDimension() - 1; 2279 type = arrTy.getEleTy(); 2280 } else if (auto strTy = type.dyn_cast<fir::RecordType>()) { 2281 type = strTy.getType(getFieldNumber(strTy, nxtOpnd)); 2282 } else if (auto strTy = type.dyn_cast<mlir::TupleType>()) { 2283 type = strTy.getType(getIntValue(nxtOpnd)); 2284 } else { 2285 return true; 2286 } 2287 } 2288 return true; 2289 } 2290 2291 private: 2292 mlir::LogicalResult 2293 doRewriteBox(fir::CoordinateOp coor, mlir::Type ty, mlir::ValueRange operands, 2294 mlir::Location loc, 2295 mlir::ConversionPatternRewriter &rewriter) const { 2296 mlir::Type boxObjTy = coor.getBaseType(); 2297 assert(boxObjTy.dyn_cast<fir::BoxType>() && "This is not a `fir.box`"); 2298 2299 mlir::Value boxBaseAddr = operands[0]; 2300 2301 // 1. SPECIAL CASE (uses `fir.len_param_index`): 2302 // %box = ... : !fir.box<!fir.type<derived{len1:i32}>> 2303 // %lenp = fir.len_param_index len1, !fir.type<derived{len1:i32}> 2304 // %addr = coordinate_of %box, %lenp 2305 if (coor.getNumOperands() == 2) { 2306 mlir::Operation *coordinateDef = 2307 (*coor.getCoor().begin()).getDefiningOp(); 2308 if (isa_and_nonnull<fir::LenParamIndexOp>(coordinateDef)) { 2309 TODO(loc, 2310 "fir.coordinate_of - fir.len_param_index is not supported yet"); 2311 } 2312 } 2313 2314 // 2. GENERAL CASE: 2315 // 2.1. (`fir.array`) 2316 // %box = ... : !fix.box<!fir.array<?xU>> 2317 // %idx = ... : index 2318 // %resultAddr = coordinate_of %box, %idx : !fir.ref<U> 2319 // 2.2 (`fir.derived`) 2320 // %box = ... : !fix.box<!fir.type<derived_type{field_1:i32}>> 2321 // %idx = ... : i32 2322 // %resultAddr = coordinate_of %box, %idx : !fir.ref<i32> 2323 // 2.3 (`fir.derived` inside `fir.array`) 2324 // %box = ... : !fir.box<!fir.array<10 x !fir.type<derived_1{field_1:f32, 2325 // field_2:f32}>>> %idx1 = ... : index %idx2 = ... : i32 %resultAddr = 2326 // coordinate_of %box, %idx1, %idx2 : !fir.ref<f32> 2327 // 2.4. TODO: Either document or disable any other case that the following 2328 // implementation might convert. 2329 mlir::LLVM::ConstantOp c0 = 2330 genConstantIndex(loc, lowerTy().indexType(), rewriter, 0); 2331 mlir::Value resultAddr = 2332 loadBaseAddrFromBox(loc, getBaseAddrTypeFromBox(boxBaseAddr.getType()), 2333 boxBaseAddr, rewriter); 2334 auto currentObjTy = fir::dyn_cast_ptrOrBoxEleTy(boxObjTy); 2335 mlir::Type voidPtrTy = ::getVoidPtrType(coor.getContext()); 2336 2337 for (unsigned i = 1, last = operands.size(); i < last; ++i) { 2338 if (auto arrTy = currentObjTy.dyn_cast<fir::SequenceType>()) { 2339 if (i != 1) 2340 TODO(loc, "fir.array nested inside other array and/or derived type"); 2341 // Applies byte strides from the box. Ignore lower bound from box 2342 // since fir.coordinate_of indexes are zero based. Lowering takes care 2343 // of lower bound aspects. This both accounts for dynamically sized 2344 // types and non contiguous arrays. 2345 auto idxTy = lowerTy().indexType(); 2346 mlir::Value off = genConstantIndex(loc, idxTy, rewriter, 0); 2347 for (unsigned index = i, lastIndex = i + arrTy.getDimension(); 2348 index < lastIndex; ++index) { 2349 mlir::Value stride = 2350 loadStrideFromBox(loc, operands[0], index - i, rewriter); 2351 auto sc = rewriter.create<mlir::LLVM::MulOp>(loc, idxTy, 2352 operands[index], stride); 2353 off = rewriter.create<mlir::LLVM::AddOp>(loc, idxTy, sc, off); 2354 } 2355 auto voidPtrBase = 2356 rewriter.create<mlir::LLVM::BitcastOp>(loc, voidPtrTy, resultAddr); 2357 SmallVector<mlir::Value> args{off}; 2358 resultAddr = rewriter.create<mlir::LLVM::GEPOp>(loc, voidPtrTy, 2359 voidPtrBase, args); 2360 i += arrTy.getDimension() - 1; 2361 currentObjTy = arrTy.getEleTy(); 2362 } else if (auto recTy = currentObjTy.dyn_cast<fir::RecordType>()) { 2363 auto recRefTy = 2364 mlir::LLVM::LLVMPointerType::get(lowerTy().convertType(recTy)); 2365 mlir::Value nxtOpnd = operands[i]; 2366 auto memObj = 2367 rewriter.create<mlir::LLVM::BitcastOp>(loc, recRefTy, resultAddr); 2368 llvm::SmallVector<mlir::Value> args = {c0, nxtOpnd}; 2369 currentObjTy = recTy.getType(getFieldNumber(recTy, nxtOpnd)); 2370 auto llvmCurrentObjTy = lowerTy().convertType(currentObjTy); 2371 auto gep = rewriter.create<mlir::LLVM::GEPOp>( 2372 loc, mlir::LLVM::LLVMPointerType::get(llvmCurrentObjTy), memObj, 2373 args); 2374 resultAddr = 2375 rewriter.create<mlir::LLVM::BitcastOp>(loc, voidPtrTy, gep); 2376 } else { 2377 fir::emitFatalError(loc, "unexpected type in coordinate_of"); 2378 } 2379 } 2380 2381 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(coor, ty, resultAddr); 2382 return success(); 2383 } 2384 2385 mlir::LogicalResult 2386 doRewriteRefOrPtr(fir::CoordinateOp coor, mlir::Type ty, 2387 mlir::ValueRange operands, mlir::Location loc, 2388 mlir::ConversionPatternRewriter &rewriter) const { 2389 mlir::Type baseObjectTy = coor.getBaseType(); 2390 2391 mlir::Type currentObjTy = fir::dyn_cast_ptrOrBoxEleTy(baseObjectTy); 2392 bool hasSubdimension = hasSubDimensions(currentObjTy); 2393 bool columnIsDeferred = !hasSubdimension; 2394 2395 if (!supportedCoordinate(currentObjTy, operands.drop_front(1))) { 2396 TODO(loc, "unsupported combination of coordinate operands"); 2397 } 2398 2399 const bool hasKnownShape = 2400 arraysHaveKnownShape(currentObjTy, operands.drop_front(1)); 2401 2402 // If only the column is `?`, then we can simply place the column value in 2403 // the 0-th GEP position. 2404 if (auto arrTy = currentObjTy.dyn_cast<fir::SequenceType>()) { 2405 if (!hasKnownShape) { 2406 const unsigned sz = arrTy.getDimension(); 2407 if (arraysHaveKnownShape(arrTy.getEleTy(), 2408 operands.drop_front(1 + sz))) { 2409 llvm::ArrayRef<int64_t> shape = arrTy.getShape(); 2410 bool allConst = true; 2411 for (unsigned i = 0; i < sz - 1; ++i) { 2412 if (shape[i] < 0) { 2413 allConst = false; 2414 break; 2415 } 2416 } 2417 if (allConst) 2418 columnIsDeferred = true; 2419 } 2420 } 2421 } 2422 2423 if (fir::hasDynamicSize(fir::unwrapSequenceType(currentObjTy))) { 2424 mlir::emitError( 2425 loc, "fir.coordinate_of with a dynamic element size is unsupported"); 2426 return failure(); 2427 } 2428 2429 if (hasKnownShape || columnIsDeferred) { 2430 SmallVector<mlir::Value> offs; 2431 if (hasKnownShape && hasSubdimension) { 2432 mlir::LLVM::ConstantOp c0 = 2433 genConstantIndex(loc, lowerTy().indexType(), rewriter, 0); 2434 offs.push_back(c0); 2435 } 2436 const std::size_t sz = operands.size(); 2437 Optional<int> dims; 2438 SmallVector<mlir::Value> arrIdx; 2439 for (std::size_t i = 1; i < sz; ++i) { 2440 mlir::Value nxtOpnd = operands[i]; 2441 2442 if (!currentObjTy) { 2443 mlir::emitError(loc, "invalid coordinate/check failed"); 2444 return failure(); 2445 } 2446 2447 // check if the i-th coordinate relates to an array 2448 if (dims.hasValue()) { 2449 arrIdx.push_back(nxtOpnd); 2450 int dimsLeft = *dims; 2451 if (dimsLeft > 1) { 2452 dims = dimsLeft - 1; 2453 continue; 2454 } 2455 currentObjTy = currentObjTy.cast<fir::SequenceType>().getEleTy(); 2456 // append array range in reverse (FIR arrays are column-major) 2457 offs.append(arrIdx.rbegin(), arrIdx.rend()); 2458 arrIdx.clear(); 2459 dims.reset(); 2460 continue; 2461 } 2462 if (auto arrTy = currentObjTy.dyn_cast<fir::SequenceType>()) { 2463 int d = arrTy.getDimension() - 1; 2464 if (d > 0) { 2465 dims = d; 2466 arrIdx.push_back(nxtOpnd); 2467 continue; 2468 } 2469 currentObjTy = currentObjTy.cast<fir::SequenceType>().getEleTy(); 2470 offs.push_back(nxtOpnd); 2471 continue; 2472 } 2473 2474 // check if the i-th coordinate relates to a field 2475 if (auto recTy = currentObjTy.dyn_cast<fir::RecordType>()) 2476 currentObjTy = recTy.getType(getFieldNumber(recTy, nxtOpnd)); 2477 else if (auto tupTy = currentObjTy.dyn_cast<mlir::TupleType>()) 2478 currentObjTy = tupTy.getType(getIntValue(nxtOpnd)); 2479 else 2480 currentObjTy = nullptr; 2481 2482 offs.push_back(nxtOpnd); 2483 } 2484 if (dims.hasValue()) 2485 offs.append(arrIdx.rbegin(), arrIdx.rend()); 2486 mlir::Value base = operands[0]; 2487 mlir::Value retval = genGEP(loc, ty, rewriter, base, offs); 2488 rewriter.replaceOp(coor, retval); 2489 return success(); 2490 } 2491 2492 mlir::emitError(loc, "fir.coordinate_of base operand has unsupported type"); 2493 return failure(); 2494 } 2495 }; 2496 2497 /// Convert `fir.field_index`. The conversion depends on whether the size of 2498 /// the record is static or dynamic. 2499 struct FieldIndexOpConversion : public FIROpConversion<fir::FieldIndexOp> { 2500 using FIROpConversion::FIROpConversion; 2501 2502 // NB: most field references should be resolved by this point 2503 mlir::LogicalResult 2504 matchAndRewrite(fir::FieldIndexOp field, OpAdaptor adaptor, 2505 mlir::ConversionPatternRewriter &rewriter) const override { 2506 auto recTy = field.getOnType().cast<fir::RecordType>(); 2507 unsigned index = recTy.getFieldIndex(field.getFieldId()); 2508 2509 if (!fir::hasDynamicSize(recTy)) { 2510 // Derived type has compile-time constant layout. Return index of the 2511 // component type in the parent type (to be used in GEP). 2512 rewriter.replaceOp(field, mlir::ValueRange{genConstantOffset( 2513 field.getLoc(), rewriter, index)}); 2514 return success(); 2515 } 2516 2517 // Derived type has compile-time constant layout. Call the compiler 2518 // generated function to determine the byte offset of the field at runtime. 2519 // This returns a non-constant. 2520 FlatSymbolRefAttr symAttr = mlir::SymbolRefAttr::get( 2521 field.getContext(), getOffsetMethodName(recTy, field.getFieldId())); 2522 NamedAttribute callAttr = rewriter.getNamedAttr("callee", symAttr); 2523 NamedAttribute fieldAttr = rewriter.getNamedAttr( 2524 "field", mlir::IntegerAttr::get(lowerTy().indexType(), index)); 2525 rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>( 2526 field, lowerTy().offsetType(), adaptor.getOperands(), 2527 llvm::ArrayRef<mlir::NamedAttribute>{callAttr, fieldAttr}); 2528 return success(); 2529 } 2530 2531 // Re-Construct the name of the compiler generated method that calculates the 2532 // offset 2533 inline static std::string getOffsetMethodName(fir::RecordType recTy, 2534 llvm::StringRef field) { 2535 return recTy.getName().str() + "P." + field.str() + ".offset"; 2536 } 2537 }; 2538 2539 /// Convert `fir.end` 2540 struct FirEndOpConversion : public FIROpConversion<fir::FirEndOp> { 2541 using FIROpConversion::FIROpConversion; 2542 2543 mlir::LogicalResult 2544 matchAndRewrite(fir::FirEndOp firEnd, OpAdaptor, 2545 mlir::ConversionPatternRewriter &rewriter) const override { 2546 TODO(firEnd.getLoc(), "fir.end codegen"); 2547 return failure(); 2548 } 2549 }; 2550 2551 /// Lower `fir.gentypedesc` to a global constant. 2552 struct GenTypeDescOpConversion : public FIROpConversion<fir::GenTypeDescOp> { 2553 using FIROpConversion::FIROpConversion; 2554 2555 mlir::LogicalResult 2556 matchAndRewrite(fir::GenTypeDescOp gentypedesc, OpAdaptor adaptor, 2557 mlir::ConversionPatternRewriter &rewriter) const override { 2558 TODO(gentypedesc.getLoc(), "fir.gentypedesc codegen"); 2559 return failure(); 2560 } 2561 }; 2562 2563 /// Lower `fir.has_value` operation to `llvm.return` operation. 2564 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> { 2565 using FIROpConversion::FIROpConversion; 2566 2567 mlir::LogicalResult 2568 matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor, 2569 mlir::ConversionPatternRewriter &rewriter) const override { 2570 rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands()); 2571 return success(); 2572 } 2573 }; 2574 2575 /// Lower `fir.global` operation to `llvm.global` operation. 2576 /// `fir.insert_on_range` operations are replaced with constant dense attribute 2577 /// if they are applied on the full range. 2578 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> { 2579 using FIROpConversion::FIROpConversion; 2580 2581 mlir::LogicalResult 2582 matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor, 2583 mlir::ConversionPatternRewriter &rewriter) const override { 2584 auto tyAttr = convertType(global.getType()); 2585 if (global.getType().isa<fir::BoxType>()) 2586 tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType(); 2587 auto loc = global.getLoc(); 2588 mlir::Attribute initAttr{}; 2589 if (global.getInitVal()) 2590 initAttr = global.getInitVal().getValue(); 2591 auto linkage = convertLinkage(global.getLinkName()); 2592 auto isConst = global.getConstant().hasValue(); 2593 auto g = rewriter.create<mlir::LLVM::GlobalOp>( 2594 loc, tyAttr, isConst, linkage, global.getSymName(), initAttr); 2595 auto &gr = g.getInitializerRegion(); 2596 rewriter.inlineRegionBefore(global.getRegion(), gr, gr.end()); 2597 if (!gr.empty()) { 2598 // Replace insert_on_range with a constant dense attribute if the 2599 // initialization is on the full range. 2600 auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>(); 2601 for (auto insertOp : insertOnRangeOps) { 2602 if (isFullRange(insertOp.getCoor(), insertOp.getType())) { 2603 auto seqTyAttr = convertType(insertOp.getType()); 2604 auto *op = insertOp.getVal().getDefiningOp(); 2605 auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op); 2606 if (!constant) { 2607 auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op); 2608 if (!convertOp) 2609 continue; 2610 constant = cast<mlir::arith::ConstantOp>( 2611 convertOp.getValue().getDefiningOp()); 2612 } 2613 mlir::Type vecType = mlir::VectorType::get( 2614 insertOp.getType().getShape(), constant.getType()); 2615 auto denseAttr = mlir::DenseElementsAttr::get( 2616 vecType.cast<ShapedType>(), constant.getValue()); 2617 rewriter.setInsertionPointAfter(insertOp); 2618 rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>( 2619 insertOp, seqTyAttr, denseAttr); 2620 } 2621 } 2622 } 2623 rewriter.eraseOp(global); 2624 return success(); 2625 } 2626 2627 bool isFullRange(mlir::DenseIntElementsAttr indexes, 2628 fir::SequenceType seqTy) const { 2629 auto extents = seqTy.getShape(); 2630 if (indexes.size() / 2 != static_cast<int64_t>(extents.size())) 2631 return false; 2632 auto cur_index = indexes.value_begin<int64_t>(); 2633 for (unsigned i = 0; i < indexes.size(); i += 2) { 2634 if (*(cur_index++) != 0) 2635 return false; 2636 if (*(cur_index++) != extents[i / 2] - 1) 2637 return false; 2638 } 2639 return true; 2640 } 2641 2642 // TODO: String comparaison should be avoided. Replace linkName with an 2643 // enumeration. 2644 mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const { 2645 if (optLinkage.hasValue()) { 2646 auto name = optLinkage.getValue(); 2647 if (name == "internal") 2648 return mlir::LLVM::Linkage::Internal; 2649 if (name == "linkonce") 2650 return mlir::LLVM::Linkage::Linkonce; 2651 if (name == "linkonce_odr") 2652 return mlir::LLVM::Linkage::LinkonceODR; 2653 if (name == "common") 2654 return mlir::LLVM::Linkage::Common; 2655 if (name == "weak") 2656 return mlir::LLVM::Linkage::Weak; 2657 } 2658 return mlir::LLVM::Linkage::External; 2659 } 2660 }; 2661 2662 /// `fir.load` --> `llvm.load` 2663 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> { 2664 using FIROpConversion::FIROpConversion; 2665 2666 mlir::LogicalResult 2667 matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor, 2668 mlir::ConversionPatternRewriter &rewriter) const override { 2669 // fir.box is a special case because it is considered as an ssa values in 2670 // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box> 2671 // and fir.box end up being the same llvm types and loading a 2672 // fir.ref<fir.box> is actually a no op in LLVM. 2673 if (load.getType().isa<fir::BoxType>()) { 2674 rewriter.replaceOp(load, adaptor.getOperands()[0]); 2675 } else { 2676 mlir::Type ty = convertType(load.getType()); 2677 ArrayRef<NamedAttribute> at = load->getAttrs(); 2678 rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>( 2679 load, ty, adaptor.getOperands(), at); 2680 } 2681 return success(); 2682 } 2683 }; 2684 2685 /// Lower `fir.no_reassoc` to LLVM IR dialect. 2686 /// TODO: how do we want to enforce this in LLVM-IR? Can we manipulate the fast 2687 /// math flags? 2688 struct NoReassocOpConversion : public FIROpConversion<fir::NoReassocOp> { 2689 using FIROpConversion::FIROpConversion; 2690 2691 mlir::LogicalResult 2692 matchAndRewrite(fir::NoReassocOp noreassoc, OpAdaptor adaptor, 2693 mlir::ConversionPatternRewriter &rewriter) const override { 2694 rewriter.replaceOp(noreassoc, adaptor.getOperands()[0]); 2695 return success(); 2696 } 2697 }; 2698 2699 static void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest, 2700 Optional<mlir::ValueRange> destOps, 2701 mlir::ConversionPatternRewriter &rewriter, 2702 mlir::Block *newBlock) { 2703 if (destOps.hasValue()) 2704 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, destOps.getValue(), 2705 newBlock, mlir::ValueRange()); 2706 else 2707 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock); 2708 } 2709 2710 template <typename A, typename B> 2711 static void genBrOp(A caseOp, mlir::Block *dest, Optional<B> destOps, 2712 mlir::ConversionPatternRewriter &rewriter) { 2713 if (destOps.hasValue()) 2714 rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, destOps.getValue(), 2715 dest); 2716 else 2717 rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, llvm::None, dest); 2718 } 2719 2720 static void genCaseLadderStep(mlir::Location loc, mlir::Value cmp, 2721 mlir::Block *dest, 2722 Optional<mlir::ValueRange> destOps, 2723 mlir::ConversionPatternRewriter &rewriter) { 2724 auto *thisBlock = rewriter.getInsertionBlock(); 2725 auto *newBlock = createBlock(rewriter, dest); 2726 rewriter.setInsertionPointToEnd(thisBlock); 2727 genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock); 2728 rewriter.setInsertionPointToEnd(newBlock); 2729 } 2730 2731 /// Conversion of `fir.select_case` 2732 /// 2733 /// The `fir.select_case` operation is converted to a if-then-else ladder. 2734 /// Depending on the case condition type, one or several comparison and 2735 /// conditional branching can be generated. 2736 /// 2737 /// A a point value case such as `case(4)`, a lower bound case such as 2738 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a 2739 /// simple comparison between the selector value and the constant value in the 2740 /// case. The block associated with the case condition is then executed if 2741 /// the comparison succeed otherwise it branch to the next block with the 2742 /// comparison for the the next case conditon. 2743 /// 2744 /// A closed interval case condition such as `case(7:10)` is converted with a 2745 /// first comparison and conditional branching for the lower bound. If 2746 /// successful, it branch to a second block with the comparison for the 2747 /// upper bound in the same case condition. 2748 /// 2749 /// TODO: lowering of CHARACTER type cases is not handled yet. 2750 struct SelectCaseOpConversion : public FIROpConversion<fir::SelectCaseOp> { 2751 using FIROpConversion::FIROpConversion; 2752 2753 mlir::LogicalResult 2754 matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor, 2755 mlir::ConversionPatternRewriter &rewriter) const override { 2756 unsigned conds = caseOp.getNumConditions(); 2757 llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue(); 2758 // Type can be CHARACTER, INTEGER, or LOGICAL (C1145) 2759 auto ty = caseOp.getSelector().getType(); 2760 if (ty.isa<fir::CharacterType>()) { 2761 TODO(caseOp.getLoc(), "fir.select_case codegen with character type"); 2762 return failure(); 2763 } 2764 mlir::Value selector = caseOp.getSelector(adaptor.getOperands()); 2765 auto loc = caseOp.getLoc(); 2766 for (unsigned t = 0; t != conds; ++t) { 2767 mlir::Block *dest = caseOp.getSuccessor(t); 2768 llvm::Optional<mlir::ValueRange> destOps = 2769 caseOp.getSuccessorOperands(adaptor.getOperands(), t); 2770 llvm::Optional<mlir::ValueRange> cmpOps = 2771 *caseOp.getCompareOperands(adaptor.getOperands(), t); 2772 mlir::Value caseArg = *(cmpOps.getValue().begin()); 2773 mlir::Attribute attr = cases[t]; 2774 if (attr.isa<fir::PointIntervalAttr>()) { 2775 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 2776 loc, mlir::LLVM::ICmpPredicate::eq, selector, caseArg); 2777 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 2778 continue; 2779 } 2780 if (attr.isa<fir::LowerBoundAttr>()) { 2781 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 2782 loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector); 2783 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 2784 continue; 2785 } 2786 if (attr.isa<fir::UpperBoundAttr>()) { 2787 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 2788 loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg); 2789 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 2790 continue; 2791 } 2792 if (attr.isa<fir::ClosedIntervalAttr>()) { 2793 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 2794 loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector); 2795 auto *thisBlock = rewriter.getInsertionBlock(); 2796 auto *newBlock1 = createBlock(rewriter, dest); 2797 auto *newBlock2 = createBlock(rewriter, dest); 2798 rewriter.setInsertionPointToEnd(thisBlock); 2799 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, newBlock1, newBlock2); 2800 rewriter.setInsertionPointToEnd(newBlock1); 2801 mlir::Value caseArg0 = *(cmpOps.getValue().begin() + 1); 2802 auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>( 2803 loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg0); 2804 genCondBrOp(loc, cmp0, dest, destOps, rewriter, newBlock2); 2805 rewriter.setInsertionPointToEnd(newBlock2); 2806 continue; 2807 } 2808 assert(attr.isa<mlir::UnitAttr>()); 2809 assert((t + 1 == conds) && "unit must be last"); 2810 genBrOp(caseOp, dest, destOps, rewriter); 2811 } 2812 return success(); 2813 } 2814 }; 2815 2816 template <typename OP> 2817 static void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select, 2818 typename OP::Adaptor adaptor, 2819 mlir::ConversionPatternRewriter &rewriter) { 2820 unsigned conds = select.getNumConditions(); 2821 auto cases = select.getCases().getValue(); 2822 mlir::Value selector = adaptor.getSelector(); 2823 auto loc = select.getLoc(); 2824 assert(conds > 0 && "select must have cases"); 2825 2826 llvm::SmallVector<mlir::Block *> destinations; 2827 llvm::SmallVector<mlir::ValueRange> destinationsOperands; 2828 mlir::Block *defaultDestination; 2829 mlir::ValueRange defaultOperands; 2830 llvm::SmallVector<int32_t> caseValues; 2831 2832 for (unsigned t = 0; t != conds; ++t) { 2833 mlir::Block *dest = select.getSuccessor(t); 2834 auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t); 2835 const mlir::Attribute &attr = cases[t]; 2836 if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) { 2837 destinations.push_back(dest); 2838 destinationsOperands.push_back(destOps.hasValue() ? *destOps 2839 : ValueRange()); 2840 caseValues.push_back(intAttr.getInt()); 2841 continue; 2842 } 2843 assert(attr.template dyn_cast_or_null<mlir::UnitAttr>()); 2844 assert((t + 1 == conds) && "unit must be last"); 2845 defaultDestination = dest; 2846 defaultOperands = destOps.hasValue() ? *destOps : ValueRange(); 2847 } 2848 2849 // LLVM::SwitchOp takes a i32 type for the selector. 2850 if (select.getSelector().getType() != rewriter.getI32Type()) 2851 selector = 2852 rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector); 2853 2854 rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>( 2855 select, selector, 2856 /*defaultDestination=*/defaultDestination, 2857 /*defaultOperands=*/defaultOperands, 2858 /*caseValues=*/caseValues, 2859 /*caseDestinations=*/destinations, 2860 /*caseOperands=*/destinationsOperands, 2861 /*branchWeights=*/ArrayRef<int32_t>()); 2862 } 2863 2864 /// conversion of fir::SelectOp to an if-then-else ladder 2865 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> { 2866 using FIROpConversion::FIROpConversion; 2867 2868 mlir::LogicalResult 2869 matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor, 2870 mlir::ConversionPatternRewriter &rewriter) const override { 2871 selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter); 2872 return success(); 2873 } 2874 }; 2875 2876 /// conversion of fir::SelectRankOp to an if-then-else ladder 2877 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> { 2878 using FIROpConversion::FIROpConversion; 2879 2880 mlir::LogicalResult 2881 matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor, 2882 mlir::ConversionPatternRewriter &rewriter) const override { 2883 selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter); 2884 return success(); 2885 } 2886 }; 2887 2888 /// Lower `fir.select_type` to LLVM IR dialect. 2889 struct SelectTypeOpConversion : public FIROpConversion<fir::SelectTypeOp> { 2890 using FIROpConversion::FIROpConversion; 2891 2892 mlir::LogicalResult 2893 matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor, 2894 mlir::ConversionPatternRewriter &rewriter) const override { 2895 mlir::emitError(select.getLoc(), 2896 "fir.select_type should have already been converted"); 2897 return failure(); 2898 } 2899 }; 2900 2901 /// `fir.store` --> `llvm.store` 2902 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> { 2903 using FIROpConversion::FIROpConversion; 2904 2905 mlir::LogicalResult 2906 matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor, 2907 mlir::ConversionPatternRewriter &rewriter) const override { 2908 if (store.getValue().getType().isa<fir::BoxType>()) { 2909 // fir.box value is actually in memory, load it first before storing it. 2910 mlir::Location loc = store.getLoc(); 2911 mlir::Type boxPtrTy = adaptor.getOperands()[0].getType(); 2912 auto val = rewriter.create<mlir::LLVM::LoadOp>( 2913 loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(), 2914 adaptor.getOperands()[0]); 2915 rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 2916 store, val, adaptor.getOperands()[1]); 2917 } else { 2918 rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 2919 store, adaptor.getOperands()[0], adaptor.getOperands()[1]); 2920 } 2921 return success(); 2922 } 2923 }; 2924 2925 namespace { 2926 2927 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for 2928 /// the character buffer and one for the buffer length. 2929 struct UnboxCharOpConversion : public FIROpConversion<fir::UnboxCharOp> { 2930 using FIROpConversion::FIROpConversion; 2931 2932 mlir::LogicalResult 2933 matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor, 2934 mlir::ConversionPatternRewriter &rewriter) const override { 2935 MLIRContext *ctx = unboxchar.getContext(); 2936 2937 mlir::Type lenTy = convertType(unboxchar.getType(1)); 2938 mlir::Value tuple = adaptor.getOperands()[0]; 2939 mlir::Type tupleTy = tuple.getType(); 2940 2941 mlir::Location loc = unboxchar.getLoc(); 2942 mlir::Value ptrToBuffer = 2943 genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 0); 2944 2945 mlir::LLVM::ExtractValueOp len = 2946 genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 1); 2947 mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len); 2948 2949 rewriter.replaceOp(unboxchar, 2950 ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast}); 2951 return success(); 2952 } 2953 }; 2954 2955 /// Lower `fir.unboxproc` operation. Unbox a procedure box value, yielding its 2956 /// components. 2957 /// TODO: Part of supporting Fortran 2003 procedure pointers. 2958 struct UnboxProcOpConversion : public FIROpConversion<fir::UnboxProcOp> { 2959 using FIROpConversion::FIROpConversion; 2960 2961 mlir::LogicalResult 2962 matchAndRewrite(fir::UnboxProcOp unboxproc, OpAdaptor adaptor, 2963 mlir::ConversionPatternRewriter &rewriter) const override { 2964 TODO(unboxproc.getLoc(), "fir.unboxproc codegen"); 2965 return failure(); 2966 } 2967 }; 2968 2969 /// convert to LLVM IR dialect `undef` 2970 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> { 2971 using FIROpConversion::FIROpConversion; 2972 2973 mlir::LogicalResult 2974 matchAndRewrite(fir::UndefOp undef, OpAdaptor, 2975 mlir::ConversionPatternRewriter &rewriter) const override { 2976 rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>( 2977 undef, convertType(undef.getType())); 2978 return success(); 2979 } 2980 }; 2981 2982 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> { 2983 using FIROpConversion::FIROpConversion; 2984 2985 mlir::LogicalResult 2986 matchAndRewrite(fir::ZeroOp zero, OpAdaptor, 2987 mlir::ConversionPatternRewriter &rewriter) const override { 2988 mlir::Type ty = convertType(zero.getType()); 2989 if (ty.isa<mlir::LLVM::LLVMPointerType>()) { 2990 rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty); 2991 } else if (ty.isa<mlir::IntegerType>()) { 2992 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 2993 zero, ty, mlir::IntegerAttr::get(zero.getType(), 0)); 2994 } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) { 2995 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 2996 zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0)); 2997 } else { 2998 // TODO: create ConstantAggregateZero for FIR aggregate/array types. 2999 return rewriter.notifyMatchFailure( 3000 zero, 3001 "conversion of fir.zero with aggregate type not implemented yet"); 3002 } 3003 return success(); 3004 } 3005 }; 3006 3007 /// `fir.unreachable` --> `llvm.unreachable` 3008 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> { 3009 using FIROpConversion::FIROpConversion; 3010 3011 mlir::LogicalResult 3012 matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor, 3013 mlir::ConversionPatternRewriter &rewriter) const override { 3014 rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach); 3015 return success(); 3016 } 3017 }; 3018 3019 /// `fir.is_present` --> 3020 /// ``` 3021 /// %0 = llvm.mlir.constant(0 : i64) 3022 /// %1 = llvm.ptrtoint %0 3023 /// %2 = llvm.icmp "ne" %1, %0 : i64 3024 /// ``` 3025 struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> { 3026 using FIROpConversion::FIROpConversion; 3027 3028 mlir::LogicalResult 3029 matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor, 3030 mlir::ConversionPatternRewriter &rewriter) const override { 3031 mlir::Type idxTy = lowerTy().indexType(); 3032 mlir::Location loc = isPresent.getLoc(); 3033 auto ptr = adaptor.getOperands()[0]; 3034 3035 if (isPresent.getVal().getType().isa<fir::BoxCharType>()) { 3036 auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>(); 3037 assert(!structTy.isOpaque() && !structTy.getBody().empty()); 3038 3039 mlir::Type ty = structTy.getBody()[0]; 3040 mlir::MLIRContext *ctx = isPresent.getContext(); 3041 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 3042 ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0); 3043 } 3044 mlir::LLVM::ConstantOp c0 = 3045 genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0); 3046 auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr); 3047 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>( 3048 isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0); 3049 3050 return success(); 3051 } 3052 }; 3053 3054 /// Create value signaling an absent optional argument in a call, e.g. 3055 /// `fir.absent !fir.ref<i64>` --> `llvm.mlir.null : !llvm.ptr<i64>` 3056 struct AbsentOpConversion : public FIROpConversion<fir::AbsentOp> { 3057 using FIROpConversion::FIROpConversion; 3058 3059 mlir::LogicalResult 3060 matchAndRewrite(fir::AbsentOp absent, OpAdaptor, 3061 mlir::ConversionPatternRewriter &rewriter) const override { 3062 mlir::Type ty = convertType(absent.getType()); 3063 mlir::Location loc = absent.getLoc(); 3064 3065 if (absent.getType().isa<fir::BoxCharType>()) { 3066 auto structTy = ty.cast<mlir::LLVM::LLVMStructType>(); 3067 assert(!structTy.isOpaque() && !structTy.getBody().empty()); 3068 auto undefStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 3069 auto nullField = 3070 rewriter.create<mlir::LLVM::NullOp>(loc, structTy.getBody()[0]); 3071 mlir::MLIRContext *ctx = absent.getContext(); 3072 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 3073 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 3074 absent, ty, undefStruct, nullField, c0); 3075 } else { 3076 rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(absent, ty); 3077 } 3078 return success(); 3079 } 3080 }; 3081 3082 // 3083 // Primitive operations on Complex types 3084 // 3085 3086 /// Generate inline code for complex addition/subtraction 3087 template <typename LLVMOP, typename OPTY> 3088 static mlir::LLVM::InsertValueOp 3089 complexSum(OPTY sumop, mlir::ValueRange opnds, 3090 mlir::ConversionPatternRewriter &rewriter, 3091 fir::LLVMTypeConverter &lowering) { 3092 mlir::Value a = opnds[0]; 3093 mlir::Value b = opnds[1]; 3094 auto loc = sumop.getLoc(); 3095 auto ctx = sumop.getContext(); 3096 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 3097 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 3098 mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType())); 3099 mlir::Type ty = lowering.convertType(sumop.getType()); 3100 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 3101 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 3102 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 3103 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 3104 auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1); 3105 auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1); 3106 auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 3107 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0); 3108 return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1); 3109 } 3110 } // namespace 3111 3112 namespace { 3113 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> { 3114 using FIROpConversion::FIROpConversion; 3115 3116 mlir::LogicalResult 3117 matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor, 3118 mlir::ConversionPatternRewriter &rewriter) const override { 3119 // given: (x + iy) + (x' + iy') 3120 // result: (x + x') + i(y + y') 3121 auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(), 3122 rewriter, lowerTy()); 3123 rewriter.replaceOp(addc, r.getResult()); 3124 return success(); 3125 } 3126 }; 3127 3128 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> { 3129 using FIROpConversion::FIROpConversion; 3130 3131 mlir::LogicalResult 3132 matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor, 3133 mlir::ConversionPatternRewriter &rewriter) const override { 3134 // given: (x + iy) - (x' + iy') 3135 // result: (x - x') + i(y - y') 3136 auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(), 3137 rewriter, lowerTy()); 3138 rewriter.replaceOp(subc, r.getResult()); 3139 return success(); 3140 } 3141 }; 3142 3143 /// Inlined complex multiply 3144 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> { 3145 using FIROpConversion::FIROpConversion; 3146 3147 mlir::LogicalResult 3148 matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor, 3149 mlir::ConversionPatternRewriter &rewriter) const override { 3150 // TODO: Can we use a call to __muldc3 ? 3151 // given: (x + iy) * (x' + iy') 3152 // result: (xx'-yy')+i(xy'+yx') 3153 mlir::Value a = adaptor.getOperands()[0]; 3154 mlir::Value b = adaptor.getOperands()[1]; 3155 auto loc = mulc.getLoc(); 3156 auto *ctx = mulc.getContext(); 3157 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 3158 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 3159 mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType())); 3160 mlir::Type ty = convertType(mulc.getType()); 3161 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 3162 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 3163 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 3164 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 3165 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 3166 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 3167 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 3168 auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx); 3169 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 3170 auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy); 3171 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 3172 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 3173 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 3174 rewriter.replaceOp(mulc, r0.getResult()); 3175 return success(); 3176 } 3177 }; 3178 3179 /// Inlined complex division 3180 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> { 3181 using FIROpConversion::FIROpConversion; 3182 3183 mlir::LogicalResult 3184 matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor, 3185 mlir::ConversionPatternRewriter &rewriter) const override { 3186 // TODO: Can we use a call to __divdc3 instead? 3187 // Just generate inline code for now. 3188 // given: (x + iy) / (x' + iy') 3189 // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y' 3190 mlir::Value a = adaptor.getOperands()[0]; 3191 mlir::Value b = adaptor.getOperands()[1]; 3192 auto loc = divc.getLoc(); 3193 auto *ctx = divc.getContext(); 3194 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 3195 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 3196 mlir::Type eleTy = convertType(getComplexEleTy(divc.getType())); 3197 mlir::Type ty = convertType(divc.getType()); 3198 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 3199 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 3200 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 3201 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 3202 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 3203 auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1); 3204 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 3205 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 3206 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 3207 auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1); 3208 auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1); 3209 auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy); 3210 auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy); 3211 auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d); 3212 auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d); 3213 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 3214 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 3215 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 3216 rewriter.replaceOp(divc, r0.getResult()); 3217 return success(); 3218 } 3219 }; 3220 3221 /// Inlined complex negation 3222 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> { 3223 using FIROpConversion::FIROpConversion; 3224 3225 mlir::LogicalResult 3226 matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor, 3227 mlir::ConversionPatternRewriter &rewriter) const override { 3228 // given: -(x + iy) 3229 // result: -x - iy 3230 auto *ctxt = neg.getContext(); 3231 auto eleTy = convertType(getComplexEleTy(neg.getType())); 3232 auto ty = convertType(neg.getType()); 3233 auto loc = neg.getLoc(); 3234 mlir::Value o0 = adaptor.getOperands()[0]; 3235 auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0)); 3236 auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1)); 3237 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0); 3238 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1); 3239 auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp); 3240 auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip); 3241 auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0); 3242 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1); 3243 return success(); 3244 } 3245 }; 3246 3247 /// Conversion pattern for operation that must be dead. The information in these 3248 /// operations is used by other operation. At this point they should not have 3249 /// anymore uses. 3250 /// These operations are normally dead after the pre-codegen pass. 3251 template <typename FromOp> 3252 struct MustBeDeadConversion : public FIROpConversion<FromOp> { 3253 explicit MustBeDeadConversion(fir::LLVMTypeConverter &lowering, 3254 const fir::FIRToLLVMPassOptions &options) 3255 : FIROpConversion<FromOp>(lowering, options) {} 3256 using OpAdaptor = typename FromOp::Adaptor; 3257 3258 mlir::LogicalResult 3259 matchAndRewrite(FromOp op, OpAdaptor adaptor, 3260 mlir::ConversionPatternRewriter &rewriter) const final { 3261 if (!op->getUses().empty()) 3262 return rewriter.notifyMatchFailure(op, "op must be dead"); 3263 rewriter.eraseOp(op); 3264 return success(); 3265 } 3266 }; 3267 3268 struct ShapeOpConversion : public MustBeDeadConversion<fir::ShapeOp> { 3269 using MustBeDeadConversion::MustBeDeadConversion; 3270 }; 3271 3272 struct ShapeShiftOpConversion : public MustBeDeadConversion<fir::ShapeShiftOp> { 3273 using MustBeDeadConversion::MustBeDeadConversion; 3274 }; 3275 3276 struct ShiftOpConversion : public MustBeDeadConversion<fir::ShiftOp> { 3277 using MustBeDeadConversion::MustBeDeadConversion; 3278 }; 3279 3280 struct SliceOpConversion : public MustBeDeadConversion<fir::SliceOp> { 3281 using MustBeDeadConversion::MustBeDeadConversion; 3282 }; 3283 3284 } // namespace 3285 3286 namespace { 3287 /// Convert FIR dialect to LLVM dialect 3288 /// 3289 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An 3290 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect. 3291 /// 3292 /// This pass is not complete yet. We are upstreaming it in small patches. 3293 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> { 3294 public: 3295 FIRToLLVMLowering() = default; 3296 FIRToLLVMLowering(fir::FIRToLLVMPassOptions options) : options{options} {} 3297 mlir::ModuleOp getModule() { return getOperation(); } 3298 3299 void runOnOperation() override final { 3300 auto mod = getModule(); 3301 if (!forcedTargetTriple.empty()) { 3302 fir::setTargetTriple(mod, forcedTargetTriple); 3303 } 3304 3305 auto *context = getModule().getContext(); 3306 fir::LLVMTypeConverter typeConverter{getModule()}; 3307 mlir::RewritePatternSet pattern(context); 3308 pattern.insert< 3309 AbsentOpConversion, AddcOpConversion, AddrOfOpConversion, 3310 AllocaOpConversion, AllocMemOpConversion, BoxAddrOpConversion, 3311 BoxCharLenOpConversion, BoxDimsOpConversion, BoxEleSizeOpConversion, 3312 BoxIsAllocOpConversion, BoxIsArrayOpConversion, BoxIsPtrOpConversion, 3313 BoxProcHostOpConversion, BoxRankOpConversion, BoxTypeDescOpConversion, 3314 CallOpConversion, CmpcOpConversion, ConstcOpConversion, 3315 ConvertOpConversion, CoordinateOpConversion, DispatchOpConversion, 3316 DispatchTableOpConversion, DTEntryOpConversion, DivcOpConversion, 3317 EmboxOpConversion, EmboxCharOpConversion, EmboxProcOpConversion, 3318 ExtractValueOpConversion, FieldIndexOpConversion, FirEndOpConversion, 3319 FreeMemOpConversion, GenTypeDescOpConversion, GlobalLenOpConversion, 3320 GlobalOpConversion, HasValueOpConversion, InsertOnRangeOpConversion, 3321 InsertValueOpConversion, IsPresentOpConversion, 3322 LenParamIndexOpConversion, LoadOpConversion, MulcOpConversion, 3323 NegcOpConversion, NoReassocOpConversion, SelectCaseOpConversion, 3324 SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion, 3325 ShapeOpConversion, ShapeShiftOpConversion, ShiftOpConversion, 3326 SliceOpConversion, StoreOpConversion, StringLitOpConversion, 3327 SubcOpConversion, UnboxCharOpConversion, UnboxProcOpConversion, 3328 UndefOpConversion, UnreachableOpConversion, XArrayCoorOpConversion, 3329 XEmboxOpConversion, XReboxOpConversion, ZeroOpConversion>(typeConverter, 3330 options); 3331 mlir::populateFuncToLLVMConversionPatterns(typeConverter, pattern); 3332 mlir::populateOpenMPToLLVMConversionPatterns(typeConverter, pattern); 3333 mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter, 3334 pattern); 3335 mlir::cf::populateControlFlowToLLVMConversionPatterns(typeConverter, 3336 pattern); 3337 mlir::ConversionTarget target{*context}; 3338 target.addLegalDialect<mlir::LLVM::LLVMDialect>(); 3339 // The OpenMP dialect is legal for Operations without regions, for those 3340 // which contains regions it is legal if the region contains only the 3341 // LLVM dialect. 3342 target.addDynamicallyLegalOp<mlir::omp::ParallelOp, mlir::omp::WsLoopOp, 3343 mlir::omp::MasterOp>([&](Operation *op) { 3344 return typeConverter.isLegal(&op->getRegion(0)); 3345 }); 3346 target.addLegalDialect<mlir::omp::OpenMPDialect>(); 3347 3348 // required NOPs for applying a full conversion 3349 target.addLegalOp<mlir::ModuleOp>(); 3350 3351 // apply the patterns 3352 if (mlir::failed(mlir::applyFullConversion(getModule(), target, 3353 std::move(pattern)))) { 3354 signalPassFailure(); 3355 } 3356 } 3357 3358 private: 3359 fir::FIRToLLVMPassOptions options; 3360 }; 3361 3362 /// Lower from LLVM IR dialect to proper LLVM-IR and dump the module 3363 struct LLVMIRLoweringPass 3364 : public mlir::PassWrapper<LLVMIRLoweringPass, 3365 mlir::OperationPass<mlir::ModuleOp>> { 3366 using Printer = fir::LLVMIRLoweringPrinter; 3367 LLVMIRLoweringPass(raw_ostream &output, Printer p) 3368 : output{output}, printer{p} {} 3369 3370 mlir::ModuleOp getModule() { return getOperation(); } 3371 3372 void runOnOperation() override final { 3373 auto *ctx = getModule().getContext(); 3374 auto optName = getModule().getName(); 3375 llvm::LLVMContext llvmCtx; 3376 if (auto llvmModule = mlir::translateModuleToLLVMIR( 3377 getModule(), llvmCtx, optName ? *optName : "FIRModule")) { 3378 printer(*llvmModule, output); 3379 return; 3380 } 3381 3382 mlir::emitError(mlir::UnknownLoc::get(ctx), "could not emit LLVM-IR\n"); 3383 signalPassFailure(); 3384 } 3385 3386 private: 3387 raw_ostream &output; 3388 Printer printer; 3389 }; 3390 3391 } // namespace 3392 3393 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() { 3394 return std::make_unique<FIRToLLVMLowering>(); 3395 } 3396 3397 std::unique_ptr<mlir::Pass> 3398 fir::createFIRToLLVMPass(FIRToLLVMPassOptions options) { 3399 return std::make_unique<FIRToLLVMLowering>(options); 3400 } 3401 3402 std::unique_ptr<mlir::Pass> 3403 fir::createLLVMDialectToLLVMPass(raw_ostream &output, 3404 fir::LLVMIRLoweringPrinter printer) { 3405 return std::make_unique<LLVMIRLoweringPass>(output, printer); 3406 } 3407