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