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