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