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