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 // `fir.call` -> `llvm.call` 502 struct CallOpConversion : public FIROpConversion<fir::CallOp> { 503 using FIROpConversion::FIROpConversion; 504 505 mlir::LogicalResult 506 matchAndRewrite(fir::CallOp call, OpAdaptor adaptor, 507 mlir::ConversionPatternRewriter &rewriter) const override { 508 SmallVector<mlir::Type> resultTys; 509 for (auto r : call.getResults()) 510 resultTys.push_back(convertType(r.getType())); 511 rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>( 512 call, resultTys, adaptor.getOperands(), call->getAttrs()); 513 return success(); 514 } 515 }; 516 517 static mlir::Type getComplexEleTy(mlir::Type complex) { 518 if (auto cc = complex.dyn_cast<mlir::ComplexType>()) 519 return cc.getElementType(); 520 return complex.cast<fir::ComplexType>().getElementType(); 521 } 522 523 /// Compare complex values 524 /// 525 /// Per 10.1, the only comparisons available are .EQ. (oeq) and .NE. (une). 526 /// 527 /// For completeness, all other comparison are done on the real component only. 528 struct CmpcOpConversion : public FIROpConversion<fir::CmpcOp> { 529 using FIROpConversion::FIROpConversion; 530 531 mlir::LogicalResult 532 matchAndRewrite(fir::CmpcOp cmp, OpAdaptor adaptor, 533 mlir::ConversionPatternRewriter &rewriter) const override { 534 mlir::ValueRange operands = adaptor.getOperands(); 535 mlir::MLIRContext *ctxt = cmp.getContext(); 536 mlir::Type eleTy = convertType(getComplexEleTy(cmp.lhs().getType())); 537 mlir::Type resTy = convertType(cmp.getType()); 538 mlir::Location loc = cmp.getLoc(); 539 auto pos0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0)); 540 SmallVector<mlir::Value, 2> rp{rewriter.create<mlir::LLVM::ExtractValueOp>( 541 loc, eleTy, operands[0], pos0), 542 rewriter.create<mlir::LLVM::ExtractValueOp>( 543 loc, eleTy, operands[1], pos0)}; 544 auto rcp = 545 rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, rp, cmp->getAttrs()); 546 auto pos1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1)); 547 SmallVector<mlir::Value, 2> ip{rewriter.create<mlir::LLVM::ExtractValueOp>( 548 loc, eleTy, operands[0], pos1), 549 rewriter.create<mlir::LLVM::ExtractValueOp>( 550 loc, eleTy, operands[1], pos1)}; 551 auto icp = 552 rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, ip, cmp->getAttrs()); 553 SmallVector<mlir::Value, 2> cp{rcp, icp}; 554 switch (cmp.getPredicate()) { 555 case mlir::arith::CmpFPredicate::OEQ: // .EQ. 556 rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(cmp, resTy, cp); 557 break; 558 case mlir::arith::CmpFPredicate::UNE: // .NE. 559 rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(cmp, resTy, cp); 560 break; 561 default: 562 rewriter.replaceOp(cmp, rcp.getResult()); 563 break; 564 } 565 return success(); 566 } 567 }; 568 569 /// convert value of from-type to value of to-type 570 struct ConvertOpConversion : public FIROpConversion<fir::ConvertOp> { 571 using FIROpConversion::FIROpConversion; 572 573 static bool isFloatingPointTy(mlir::Type ty) { 574 return ty.isa<mlir::FloatType>(); 575 } 576 577 mlir::LogicalResult 578 matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor, 579 mlir::ConversionPatternRewriter &rewriter) const override { 580 auto fromTy = convertType(convert.value().getType()); 581 auto toTy = convertType(convert.res().getType()); 582 mlir::Value op0 = adaptor.getOperands()[0]; 583 if (fromTy == toTy) { 584 rewriter.replaceOp(convert, op0); 585 return success(); 586 } 587 auto loc = convert.getLoc(); 588 auto convertFpToFp = [&](mlir::Value val, unsigned fromBits, 589 unsigned toBits, mlir::Type toTy) -> mlir::Value { 590 if (fromBits == toBits) { 591 // TODO: Converting between two floating-point representations with the 592 // same bitwidth is not allowed for now. 593 mlir::emitError(loc, 594 "cannot implicitly convert between two floating-point " 595 "representations of the same bitwidth"); 596 return {}; 597 } 598 if (fromBits > toBits) 599 return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val); 600 return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val); 601 }; 602 // Complex to complex conversion. 603 if (fir::isa_complex(convert.value().getType()) && 604 fir::isa_complex(convert.res().getType())) { 605 // Special case: handle the conversion of a complex such that both the 606 // real and imaginary parts are converted together. 607 auto zero = mlir::ArrayAttr::get(convert.getContext(), 608 rewriter.getI32IntegerAttr(0)); 609 auto one = mlir::ArrayAttr::get(convert.getContext(), 610 rewriter.getI32IntegerAttr(1)); 611 auto ty = convertType(getComplexEleTy(convert.value().getType())); 612 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, zero); 613 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, one); 614 auto nt = convertType(getComplexEleTy(convert.res().getType())); 615 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty); 616 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt); 617 auto rc = convertFpToFp(rp, fromBits, toBits, nt); 618 auto ic = convertFpToFp(ip, fromBits, toBits, nt); 619 auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy); 620 auto i1 = 621 rewriter.create<mlir::LLVM::InsertValueOp>(loc, toTy, un, rc, zero); 622 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, toTy, i1, 623 ic, one); 624 return mlir::success(); 625 } 626 // Floating point to floating point conversion. 627 if (isFloatingPointTy(fromTy)) { 628 if (isFloatingPointTy(toTy)) { 629 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy); 630 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy); 631 auto v = convertFpToFp(op0, fromBits, toBits, toTy); 632 rewriter.replaceOp(convert, v); 633 return mlir::success(); 634 } 635 if (toTy.isa<mlir::IntegerType>()) { 636 rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0); 637 return mlir::success(); 638 } 639 } else if (fromTy.isa<mlir::IntegerType>()) { 640 // Integer to integer conversion. 641 if (toTy.isa<mlir::IntegerType>()) { 642 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy); 643 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy); 644 assert(fromBits != toBits); 645 if (fromBits > toBits) { 646 rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0); 647 return mlir::success(); 648 } 649 rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0); 650 return mlir::success(); 651 } 652 // Integer to floating point conversion. 653 if (isFloatingPointTy(toTy)) { 654 rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0); 655 return mlir::success(); 656 } 657 // Integer to pointer conversion. 658 if (toTy.isa<mlir::LLVM::LLVMPointerType>()) { 659 rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0); 660 return mlir::success(); 661 } 662 } else if (fromTy.isa<mlir::LLVM::LLVMPointerType>()) { 663 // Pointer to integer conversion. 664 if (toTy.isa<mlir::IntegerType>()) { 665 rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0); 666 return mlir::success(); 667 } 668 // Pointer to pointer conversion. 669 if (toTy.isa<mlir::LLVM::LLVMPointerType>()) { 670 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0); 671 return mlir::success(); 672 } 673 } 674 return emitError(loc) << "cannot convert " << fromTy << " to " << toTy; 675 } 676 }; 677 678 /// Lower `fir.dispatch` operation. A virtual call to a method in a dispatch 679 /// table. 680 struct DispatchOpConversion : public FIROpConversion<fir::DispatchOp> { 681 using FIROpConversion::FIROpConversion; 682 683 mlir::LogicalResult 684 matchAndRewrite(fir::DispatchOp dispatch, OpAdaptor adaptor, 685 mlir::ConversionPatternRewriter &rewriter) const override { 686 return rewriter.notifyMatchFailure( 687 dispatch, "fir.dispatch codegen is not implemented yet"); 688 } 689 }; 690 691 /// Lower `fir.dispatch_table` operation. The dispatch table for a Fortran 692 /// derived type. 693 struct DispatchTableOpConversion 694 : public FIROpConversion<fir::DispatchTableOp> { 695 using FIROpConversion::FIROpConversion; 696 697 mlir::LogicalResult 698 matchAndRewrite(fir::DispatchTableOp dispTab, OpAdaptor adaptor, 699 mlir::ConversionPatternRewriter &rewriter) const override { 700 return rewriter.notifyMatchFailure( 701 dispTab, "fir.dispatch_table codegen is not implemented yet"); 702 } 703 }; 704 705 /// Lower `fir.dt_entry` operation. An entry in a dispatch table; binds a 706 /// method-name to a function. 707 struct DTEntryOpConversion : public FIROpConversion<fir::DTEntryOp> { 708 using FIROpConversion::FIROpConversion; 709 710 mlir::LogicalResult 711 matchAndRewrite(fir::DTEntryOp dtEnt, OpAdaptor adaptor, 712 mlir::ConversionPatternRewriter &rewriter) const override { 713 return rewriter.notifyMatchFailure( 714 dtEnt, "fir.dt_entry codegen is not implemented yet"); 715 } 716 }; 717 718 /// Lower `fir.global_len` operation. 719 struct GlobalLenOpConversion : public FIROpConversion<fir::GlobalLenOp> { 720 using FIROpConversion::FIROpConversion; 721 722 mlir::LogicalResult 723 matchAndRewrite(fir::GlobalLenOp globalLen, OpAdaptor adaptor, 724 mlir::ConversionPatternRewriter &rewriter) const override { 725 return rewriter.notifyMatchFailure( 726 globalLen, "fir.global_len codegen is not implemented yet"); 727 } 728 }; 729 730 /// Lower `fir.gentypedesc` to a global constant. 731 struct GenTypeDescOpConversion : public FIROpConversion<fir::GenTypeDescOp> { 732 using FIROpConversion::FIROpConversion; 733 734 mlir::LogicalResult 735 matchAndRewrite(fir::GenTypeDescOp gentypedesc, OpAdaptor adaptor, 736 mlir::ConversionPatternRewriter &rewriter) const override { 737 return rewriter.notifyMatchFailure( 738 gentypedesc, "fir.fir.gentypedesc codegen is not implemented yet"); 739 } 740 }; 741 742 /// Lower `fir.has_value` operation to `llvm.return` operation. 743 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> { 744 using FIROpConversion::FIROpConversion; 745 746 mlir::LogicalResult 747 matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor, 748 mlir::ConversionPatternRewriter &rewriter) const override { 749 rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands()); 750 return success(); 751 } 752 }; 753 754 /// Lower `fir.global` operation to `llvm.global` operation. 755 /// `fir.insert_on_range` operations are replaced with constant dense attribute 756 /// if they are applied on the full range. 757 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> { 758 using FIROpConversion::FIROpConversion; 759 760 mlir::LogicalResult 761 matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor, 762 mlir::ConversionPatternRewriter &rewriter) const override { 763 auto tyAttr = convertType(global.getType()); 764 if (global.getType().isa<fir::BoxType>()) 765 tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType(); 766 auto loc = global.getLoc(); 767 mlir::Attribute initAttr{}; 768 if (global.initVal()) 769 initAttr = global.initVal().getValue(); 770 auto linkage = convertLinkage(global.linkName()); 771 auto isConst = global.constant().hasValue(); 772 auto g = rewriter.create<mlir::LLVM::GlobalOp>( 773 loc, tyAttr, isConst, linkage, global.sym_name(), initAttr); 774 auto &gr = g.getInitializerRegion(); 775 rewriter.inlineRegionBefore(global.region(), gr, gr.end()); 776 if (!gr.empty()) { 777 // Replace insert_on_range with a constant dense attribute if the 778 // initialization is on the full range. 779 auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>(); 780 for (auto insertOp : insertOnRangeOps) { 781 if (isFullRange(insertOp.coor(), insertOp.getType())) { 782 auto seqTyAttr = convertType(insertOp.getType()); 783 auto *op = insertOp.val().getDefiningOp(); 784 auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op); 785 if (!constant) { 786 auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op); 787 if (!convertOp) 788 continue; 789 constant = cast<mlir::arith::ConstantOp>( 790 convertOp.value().getDefiningOp()); 791 } 792 mlir::Type vecType = mlir::VectorType::get( 793 insertOp.getType().getShape(), constant.getType()); 794 auto denseAttr = mlir::DenseElementsAttr::get( 795 vecType.cast<ShapedType>(), constant.value()); 796 rewriter.setInsertionPointAfter(insertOp); 797 rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>( 798 insertOp, seqTyAttr, denseAttr); 799 } 800 } 801 } 802 rewriter.eraseOp(global); 803 return success(); 804 } 805 806 bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const { 807 auto extents = seqTy.getShape(); 808 if (indexes.size() / 2 != extents.size()) 809 return false; 810 for (unsigned i = 0; i < indexes.size(); i += 2) { 811 if (indexes[i].cast<IntegerAttr>().getInt() != 0) 812 return false; 813 if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1) 814 return false; 815 } 816 return true; 817 } 818 819 // TODO: String comparaison should be avoided. Replace linkName with an 820 // enumeration. 821 mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const { 822 if (optLinkage.hasValue()) { 823 auto name = optLinkage.getValue(); 824 if (name == "internal") 825 return mlir::LLVM::Linkage::Internal; 826 if (name == "linkonce") 827 return mlir::LLVM::Linkage::Linkonce; 828 if (name == "common") 829 return mlir::LLVM::Linkage::Common; 830 if (name == "weak") 831 return mlir::LLVM::Linkage::Weak; 832 } 833 return mlir::LLVM::Linkage::External; 834 } 835 }; 836 837 void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest, 838 Optional<mlir::ValueRange> destOps, 839 mlir::ConversionPatternRewriter &rewriter, 840 mlir::Block *newBlock) { 841 if (destOps.hasValue()) 842 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, destOps.getValue(), 843 newBlock, mlir::ValueRange()); 844 else 845 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock); 846 } 847 848 template <typename A, typename B> 849 void genBrOp(A caseOp, mlir::Block *dest, Optional<B> destOps, 850 mlir::ConversionPatternRewriter &rewriter) { 851 if (destOps.hasValue()) 852 rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, destOps.getValue(), 853 dest); 854 else 855 rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, llvm::None, dest); 856 } 857 858 void genCaseLadderStep(mlir::Location loc, mlir::Value cmp, mlir::Block *dest, 859 Optional<mlir::ValueRange> destOps, 860 mlir::ConversionPatternRewriter &rewriter) { 861 auto *thisBlock = rewriter.getInsertionBlock(); 862 auto *newBlock = createBlock(rewriter, dest); 863 rewriter.setInsertionPointToEnd(thisBlock); 864 genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock); 865 rewriter.setInsertionPointToEnd(newBlock); 866 } 867 868 /// Conversion of `fir.select_case` 869 /// 870 /// The `fir.select_case` operation is converted to a if-then-else ladder. 871 /// Depending on the case condition type, one or several comparison and 872 /// conditional branching can be generated. 873 /// 874 /// A a point value case such as `case(4)`, a lower bound case such as 875 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a 876 /// simple comparison between the selector value and the constant value in the 877 /// case. The block associated with the case condition is then executed if 878 /// the comparison succeed otherwise it branch to the next block with the 879 /// comparison for the the next case conditon. 880 /// 881 /// A closed interval case condition such as `case(7:10)` is converted with a 882 /// first comparison and conditional branching for the lower bound. If 883 /// successful, it branch to a second block with the comparison for the 884 /// upper bound in the same case condition. 885 /// 886 /// TODO: lowering of CHARACTER type cases is not handled yet. 887 struct SelectCaseOpConversion : public FIROpConversion<fir::SelectCaseOp> { 888 using FIROpConversion::FIROpConversion; 889 890 mlir::LogicalResult 891 matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor, 892 mlir::ConversionPatternRewriter &rewriter) const override { 893 unsigned conds = caseOp.getNumConditions(); 894 llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue(); 895 // Type can be CHARACTER, INTEGER, or LOGICAL (C1145) 896 LLVM_ATTRIBUTE_UNUSED auto ty = caseOp.getSelector().getType(); 897 if (ty.isa<fir::CharacterType>()) 898 return rewriter.notifyMatchFailure(caseOp, 899 "conversion of fir.select_case with " 900 "character type not implemented yet"); 901 mlir::Value selector = caseOp.getSelector(adaptor.getOperands()); 902 auto loc = caseOp.getLoc(); 903 for (unsigned t = 0; t != conds; ++t) { 904 mlir::Block *dest = caseOp.getSuccessor(t); 905 llvm::Optional<mlir::ValueRange> destOps = 906 caseOp.getSuccessorOperands(adaptor.getOperands(), t); 907 llvm::Optional<mlir::ValueRange> cmpOps = 908 *caseOp.getCompareOperands(adaptor.getOperands(), t); 909 mlir::Value caseArg = *(cmpOps.getValue().begin()); 910 mlir::Attribute attr = cases[t]; 911 if (attr.isa<fir::PointIntervalAttr>()) { 912 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 913 loc, mlir::LLVM::ICmpPredicate::eq, selector, caseArg); 914 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 915 continue; 916 } 917 if (attr.isa<fir::LowerBoundAttr>()) { 918 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 919 loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector); 920 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 921 continue; 922 } 923 if (attr.isa<fir::UpperBoundAttr>()) { 924 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 925 loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg); 926 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 927 continue; 928 } 929 if (attr.isa<fir::ClosedIntervalAttr>()) { 930 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 931 loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector); 932 auto *thisBlock = rewriter.getInsertionBlock(); 933 auto *newBlock1 = createBlock(rewriter, dest); 934 auto *newBlock2 = createBlock(rewriter, dest); 935 rewriter.setInsertionPointToEnd(thisBlock); 936 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, newBlock1, newBlock2); 937 rewriter.setInsertionPointToEnd(newBlock1); 938 mlir::Value caseArg0 = *(cmpOps.getValue().begin() + 1); 939 auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>( 940 loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg0); 941 genCondBrOp(loc, cmp0, dest, destOps, rewriter, newBlock2); 942 rewriter.setInsertionPointToEnd(newBlock2); 943 continue; 944 } 945 assert(attr.isa<mlir::UnitAttr>()); 946 assert((t + 1 == conds) && "unit must be last"); 947 genBrOp(caseOp, dest, destOps, rewriter); 948 } 949 return success(); 950 } 951 }; 952 953 template <typename OP> 954 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select, 955 typename OP::Adaptor adaptor, 956 mlir::ConversionPatternRewriter &rewriter) { 957 unsigned conds = select.getNumConditions(); 958 auto cases = select.getCases().getValue(); 959 mlir::Value selector = adaptor.selector(); 960 auto loc = select.getLoc(); 961 assert(conds > 0 && "select must have cases"); 962 963 llvm::SmallVector<mlir::Block *> destinations; 964 llvm::SmallVector<mlir::ValueRange> destinationsOperands; 965 mlir::Block *defaultDestination; 966 mlir::ValueRange defaultOperands; 967 llvm::SmallVector<int32_t> caseValues; 968 969 for (unsigned t = 0; t != conds; ++t) { 970 mlir::Block *dest = select.getSuccessor(t); 971 auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t); 972 const mlir::Attribute &attr = cases[t]; 973 if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) { 974 destinations.push_back(dest); 975 destinationsOperands.push_back(destOps.hasValue() ? *destOps 976 : ValueRange()); 977 caseValues.push_back(intAttr.getInt()); 978 continue; 979 } 980 assert(attr.template dyn_cast_or_null<mlir::UnitAttr>()); 981 assert((t + 1 == conds) && "unit must be last"); 982 defaultDestination = dest; 983 defaultOperands = destOps.hasValue() ? *destOps : ValueRange(); 984 } 985 986 // LLVM::SwitchOp takes a i32 type for the selector. 987 if (select.getSelector().getType() != rewriter.getI32Type()) 988 selector = 989 rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector); 990 991 rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>( 992 select, selector, 993 /*defaultDestination=*/defaultDestination, 994 /*defaultOperands=*/defaultOperands, 995 /*caseValues=*/caseValues, 996 /*caseDestinations=*/destinations, 997 /*caseOperands=*/destinationsOperands, 998 /*branchWeights=*/ArrayRef<int32_t>()); 999 } 1000 1001 /// conversion of fir::SelectOp to an if-then-else ladder 1002 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> { 1003 using FIROpConversion::FIROpConversion; 1004 1005 mlir::LogicalResult 1006 matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor, 1007 mlir::ConversionPatternRewriter &rewriter) const override { 1008 selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter); 1009 return success(); 1010 } 1011 }; 1012 1013 /// `fir.load` --> `llvm.load` 1014 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> { 1015 using FIROpConversion::FIROpConversion; 1016 1017 mlir::LogicalResult 1018 matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor, 1019 mlir::ConversionPatternRewriter &rewriter) const override { 1020 // fir.box is a special case because it is considered as an ssa values in 1021 // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box> 1022 // and fir.box end up being the same llvm types and loading a 1023 // fir.ref<fir.box> is actually a no op in LLVM. 1024 if (load.getType().isa<fir::BoxType>()) { 1025 rewriter.replaceOp(load, adaptor.getOperands()[0]); 1026 } else { 1027 mlir::Type ty = convertType(load.getType()); 1028 ArrayRef<NamedAttribute> at = load->getAttrs(); 1029 rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>( 1030 load, ty, adaptor.getOperands(), at); 1031 } 1032 return success(); 1033 } 1034 }; 1035 1036 /// Lower `fir.select_type` to LLVM IR dialect. 1037 struct SelectTypeOpConversion : public FIROpConversion<fir::SelectTypeOp> { 1038 using FIROpConversion::FIROpConversion; 1039 1040 mlir::LogicalResult 1041 matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor, 1042 mlir::ConversionPatternRewriter &rewriter) const override { 1043 return rewriter.notifyMatchFailure( 1044 select, "fir.select_type codegen is not implemented yet"); 1045 } 1046 }; 1047 1048 /// conversion of fir::SelectRankOp to an if-then-else ladder 1049 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> { 1050 using FIROpConversion::FIROpConversion; 1051 1052 mlir::LogicalResult 1053 matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor, 1054 mlir::ConversionPatternRewriter &rewriter) const override { 1055 selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter); 1056 return success(); 1057 } 1058 }; 1059 1060 /// `fir.store` --> `llvm.store` 1061 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> { 1062 using FIROpConversion::FIROpConversion; 1063 1064 mlir::LogicalResult 1065 matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor, 1066 mlir::ConversionPatternRewriter &rewriter) const override { 1067 if (store.value().getType().isa<fir::BoxType>()) { 1068 // fir.box value is actually in memory, load it first before storing it. 1069 mlir::Location loc = store.getLoc(); 1070 mlir::Type boxPtrTy = adaptor.getOperands()[0].getType(); 1071 auto val = rewriter.create<mlir::LLVM::LoadOp>( 1072 loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(), 1073 adaptor.getOperands()[0]); 1074 rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 1075 store, val, adaptor.getOperands()[1]); 1076 } else { 1077 rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 1078 store, adaptor.getOperands()[0], adaptor.getOperands()[1]); 1079 } 1080 return success(); 1081 } 1082 }; 1083 1084 /// convert to LLVM IR dialect `undef` 1085 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> { 1086 using FIROpConversion::FIROpConversion; 1087 1088 mlir::LogicalResult 1089 matchAndRewrite(fir::UndefOp undef, OpAdaptor, 1090 mlir::ConversionPatternRewriter &rewriter) const override { 1091 rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>( 1092 undef, convertType(undef.getType())); 1093 return success(); 1094 } 1095 }; 1096 1097 /// `fir.unreachable` --> `llvm.unreachable` 1098 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> { 1099 using FIROpConversion::FIROpConversion; 1100 1101 mlir::LogicalResult 1102 matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor, 1103 mlir::ConversionPatternRewriter &rewriter) const override { 1104 rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach); 1105 return success(); 1106 } 1107 }; 1108 1109 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> { 1110 using FIROpConversion::FIROpConversion; 1111 1112 mlir::LogicalResult 1113 matchAndRewrite(fir::ZeroOp zero, OpAdaptor, 1114 mlir::ConversionPatternRewriter &rewriter) const override { 1115 auto ty = convertType(zero.getType()); 1116 if (ty.isa<mlir::LLVM::LLVMPointerType>()) { 1117 rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty); 1118 } else if (ty.isa<mlir::IntegerType>()) { 1119 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 1120 zero, ty, mlir::IntegerAttr::get(zero.getType(), 0)); 1121 } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) { 1122 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 1123 zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0)); 1124 } else { 1125 // TODO: create ConstantAggregateZero for FIR aggregate/array types. 1126 return rewriter.notifyMatchFailure( 1127 zero, 1128 "conversion of fir.zero with aggregate type not implemented yet"); 1129 } 1130 return success(); 1131 } 1132 }; 1133 1134 // Code shared between insert_value and extract_value Ops. 1135 struct ValueOpCommon { 1136 // Translate the arguments pertaining to any multidimensional array to 1137 // row-major order for LLVM-IR. 1138 static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs, 1139 mlir::Type ty) { 1140 assert(ty && "type is null"); 1141 const auto end = attrs.size(); 1142 for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) { 1143 if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 1144 const auto dim = getDimension(seq); 1145 if (dim > 1) { 1146 auto ub = std::min(i + dim, end); 1147 std::reverse(attrs.begin() + i, attrs.begin() + ub); 1148 i += dim - 1; 1149 } 1150 ty = getArrayElementType(seq); 1151 } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) { 1152 ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()]; 1153 } else { 1154 llvm_unreachable("index into invalid type"); 1155 } 1156 } 1157 } 1158 1159 static llvm::SmallVector<mlir::Attribute> 1160 collectIndices(mlir::ConversionPatternRewriter &rewriter, 1161 mlir::ArrayAttr arrAttr) { 1162 llvm::SmallVector<mlir::Attribute> attrs; 1163 for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) { 1164 if (i->isa<mlir::IntegerAttr>()) { 1165 attrs.push_back(*i); 1166 } else { 1167 auto fieldName = i->cast<mlir::StringAttr>().getValue(); 1168 ++i; 1169 auto ty = i->cast<mlir::TypeAttr>().getValue(); 1170 auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName); 1171 attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index)); 1172 } 1173 } 1174 return attrs; 1175 } 1176 1177 private: 1178 static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) { 1179 unsigned result = 1; 1180 for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>(); 1181 eleTy; 1182 eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>()) 1183 ++result; 1184 return result; 1185 } 1186 1187 static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) { 1188 auto eleTy = ty.getElementType(); 1189 while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>()) 1190 eleTy = arrTy.getElementType(); 1191 return eleTy; 1192 } 1193 }; 1194 1195 /// Extract a subobject value from an ssa-value of aggregate type 1196 struct ExtractValueOpConversion 1197 : public FIROpAndTypeConversion<fir::ExtractValueOp>, 1198 public ValueOpCommon { 1199 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1200 1201 mlir::LogicalResult 1202 doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor, 1203 mlir::ConversionPatternRewriter &rewriter) const override { 1204 auto attrs = collectIndices(rewriter, extractVal.coor()); 1205 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 1206 auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs); 1207 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>( 1208 extractVal, ty, adaptor.getOperands()[0], position); 1209 return success(); 1210 } 1211 }; 1212 1213 /// InsertValue is the generalized instruction for the composition of new 1214 /// aggregate type values. 1215 struct InsertValueOpConversion 1216 : public FIROpAndTypeConversion<fir::InsertValueOp>, 1217 public ValueOpCommon { 1218 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1219 1220 mlir::LogicalResult 1221 doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor, 1222 mlir::ConversionPatternRewriter &rewriter) const override { 1223 auto attrs = collectIndices(rewriter, insertVal.coor()); 1224 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 1225 auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs); 1226 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1227 insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1], 1228 position); 1229 return success(); 1230 } 1231 }; 1232 1233 /// InsertOnRange inserts a value into a sequence over a range of offsets. 1234 struct InsertOnRangeOpConversion 1235 : public FIROpAndTypeConversion<fir::InsertOnRangeOp> { 1236 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1237 1238 // Increments an array of subscripts in a row major fasion. 1239 void incrementSubscripts(const SmallVector<uint64_t> &dims, 1240 SmallVector<uint64_t> &subscripts) const { 1241 for (size_t i = dims.size(); i > 0; --i) { 1242 if (++subscripts[i - 1] < dims[i - 1]) { 1243 return; 1244 } 1245 subscripts[i - 1] = 0; 1246 } 1247 } 1248 1249 mlir::LogicalResult 1250 doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor, 1251 mlir::ConversionPatternRewriter &rewriter) const override { 1252 1253 llvm::SmallVector<uint64_t> dims; 1254 auto type = adaptor.getOperands()[0].getType(); 1255 1256 // Iteratively extract the array dimensions from the type. 1257 while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 1258 dims.push_back(t.getNumElements()); 1259 type = t.getElementType(); 1260 } 1261 1262 SmallVector<uint64_t> lBounds; 1263 SmallVector<uint64_t> uBounds; 1264 1265 // Extract integer value from the attribute 1266 SmallVector<int64_t> coordinates = llvm::to_vector<4>( 1267 llvm::map_range(range.coor(), [](Attribute a) -> int64_t { 1268 return a.cast<IntegerAttr>().getInt(); 1269 })); 1270 1271 // Unzip the upper and lower bound and convert to a row major format. 1272 for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) { 1273 uBounds.push_back(*i++); 1274 lBounds.push_back(*i); 1275 } 1276 1277 auto &subscripts = lBounds; 1278 auto loc = range.getLoc(); 1279 mlir::Value lastOp = adaptor.getOperands()[0]; 1280 mlir::Value insertVal = adaptor.getOperands()[1]; 1281 1282 auto i64Ty = rewriter.getI64Type(); 1283 while (subscripts != uBounds) { 1284 // Convert uint64_t's to Attribute's. 1285 SmallVector<mlir::Attribute> subscriptAttrs; 1286 for (const auto &subscript : subscripts) 1287 subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript)); 1288 lastOp = rewriter.create<mlir::LLVM::InsertValueOp>( 1289 loc, ty, lastOp, insertVal, 1290 ArrayAttr::get(range.getContext(), subscriptAttrs)); 1291 1292 incrementSubscripts(dims, subscripts); 1293 } 1294 1295 // Convert uint64_t's to Attribute's. 1296 SmallVector<mlir::Attribute> subscriptAttrs; 1297 for (const auto &subscript : subscripts) 1298 subscriptAttrs.push_back( 1299 IntegerAttr::get(rewriter.getI64Type(), subscript)); 1300 mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs); 1301 1302 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1303 range, ty, lastOp, insertVal, 1304 ArrayAttr::get(range.getContext(), arrayRef)); 1305 1306 return success(); 1307 } 1308 }; 1309 1310 // 1311 // Primitive operations on Complex types 1312 // 1313 1314 /// Generate inline code for complex addition/subtraction 1315 template <typename LLVMOP, typename OPTY> 1316 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds, 1317 mlir::ConversionPatternRewriter &rewriter, 1318 fir::LLVMTypeConverter &lowering) { 1319 mlir::Value a = opnds[0]; 1320 mlir::Value b = opnds[1]; 1321 auto loc = sumop.getLoc(); 1322 auto ctx = sumop.getContext(); 1323 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1324 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1325 mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType())); 1326 mlir::Type ty = lowering.convertType(sumop.getType()); 1327 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1328 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1329 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1330 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1331 auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1); 1332 auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1); 1333 auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1334 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0); 1335 return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1); 1336 } 1337 1338 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> { 1339 using FIROpConversion::FIROpConversion; 1340 1341 mlir::LogicalResult 1342 matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor, 1343 mlir::ConversionPatternRewriter &rewriter) const override { 1344 // given: (x + iy) + (x' + iy') 1345 // result: (x + x') + i(y + y') 1346 auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(), 1347 rewriter, lowerTy()); 1348 rewriter.replaceOp(addc, r.getResult()); 1349 return success(); 1350 } 1351 }; 1352 1353 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> { 1354 using FIROpConversion::FIROpConversion; 1355 1356 mlir::LogicalResult 1357 matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor, 1358 mlir::ConversionPatternRewriter &rewriter) const override { 1359 // given: (x + iy) - (x' + iy') 1360 // result: (x - x') + i(y - y') 1361 auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(), 1362 rewriter, lowerTy()); 1363 rewriter.replaceOp(subc, r.getResult()); 1364 return success(); 1365 } 1366 }; 1367 1368 /// Inlined complex multiply 1369 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> { 1370 using FIROpConversion::FIROpConversion; 1371 1372 mlir::LogicalResult 1373 matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor, 1374 mlir::ConversionPatternRewriter &rewriter) const override { 1375 // TODO: Can we use a call to __muldc3 ? 1376 // given: (x + iy) * (x' + iy') 1377 // result: (xx'-yy')+i(xy'+yx') 1378 mlir::Value a = adaptor.getOperands()[0]; 1379 mlir::Value b = adaptor.getOperands()[1]; 1380 auto loc = mulc.getLoc(); 1381 auto *ctx = mulc.getContext(); 1382 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1383 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1384 mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType())); 1385 mlir::Type ty = convertType(mulc.getType()); 1386 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1387 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1388 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1389 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1390 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 1391 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 1392 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 1393 auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx); 1394 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 1395 auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy); 1396 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1397 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 1398 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 1399 rewriter.replaceOp(mulc, r0.getResult()); 1400 return success(); 1401 } 1402 }; 1403 1404 /// Inlined complex division 1405 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> { 1406 using FIROpConversion::FIROpConversion; 1407 1408 mlir::LogicalResult 1409 matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor, 1410 mlir::ConversionPatternRewriter &rewriter) const override { 1411 // TODO: Can we use a call to __divdc3 instead? 1412 // Just generate inline code for now. 1413 // given: (x + iy) / (x' + iy') 1414 // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y' 1415 mlir::Value a = adaptor.getOperands()[0]; 1416 mlir::Value b = adaptor.getOperands()[1]; 1417 auto loc = divc.getLoc(); 1418 auto *ctx = divc.getContext(); 1419 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1420 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1421 mlir::Type eleTy = convertType(getComplexEleTy(divc.getType())); 1422 mlir::Type ty = convertType(divc.getType()); 1423 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1424 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1425 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1426 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1427 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 1428 auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1); 1429 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 1430 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 1431 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 1432 auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1); 1433 auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1); 1434 auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy); 1435 auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy); 1436 auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d); 1437 auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d); 1438 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1439 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 1440 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 1441 rewriter.replaceOp(divc, r0.getResult()); 1442 return success(); 1443 } 1444 }; 1445 1446 /// Inlined complex negation 1447 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> { 1448 using FIROpConversion::FIROpConversion; 1449 1450 mlir::LogicalResult 1451 matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor, 1452 mlir::ConversionPatternRewriter &rewriter) const override { 1453 // given: -(x + iy) 1454 // result: -x - iy 1455 auto *ctxt = neg.getContext(); 1456 auto eleTy = convertType(getComplexEleTy(neg.getType())); 1457 auto ty = convertType(neg.getType()); 1458 auto loc = neg.getLoc(); 1459 mlir::Value o0 = adaptor.getOperands()[0]; 1460 auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0)); 1461 auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1)); 1462 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0); 1463 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1); 1464 auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp); 1465 auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip); 1466 auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0); 1467 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1); 1468 return success(); 1469 } 1470 }; 1471 1472 /// `fir.is_present` --> 1473 /// ``` 1474 /// %0 = llvm.mlir.constant(0 : i64) 1475 /// %1 = llvm.ptrtoint %0 1476 /// %2 = llvm.icmp "ne" %1, %0 : i64 1477 /// ``` 1478 struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> { 1479 using FIROpConversion::FIROpConversion; 1480 1481 mlir::LogicalResult 1482 matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor, 1483 mlir::ConversionPatternRewriter &rewriter) const override { 1484 mlir::Type idxTy = lowerTy().indexType(); 1485 mlir::Location loc = isPresent.getLoc(); 1486 auto ptr = adaptor.getOperands()[0]; 1487 1488 if (isPresent.val().getType().isa<fir::BoxCharType>()) { 1489 auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>(); 1490 assert(!structTy.isOpaque() && !structTy.getBody().empty()); 1491 1492 mlir::Type ty = structTy.getBody()[0]; 1493 mlir::MLIRContext *ctx = isPresent.getContext(); 1494 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1495 ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0); 1496 } 1497 mlir::LLVM::ConstantOp c0 = 1498 genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0); 1499 auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr); 1500 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>( 1501 isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0); 1502 1503 return success(); 1504 } 1505 }; 1506 1507 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of 1508 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element 1509 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd 1510 /// element is the length of the character buffer (`#n`). 1511 struct EmboxCharOpConversion : public FIROpConversion<fir::EmboxCharOp> { 1512 using FIROpConversion::FIROpConversion; 1513 1514 mlir::LogicalResult 1515 matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor, 1516 mlir::ConversionPatternRewriter &rewriter) const override { 1517 mlir::ValueRange operands = adaptor.getOperands(); 1518 MLIRContext *ctx = emboxChar.getContext(); 1519 1520 mlir::Value charBuffer = operands[0]; 1521 mlir::Value charBufferLen = operands[1]; 1522 1523 mlir::Location loc = emboxChar.getLoc(); 1524 mlir::Type llvmStructTy = convertType(emboxChar.getType()); 1525 auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy); 1526 1527 mlir::Type lenTy = 1528 llvmStructTy.cast<mlir::LLVM::LLVMStructType>().getBody()[1]; 1529 mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen); 1530 1531 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1532 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1533 auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>( 1534 loc, llvmStructTy, llvmStruct, charBuffer, c0); 1535 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1536 emboxChar, llvmStructTy, insertBufferOp, lenAfterCast, c1); 1537 1538 return success(); 1539 } 1540 }; 1541 1542 /// Construct an `llvm.extractvalue` instruction. It will return value at 1543 /// element \p x from \p tuple. 1544 mlir::LLVM::ExtractValueOp 1545 genExtractValueWithIndex(mlir::Location loc, mlir::Value tuple, mlir::Type ty, 1546 mlir::ConversionPatternRewriter &rewriter, 1547 mlir::MLIRContext *ctx, int x) { 1548 auto cx = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(x)); 1549 auto xty = ty.cast<mlir::LLVM::LLVMStructType>().getBody()[x]; 1550 return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, xty, tuple, cx); 1551 } 1552 1553 /// Convert `!fir.boxchar_len` to `!llvm.extractvalue` for the 2nd part of the 1554 /// boxchar. 1555 struct BoxCharLenOpConversion : public FIROpConversion<fir::BoxCharLenOp> { 1556 using FIROpConversion::FIROpConversion; 1557 1558 mlir::LogicalResult 1559 matchAndRewrite(fir::BoxCharLenOp boxCharLen, OpAdaptor adaptor, 1560 mlir::ConversionPatternRewriter &rewriter) const override { 1561 mlir::Value boxChar = adaptor.getOperands()[0]; 1562 mlir::Location loc = boxChar.getLoc(); 1563 mlir::MLIRContext *ctx = boxChar.getContext(); 1564 mlir::Type returnValTy = boxCharLen.getResult().getType(); 1565 1566 constexpr int boxcharLenIdx = 1; 1567 mlir::LLVM::ExtractValueOp len = genExtractValueWithIndex( 1568 loc, boxChar, boxChar.getType(), rewriter, ctx, boxcharLenIdx); 1569 mlir::Value lenAfterCast = integerCast(loc, rewriter, returnValTy, len); 1570 rewriter.replaceOp(boxCharLen, lenAfterCast); 1571 1572 return success(); 1573 } 1574 }; 1575 1576 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for 1577 /// the character buffer and one for the buffer length. 1578 struct UnboxCharOpConversion : public FIROpConversion<fir::UnboxCharOp> { 1579 using FIROpConversion::FIROpConversion; 1580 1581 mlir::LogicalResult 1582 matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor, 1583 mlir::ConversionPatternRewriter &rewriter) const override { 1584 MLIRContext *ctx = unboxchar.getContext(); 1585 1586 mlir::Type lenTy = convertType(unboxchar.getType(1)); 1587 mlir::Value tuple = adaptor.getOperands()[0]; 1588 mlir::Type tupleTy = tuple.getType(); 1589 1590 mlir::Location loc = unboxchar.getLoc(); 1591 mlir::Value ptrToBuffer = 1592 genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 0); 1593 1594 mlir::LLVM::ExtractValueOp len = 1595 genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 1); 1596 mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len); 1597 1598 rewriter.replaceOp(unboxchar, 1599 ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast}); 1600 return success(); 1601 } 1602 }; 1603 1604 } // namespace 1605 1606 namespace { 1607 /// Convert FIR dialect to LLVM dialect 1608 /// 1609 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An 1610 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect. 1611 /// 1612 /// This pass is not complete yet. We are upstreaming it in small patches. 1613 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> { 1614 public: 1615 mlir::ModuleOp getModule() { return getOperation(); } 1616 1617 void runOnOperation() override final { 1618 auto mod = getModule(); 1619 if (!forcedTargetTriple.empty()) { 1620 fir::setTargetTriple(mod, forcedTargetTriple); 1621 } 1622 1623 auto *context = getModule().getContext(); 1624 fir::LLVMTypeConverter typeConverter{getModule()}; 1625 mlir::OwningRewritePatternList pattern(context); 1626 pattern.insert< 1627 AbsentOpConversion, AddcOpConversion, AddrOfOpConversion, 1628 AllocaOpConversion, BoxAddrOpConversion, BoxCharLenOpConversion, 1629 BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion, 1630 BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxRankOpConversion, 1631 CallOpConversion, CmpcOpConversion, ConvertOpConversion, 1632 DispatchOpConversion, DispatchTableOpConversion, DTEntryOpConversion, 1633 DivcOpConversion, EmboxCharOpConversion, ExtractValueOpConversion, 1634 HasValueOpConversion, GenTypeDescOpConversion, GlobalLenOpConversion, 1635 GlobalOpConversion, InsertOnRangeOpConversion, InsertValueOpConversion, 1636 IsPresentOpConversion, LoadOpConversion, NegcOpConversion, 1637 MulcOpConversion, SelectCaseOpConversion, SelectOpConversion, 1638 SelectRankOpConversion, SelectTypeOpConversion, StoreOpConversion, 1639 StringLitOpConversion, SubcOpConversion, UnboxCharOpConversion, 1640 UndefOpConversion, UnreachableOpConversion, ZeroOpConversion>( 1641 typeConverter); 1642 mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern); 1643 mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter, 1644 pattern); 1645 mlir::ConversionTarget target{*context}; 1646 target.addLegalDialect<mlir::LLVM::LLVMDialect>(); 1647 1648 // required NOPs for applying a full conversion 1649 target.addLegalOp<mlir::ModuleOp>(); 1650 1651 // apply the patterns 1652 if (mlir::failed(mlir::applyFullConversion(getModule(), target, 1653 std::move(pattern)))) { 1654 signalPassFailure(); 1655 } 1656 } 1657 }; 1658 } // namespace 1659 1660 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() { 1661 return std::make_unique<FIRToLLVMLowering>(); 1662 } 1663