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/FIROps.h" 17 #include "flang/Optimizer/Dialect/FIRType.h" 18 #include "flang/Optimizer/Support/FIRContext.h" 19 #include "mlir/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.h" 20 #include "mlir/Conversion/LLVMCommon/Pattern.h" 21 #include "mlir/Conversion/LLVMCommon/TypeConverter.h" 22 #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h" 23 #include "mlir/IR/BuiltinTypes.h" 24 #include "mlir/IR/Matchers.h" 25 #include "mlir/Pass/Pass.h" 26 #include "llvm/ADT/ArrayRef.h" 27 28 #define DEBUG_TYPE "flang-codegen" 29 30 // fir::LLVMTypeConverter for converting to LLVM IR dialect types. 31 #include "TypeConverter.h" 32 33 /// `fir.box` attribute values as defined for CFI_attribute_t in 34 /// flang/ISO_Fortran_binding.h. 35 static constexpr unsigned kAttrPointer = CFI_attribute_pointer; 36 static constexpr unsigned kAttrAllocatable = CFI_attribute_allocatable; 37 38 namespace { 39 /// FIR conversion pattern template 40 template <typename FromOp> 41 class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> { 42 public: 43 explicit FIROpConversion(fir::LLVMTypeConverter &lowering) 44 : mlir::ConvertOpToLLVMPattern<FromOp>(lowering) {} 45 46 protected: 47 mlir::Type convertType(mlir::Type ty) const { 48 return lowerTy().convertType(ty); 49 } 50 51 mlir::LLVM::ConstantOp 52 genConstantOffset(mlir::Location loc, 53 mlir::ConversionPatternRewriter &rewriter, 54 int offset) const { 55 auto ity = lowerTy().offsetType(); 56 auto cattr = rewriter.getI32IntegerAttr(offset); 57 return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr); 58 } 59 60 /// Construct code sequence to extract the specifc value from a `fir.box`. 61 mlir::Value getValueFromBox(mlir::Location loc, mlir::Value box, 62 mlir::Type resultTy, 63 mlir::ConversionPatternRewriter &rewriter, 64 unsigned boxValue) const { 65 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 66 mlir::LLVM::ConstantOp cValuePos = 67 genConstantOffset(loc, rewriter, boxValue); 68 auto pty = mlir::LLVM::LLVMPointerType::get(resultTy); 69 auto p = rewriter.create<mlir::LLVM::GEPOp>( 70 loc, pty, mlir::ValueRange{box, c0, cValuePos}); 71 return rewriter.create<mlir::LLVM::LoadOp>(loc, resultTy, p); 72 } 73 74 /// Method to construct code sequence to get the triple for dimension `dim` 75 /// from a box. 76 SmallVector<mlir::Value, 3> 77 getDimsFromBox(mlir::Location loc, ArrayRef<mlir::Type> retTys, 78 mlir::Value box, mlir::Value dim, 79 mlir::ConversionPatternRewriter &rewriter) const { 80 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 81 mlir::LLVM::ConstantOp cDims = 82 genConstantOffset(loc, rewriter, kDimsPosInBox); 83 mlir::LLVM::LoadOp l0 = 84 loadFromOffset(loc, box, c0, cDims, dim, 0, retTys[0], rewriter); 85 mlir::LLVM::LoadOp l1 = 86 loadFromOffset(loc, box, c0, cDims, dim, 1, retTys[1], rewriter); 87 mlir::LLVM::LoadOp l2 = 88 loadFromOffset(loc, box, c0, cDims, dim, 2, retTys[2], rewriter); 89 return {l0.getResult(), l1.getResult(), l2.getResult()}; 90 } 91 92 mlir::LLVM::LoadOp 93 loadFromOffset(mlir::Location loc, mlir::Value a, mlir::LLVM::ConstantOp c0, 94 mlir::LLVM::ConstantOp cDims, mlir::Value dim, int off, 95 mlir::Type ty, 96 mlir::ConversionPatternRewriter &rewriter) const { 97 auto pty = mlir::LLVM::LLVMPointerType::get(ty); 98 mlir::LLVM::ConstantOp c = genConstantOffset(loc, rewriter, off); 99 mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, a, c0, cDims, dim, c); 100 return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p); 101 } 102 103 /// Read base address from a fir.box. Returned address has type ty. 104 mlir::Value 105 loadBaseAddrFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box, 106 mlir::ConversionPatternRewriter &rewriter) const { 107 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 108 mlir::LLVM::ConstantOp cAddr = 109 genConstantOffset(loc, rewriter, kAddrPosInBox); 110 auto pty = mlir::LLVM::LLVMPointerType::get(ty); 111 mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cAddr); 112 return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p); 113 } 114 115 mlir::Value 116 loadElementSizeFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box, 117 mlir::ConversionPatternRewriter &rewriter) const { 118 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 119 mlir::LLVM::ConstantOp cElemLen = 120 genConstantOffset(loc, rewriter, kElemLenPosInBox); 121 auto pty = mlir::LLVM::LLVMPointerType::get(ty); 122 mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cElemLen); 123 return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p); 124 } 125 126 // Load the attribute from the \p box and perform a check against \p maskValue 127 // The final comparison is implemented as `(attribute & maskValue) != 0`. 128 mlir::Value genBoxAttributeCheck(mlir::Location loc, mlir::Value box, 129 mlir::ConversionPatternRewriter &rewriter, 130 unsigned maskValue) const { 131 mlir::Type attrTy = rewriter.getI32Type(); 132 mlir::Value attribute = 133 getValueFromBox(loc, box, attrTy, rewriter, kAttributePosInBox); 134 mlir::LLVM::ConstantOp attrMask = 135 genConstantOffset(loc, rewriter, maskValue); 136 auto maskRes = 137 rewriter.create<mlir::LLVM::AndOp>(loc, attrTy, attribute, attrMask); 138 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 139 return rewriter.create<mlir::LLVM::ICmpOp>( 140 loc, mlir::LLVM::ICmpPredicate::ne, maskRes, c0); 141 } 142 143 template <typename... ARGS> 144 mlir::LLVM::GEPOp genGEP(mlir::Location loc, mlir::Type ty, 145 mlir::ConversionPatternRewriter &rewriter, 146 mlir::Value base, ARGS... args) const { 147 SmallVector<mlir::Value> cv{args...}; 148 return rewriter.create<mlir::LLVM::GEPOp>(loc, ty, base, cv); 149 } 150 151 fir::LLVMTypeConverter &lowerTy() const { 152 return *static_cast<fir::LLVMTypeConverter *>(this->getTypeConverter()); 153 } 154 }; 155 156 /// FIR conversion pattern template 157 template <typename FromOp> 158 class FIROpAndTypeConversion : public FIROpConversion<FromOp> { 159 public: 160 using FIROpConversion<FromOp>::FIROpConversion; 161 using OpAdaptor = typename FromOp::Adaptor; 162 163 mlir::LogicalResult 164 matchAndRewrite(FromOp op, OpAdaptor adaptor, 165 mlir::ConversionPatternRewriter &rewriter) const final { 166 mlir::Type ty = this->convertType(op.getType()); 167 return doRewrite(op, ty, adaptor, rewriter); 168 } 169 170 virtual mlir::LogicalResult 171 doRewrite(FromOp addr, mlir::Type ty, OpAdaptor adaptor, 172 mlir::ConversionPatternRewriter &rewriter) const = 0; 173 }; 174 175 // Lower `fir.address_of` operation to `llvm.address_of` operation. 176 struct AddrOfOpConversion : public FIROpConversion<fir::AddrOfOp> { 177 using FIROpConversion::FIROpConversion; 178 179 mlir::LogicalResult 180 matchAndRewrite(fir::AddrOfOp addr, OpAdaptor adaptor, 181 mlir::ConversionPatternRewriter &rewriter) const override { 182 auto ty = convertType(addr.getType()); 183 rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>( 184 addr, ty, addr.symbol().getRootReference().getValue()); 185 return success(); 186 } 187 }; 188 189 /// Lower `fir.box_addr` to the sequence of operations to extract the first 190 /// element of the box. 191 struct BoxAddrOpConversion : public FIROpConversion<fir::BoxAddrOp> { 192 using FIROpConversion::FIROpConversion; 193 194 mlir::LogicalResult 195 matchAndRewrite(fir::BoxAddrOp boxaddr, OpAdaptor adaptor, 196 mlir::ConversionPatternRewriter &rewriter) const override { 197 mlir::Value a = adaptor.getOperands()[0]; 198 auto loc = boxaddr.getLoc(); 199 mlir::Type ty = convertType(boxaddr.getType()); 200 if (auto argty = boxaddr.val().getType().dyn_cast<fir::BoxType>()) { 201 rewriter.replaceOp(boxaddr, loadBaseAddrFromBox(loc, ty, a, rewriter)); 202 } else { 203 auto c0attr = rewriter.getI32IntegerAttr(0); 204 auto c0 = mlir::ArrayAttr::get(boxaddr.getContext(), c0attr); 205 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(boxaddr, ty, a, 206 c0); 207 } 208 return success(); 209 } 210 }; 211 212 /// Lower `fir.box_dims` to a sequence of operations to extract the requested 213 /// dimension infomartion from the boxed value. 214 /// Result in a triple set of GEPs and loads. 215 struct BoxDimsOpConversion : public FIROpConversion<fir::BoxDimsOp> { 216 using FIROpConversion::FIROpConversion; 217 218 mlir::LogicalResult 219 matchAndRewrite(fir::BoxDimsOp boxdims, OpAdaptor adaptor, 220 mlir::ConversionPatternRewriter &rewriter) const override { 221 SmallVector<mlir::Type, 3> resultTypes = { 222 convertType(boxdims.getResult(0).getType()), 223 convertType(boxdims.getResult(1).getType()), 224 convertType(boxdims.getResult(2).getType()), 225 }; 226 auto results = 227 getDimsFromBox(boxdims.getLoc(), resultTypes, adaptor.getOperands()[0], 228 adaptor.getOperands()[1], rewriter); 229 rewriter.replaceOp(boxdims, results); 230 return success(); 231 } 232 }; 233 234 /// Lower `fir.box_elesize` to a sequence of operations ro extract the size of 235 /// an element in the boxed value. 236 struct BoxEleSizeOpConversion : public FIROpConversion<fir::BoxEleSizeOp> { 237 using FIROpConversion::FIROpConversion; 238 239 mlir::LogicalResult 240 matchAndRewrite(fir::BoxEleSizeOp boxelesz, OpAdaptor adaptor, 241 mlir::ConversionPatternRewriter &rewriter) const override { 242 mlir::Value a = adaptor.getOperands()[0]; 243 auto loc = boxelesz.getLoc(); 244 auto ty = convertType(boxelesz.getType()); 245 auto elemSize = getValueFromBox(loc, a, ty, rewriter, kElemLenPosInBox); 246 rewriter.replaceOp(boxelesz, elemSize); 247 return success(); 248 } 249 }; 250 251 /// Lower `fir.box_isalloc` to a sequence of operations to determine if the 252 /// boxed value was from an ALLOCATABLE entity. 253 struct BoxIsAllocOpConversion : public FIROpConversion<fir::BoxIsAllocOp> { 254 using FIROpConversion::FIROpConversion; 255 256 mlir::LogicalResult 257 matchAndRewrite(fir::BoxIsAllocOp boxisalloc, OpAdaptor adaptor, 258 mlir::ConversionPatternRewriter &rewriter) const override { 259 mlir::Value box = adaptor.getOperands()[0]; 260 auto loc = boxisalloc.getLoc(); 261 mlir::Value check = 262 genBoxAttributeCheck(loc, box, rewriter, kAttrAllocatable); 263 rewriter.replaceOp(boxisalloc, check); 264 return success(); 265 } 266 }; 267 268 /// Lower `fir.box_isarray` to a sequence of operations to determine if the 269 /// boxed is an array. 270 struct BoxIsArrayOpConversion : public FIROpConversion<fir::BoxIsArrayOp> { 271 using FIROpConversion::FIROpConversion; 272 273 mlir::LogicalResult 274 matchAndRewrite(fir::BoxIsArrayOp boxisarray, OpAdaptor adaptor, 275 mlir::ConversionPatternRewriter &rewriter) const override { 276 mlir::Value a = adaptor.getOperands()[0]; 277 auto loc = boxisarray.getLoc(); 278 auto rank = 279 getValueFromBox(loc, a, rewriter.getI32Type(), rewriter, kRankPosInBox); 280 auto c0 = genConstantOffset(loc, rewriter, 0); 281 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>( 282 boxisarray, mlir::LLVM::ICmpPredicate::ne, rank, c0); 283 return success(); 284 } 285 }; 286 287 /// Lower `fir.box_isptr` to a sequence of operations to determined if the 288 /// boxed value was from a POINTER entity. 289 struct BoxIsPtrOpConversion : public FIROpConversion<fir::BoxIsPtrOp> { 290 using FIROpConversion::FIROpConversion; 291 292 mlir::LogicalResult 293 matchAndRewrite(fir::BoxIsPtrOp boxisptr, OpAdaptor adaptor, 294 mlir::ConversionPatternRewriter &rewriter) const override { 295 mlir::Value box = adaptor.getOperands()[0]; 296 auto loc = boxisptr.getLoc(); 297 mlir::Value check = genBoxAttributeCheck(loc, box, rewriter, kAttrPointer); 298 rewriter.replaceOp(boxisptr, check); 299 return success(); 300 } 301 }; 302 303 /// Lower `fir.box_rank` to the sequence of operation to extract the rank from 304 /// the box. 305 struct BoxRankOpConversion : public FIROpConversion<fir::BoxRankOp> { 306 using FIROpConversion::FIROpConversion; 307 308 mlir::LogicalResult 309 matchAndRewrite(fir::BoxRankOp boxrank, OpAdaptor adaptor, 310 mlir::ConversionPatternRewriter &rewriter) const override { 311 mlir::Value a = adaptor.getOperands()[0]; 312 auto loc = boxrank.getLoc(); 313 mlir::Type ty = convertType(boxrank.getType()); 314 auto result = getValueFromBox(loc, a, ty, rewriter, kRankPosInBox); 315 rewriter.replaceOp(boxrank, result); 316 return success(); 317 } 318 }; 319 320 // `fir.call` -> `llvm.call` 321 struct CallOpConversion : public FIROpConversion<fir::CallOp> { 322 using FIROpConversion::FIROpConversion; 323 324 mlir::LogicalResult 325 matchAndRewrite(fir::CallOp call, OpAdaptor adaptor, 326 mlir::ConversionPatternRewriter &rewriter) const override { 327 SmallVector<mlir::Type> resultTys; 328 for (auto r : call.getResults()) 329 resultTys.push_back(convertType(r.getType())); 330 rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>( 331 call, resultTys, adaptor.getOperands(), call->getAttrs()); 332 return success(); 333 } 334 }; 335 336 static mlir::Type getComplexEleTy(mlir::Type complex) { 337 if (auto cc = complex.dyn_cast<mlir::ComplexType>()) 338 return cc.getElementType(); 339 return complex.cast<fir::ComplexType>().getElementType(); 340 } 341 342 /// convert value of from-type to value of to-type 343 struct ConvertOpConversion : public FIROpConversion<fir::ConvertOp> { 344 using FIROpConversion::FIROpConversion; 345 346 static bool isFloatingPointTy(mlir::Type ty) { 347 return ty.isa<mlir::FloatType>(); 348 } 349 350 mlir::LogicalResult 351 matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor, 352 mlir::ConversionPatternRewriter &rewriter) const override { 353 auto fromTy = convertType(convert.value().getType()); 354 auto toTy = convertType(convert.res().getType()); 355 mlir::Value op0 = adaptor.getOperands()[0]; 356 if (fromTy == toTy) { 357 rewriter.replaceOp(convert, op0); 358 return success(); 359 } 360 auto loc = convert.getLoc(); 361 auto convertFpToFp = [&](mlir::Value val, unsigned fromBits, 362 unsigned toBits, mlir::Type toTy) -> mlir::Value { 363 if (fromBits == toBits) { 364 // TODO: Converting between two floating-point representations with the 365 // same bitwidth is not allowed for now. 366 mlir::emitError(loc, 367 "cannot implicitly convert between two floating-point " 368 "representations of the same bitwidth"); 369 return {}; 370 } 371 if (fromBits > toBits) 372 return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val); 373 return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val); 374 }; 375 // Complex to complex conversion. 376 if (fir::isa_complex(convert.value().getType()) && 377 fir::isa_complex(convert.res().getType())) { 378 // Special case: handle the conversion of a complex such that both the 379 // real and imaginary parts are converted together. 380 auto zero = mlir::ArrayAttr::get(convert.getContext(), 381 rewriter.getI32IntegerAttr(0)); 382 auto one = mlir::ArrayAttr::get(convert.getContext(), 383 rewriter.getI32IntegerAttr(1)); 384 auto ty = convertType(getComplexEleTy(convert.value().getType())); 385 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, zero); 386 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, one); 387 auto nt = convertType(getComplexEleTy(convert.res().getType())); 388 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty); 389 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt); 390 auto rc = convertFpToFp(rp, fromBits, toBits, nt); 391 auto ic = convertFpToFp(ip, fromBits, toBits, nt); 392 auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy); 393 auto i1 = 394 rewriter.create<mlir::LLVM::InsertValueOp>(loc, toTy, un, rc, zero); 395 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, toTy, i1, 396 ic, one); 397 return mlir::success(); 398 } 399 // Floating point to floating point conversion. 400 if (isFloatingPointTy(fromTy)) { 401 if (isFloatingPointTy(toTy)) { 402 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy); 403 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy); 404 auto v = convertFpToFp(op0, fromBits, toBits, toTy); 405 rewriter.replaceOp(convert, v); 406 return mlir::success(); 407 } 408 if (toTy.isa<mlir::IntegerType>()) { 409 rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0); 410 return mlir::success(); 411 } 412 } else if (fromTy.isa<mlir::IntegerType>()) { 413 // Integer to integer conversion. 414 if (toTy.isa<mlir::IntegerType>()) { 415 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy); 416 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy); 417 assert(fromBits != toBits); 418 if (fromBits > toBits) { 419 rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0); 420 return mlir::success(); 421 } 422 rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0); 423 return mlir::success(); 424 } 425 // Integer to floating point conversion. 426 if (isFloatingPointTy(toTy)) { 427 rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0); 428 return mlir::success(); 429 } 430 // Integer to pointer conversion. 431 if (toTy.isa<mlir::LLVM::LLVMPointerType>()) { 432 rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0); 433 return mlir::success(); 434 } 435 } else if (fromTy.isa<mlir::LLVM::LLVMPointerType>()) { 436 // Pointer to integer conversion. 437 if (toTy.isa<mlir::IntegerType>()) { 438 rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0); 439 return mlir::success(); 440 } 441 // Pointer to pointer conversion. 442 if (toTy.isa<mlir::LLVM::LLVMPointerType>()) { 443 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0); 444 return mlir::success(); 445 } 446 } 447 return emitError(loc) << "cannot convert " << fromTy << " to " << toTy; 448 } 449 }; 450 451 /// Lower `fir.has_value` operation to `llvm.return` operation. 452 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> { 453 using FIROpConversion::FIROpConversion; 454 455 mlir::LogicalResult 456 matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor, 457 mlir::ConversionPatternRewriter &rewriter) const override { 458 rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands()); 459 return success(); 460 } 461 }; 462 463 /// Lower `fir.global` operation to `llvm.global` operation. 464 /// `fir.insert_on_range` operations are replaced with constant dense attribute 465 /// if they are applied on the full range. 466 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> { 467 using FIROpConversion::FIROpConversion; 468 469 mlir::LogicalResult 470 matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor, 471 mlir::ConversionPatternRewriter &rewriter) const override { 472 auto tyAttr = convertType(global.getType()); 473 if (global.getType().isa<fir::BoxType>()) 474 tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType(); 475 auto loc = global.getLoc(); 476 mlir::Attribute initAttr{}; 477 if (global.initVal()) 478 initAttr = global.initVal().getValue(); 479 auto linkage = convertLinkage(global.linkName()); 480 auto isConst = global.constant().hasValue(); 481 auto g = rewriter.create<mlir::LLVM::GlobalOp>( 482 loc, tyAttr, isConst, linkage, global.sym_name(), initAttr); 483 auto &gr = g.getInitializerRegion(); 484 rewriter.inlineRegionBefore(global.region(), gr, gr.end()); 485 if (!gr.empty()) { 486 // Replace insert_on_range with a constant dense attribute if the 487 // initialization is on the full range. 488 auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>(); 489 for (auto insertOp : insertOnRangeOps) { 490 if (isFullRange(insertOp.coor(), insertOp.getType())) { 491 auto seqTyAttr = convertType(insertOp.getType()); 492 auto *op = insertOp.val().getDefiningOp(); 493 auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op); 494 if (!constant) { 495 auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op); 496 if (!convertOp) 497 continue; 498 constant = cast<mlir::arith::ConstantOp>( 499 convertOp.value().getDefiningOp()); 500 } 501 mlir::Type vecType = mlir::VectorType::get( 502 insertOp.getType().getShape(), constant.getType()); 503 auto denseAttr = mlir::DenseElementsAttr::get( 504 vecType.cast<ShapedType>(), constant.value()); 505 rewriter.setInsertionPointAfter(insertOp); 506 rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>( 507 insertOp, seqTyAttr, denseAttr); 508 } 509 } 510 } 511 rewriter.eraseOp(global); 512 return success(); 513 } 514 515 bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const { 516 auto extents = seqTy.getShape(); 517 if (indexes.size() / 2 != extents.size()) 518 return false; 519 for (unsigned i = 0; i < indexes.size(); i += 2) { 520 if (indexes[i].cast<IntegerAttr>().getInt() != 0) 521 return false; 522 if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1) 523 return false; 524 } 525 return true; 526 } 527 528 // TODO: String comparaison should be avoided. Replace linkName with an 529 // enumeration. 530 mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const { 531 if (optLinkage.hasValue()) { 532 auto name = optLinkage.getValue(); 533 if (name == "internal") 534 return mlir::LLVM::Linkage::Internal; 535 if (name == "linkonce") 536 return mlir::LLVM::Linkage::Linkonce; 537 if (name == "common") 538 return mlir::LLVM::Linkage::Common; 539 if (name == "weak") 540 return mlir::LLVM::Linkage::Weak; 541 } 542 return mlir::LLVM::Linkage::External; 543 } 544 }; 545 546 template <typename OP> 547 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select, 548 typename OP::Adaptor adaptor, 549 mlir::ConversionPatternRewriter &rewriter) { 550 unsigned conds = select.getNumConditions(); 551 auto cases = select.getCases().getValue(); 552 mlir::Value selector = adaptor.selector(); 553 auto loc = select.getLoc(); 554 assert(conds > 0 && "select must have cases"); 555 556 llvm::SmallVector<mlir::Block *> destinations; 557 llvm::SmallVector<mlir::ValueRange> destinationsOperands; 558 mlir::Block *defaultDestination; 559 mlir::ValueRange defaultOperands; 560 llvm::SmallVector<int32_t> caseValues; 561 562 for (unsigned t = 0; t != conds; ++t) { 563 mlir::Block *dest = select.getSuccessor(t); 564 auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t); 565 const mlir::Attribute &attr = cases[t]; 566 if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) { 567 destinations.push_back(dest); 568 destinationsOperands.push_back(destOps.hasValue() ? *destOps 569 : ValueRange()); 570 caseValues.push_back(intAttr.getInt()); 571 continue; 572 } 573 assert(attr.template dyn_cast_or_null<mlir::UnitAttr>()); 574 assert((t + 1 == conds) && "unit must be last"); 575 defaultDestination = dest; 576 defaultOperands = destOps.hasValue() ? *destOps : ValueRange(); 577 } 578 579 // LLVM::SwitchOp takes a i32 type for the selector. 580 if (select.getSelector().getType() != rewriter.getI32Type()) 581 selector = 582 rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector); 583 584 rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>( 585 select, selector, 586 /*defaultDestination=*/defaultDestination, 587 /*defaultOperands=*/defaultOperands, 588 /*caseValues=*/caseValues, 589 /*caseDestinations=*/destinations, 590 /*caseOperands=*/destinationsOperands, 591 /*branchWeights=*/ArrayRef<int32_t>()); 592 } 593 594 /// conversion of fir::SelectOp to an if-then-else ladder 595 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> { 596 using FIROpConversion::FIROpConversion; 597 598 mlir::LogicalResult 599 matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor, 600 mlir::ConversionPatternRewriter &rewriter) const override { 601 selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter); 602 return success(); 603 } 604 }; 605 606 /// `fir.load` --> `llvm.load` 607 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> { 608 using FIROpConversion::FIROpConversion; 609 610 mlir::LogicalResult 611 matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor, 612 mlir::ConversionPatternRewriter &rewriter) const override { 613 // fir.box is a special case because it is considered as an ssa values in 614 // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box> 615 // and fir.box end up being the same llvm types and loading a 616 // fir.ref<fir.box> is actually a no op in LLVM. 617 if (load.getType().isa<fir::BoxType>()) { 618 rewriter.replaceOp(load, adaptor.getOperands()[0]); 619 } else { 620 mlir::Type ty = convertType(load.getType()); 621 ArrayRef<NamedAttribute> at = load->getAttrs(); 622 rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>( 623 load, ty, adaptor.getOperands(), at); 624 } 625 return success(); 626 } 627 }; 628 629 /// conversion of fir::SelectRankOp to an if-then-else ladder 630 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> { 631 using FIROpConversion::FIROpConversion; 632 633 mlir::LogicalResult 634 matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor, 635 mlir::ConversionPatternRewriter &rewriter) const override { 636 selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter); 637 return success(); 638 } 639 }; 640 641 /// `fir.store` --> `llvm.store` 642 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> { 643 using FIROpConversion::FIROpConversion; 644 645 mlir::LogicalResult 646 matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor, 647 mlir::ConversionPatternRewriter &rewriter) const override { 648 if (store.value().getType().isa<fir::BoxType>()) { 649 // fir.box value is actually in memory, load it first before storing it. 650 mlir::Location loc = store.getLoc(); 651 mlir::Type boxPtrTy = adaptor.getOperands()[0].getType(); 652 auto val = rewriter.create<mlir::LLVM::LoadOp>( 653 loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(), 654 adaptor.getOperands()[0]); 655 rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 656 store, val, adaptor.getOperands()[1]); 657 } else { 658 rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 659 store, adaptor.getOperands()[0], adaptor.getOperands()[1]); 660 } 661 return success(); 662 } 663 }; 664 665 /// convert to LLVM IR dialect `undef` 666 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> { 667 using FIROpConversion::FIROpConversion; 668 669 mlir::LogicalResult 670 matchAndRewrite(fir::UndefOp undef, OpAdaptor, 671 mlir::ConversionPatternRewriter &rewriter) const override { 672 rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>( 673 undef, convertType(undef.getType())); 674 return success(); 675 } 676 }; 677 678 /// `fir.unreachable` --> `llvm.unreachable` 679 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> { 680 using FIROpConversion::FIROpConversion; 681 682 mlir::LogicalResult 683 matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor, 684 mlir::ConversionPatternRewriter &rewriter) const override { 685 rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach); 686 return success(); 687 } 688 }; 689 690 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> { 691 using FIROpConversion::FIROpConversion; 692 693 mlir::LogicalResult 694 matchAndRewrite(fir::ZeroOp zero, OpAdaptor, 695 mlir::ConversionPatternRewriter &rewriter) const override { 696 auto ty = convertType(zero.getType()); 697 if (ty.isa<mlir::LLVM::LLVMPointerType>()) { 698 rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty); 699 } else if (ty.isa<mlir::IntegerType>()) { 700 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 701 zero, ty, mlir::IntegerAttr::get(zero.getType(), 0)); 702 } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) { 703 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 704 zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0)); 705 } else { 706 // TODO: create ConstantAggregateZero for FIR aggregate/array types. 707 return rewriter.notifyMatchFailure( 708 zero, 709 "conversion of fir.zero with aggregate type not implemented yet"); 710 } 711 return success(); 712 } 713 }; 714 715 // Code shared between insert_value and extract_value Ops. 716 struct ValueOpCommon { 717 // Translate the arguments pertaining to any multidimensional array to 718 // row-major order for LLVM-IR. 719 static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs, 720 mlir::Type ty) { 721 assert(ty && "type is null"); 722 const auto end = attrs.size(); 723 for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) { 724 if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 725 const auto dim = getDimension(seq); 726 if (dim > 1) { 727 auto ub = std::min(i + dim, end); 728 std::reverse(attrs.begin() + i, attrs.begin() + ub); 729 i += dim - 1; 730 } 731 ty = getArrayElementType(seq); 732 } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) { 733 ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()]; 734 } else { 735 llvm_unreachable("index into invalid type"); 736 } 737 } 738 } 739 740 static llvm::SmallVector<mlir::Attribute> 741 collectIndices(mlir::ConversionPatternRewriter &rewriter, 742 mlir::ArrayAttr arrAttr) { 743 llvm::SmallVector<mlir::Attribute> attrs; 744 for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) { 745 if (i->isa<mlir::IntegerAttr>()) { 746 attrs.push_back(*i); 747 } else { 748 auto fieldName = i->cast<mlir::StringAttr>().getValue(); 749 ++i; 750 auto ty = i->cast<mlir::TypeAttr>().getValue(); 751 auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName); 752 attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index)); 753 } 754 } 755 return attrs; 756 } 757 758 private: 759 static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) { 760 unsigned result = 1; 761 for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>(); 762 eleTy; 763 eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>()) 764 ++result; 765 return result; 766 } 767 768 static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) { 769 auto eleTy = ty.getElementType(); 770 while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>()) 771 eleTy = arrTy.getElementType(); 772 return eleTy; 773 } 774 }; 775 776 /// Extract a subobject value from an ssa-value of aggregate type 777 struct ExtractValueOpConversion 778 : public FIROpAndTypeConversion<fir::ExtractValueOp>, 779 public ValueOpCommon { 780 using FIROpAndTypeConversion::FIROpAndTypeConversion; 781 782 mlir::LogicalResult 783 doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor, 784 mlir::ConversionPatternRewriter &rewriter) const override { 785 auto attrs = collectIndices(rewriter, extractVal.coor()); 786 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 787 auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs); 788 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>( 789 extractVal, ty, adaptor.getOperands()[0], position); 790 return success(); 791 } 792 }; 793 794 /// InsertValue is the generalized instruction for the composition of new 795 /// aggregate type values. 796 struct InsertValueOpConversion 797 : public FIROpAndTypeConversion<fir::InsertValueOp>, 798 public ValueOpCommon { 799 using FIROpAndTypeConversion::FIROpAndTypeConversion; 800 801 mlir::LogicalResult 802 doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor, 803 mlir::ConversionPatternRewriter &rewriter) const override { 804 auto attrs = collectIndices(rewriter, insertVal.coor()); 805 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 806 auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs); 807 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 808 insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1], 809 position); 810 return success(); 811 } 812 }; 813 814 /// InsertOnRange inserts a value into a sequence over a range of offsets. 815 struct InsertOnRangeOpConversion 816 : public FIROpAndTypeConversion<fir::InsertOnRangeOp> { 817 using FIROpAndTypeConversion::FIROpAndTypeConversion; 818 819 // Increments an array of subscripts in a row major fasion. 820 void incrementSubscripts(const SmallVector<uint64_t> &dims, 821 SmallVector<uint64_t> &subscripts) const { 822 for (size_t i = dims.size(); i > 0; --i) { 823 if (++subscripts[i - 1] < dims[i - 1]) { 824 return; 825 } 826 subscripts[i - 1] = 0; 827 } 828 } 829 830 mlir::LogicalResult 831 doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor, 832 mlir::ConversionPatternRewriter &rewriter) const override { 833 834 llvm::SmallVector<uint64_t> dims; 835 auto type = adaptor.getOperands()[0].getType(); 836 837 // Iteratively extract the array dimensions from the type. 838 while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 839 dims.push_back(t.getNumElements()); 840 type = t.getElementType(); 841 } 842 843 SmallVector<uint64_t> lBounds; 844 SmallVector<uint64_t> uBounds; 845 846 // Extract integer value from the attribute 847 SmallVector<int64_t> coordinates = llvm::to_vector<4>( 848 llvm::map_range(range.coor(), [](Attribute a) -> int64_t { 849 return a.cast<IntegerAttr>().getInt(); 850 })); 851 852 // Unzip the upper and lower bound and convert to a row major format. 853 for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) { 854 uBounds.push_back(*i++); 855 lBounds.push_back(*i); 856 } 857 858 auto &subscripts = lBounds; 859 auto loc = range.getLoc(); 860 mlir::Value lastOp = adaptor.getOperands()[0]; 861 mlir::Value insertVal = adaptor.getOperands()[1]; 862 863 auto i64Ty = rewriter.getI64Type(); 864 while (subscripts != uBounds) { 865 // Convert uint64_t's to Attribute's. 866 SmallVector<mlir::Attribute> subscriptAttrs; 867 for (const auto &subscript : subscripts) 868 subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript)); 869 lastOp = rewriter.create<mlir::LLVM::InsertValueOp>( 870 loc, ty, lastOp, insertVal, 871 ArrayAttr::get(range.getContext(), subscriptAttrs)); 872 873 incrementSubscripts(dims, subscripts); 874 } 875 876 // Convert uint64_t's to Attribute's. 877 SmallVector<mlir::Attribute> subscriptAttrs; 878 for (const auto &subscript : subscripts) 879 subscriptAttrs.push_back( 880 IntegerAttr::get(rewriter.getI64Type(), subscript)); 881 mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs); 882 883 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 884 range, ty, lastOp, insertVal, 885 ArrayAttr::get(range.getContext(), arrayRef)); 886 887 return success(); 888 } 889 }; 890 891 // 892 // Primitive operations on Complex types 893 // 894 895 /// Generate inline code for complex addition/subtraction 896 template <typename LLVMOP, typename OPTY> 897 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds, 898 mlir::ConversionPatternRewriter &rewriter, 899 fir::LLVMTypeConverter &lowering) { 900 mlir::Value a = opnds[0]; 901 mlir::Value b = opnds[1]; 902 auto loc = sumop.getLoc(); 903 auto ctx = sumop.getContext(); 904 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 905 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 906 mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType())); 907 mlir::Type ty = lowering.convertType(sumop.getType()); 908 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 909 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 910 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 911 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 912 auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1); 913 auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1); 914 auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 915 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0); 916 return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1); 917 } 918 919 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> { 920 using FIROpConversion::FIROpConversion; 921 922 mlir::LogicalResult 923 matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor, 924 mlir::ConversionPatternRewriter &rewriter) const override { 925 // given: (x + iy) + (x' + iy') 926 // result: (x + x') + i(y + y') 927 auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(), 928 rewriter, lowerTy()); 929 rewriter.replaceOp(addc, r.getResult()); 930 return success(); 931 } 932 }; 933 934 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> { 935 using FIROpConversion::FIROpConversion; 936 937 mlir::LogicalResult 938 matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor, 939 mlir::ConversionPatternRewriter &rewriter) const override { 940 // given: (x + iy) - (x' + iy') 941 // result: (x - x') + i(y - y') 942 auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(), 943 rewriter, lowerTy()); 944 rewriter.replaceOp(subc, r.getResult()); 945 return success(); 946 } 947 }; 948 949 /// Inlined complex multiply 950 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> { 951 using FIROpConversion::FIROpConversion; 952 953 mlir::LogicalResult 954 matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor, 955 mlir::ConversionPatternRewriter &rewriter) const override { 956 // TODO: Can we use a call to __muldc3 ? 957 // given: (x + iy) * (x' + iy') 958 // result: (xx'-yy')+i(xy'+yx') 959 mlir::Value a = adaptor.getOperands()[0]; 960 mlir::Value b = adaptor.getOperands()[1]; 961 auto loc = mulc.getLoc(); 962 auto *ctx = mulc.getContext(); 963 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 964 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 965 mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType())); 966 mlir::Type ty = convertType(mulc.getType()); 967 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 968 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 969 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 970 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 971 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 972 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 973 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 974 auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx); 975 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 976 auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy); 977 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 978 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 979 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 980 rewriter.replaceOp(mulc, r0.getResult()); 981 return success(); 982 } 983 }; 984 985 /// Inlined complex division 986 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> { 987 using FIROpConversion::FIROpConversion; 988 989 mlir::LogicalResult 990 matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor, 991 mlir::ConversionPatternRewriter &rewriter) const override { 992 // TODO: Can we use a call to __divdc3 instead? 993 // Just generate inline code for now. 994 // given: (x + iy) / (x' + iy') 995 // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y' 996 mlir::Value a = adaptor.getOperands()[0]; 997 mlir::Value b = adaptor.getOperands()[1]; 998 auto loc = divc.getLoc(); 999 auto *ctx = divc.getContext(); 1000 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1001 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1002 mlir::Type eleTy = convertType(getComplexEleTy(divc.getType())); 1003 mlir::Type ty = convertType(divc.getType()); 1004 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1005 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1006 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1007 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1008 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 1009 auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1); 1010 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 1011 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 1012 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 1013 auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1); 1014 auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1); 1015 auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy); 1016 auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy); 1017 auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d); 1018 auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d); 1019 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1020 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 1021 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 1022 rewriter.replaceOp(divc, r0.getResult()); 1023 return success(); 1024 } 1025 }; 1026 1027 /// Inlined complex negation 1028 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> { 1029 using FIROpConversion::FIROpConversion; 1030 1031 mlir::LogicalResult 1032 matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor, 1033 mlir::ConversionPatternRewriter &rewriter) const override { 1034 // given: -(x + iy) 1035 // result: -x - iy 1036 auto *ctxt = neg.getContext(); 1037 auto eleTy = convertType(getComplexEleTy(neg.getType())); 1038 auto ty = convertType(neg.getType()); 1039 auto loc = neg.getLoc(); 1040 mlir::Value o0 = adaptor.getOperands()[0]; 1041 auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0)); 1042 auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1)); 1043 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0); 1044 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1); 1045 auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp); 1046 auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip); 1047 auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0); 1048 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1); 1049 return success(); 1050 } 1051 }; 1052 1053 } // namespace 1054 1055 namespace { 1056 /// Convert FIR dialect to LLVM dialect 1057 /// 1058 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An 1059 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect. 1060 /// 1061 /// This pass is not complete yet. We are upstreaming it in small patches. 1062 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> { 1063 public: 1064 mlir::ModuleOp getModule() { return getOperation(); } 1065 1066 void runOnOperation() override final { 1067 auto mod = getModule(); 1068 if (!forcedTargetTriple.empty()) { 1069 fir::setTargetTriple(mod, forcedTargetTriple); 1070 } 1071 1072 auto *context = getModule().getContext(); 1073 fir::LLVMTypeConverter typeConverter{getModule()}; 1074 mlir::OwningRewritePatternList pattern(context); 1075 pattern.insert< 1076 AddcOpConversion, AddrOfOpConversion, BoxAddrOpConversion, 1077 BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion, 1078 BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxRankOpConversion, 1079 CallOpConversion, ConvertOpConversion, DivcOpConversion, 1080 ExtractValueOpConversion, HasValueOpConversion, GlobalOpConversion, 1081 InsertOnRangeOpConversion, InsertValueOpConversion, LoadOpConversion, 1082 NegcOpConversion, MulcOpConversion, SelectOpConversion, 1083 SelectRankOpConversion, StoreOpConversion, SubcOpConversion, 1084 UndefOpConversion, UnreachableOpConversion, ZeroOpConversion>( 1085 typeConverter); 1086 mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern); 1087 mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter, 1088 pattern); 1089 mlir::ConversionTarget target{*context}; 1090 target.addLegalDialect<mlir::LLVM::LLVMDialect>(); 1091 1092 // required NOPs for applying a full conversion 1093 target.addLegalOp<mlir::ModuleOp>(); 1094 1095 // apply the patterns 1096 if (mlir::failed(mlir::applyFullConversion(getModule(), target, 1097 std::move(pattern)))) { 1098 signalPassFailure(); 1099 } 1100 } 1101 }; 1102 } // namespace 1103 1104 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() { 1105 return std::make_unique<FIRToLLVMLowering>(); 1106 } 1107