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