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 "PassDetail.h" 15 #include "flang/ISO_Fortran_binding.h" 16 #include "flang/Optimizer/Dialect/FIRAttr.h" 17 #include "flang/Optimizer/Dialect/FIROps.h" 18 #include "mlir/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.h" 19 #include "mlir/Conversion/LLVMCommon/Pattern.h" 20 #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h" 21 #include "mlir/IR/BuiltinTypes.h" 22 #include "mlir/IR/Matchers.h" 23 #include "mlir/Pass/Pass.h" 24 #include "llvm/ADT/ArrayRef.h" 25 26 #define DEBUG_TYPE "flang-codegen" 27 28 // fir::LLVMTypeConverter for converting to LLVM IR dialect types. 29 #include "TypeConverter.h" 30 31 /// `fir.box` attribute values as defined for CFI_attribute_t in 32 /// flang/ISO_Fortran_binding.h. 33 static constexpr unsigned kAttrPointer = CFI_attribute_pointer; 34 static constexpr unsigned kAttrAllocatable = CFI_attribute_allocatable; 35 36 static mlir::LLVM::ConstantOp 37 genConstantIndex(mlir::Location loc, mlir::Type ity, 38 mlir::ConversionPatternRewriter &rewriter, 39 std::int64_t offset) { 40 auto cattr = rewriter.getI64IntegerAttr(offset); 41 return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr); 42 } 43 44 static Block *createBlock(mlir::ConversionPatternRewriter &rewriter, 45 mlir::Block *insertBefore) { 46 assert(insertBefore && "expected valid insertion block"); 47 return rewriter.createBlock(insertBefore->getParent(), 48 mlir::Region::iterator(insertBefore)); 49 } 50 51 namespace { 52 /// FIR conversion pattern template 53 template <typename FromOp> 54 class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> { 55 public: 56 explicit FIROpConversion(fir::LLVMTypeConverter &lowering) 57 : mlir::ConvertOpToLLVMPattern<FromOp>(lowering) {} 58 59 protected: 60 mlir::Type convertType(mlir::Type ty) const { 61 return lowerTy().convertType(ty); 62 } 63 64 mlir::LLVM::ConstantOp 65 genConstantOffset(mlir::Location loc, 66 mlir::ConversionPatternRewriter &rewriter, 67 int offset) const { 68 auto ity = lowerTy().offsetType(); 69 auto cattr = rewriter.getI32IntegerAttr(offset); 70 return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr); 71 } 72 73 /// Construct code sequence to extract the specifc value from a `fir.box`. 74 mlir::Value getValueFromBox(mlir::Location loc, mlir::Value box, 75 mlir::Type resultTy, 76 mlir::ConversionPatternRewriter &rewriter, 77 unsigned boxValue) const { 78 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 79 mlir::LLVM::ConstantOp cValuePos = 80 genConstantOffset(loc, rewriter, boxValue); 81 auto pty = mlir::LLVM::LLVMPointerType::get(resultTy); 82 auto p = rewriter.create<mlir::LLVM::GEPOp>( 83 loc, pty, mlir::ValueRange{box, c0, cValuePos}); 84 return rewriter.create<mlir::LLVM::LoadOp>(loc, resultTy, p); 85 } 86 87 /// Method to construct code sequence to get the triple for dimension `dim` 88 /// from a box. 89 SmallVector<mlir::Value, 3> 90 getDimsFromBox(mlir::Location loc, ArrayRef<mlir::Type> retTys, 91 mlir::Value box, mlir::Value dim, 92 mlir::ConversionPatternRewriter &rewriter) const { 93 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 94 mlir::LLVM::ConstantOp cDims = 95 genConstantOffset(loc, rewriter, kDimsPosInBox); 96 mlir::LLVM::LoadOp l0 = 97 loadFromOffset(loc, box, c0, cDims, dim, 0, retTys[0], rewriter); 98 mlir::LLVM::LoadOp l1 = 99 loadFromOffset(loc, box, c0, cDims, dim, 1, retTys[1], rewriter); 100 mlir::LLVM::LoadOp l2 = 101 loadFromOffset(loc, box, c0, cDims, dim, 2, retTys[2], rewriter); 102 return {l0.getResult(), l1.getResult(), l2.getResult()}; 103 } 104 105 mlir::LLVM::LoadOp 106 loadFromOffset(mlir::Location loc, mlir::Value a, mlir::LLVM::ConstantOp c0, 107 mlir::LLVM::ConstantOp cDims, mlir::Value dim, int off, 108 mlir::Type ty, 109 mlir::ConversionPatternRewriter &rewriter) const { 110 auto pty = mlir::LLVM::LLVMPointerType::get(ty); 111 mlir::LLVM::ConstantOp c = genConstantOffset(loc, rewriter, off); 112 mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, a, c0, cDims, dim, c); 113 return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p); 114 } 115 116 /// Read base address from a fir.box. Returned address has type ty. 117 mlir::Value 118 loadBaseAddrFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box, 119 mlir::ConversionPatternRewriter &rewriter) const { 120 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 121 mlir::LLVM::ConstantOp cAddr = 122 genConstantOffset(loc, rewriter, kAddrPosInBox); 123 auto pty = mlir::LLVM::LLVMPointerType::get(ty); 124 mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cAddr); 125 return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p); 126 } 127 128 mlir::Value 129 loadElementSizeFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box, 130 mlir::ConversionPatternRewriter &rewriter) const { 131 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 132 mlir::LLVM::ConstantOp cElemLen = 133 genConstantOffset(loc, rewriter, kElemLenPosInBox); 134 auto pty = mlir::LLVM::LLVMPointerType::get(ty); 135 mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cElemLen); 136 return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p); 137 } 138 139 // Load the attribute from the \p box and perform a check against \p maskValue 140 // The final comparison is implemented as `(attribute & maskValue) != 0`. 141 mlir::Value genBoxAttributeCheck(mlir::Location loc, mlir::Value box, 142 mlir::ConversionPatternRewriter &rewriter, 143 unsigned maskValue) const { 144 mlir::Type attrTy = rewriter.getI32Type(); 145 mlir::Value attribute = 146 getValueFromBox(loc, box, attrTy, rewriter, kAttributePosInBox); 147 mlir::LLVM::ConstantOp attrMask = 148 genConstantOffset(loc, rewriter, maskValue); 149 auto maskRes = 150 rewriter.create<mlir::LLVM::AndOp>(loc, attrTy, attribute, attrMask); 151 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 152 return rewriter.create<mlir::LLVM::ICmpOp>( 153 loc, mlir::LLVM::ICmpPredicate::ne, maskRes, c0); 154 } 155 156 template <typename... ARGS> 157 mlir::LLVM::GEPOp genGEP(mlir::Location loc, mlir::Type ty, 158 mlir::ConversionPatternRewriter &rewriter, 159 mlir::Value base, ARGS... args) const { 160 SmallVector<mlir::Value> cv{args...}; 161 return rewriter.create<mlir::LLVM::GEPOp>(loc, ty, base, cv); 162 } 163 164 /// Perform an extension or truncation as needed on an integer value. Lowering 165 /// to the specific target may involve some sign-extending or truncation of 166 /// values, particularly to fit them from abstract box types to the 167 /// appropriate reified structures. 168 mlir::Value integerCast(mlir::Location loc, 169 mlir::ConversionPatternRewriter &rewriter, 170 mlir::Type ty, mlir::Value val) const { 171 auto valTy = val.getType(); 172 // If the value was not yet lowered, lower its type so that it can 173 // be used in getPrimitiveTypeSizeInBits. 174 if (!valTy.isa<mlir::IntegerType>()) 175 valTy = convertType(valTy); 176 auto toSize = mlir::LLVM::getPrimitiveTypeSizeInBits(ty); 177 auto fromSize = mlir::LLVM::getPrimitiveTypeSizeInBits(valTy); 178 if (toSize < fromSize) 179 return rewriter.create<mlir::LLVM::TruncOp>(loc, ty, val); 180 if (toSize > fromSize) 181 return rewriter.create<mlir::LLVM::SExtOp>(loc, ty, val); 182 return val; 183 } 184 185 fir::LLVMTypeConverter &lowerTy() const { 186 return *static_cast<fir::LLVMTypeConverter *>(this->getTypeConverter()); 187 } 188 }; 189 190 /// FIR conversion pattern template 191 template <typename FromOp> 192 class FIROpAndTypeConversion : public FIROpConversion<FromOp> { 193 public: 194 using FIROpConversion<FromOp>::FIROpConversion; 195 using OpAdaptor = typename FromOp::Adaptor; 196 197 mlir::LogicalResult 198 matchAndRewrite(FromOp op, OpAdaptor adaptor, 199 mlir::ConversionPatternRewriter &rewriter) const final { 200 mlir::Type ty = this->convertType(op.getType()); 201 return doRewrite(op, ty, adaptor, rewriter); 202 } 203 204 virtual mlir::LogicalResult 205 doRewrite(FromOp addr, mlir::Type ty, OpAdaptor adaptor, 206 mlir::ConversionPatternRewriter &rewriter) const = 0; 207 }; 208 209 /// Create value signaling an absent optional argument in a call, e.g. 210 /// `fir.absent !fir.ref<i64>` --> `llvm.mlir.null : !llvm.ptr<i64>` 211 struct AbsentOpConversion : public FIROpConversion<fir::AbsentOp> { 212 using FIROpConversion::FIROpConversion; 213 214 mlir::LogicalResult 215 matchAndRewrite(fir::AbsentOp absent, OpAdaptor, 216 mlir::ConversionPatternRewriter &rewriter) const override { 217 mlir::Type ty = convertType(absent.getType()); 218 mlir::Location loc = absent.getLoc(); 219 220 if (absent.getType().isa<fir::BoxCharType>()) { 221 auto structTy = ty.cast<mlir::LLVM::LLVMStructType>(); 222 assert(!structTy.isOpaque() && !structTy.getBody().empty()); 223 auto undefStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 224 auto nullField = 225 rewriter.create<mlir::LLVM::NullOp>(loc, structTy.getBody()[0]); 226 mlir::MLIRContext *ctx = absent.getContext(); 227 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 228 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 229 absent, ty, undefStruct, nullField, c0); 230 } else { 231 rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(absent, ty); 232 } 233 return success(); 234 } 235 }; 236 237 // Lower `fir.address_of` operation to `llvm.address_of` operation. 238 struct AddrOfOpConversion : public FIROpConversion<fir::AddrOfOp> { 239 using FIROpConversion::FIROpConversion; 240 241 mlir::LogicalResult 242 matchAndRewrite(fir::AddrOfOp addr, OpAdaptor adaptor, 243 mlir::ConversionPatternRewriter &rewriter) const override { 244 auto ty = convertType(addr.getType()); 245 rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>( 246 addr, ty, addr.symbol().getRootReference().getValue()); 247 return success(); 248 } 249 }; 250 } // namespace 251 252 /// Lookup the function to compute the memory size of this parametric derived 253 /// type. The size of the object may depend on the LEN type parameters of the 254 /// derived type. 255 static mlir::LLVM::LLVMFuncOp 256 getDependentTypeMemSizeFn(fir::RecordType recTy, fir::AllocaOp op, 257 mlir::ConversionPatternRewriter &rewriter) { 258 auto module = op->getParentOfType<mlir::ModuleOp>(); 259 std::string name = recTy.getName().str() + "P.mem.size"; 260 return module.lookupSymbol<mlir::LLVM::LLVMFuncOp>(name); 261 } 262 263 namespace { 264 /// convert to LLVM IR dialect `alloca` 265 struct AllocaOpConversion : public FIROpConversion<fir::AllocaOp> { 266 using FIROpConversion::FIROpConversion; 267 268 mlir::LogicalResult 269 matchAndRewrite(fir::AllocaOp alloc, OpAdaptor adaptor, 270 mlir::ConversionPatternRewriter &rewriter) const override { 271 mlir::ValueRange operands = adaptor.getOperands(); 272 auto loc = alloc.getLoc(); 273 mlir::Type ity = lowerTy().indexType(); 274 unsigned i = 0; 275 mlir::Value size = genConstantIndex(loc, ity, rewriter, 1).getResult(); 276 mlir::Type ty = convertType(alloc.getType()); 277 mlir::Type resultTy = ty; 278 if (alloc.hasLenParams()) { 279 unsigned end = alloc.numLenParams(); 280 llvm::SmallVector<mlir::Value> lenParams; 281 for (; i < end; ++i) 282 lenParams.push_back(operands[i]); 283 mlir::Type scalarType = fir::unwrapSequenceType(alloc.getInType()); 284 if (auto chrTy = scalarType.dyn_cast<fir::CharacterType>()) { 285 fir::CharacterType rawCharTy = fir::CharacterType::getUnknownLen( 286 chrTy.getContext(), chrTy.getFKind()); 287 ty = mlir::LLVM::LLVMPointerType::get(convertType(rawCharTy)); 288 assert(end == 1); 289 size = integerCast(loc, rewriter, ity, lenParams[0]); 290 } else if (auto recTy = scalarType.dyn_cast<fir::RecordType>()) { 291 mlir::LLVM::LLVMFuncOp memSizeFn = 292 getDependentTypeMemSizeFn(recTy, alloc, rewriter); 293 if (!memSizeFn) 294 emitError(loc, "did not find allocation function"); 295 mlir::NamedAttribute attr = rewriter.getNamedAttr( 296 "callee", mlir::SymbolRefAttr::get(memSizeFn)); 297 auto call = rewriter.create<mlir::LLVM::CallOp>( 298 loc, ity, lenParams, llvm::ArrayRef<mlir::NamedAttribute>{attr}); 299 size = call.getResult(0); 300 ty = mlir::LLVM::LLVMPointerType::get( 301 mlir::IntegerType::get(alloc.getContext(), 8)); 302 } else { 303 return emitError(loc, "unexpected type ") 304 << scalarType << " with type parameters"; 305 } 306 } 307 if (alloc.hasShapeOperands()) { 308 mlir::Type allocEleTy = fir::unwrapRefType(alloc.getType()); 309 // Scale the size by constant factors encoded in the array type. 310 if (auto seqTy = allocEleTy.dyn_cast<fir::SequenceType>()) { 311 fir::SequenceType::Extent constSize = 1; 312 for (auto extent : seqTy.getShape()) 313 if (extent != fir::SequenceType::getUnknownExtent()) 314 constSize *= extent; 315 mlir::Value constVal{ 316 genConstantIndex(loc, ity, rewriter, constSize).getResult()}; 317 size = rewriter.create<mlir::LLVM::MulOp>(loc, ity, size, constVal); 318 } 319 unsigned end = operands.size(); 320 for (; i < end; ++i) 321 size = rewriter.create<mlir::LLVM::MulOp>( 322 loc, ity, size, integerCast(loc, rewriter, ity, operands[i])); 323 } 324 if (ty == resultTy) { 325 // Do not emit the bitcast if ty and resultTy are the same. 326 rewriter.replaceOpWithNewOp<mlir::LLVM::AllocaOp>(alloc, ty, size, 327 alloc->getAttrs()); 328 } else { 329 auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, ty, size, 330 alloc->getAttrs()); 331 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(alloc, resultTy, al); 332 } 333 return success(); 334 } 335 }; 336 337 /// Lower `fir.box_addr` to the sequence of operations to extract the first 338 /// element of the box. 339 struct BoxAddrOpConversion : public FIROpConversion<fir::BoxAddrOp> { 340 using FIROpConversion::FIROpConversion; 341 342 mlir::LogicalResult 343 matchAndRewrite(fir::BoxAddrOp boxaddr, OpAdaptor adaptor, 344 mlir::ConversionPatternRewriter &rewriter) const override { 345 mlir::Value a = adaptor.getOperands()[0]; 346 auto loc = boxaddr.getLoc(); 347 mlir::Type ty = convertType(boxaddr.getType()); 348 if (auto argty = boxaddr.val().getType().dyn_cast<fir::BoxType>()) { 349 rewriter.replaceOp(boxaddr, loadBaseAddrFromBox(loc, ty, a, rewriter)); 350 } else { 351 auto c0attr = rewriter.getI32IntegerAttr(0); 352 auto c0 = mlir::ArrayAttr::get(boxaddr.getContext(), c0attr); 353 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(boxaddr, ty, a, 354 c0); 355 } 356 return success(); 357 } 358 }; 359 360 /// Lower `fir.box_dims` to a sequence of operations to extract the requested 361 /// dimension infomartion from the boxed value. 362 /// Result in a triple set of GEPs and loads. 363 struct BoxDimsOpConversion : public FIROpConversion<fir::BoxDimsOp> { 364 using FIROpConversion::FIROpConversion; 365 366 mlir::LogicalResult 367 matchAndRewrite(fir::BoxDimsOp boxdims, OpAdaptor adaptor, 368 mlir::ConversionPatternRewriter &rewriter) const override { 369 SmallVector<mlir::Type, 3> resultTypes = { 370 convertType(boxdims.getResult(0).getType()), 371 convertType(boxdims.getResult(1).getType()), 372 convertType(boxdims.getResult(2).getType()), 373 }; 374 auto results = 375 getDimsFromBox(boxdims.getLoc(), resultTypes, adaptor.getOperands()[0], 376 adaptor.getOperands()[1], rewriter); 377 rewriter.replaceOp(boxdims, results); 378 return success(); 379 } 380 }; 381 382 /// Lower `fir.box_elesize` to a sequence of operations ro extract the size of 383 /// an element in the boxed value. 384 struct BoxEleSizeOpConversion : public FIROpConversion<fir::BoxEleSizeOp> { 385 using FIROpConversion::FIROpConversion; 386 387 mlir::LogicalResult 388 matchAndRewrite(fir::BoxEleSizeOp boxelesz, OpAdaptor adaptor, 389 mlir::ConversionPatternRewriter &rewriter) const override { 390 mlir::Value a = adaptor.getOperands()[0]; 391 auto loc = boxelesz.getLoc(); 392 auto ty = convertType(boxelesz.getType()); 393 auto elemSize = getValueFromBox(loc, a, ty, rewriter, kElemLenPosInBox); 394 rewriter.replaceOp(boxelesz, elemSize); 395 return success(); 396 } 397 }; 398 399 /// Lower `fir.box_isalloc` to a sequence of operations to determine if the 400 /// boxed value was from an ALLOCATABLE entity. 401 struct BoxIsAllocOpConversion : public FIROpConversion<fir::BoxIsAllocOp> { 402 using FIROpConversion::FIROpConversion; 403 404 mlir::LogicalResult 405 matchAndRewrite(fir::BoxIsAllocOp boxisalloc, OpAdaptor adaptor, 406 mlir::ConversionPatternRewriter &rewriter) const override { 407 mlir::Value box = adaptor.getOperands()[0]; 408 auto loc = boxisalloc.getLoc(); 409 mlir::Value check = 410 genBoxAttributeCheck(loc, box, rewriter, kAttrAllocatable); 411 rewriter.replaceOp(boxisalloc, check); 412 return success(); 413 } 414 }; 415 416 /// Lower `fir.box_isarray` to a sequence of operations to determine if the 417 /// boxed is an array. 418 struct BoxIsArrayOpConversion : public FIROpConversion<fir::BoxIsArrayOp> { 419 using FIROpConversion::FIROpConversion; 420 421 mlir::LogicalResult 422 matchAndRewrite(fir::BoxIsArrayOp boxisarray, OpAdaptor adaptor, 423 mlir::ConversionPatternRewriter &rewriter) const override { 424 mlir::Value a = adaptor.getOperands()[0]; 425 auto loc = boxisarray.getLoc(); 426 auto rank = 427 getValueFromBox(loc, a, rewriter.getI32Type(), rewriter, kRankPosInBox); 428 auto c0 = genConstantOffset(loc, rewriter, 0); 429 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>( 430 boxisarray, mlir::LLVM::ICmpPredicate::ne, rank, c0); 431 return success(); 432 } 433 }; 434 435 /// Lower `fir.box_isptr` to a sequence of operations to determined if the 436 /// boxed value was from a POINTER entity. 437 struct BoxIsPtrOpConversion : public FIROpConversion<fir::BoxIsPtrOp> { 438 using FIROpConversion::FIROpConversion; 439 440 mlir::LogicalResult 441 matchAndRewrite(fir::BoxIsPtrOp boxisptr, OpAdaptor adaptor, 442 mlir::ConversionPatternRewriter &rewriter) const override { 443 mlir::Value box = adaptor.getOperands()[0]; 444 auto loc = boxisptr.getLoc(); 445 mlir::Value check = genBoxAttributeCheck(loc, box, rewriter, kAttrPointer); 446 rewriter.replaceOp(boxisptr, check); 447 return success(); 448 } 449 }; 450 451 /// Lower `fir.box_rank` to the sequence of operation to extract the rank from 452 /// the box. 453 struct BoxRankOpConversion : public FIROpConversion<fir::BoxRankOp> { 454 using FIROpConversion::FIROpConversion; 455 456 mlir::LogicalResult 457 matchAndRewrite(fir::BoxRankOp boxrank, OpAdaptor adaptor, 458 mlir::ConversionPatternRewriter &rewriter) const override { 459 mlir::Value a = adaptor.getOperands()[0]; 460 auto loc = boxrank.getLoc(); 461 mlir::Type ty = convertType(boxrank.getType()); 462 auto result = getValueFromBox(loc, a, ty, rewriter, kRankPosInBox); 463 rewriter.replaceOp(boxrank, result); 464 return success(); 465 } 466 }; 467 468 /// Lower `fir.string_lit` to LLVM IR dialect operation. 469 struct StringLitOpConversion : public FIROpConversion<fir::StringLitOp> { 470 using FIROpConversion::FIROpConversion; 471 472 mlir::LogicalResult 473 matchAndRewrite(fir::StringLitOp constop, OpAdaptor adaptor, 474 mlir::ConversionPatternRewriter &rewriter) const override { 475 auto ty = convertType(constop.getType()); 476 auto attr = constop.getValue(); 477 if (attr.isa<mlir::StringAttr>()) { 478 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(constop, ty, attr); 479 return success(); 480 } 481 482 auto arr = attr.cast<mlir::ArrayAttr>(); 483 auto charTy = constop.getType().cast<fir::CharacterType>(); 484 unsigned bits = lowerTy().characterBitsize(charTy); 485 mlir::Type intTy = rewriter.getIntegerType(bits); 486 auto attrs = llvm::map_range( 487 arr.getValue(), [intTy, bits](mlir::Attribute attr) -> Attribute { 488 return mlir::IntegerAttr::get( 489 intTy, 490 attr.cast<mlir::IntegerAttr>().getValue().sextOrTrunc(bits)); 491 }); 492 mlir::Type vecType = mlir::VectorType::get(arr.size(), intTy); 493 auto denseAttr = mlir::DenseElementsAttr::get( 494 vecType.cast<mlir::ShapedType>(), llvm::to_vector<8>(attrs)); 495 rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(constop, ty, 496 denseAttr); 497 return success(); 498 } 499 }; 500 501 /// Lower `fir.box_tdesc` to the sequence of operations to extract the type 502 /// descriptor from the box. 503 struct BoxTypeDescOpConversion : public FIROpConversion<fir::BoxTypeDescOp> { 504 using FIROpConversion::FIROpConversion; 505 506 mlir::LogicalResult 507 matchAndRewrite(fir::BoxTypeDescOp boxtypedesc, OpAdaptor adaptor, 508 mlir::ConversionPatternRewriter &rewriter) const override { 509 mlir::Value box = adaptor.getOperands()[0]; 510 auto loc = boxtypedesc.getLoc(); 511 mlir::Type typeTy = 512 fir::getDescFieldTypeModel<kTypePosInBox>()(boxtypedesc.getContext()); 513 auto result = getValueFromBox(loc, box, typeTy, rewriter, kTypePosInBox); 514 auto typePtrTy = mlir::LLVM::LLVMPointerType::get(typeTy); 515 rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(boxtypedesc, typePtrTy, 516 result); 517 return success(); 518 } 519 }; 520 521 // `fir.call` -> `llvm.call` 522 struct CallOpConversion : public FIROpConversion<fir::CallOp> { 523 using FIROpConversion::FIROpConversion; 524 525 mlir::LogicalResult 526 matchAndRewrite(fir::CallOp call, OpAdaptor adaptor, 527 mlir::ConversionPatternRewriter &rewriter) const override { 528 SmallVector<mlir::Type> resultTys; 529 for (auto r : call.getResults()) 530 resultTys.push_back(convertType(r.getType())); 531 rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>( 532 call, resultTys, adaptor.getOperands(), call->getAttrs()); 533 return success(); 534 } 535 }; 536 537 static mlir::Type getComplexEleTy(mlir::Type complex) { 538 if (auto cc = complex.dyn_cast<mlir::ComplexType>()) 539 return cc.getElementType(); 540 return complex.cast<fir::ComplexType>().getElementType(); 541 } 542 543 /// Compare complex values 544 /// 545 /// Per 10.1, the only comparisons available are .EQ. (oeq) and .NE. (une). 546 /// 547 /// For completeness, all other comparison are done on the real component only. 548 struct CmpcOpConversion : public FIROpConversion<fir::CmpcOp> { 549 using FIROpConversion::FIROpConversion; 550 551 mlir::LogicalResult 552 matchAndRewrite(fir::CmpcOp cmp, OpAdaptor adaptor, 553 mlir::ConversionPatternRewriter &rewriter) const override { 554 mlir::ValueRange operands = adaptor.getOperands(); 555 mlir::MLIRContext *ctxt = cmp.getContext(); 556 mlir::Type eleTy = convertType(getComplexEleTy(cmp.lhs().getType())); 557 mlir::Type resTy = convertType(cmp.getType()); 558 mlir::Location loc = cmp.getLoc(); 559 auto pos0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0)); 560 SmallVector<mlir::Value, 2> rp{rewriter.create<mlir::LLVM::ExtractValueOp>( 561 loc, eleTy, operands[0], pos0), 562 rewriter.create<mlir::LLVM::ExtractValueOp>( 563 loc, eleTy, operands[1], pos0)}; 564 auto rcp = 565 rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, rp, cmp->getAttrs()); 566 auto pos1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1)); 567 SmallVector<mlir::Value, 2> ip{rewriter.create<mlir::LLVM::ExtractValueOp>( 568 loc, eleTy, operands[0], pos1), 569 rewriter.create<mlir::LLVM::ExtractValueOp>( 570 loc, eleTy, operands[1], pos1)}; 571 auto icp = 572 rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, ip, cmp->getAttrs()); 573 SmallVector<mlir::Value, 2> cp{rcp, icp}; 574 switch (cmp.getPredicate()) { 575 case mlir::arith::CmpFPredicate::OEQ: // .EQ. 576 rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(cmp, resTy, cp); 577 break; 578 case mlir::arith::CmpFPredicate::UNE: // .NE. 579 rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(cmp, resTy, cp); 580 break; 581 default: 582 rewriter.replaceOp(cmp, rcp.getResult()); 583 break; 584 } 585 return success(); 586 } 587 }; 588 589 /// convert value of from-type to value of to-type 590 struct ConvertOpConversion : public FIROpConversion<fir::ConvertOp> { 591 using FIROpConversion::FIROpConversion; 592 593 static bool isFloatingPointTy(mlir::Type ty) { 594 return ty.isa<mlir::FloatType>(); 595 } 596 597 mlir::LogicalResult 598 matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor, 599 mlir::ConversionPatternRewriter &rewriter) const override { 600 auto fromTy = convertType(convert.value().getType()); 601 auto toTy = convertType(convert.res().getType()); 602 mlir::Value op0 = adaptor.getOperands()[0]; 603 if (fromTy == toTy) { 604 rewriter.replaceOp(convert, op0); 605 return success(); 606 } 607 auto loc = convert.getLoc(); 608 auto convertFpToFp = [&](mlir::Value val, unsigned fromBits, 609 unsigned toBits, mlir::Type toTy) -> mlir::Value { 610 if (fromBits == toBits) { 611 // TODO: Converting between two floating-point representations with the 612 // same bitwidth is not allowed for now. 613 mlir::emitError(loc, 614 "cannot implicitly convert between two floating-point " 615 "representations of the same bitwidth"); 616 return {}; 617 } 618 if (fromBits > toBits) 619 return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val); 620 return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val); 621 }; 622 // Complex to complex conversion. 623 if (fir::isa_complex(convert.value().getType()) && 624 fir::isa_complex(convert.res().getType())) { 625 // Special case: handle the conversion of a complex such that both the 626 // real and imaginary parts are converted together. 627 auto zero = mlir::ArrayAttr::get(convert.getContext(), 628 rewriter.getI32IntegerAttr(0)); 629 auto one = mlir::ArrayAttr::get(convert.getContext(), 630 rewriter.getI32IntegerAttr(1)); 631 auto ty = convertType(getComplexEleTy(convert.value().getType())); 632 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, zero); 633 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, one); 634 auto nt = convertType(getComplexEleTy(convert.res().getType())); 635 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty); 636 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt); 637 auto rc = convertFpToFp(rp, fromBits, toBits, nt); 638 auto ic = convertFpToFp(ip, fromBits, toBits, nt); 639 auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy); 640 auto i1 = 641 rewriter.create<mlir::LLVM::InsertValueOp>(loc, toTy, un, rc, zero); 642 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, toTy, i1, 643 ic, one); 644 return mlir::success(); 645 } 646 // Floating point to floating point conversion. 647 if (isFloatingPointTy(fromTy)) { 648 if (isFloatingPointTy(toTy)) { 649 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy); 650 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy); 651 auto v = convertFpToFp(op0, fromBits, toBits, toTy); 652 rewriter.replaceOp(convert, v); 653 return mlir::success(); 654 } 655 if (toTy.isa<mlir::IntegerType>()) { 656 rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0); 657 return mlir::success(); 658 } 659 } else if (fromTy.isa<mlir::IntegerType>()) { 660 // Integer to integer conversion. 661 if (toTy.isa<mlir::IntegerType>()) { 662 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy); 663 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy); 664 assert(fromBits != toBits); 665 if (fromBits > toBits) { 666 rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0); 667 return mlir::success(); 668 } 669 rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0); 670 return mlir::success(); 671 } 672 // Integer to floating point conversion. 673 if (isFloatingPointTy(toTy)) { 674 rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0); 675 return mlir::success(); 676 } 677 // Integer to pointer conversion. 678 if (toTy.isa<mlir::LLVM::LLVMPointerType>()) { 679 rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0); 680 return mlir::success(); 681 } 682 } else if (fromTy.isa<mlir::LLVM::LLVMPointerType>()) { 683 // Pointer to integer conversion. 684 if (toTy.isa<mlir::IntegerType>()) { 685 rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0); 686 return mlir::success(); 687 } 688 // Pointer to pointer conversion. 689 if (toTy.isa<mlir::LLVM::LLVMPointerType>()) { 690 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0); 691 return mlir::success(); 692 } 693 } 694 return emitError(loc) << "cannot convert " << fromTy << " to " << toTy; 695 } 696 }; 697 698 /// Lower `fir.dispatch` operation. A virtual call to a method in a dispatch 699 /// table. 700 struct DispatchOpConversion : public FIROpConversion<fir::DispatchOp> { 701 using FIROpConversion::FIROpConversion; 702 703 mlir::LogicalResult 704 matchAndRewrite(fir::DispatchOp dispatch, OpAdaptor adaptor, 705 mlir::ConversionPatternRewriter &rewriter) const override { 706 return rewriter.notifyMatchFailure( 707 dispatch, "fir.dispatch codegen is not implemented yet"); 708 } 709 }; 710 711 /// Lower `fir.dispatch_table` operation. The dispatch table for a Fortran 712 /// derived type. 713 struct DispatchTableOpConversion 714 : public FIROpConversion<fir::DispatchTableOp> { 715 using FIROpConversion::FIROpConversion; 716 717 mlir::LogicalResult 718 matchAndRewrite(fir::DispatchTableOp dispTab, OpAdaptor adaptor, 719 mlir::ConversionPatternRewriter &rewriter) const override { 720 return rewriter.notifyMatchFailure( 721 dispTab, "fir.dispatch_table codegen is not implemented yet"); 722 } 723 }; 724 725 /// Lower `fir.dt_entry` operation. An entry in a dispatch table; binds a 726 /// method-name to a function. 727 struct DTEntryOpConversion : public FIROpConversion<fir::DTEntryOp> { 728 using FIROpConversion::FIROpConversion; 729 730 mlir::LogicalResult 731 matchAndRewrite(fir::DTEntryOp dtEnt, OpAdaptor adaptor, 732 mlir::ConversionPatternRewriter &rewriter) const override { 733 return rewriter.notifyMatchFailure( 734 dtEnt, "fir.dt_entry codegen is not implemented yet"); 735 } 736 }; 737 738 /// Lower `fir.global_len` operation. 739 struct GlobalLenOpConversion : public FIROpConversion<fir::GlobalLenOp> { 740 using FIROpConversion::FIROpConversion; 741 742 mlir::LogicalResult 743 matchAndRewrite(fir::GlobalLenOp globalLen, OpAdaptor adaptor, 744 mlir::ConversionPatternRewriter &rewriter) const override { 745 return rewriter.notifyMatchFailure( 746 globalLen, "fir.global_len codegen is not implemented yet"); 747 } 748 }; 749 750 /// Lower `fir.gentypedesc` to a global constant. 751 struct GenTypeDescOpConversion : public FIROpConversion<fir::GenTypeDescOp> { 752 using FIROpConversion::FIROpConversion; 753 754 mlir::LogicalResult 755 matchAndRewrite(fir::GenTypeDescOp gentypedesc, OpAdaptor adaptor, 756 mlir::ConversionPatternRewriter &rewriter) const override { 757 return rewriter.notifyMatchFailure( 758 gentypedesc, "fir.fir.gentypedesc codegen is not implemented yet"); 759 } 760 }; 761 762 /// Lower `fir.has_value` operation to `llvm.return` operation. 763 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> { 764 using FIROpConversion::FIROpConversion; 765 766 mlir::LogicalResult 767 matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor, 768 mlir::ConversionPatternRewriter &rewriter) const override { 769 rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands()); 770 return success(); 771 } 772 }; 773 774 /// Lower `fir.global` operation to `llvm.global` operation. 775 /// `fir.insert_on_range` operations are replaced with constant dense attribute 776 /// if they are applied on the full range. 777 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> { 778 using FIROpConversion::FIROpConversion; 779 780 mlir::LogicalResult 781 matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor, 782 mlir::ConversionPatternRewriter &rewriter) const override { 783 auto tyAttr = convertType(global.getType()); 784 if (global.getType().isa<fir::BoxType>()) 785 tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType(); 786 auto loc = global.getLoc(); 787 mlir::Attribute initAttr{}; 788 if (global.initVal()) 789 initAttr = global.initVal().getValue(); 790 auto linkage = convertLinkage(global.linkName()); 791 auto isConst = global.constant().hasValue(); 792 auto g = rewriter.create<mlir::LLVM::GlobalOp>( 793 loc, tyAttr, isConst, linkage, global.sym_name(), initAttr); 794 auto &gr = g.getInitializerRegion(); 795 rewriter.inlineRegionBefore(global.region(), gr, gr.end()); 796 if (!gr.empty()) { 797 // Replace insert_on_range with a constant dense attribute if the 798 // initialization is on the full range. 799 auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>(); 800 for (auto insertOp : insertOnRangeOps) { 801 if (isFullRange(insertOp.coor(), insertOp.getType())) { 802 auto seqTyAttr = convertType(insertOp.getType()); 803 auto *op = insertOp.val().getDefiningOp(); 804 auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op); 805 if (!constant) { 806 auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op); 807 if (!convertOp) 808 continue; 809 constant = cast<mlir::arith::ConstantOp>( 810 convertOp.value().getDefiningOp()); 811 } 812 mlir::Type vecType = mlir::VectorType::get( 813 insertOp.getType().getShape(), constant.getType()); 814 auto denseAttr = mlir::DenseElementsAttr::get( 815 vecType.cast<ShapedType>(), constant.value()); 816 rewriter.setInsertionPointAfter(insertOp); 817 rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>( 818 insertOp, seqTyAttr, denseAttr); 819 } 820 } 821 } 822 rewriter.eraseOp(global); 823 return success(); 824 } 825 826 bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const { 827 auto extents = seqTy.getShape(); 828 if (indexes.size() / 2 != extents.size()) 829 return false; 830 for (unsigned i = 0; i < indexes.size(); i += 2) { 831 if (indexes[i].cast<IntegerAttr>().getInt() != 0) 832 return false; 833 if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1) 834 return false; 835 } 836 return true; 837 } 838 839 // TODO: String comparaison should be avoided. Replace linkName with an 840 // enumeration. 841 mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const { 842 if (optLinkage.hasValue()) { 843 auto name = optLinkage.getValue(); 844 if (name == "internal") 845 return mlir::LLVM::Linkage::Internal; 846 if (name == "linkonce") 847 return mlir::LLVM::Linkage::Linkonce; 848 if (name == "common") 849 return mlir::LLVM::Linkage::Common; 850 if (name == "weak") 851 return mlir::LLVM::Linkage::Weak; 852 } 853 return mlir::LLVM::Linkage::External; 854 } 855 }; 856 857 void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest, 858 Optional<mlir::ValueRange> destOps, 859 mlir::ConversionPatternRewriter &rewriter, 860 mlir::Block *newBlock) { 861 if (destOps.hasValue()) 862 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, destOps.getValue(), 863 newBlock, mlir::ValueRange()); 864 else 865 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock); 866 } 867 868 template <typename A, typename B> 869 void genBrOp(A caseOp, mlir::Block *dest, Optional<B> destOps, 870 mlir::ConversionPatternRewriter &rewriter) { 871 if (destOps.hasValue()) 872 rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, destOps.getValue(), 873 dest); 874 else 875 rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, llvm::None, dest); 876 } 877 878 void genCaseLadderStep(mlir::Location loc, mlir::Value cmp, mlir::Block *dest, 879 Optional<mlir::ValueRange> destOps, 880 mlir::ConversionPatternRewriter &rewriter) { 881 auto *thisBlock = rewriter.getInsertionBlock(); 882 auto *newBlock = createBlock(rewriter, dest); 883 rewriter.setInsertionPointToEnd(thisBlock); 884 genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock); 885 rewriter.setInsertionPointToEnd(newBlock); 886 } 887 888 /// Conversion of `fir.select_case` 889 /// 890 /// The `fir.select_case` operation is converted to a if-then-else ladder. 891 /// Depending on the case condition type, one or several comparison and 892 /// conditional branching can be generated. 893 /// 894 /// A a point value case such as `case(4)`, a lower bound case such as 895 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a 896 /// simple comparison between the selector value and the constant value in the 897 /// case. The block associated with the case condition is then executed if 898 /// the comparison succeed otherwise it branch to the next block with the 899 /// comparison for the the next case conditon. 900 /// 901 /// A closed interval case condition such as `case(7:10)` is converted with a 902 /// first comparison and conditional branching for the lower bound. If 903 /// successful, it branch to a second block with the comparison for the 904 /// upper bound in the same case condition. 905 /// 906 /// TODO: lowering of CHARACTER type cases is not handled yet. 907 struct SelectCaseOpConversion : public FIROpConversion<fir::SelectCaseOp> { 908 using FIROpConversion::FIROpConversion; 909 910 mlir::LogicalResult 911 matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor, 912 mlir::ConversionPatternRewriter &rewriter) const override { 913 unsigned conds = caseOp.getNumConditions(); 914 llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue(); 915 // Type can be CHARACTER, INTEGER, or LOGICAL (C1145) 916 LLVM_ATTRIBUTE_UNUSED auto ty = caseOp.getSelector().getType(); 917 if (ty.isa<fir::CharacterType>()) 918 return rewriter.notifyMatchFailure(caseOp, 919 "conversion of fir.select_case with " 920 "character type not implemented yet"); 921 mlir::Value selector = caseOp.getSelector(adaptor.getOperands()); 922 auto loc = caseOp.getLoc(); 923 for (unsigned t = 0; t != conds; ++t) { 924 mlir::Block *dest = caseOp.getSuccessor(t); 925 llvm::Optional<mlir::ValueRange> destOps = 926 caseOp.getSuccessorOperands(adaptor.getOperands(), t); 927 llvm::Optional<mlir::ValueRange> cmpOps = 928 *caseOp.getCompareOperands(adaptor.getOperands(), t); 929 mlir::Value caseArg = *(cmpOps.getValue().begin()); 930 mlir::Attribute attr = cases[t]; 931 if (attr.isa<fir::PointIntervalAttr>()) { 932 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 933 loc, mlir::LLVM::ICmpPredicate::eq, selector, caseArg); 934 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 935 continue; 936 } 937 if (attr.isa<fir::LowerBoundAttr>()) { 938 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 939 loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector); 940 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 941 continue; 942 } 943 if (attr.isa<fir::UpperBoundAttr>()) { 944 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 945 loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg); 946 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 947 continue; 948 } 949 if (attr.isa<fir::ClosedIntervalAttr>()) { 950 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 951 loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector); 952 auto *thisBlock = rewriter.getInsertionBlock(); 953 auto *newBlock1 = createBlock(rewriter, dest); 954 auto *newBlock2 = createBlock(rewriter, dest); 955 rewriter.setInsertionPointToEnd(thisBlock); 956 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, newBlock1, newBlock2); 957 rewriter.setInsertionPointToEnd(newBlock1); 958 mlir::Value caseArg0 = *(cmpOps.getValue().begin() + 1); 959 auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>( 960 loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg0); 961 genCondBrOp(loc, cmp0, dest, destOps, rewriter, newBlock2); 962 rewriter.setInsertionPointToEnd(newBlock2); 963 continue; 964 } 965 assert(attr.isa<mlir::UnitAttr>()); 966 assert((t + 1 == conds) && "unit must be last"); 967 genBrOp(caseOp, dest, destOps, rewriter); 968 } 969 return success(); 970 } 971 }; 972 973 template <typename OP> 974 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select, 975 typename OP::Adaptor adaptor, 976 mlir::ConversionPatternRewriter &rewriter) { 977 unsigned conds = select.getNumConditions(); 978 auto cases = select.getCases().getValue(); 979 mlir::Value selector = adaptor.selector(); 980 auto loc = select.getLoc(); 981 assert(conds > 0 && "select must have cases"); 982 983 llvm::SmallVector<mlir::Block *> destinations; 984 llvm::SmallVector<mlir::ValueRange> destinationsOperands; 985 mlir::Block *defaultDestination; 986 mlir::ValueRange defaultOperands; 987 llvm::SmallVector<int32_t> caseValues; 988 989 for (unsigned t = 0; t != conds; ++t) { 990 mlir::Block *dest = select.getSuccessor(t); 991 auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t); 992 const mlir::Attribute &attr = cases[t]; 993 if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) { 994 destinations.push_back(dest); 995 destinationsOperands.push_back(destOps.hasValue() ? *destOps 996 : ValueRange()); 997 caseValues.push_back(intAttr.getInt()); 998 continue; 999 } 1000 assert(attr.template dyn_cast_or_null<mlir::UnitAttr>()); 1001 assert((t + 1 == conds) && "unit must be last"); 1002 defaultDestination = dest; 1003 defaultOperands = destOps.hasValue() ? *destOps : ValueRange(); 1004 } 1005 1006 // LLVM::SwitchOp takes a i32 type for the selector. 1007 if (select.getSelector().getType() != rewriter.getI32Type()) 1008 selector = 1009 rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector); 1010 1011 rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>( 1012 select, selector, 1013 /*defaultDestination=*/defaultDestination, 1014 /*defaultOperands=*/defaultOperands, 1015 /*caseValues=*/caseValues, 1016 /*caseDestinations=*/destinations, 1017 /*caseOperands=*/destinationsOperands, 1018 /*branchWeights=*/ArrayRef<int32_t>()); 1019 } 1020 1021 /// conversion of fir::SelectOp to an if-then-else ladder 1022 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> { 1023 using FIROpConversion::FIROpConversion; 1024 1025 mlir::LogicalResult 1026 matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor, 1027 mlir::ConversionPatternRewriter &rewriter) const override { 1028 selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter); 1029 return success(); 1030 } 1031 }; 1032 1033 /// `fir.load` --> `llvm.load` 1034 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> { 1035 using FIROpConversion::FIROpConversion; 1036 1037 mlir::LogicalResult 1038 matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor, 1039 mlir::ConversionPatternRewriter &rewriter) const override { 1040 // fir.box is a special case because it is considered as an ssa values in 1041 // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box> 1042 // and fir.box end up being the same llvm types and loading a 1043 // fir.ref<fir.box> is actually a no op in LLVM. 1044 if (load.getType().isa<fir::BoxType>()) { 1045 rewriter.replaceOp(load, adaptor.getOperands()[0]); 1046 } else { 1047 mlir::Type ty = convertType(load.getType()); 1048 ArrayRef<NamedAttribute> at = load->getAttrs(); 1049 rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>( 1050 load, ty, adaptor.getOperands(), at); 1051 } 1052 return success(); 1053 } 1054 }; 1055 1056 /// Lower `fir.select_type` to LLVM IR dialect. 1057 struct SelectTypeOpConversion : public FIROpConversion<fir::SelectTypeOp> { 1058 using FIROpConversion::FIROpConversion; 1059 1060 mlir::LogicalResult 1061 matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor, 1062 mlir::ConversionPatternRewriter &rewriter) const override { 1063 return rewriter.notifyMatchFailure( 1064 select, "fir.select_type codegen is not implemented yet"); 1065 } 1066 }; 1067 1068 /// conversion of fir::SelectRankOp to an if-then-else ladder 1069 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> { 1070 using FIROpConversion::FIROpConversion; 1071 1072 mlir::LogicalResult 1073 matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor, 1074 mlir::ConversionPatternRewriter &rewriter) const override { 1075 selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter); 1076 return success(); 1077 } 1078 }; 1079 1080 /// `fir.store` --> `llvm.store` 1081 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> { 1082 using FIROpConversion::FIROpConversion; 1083 1084 mlir::LogicalResult 1085 matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor, 1086 mlir::ConversionPatternRewriter &rewriter) const override { 1087 if (store.value().getType().isa<fir::BoxType>()) { 1088 // fir.box value is actually in memory, load it first before storing it. 1089 mlir::Location loc = store.getLoc(); 1090 mlir::Type boxPtrTy = adaptor.getOperands()[0].getType(); 1091 auto val = rewriter.create<mlir::LLVM::LoadOp>( 1092 loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(), 1093 adaptor.getOperands()[0]); 1094 rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 1095 store, val, adaptor.getOperands()[1]); 1096 } else { 1097 rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 1098 store, adaptor.getOperands()[0], adaptor.getOperands()[1]); 1099 } 1100 return success(); 1101 } 1102 }; 1103 1104 /// convert to LLVM IR dialect `undef` 1105 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> { 1106 using FIROpConversion::FIROpConversion; 1107 1108 mlir::LogicalResult 1109 matchAndRewrite(fir::UndefOp undef, OpAdaptor, 1110 mlir::ConversionPatternRewriter &rewriter) const override { 1111 rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>( 1112 undef, convertType(undef.getType())); 1113 return success(); 1114 } 1115 }; 1116 1117 /// `fir.unreachable` --> `llvm.unreachable` 1118 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> { 1119 using FIROpConversion::FIROpConversion; 1120 1121 mlir::LogicalResult 1122 matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor, 1123 mlir::ConversionPatternRewriter &rewriter) const override { 1124 rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach); 1125 return success(); 1126 } 1127 }; 1128 1129 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> { 1130 using FIROpConversion::FIROpConversion; 1131 1132 mlir::LogicalResult 1133 matchAndRewrite(fir::ZeroOp zero, OpAdaptor, 1134 mlir::ConversionPatternRewriter &rewriter) const override { 1135 auto ty = convertType(zero.getType()); 1136 if (ty.isa<mlir::LLVM::LLVMPointerType>()) { 1137 rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty); 1138 } else if (ty.isa<mlir::IntegerType>()) { 1139 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 1140 zero, ty, mlir::IntegerAttr::get(zero.getType(), 0)); 1141 } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) { 1142 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 1143 zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0)); 1144 } else { 1145 // TODO: create ConstantAggregateZero for FIR aggregate/array types. 1146 return rewriter.notifyMatchFailure( 1147 zero, 1148 "conversion of fir.zero with aggregate type not implemented yet"); 1149 } 1150 return success(); 1151 } 1152 }; 1153 1154 // Code shared between insert_value and extract_value Ops. 1155 struct ValueOpCommon { 1156 // Translate the arguments pertaining to any multidimensional array to 1157 // row-major order for LLVM-IR. 1158 static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs, 1159 mlir::Type ty) { 1160 assert(ty && "type is null"); 1161 const auto end = attrs.size(); 1162 for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) { 1163 if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 1164 const auto dim = getDimension(seq); 1165 if (dim > 1) { 1166 auto ub = std::min(i + dim, end); 1167 std::reverse(attrs.begin() + i, attrs.begin() + ub); 1168 i += dim - 1; 1169 } 1170 ty = getArrayElementType(seq); 1171 } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) { 1172 ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()]; 1173 } else { 1174 llvm_unreachable("index into invalid type"); 1175 } 1176 } 1177 } 1178 1179 static llvm::SmallVector<mlir::Attribute> 1180 collectIndices(mlir::ConversionPatternRewriter &rewriter, 1181 mlir::ArrayAttr arrAttr) { 1182 llvm::SmallVector<mlir::Attribute> attrs; 1183 for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) { 1184 if (i->isa<mlir::IntegerAttr>()) { 1185 attrs.push_back(*i); 1186 } else { 1187 auto fieldName = i->cast<mlir::StringAttr>().getValue(); 1188 ++i; 1189 auto ty = i->cast<mlir::TypeAttr>().getValue(); 1190 auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName); 1191 attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index)); 1192 } 1193 } 1194 return attrs; 1195 } 1196 1197 private: 1198 static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) { 1199 unsigned result = 1; 1200 for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>(); 1201 eleTy; 1202 eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>()) 1203 ++result; 1204 return result; 1205 } 1206 1207 static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) { 1208 auto eleTy = ty.getElementType(); 1209 while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>()) 1210 eleTy = arrTy.getElementType(); 1211 return eleTy; 1212 } 1213 }; 1214 1215 /// Extract a subobject value from an ssa-value of aggregate type 1216 struct ExtractValueOpConversion 1217 : public FIROpAndTypeConversion<fir::ExtractValueOp>, 1218 public ValueOpCommon { 1219 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1220 1221 mlir::LogicalResult 1222 doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor, 1223 mlir::ConversionPatternRewriter &rewriter) const override { 1224 auto attrs = collectIndices(rewriter, extractVal.coor()); 1225 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 1226 auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs); 1227 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>( 1228 extractVal, ty, adaptor.getOperands()[0], position); 1229 return success(); 1230 } 1231 }; 1232 1233 /// InsertValue is the generalized instruction for the composition of new 1234 /// aggregate type values. 1235 struct InsertValueOpConversion 1236 : public FIROpAndTypeConversion<fir::InsertValueOp>, 1237 public ValueOpCommon { 1238 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1239 1240 mlir::LogicalResult 1241 doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor, 1242 mlir::ConversionPatternRewriter &rewriter) const override { 1243 auto attrs = collectIndices(rewriter, insertVal.coor()); 1244 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 1245 auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs); 1246 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1247 insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1], 1248 position); 1249 return success(); 1250 } 1251 }; 1252 1253 /// InsertOnRange inserts a value into a sequence over a range of offsets. 1254 struct InsertOnRangeOpConversion 1255 : public FIROpAndTypeConversion<fir::InsertOnRangeOp> { 1256 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1257 1258 // Increments an array of subscripts in a row major fasion. 1259 void incrementSubscripts(const SmallVector<uint64_t> &dims, 1260 SmallVector<uint64_t> &subscripts) const { 1261 for (size_t i = dims.size(); i > 0; --i) { 1262 if (++subscripts[i - 1] < dims[i - 1]) { 1263 return; 1264 } 1265 subscripts[i - 1] = 0; 1266 } 1267 } 1268 1269 mlir::LogicalResult 1270 doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor, 1271 mlir::ConversionPatternRewriter &rewriter) const override { 1272 1273 llvm::SmallVector<uint64_t> dims; 1274 auto type = adaptor.getOperands()[0].getType(); 1275 1276 // Iteratively extract the array dimensions from the type. 1277 while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 1278 dims.push_back(t.getNumElements()); 1279 type = t.getElementType(); 1280 } 1281 1282 SmallVector<uint64_t> lBounds; 1283 SmallVector<uint64_t> uBounds; 1284 1285 // Extract integer value from the attribute 1286 SmallVector<int64_t> coordinates = llvm::to_vector<4>( 1287 llvm::map_range(range.coor(), [](Attribute a) -> int64_t { 1288 return a.cast<IntegerAttr>().getInt(); 1289 })); 1290 1291 // Unzip the upper and lower bound and convert to a row major format. 1292 for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) { 1293 uBounds.push_back(*i++); 1294 lBounds.push_back(*i); 1295 } 1296 1297 auto &subscripts = lBounds; 1298 auto loc = range.getLoc(); 1299 mlir::Value lastOp = adaptor.getOperands()[0]; 1300 mlir::Value insertVal = adaptor.getOperands()[1]; 1301 1302 auto i64Ty = rewriter.getI64Type(); 1303 while (subscripts != uBounds) { 1304 // Convert uint64_t's to Attribute's. 1305 SmallVector<mlir::Attribute> subscriptAttrs; 1306 for (const auto &subscript : subscripts) 1307 subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript)); 1308 lastOp = rewriter.create<mlir::LLVM::InsertValueOp>( 1309 loc, ty, lastOp, insertVal, 1310 ArrayAttr::get(range.getContext(), subscriptAttrs)); 1311 1312 incrementSubscripts(dims, subscripts); 1313 } 1314 1315 // Convert uint64_t's to Attribute's. 1316 SmallVector<mlir::Attribute> subscriptAttrs; 1317 for (const auto &subscript : subscripts) 1318 subscriptAttrs.push_back( 1319 IntegerAttr::get(rewriter.getI64Type(), subscript)); 1320 mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs); 1321 1322 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1323 range, ty, lastOp, insertVal, 1324 ArrayAttr::get(range.getContext(), arrayRef)); 1325 1326 return success(); 1327 } 1328 }; 1329 1330 // 1331 // Primitive operations on Complex types 1332 // 1333 1334 /// Generate inline code for complex addition/subtraction 1335 template <typename LLVMOP, typename OPTY> 1336 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds, 1337 mlir::ConversionPatternRewriter &rewriter, 1338 fir::LLVMTypeConverter &lowering) { 1339 mlir::Value a = opnds[0]; 1340 mlir::Value b = opnds[1]; 1341 auto loc = sumop.getLoc(); 1342 auto ctx = sumop.getContext(); 1343 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1344 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1345 mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType())); 1346 mlir::Type ty = lowering.convertType(sumop.getType()); 1347 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1348 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1349 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1350 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1351 auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1); 1352 auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1); 1353 auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1354 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0); 1355 return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1); 1356 } 1357 1358 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> { 1359 using FIROpConversion::FIROpConversion; 1360 1361 mlir::LogicalResult 1362 matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor, 1363 mlir::ConversionPatternRewriter &rewriter) const override { 1364 // given: (x + iy) + (x' + iy') 1365 // result: (x + x') + i(y + y') 1366 auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(), 1367 rewriter, lowerTy()); 1368 rewriter.replaceOp(addc, r.getResult()); 1369 return success(); 1370 } 1371 }; 1372 1373 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> { 1374 using FIROpConversion::FIROpConversion; 1375 1376 mlir::LogicalResult 1377 matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor, 1378 mlir::ConversionPatternRewriter &rewriter) const override { 1379 // given: (x + iy) - (x' + iy') 1380 // result: (x - x') + i(y - y') 1381 auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(), 1382 rewriter, lowerTy()); 1383 rewriter.replaceOp(subc, r.getResult()); 1384 return success(); 1385 } 1386 }; 1387 1388 /// Inlined complex multiply 1389 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> { 1390 using FIROpConversion::FIROpConversion; 1391 1392 mlir::LogicalResult 1393 matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor, 1394 mlir::ConversionPatternRewriter &rewriter) const override { 1395 // TODO: Can we use a call to __muldc3 ? 1396 // given: (x + iy) * (x' + iy') 1397 // result: (xx'-yy')+i(xy'+yx') 1398 mlir::Value a = adaptor.getOperands()[0]; 1399 mlir::Value b = adaptor.getOperands()[1]; 1400 auto loc = mulc.getLoc(); 1401 auto *ctx = mulc.getContext(); 1402 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1403 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1404 mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType())); 1405 mlir::Type ty = convertType(mulc.getType()); 1406 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1407 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1408 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1409 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1410 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 1411 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 1412 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 1413 auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx); 1414 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 1415 auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy); 1416 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1417 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 1418 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 1419 rewriter.replaceOp(mulc, r0.getResult()); 1420 return success(); 1421 } 1422 }; 1423 1424 /// Inlined complex division 1425 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> { 1426 using FIROpConversion::FIROpConversion; 1427 1428 mlir::LogicalResult 1429 matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor, 1430 mlir::ConversionPatternRewriter &rewriter) const override { 1431 // TODO: Can we use a call to __divdc3 instead? 1432 // Just generate inline code for now. 1433 // given: (x + iy) / (x' + iy') 1434 // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y' 1435 mlir::Value a = adaptor.getOperands()[0]; 1436 mlir::Value b = adaptor.getOperands()[1]; 1437 auto loc = divc.getLoc(); 1438 auto *ctx = divc.getContext(); 1439 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1440 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1441 mlir::Type eleTy = convertType(getComplexEleTy(divc.getType())); 1442 mlir::Type ty = convertType(divc.getType()); 1443 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1444 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1445 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1446 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1447 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 1448 auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1); 1449 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 1450 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 1451 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 1452 auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1); 1453 auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1); 1454 auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy); 1455 auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy); 1456 auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d); 1457 auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d); 1458 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1459 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 1460 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 1461 rewriter.replaceOp(divc, r0.getResult()); 1462 return success(); 1463 } 1464 }; 1465 1466 /// Inlined complex negation 1467 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> { 1468 using FIROpConversion::FIROpConversion; 1469 1470 mlir::LogicalResult 1471 matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor, 1472 mlir::ConversionPatternRewriter &rewriter) const override { 1473 // given: -(x + iy) 1474 // result: -x - iy 1475 auto *ctxt = neg.getContext(); 1476 auto eleTy = convertType(getComplexEleTy(neg.getType())); 1477 auto ty = convertType(neg.getType()); 1478 auto loc = neg.getLoc(); 1479 mlir::Value o0 = adaptor.getOperands()[0]; 1480 auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0)); 1481 auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1)); 1482 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0); 1483 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1); 1484 auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp); 1485 auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip); 1486 auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0); 1487 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1); 1488 return success(); 1489 } 1490 }; 1491 1492 /// Conversion pattern for operation that must be dead. The information in these 1493 /// operations is used by other operation. At this point they should not have 1494 /// anymore uses. 1495 /// These operations are normally dead after the pre-codegen pass. 1496 template <typename FromOp> 1497 struct MustBeDeadConversion : public FIROpConversion<FromOp> { 1498 explicit MustBeDeadConversion(fir::LLVMTypeConverter &lowering) 1499 : FIROpConversion<FromOp>(lowering) {} 1500 using OpAdaptor = typename FromOp::Adaptor; 1501 1502 mlir::LogicalResult 1503 matchAndRewrite(FromOp op, OpAdaptor adaptor, 1504 mlir::ConversionPatternRewriter &rewriter) const final { 1505 if (!op->getUses().empty()) 1506 return rewriter.notifyMatchFailure(op, "op must be dead"); 1507 rewriter.eraseOp(op); 1508 return success(); 1509 } 1510 }; 1511 1512 struct ShapeOpConversion : public MustBeDeadConversion<fir::ShapeOp> { 1513 using MustBeDeadConversion::MustBeDeadConversion; 1514 }; 1515 1516 struct ShapeShiftOpConversion : public MustBeDeadConversion<fir::ShapeShiftOp> { 1517 using MustBeDeadConversion::MustBeDeadConversion; 1518 }; 1519 1520 struct ShiftOpConversion : public MustBeDeadConversion<fir::ShiftOp> { 1521 using MustBeDeadConversion::MustBeDeadConversion; 1522 }; 1523 1524 struct SliceOpConversion : public MustBeDeadConversion<fir::SliceOp> { 1525 using MustBeDeadConversion::MustBeDeadConversion; 1526 }; 1527 1528 /// `fir.is_present` --> 1529 /// ``` 1530 /// %0 = llvm.mlir.constant(0 : i64) 1531 /// %1 = llvm.ptrtoint %0 1532 /// %2 = llvm.icmp "ne" %1, %0 : i64 1533 /// ``` 1534 struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> { 1535 using FIROpConversion::FIROpConversion; 1536 1537 mlir::LogicalResult 1538 matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor, 1539 mlir::ConversionPatternRewriter &rewriter) const override { 1540 mlir::Type idxTy = lowerTy().indexType(); 1541 mlir::Location loc = isPresent.getLoc(); 1542 auto ptr = adaptor.getOperands()[0]; 1543 1544 if (isPresent.val().getType().isa<fir::BoxCharType>()) { 1545 auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>(); 1546 assert(!structTy.isOpaque() && !structTy.getBody().empty()); 1547 1548 mlir::Type ty = structTy.getBody()[0]; 1549 mlir::MLIRContext *ctx = isPresent.getContext(); 1550 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1551 ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0); 1552 } 1553 mlir::LLVM::ConstantOp c0 = 1554 genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0); 1555 auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr); 1556 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>( 1557 isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0); 1558 1559 return success(); 1560 } 1561 }; 1562 1563 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of 1564 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element 1565 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd 1566 /// element is the length of the character buffer (`#n`). 1567 struct EmboxCharOpConversion : public FIROpConversion<fir::EmboxCharOp> { 1568 using FIROpConversion::FIROpConversion; 1569 1570 mlir::LogicalResult 1571 matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor, 1572 mlir::ConversionPatternRewriter &rewriter) const override { 1573 mlir::ValueRange operands = adaptor.getOperands(); 1574 MLIRContext *ctx = emboxChar.getContext(); 1575 1576 mlir::Value charBuffer = operands[0]; 1577 mlir::Value charBufferLen = operands[1]; 1578 1579 mlir::Location loc = emboxChar.getLoc(); 1580 mlir::Type llvmStructTy = convertType(emboxChar.getType()); 1581 auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy); 1582 1583 mlir::Type lenTy = 1584 llvmStructTy.cast<mlir::LLVM::LLVMStructType>().getBody()[1]; 1585 mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen); 1586 1587 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1588 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1589 auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>( 1590 loc, llvmStructTy, llvmStruct, charBuffer, c0); 1591 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1592 emboxChar, llvmStructTy, insertBufferOp, lenAfterCast, c1); 1593 1594 return success(); 1595 } 1596 }; 1597 1598 /// Construct an `llvm.extractvalue` instruction. It will return value at 1599 /// element \p x from \p tuple. 1600 mlir::LLVM::ExtractValueOp 1601 genExtractValueWithIndex(mlir::Location loc, mlir::Value tuple, mlir::Type ty, 1602 mlir::ConversionPatternRewriter &rewriter, 1603 mlir::MLIRContext *ctx, int x) { 1604 auto cx = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(x)); 1605 auto xty = ty.cast<mlir::LLVM::LLVMStructType>().getBody()[x]; 1606 return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, xty, tuple, cx); 1607 } 1608 1609 /// Convert `!fir.boxchar_len` to `!llvm.extractvalue` for the 2nd part of the 1610 /// boxchar. 1611 struct BoxCharLenOpConversion : public FIROpConversion<fir::BoxCharLenOp> { 1612 using FIROpConversion::FIROpConversion; 1613 1614 mlir::LogicalResult 1615 matchAndRewrite(fir::BoxCharLenOp boxCharLen, OpAdaptor adaptor, 1616 mlir::ConversionPatternRewriter &rewriter) const override { 1617 mlir::Value boxChar = adaptor.getOperands()[0]; 1618 mlir::Location loc = boxChar.getLoc(); 1619 mlir::MLIRContext *ctx = boxChar.getContext(); 1620 mlir::Type returnValTy = boxCharLen.getResult().getType(); 1621 1622 constexpr int boxcharLenIdx = 1; 1623 mlir::LLVM::ExtractValueOp len = genExtractValueWithIndex( 1624 loc, boxChar, boxChar.getType(), rewriter, ctx, boxcharLenIdx); 1625 mlir::Value lenAfterCast = integerCast(loc, rewriter, returnValTy, len); 1626 rewriter.replaceOp(boxCharLen, lenAfterCast); 1627 1628 return success(); 1629 } 1630 }; 1631 1632 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for 1633 /// the character buffer and one for the buffer length. 1634 struct UnboxCharOpConversion : public FIROpConversion<fir::UnboxCharOp> { 1635 using FIROpConversion::FIROpConversion; 1636 1637 mlir::LogicalResult 1638 matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor, 1639 mlir::ConversionPatternRewriter &rewriter) const override { 1640 MLIRContext *ctx = unboxchar.getContext(); 1641 1642 mlir::Type lenTy = convertType(unboxchar.getType(1)); 1643 mlir::Value tuple = adaptor.getOperands()[0]; 1644 mlir::Type tupleTy = tuple.getType(); 1645 1646 mlir::Location loc = unboxchar.getLoc(); 1647 mlir::Value ptrToBuffer = 1648 genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 0); 1649 1650 mlir::LLVM::ExtractValueOp len = 1651 genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 1); 1652 mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len); 1653 1654 rewriter.replaceOp(unboxchar, 1655 ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast}); 1656 return success(); 1657 } 1658 }; 1659 1660 } // namespace 1661 1662 namespace { 1663 /// Convert FIR dialect to LLVM dialect 1664 /// 1665 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An 1666 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect. 1667 /// 1668 /// This pass is not complete yet. We are upstreaming it in small patches. 1669 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> { 1670 public: 1671 mlir::ModuleOp getModule() { return getOperation(); } 1672 1673 void runOnOperation() override final { 1674 auto mod = getModule(); 1675 if (!forcedTargetTriple.empty()) { 1676 fir::setTargetTriple(mod, forcedTargetTriple); 1677 } 1678 1679 auto *context = getModule().getContext(); 1680 fir::LLVMTypeConverter typeConverter{getModule()}; 1681 mlir::OwningRewritePatternList pattern(context); 1682 pattern.insert< 1683 AbsentOpConversion, AddcOpConversion, AddrOfOpConversion, 1684 AllocaOpConversion, BoxAddrOpConversion, BoxCharLenOpConversion, 1685 BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion, 1686 BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxRankOpConversion, 1687 BoxTypeDescOpConversion, CallOpConversion, CmpcOpConversion, 1688 ConvertOpConversion, DispatchOpConversion, DispatchTableOpConversion, 1689 DTEntryOpConversion, DivcOpConversion, EmboxCharOpConversion, 1690 ExtractValueOpConversion, HasValueOpConversion, GenTypeDescOpConversion, 1691 GlobalLenOpConversion, GlobalOpConversion, InsertOnRangeOpConversion, 1692 InsertValueOpConversion, IsPresentOpConversion, LoadOpConversion, 1693 NegcOpConversion, MulcOpConversion, SelectCaseOpConversion, 1694 SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion, 1695 ShapeOpConversion, ShapeShiftOpConversion, ShiftOpConversion, 1696 SliceOpConversion, StoreOpConversion, StringLitOpConversion, 1697 SubcOpConversion, UnboxCharOpConversion, UndefOpConversion, 1698 UnreachableOpConversion, ZeroOpConversion>(typeConverter); 1699 mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern); 1700 mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter, 1701 pattern); 1702 mlir::ConversionTarget target{*context}; 1703 target.addLegalDialect<mlir::LLVM::LLVMDialect>(); 1704 1705 // required NOPs for applying a full conversion 1706 target.addLegalOp<mlir::ModuleOp>(); 1707 1708 // apply the patterns 1709 if (mlir::failed(mlir::applyFullConversion(getModule(), target, 1710 std::move(pattern)))) { 1711 signalPassFailure(); 1712 } 1713 } 1714 }; 1715 } // namespace 1716 1717 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() { 1718 return std::make_unique<FIRToLLVMLowering>(); 1719 } 1720