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