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/Optimizer/Dialect/FIROps.h" 16 #include "flang/Optimizer/Dialect/FIRType.h" 17 #include "flang/Optimizer/Support/FIRContext.h" 18 #include "mlir/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.h" 19 #include "mlir/Conversion/LLVMCommon/Pattern.h" 20 #include "mlir/Conversion/LLVMCommon/TypeConverter.h" 21 #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h" 22 #include "mlir/IR/BuiltinTypes.h" 23 #include "mlir/IR/Matchers.h" 24 #include "mlir/Pass/Pass.h" 25 #include "llvm/ADT/ArrayRef.h" 26 27 #define DEBUG_TYPE "flang-codegen" 28 29 // fir::LLVMTypeConverter for converting to LLVM IR dialect types. 30 #include "TypeConverter.h" 31 32 namespace { 33 /// FIR conversion pattern template 34 template <typename FromOp> 35 class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> { 36 public: 37 explicit FIROpConversion(fir::LLVMTypeConverter &lowering) 38 : mlir::ConvertOpToLLVMPattern<FromOp>(lowering) {} 39 40 protected: 41 mlir::Type convertType(mlir::Type ty) const { 42 return lowerTy().convertType(ty); 43 } 44 45 fir::LLVMTypeConverter &lowerTy() const { 46 return *static_cast<fir::LLVMTypeConverter *>(this->getTypeConverter()); 47 } 48 }; 49 50 /// FIR conversion pattern template 51 template <typename FromOp> 52 class FIROpAndTypeConversion : public FIROpConversion<FromOp> { 53 public: 54 using FIROpConversion<FromOp>::FIROpConversion; 55 using OpAdaptor = typename FromOp::Adaptor; 56 57 mlir::LogicalResult 58 matchAndRewrite(FromOp op, OpAdaptor adaptor, 59 mlir::ConversionPatternRewriter &rewriter) const final { 60 mlir::Type ty = this->convertType(op.getType()); 61 return doRewrite(op, ty, adaptor, rewriter); 62 } 63 64 virtual mlir::LogicalResult 65 doRewrite(FromOp addr, mlir::Type ty, OpAdaptor adaptor, 66 mlir::ConversionPatternRewriter &rewriter) const = 0; 67 }; 68 69 // Lower `fir.address_of` operation to `llvm.address_of` operation. 70 struct AddrOfOpConversion : public FIROpConversion<fir::AddrOfOp> { 71 using FIROpConversion::FIROpConversion; 72 73 mlir::LogicalResult 74 matchAndRewrite(fir::AddrOfOp addr, OpAdaptor adaptor, 75 mlir::ConversionPatternRewriter &rewriter) const override { 76 auto ty = convertType(addr.getType()); 77 rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>( 78 addr, ty, addr.symbol().getRootReference().getValue()); 79 return success(); 80 } 81 }; 82 83 // `fir.call` -> `llvm.call` 84 struct CallOpConversion : public FIROpConversion<fir::CallOp> { 85 using FIROpConversion::FIROpConversion; 86 87 mlir::LogicalResult 88 matchAndRewrite(fir::CallOp call, OpAdaptor adaptor, 89 mlir::ConversionPatternRewriter &rewriter) const override { 90 SmallVector<mlir::Type> resultTys; 91 for (auto r : call.getResults()) 92 resultTys.push_back(convertType(r.getType())); 93 rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>( 94 call, resultTys, adaptor.getOperands(), call->getAttrs()); 95 return success(); 96 } 97 }; 98 99 /// Lower `fir.has_value` operation to `llvm.return` operation. 100 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> { 101 using FIROpConversion::FIROpConversion; 102 103 mlir::LogicalResult 104 matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor, 105 mlir::ConversionPatternRewriter &rewriter) const override { 106 rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands()); 107 return success(); 108 } 109 }; 110 111 /// Lower `fir.global` operation to `llvm.global` operation. 112 /// `fir.insert_on_range` operations are replaced with constant dense attribute 113 /// if they are applied on the full range. 114 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> { 115 using FIROpConversion::FIROpConversion; 116 117 mlir::LogicalResult 118 matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor, 119 mlir::ConversionPatternRewriter &rewriter) const override { 120 auto tyAttr = convertType(global.getType()); 121 if (global.getType().isa<fir::BoxType>()) 122 tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType(); 123 auto loc = global.getLoc(); 124 mlir::Attribute initAttr{}; 125 if (global.initVal()) 126 initAttr = global.initVal().getValue(); 127 auto linkage = convertLinkage(global.linkName()); 128 auto isConst = global.constant().hasValue(); 129 auto g = rewriter.create<mlir::LLVM::GlobalOp>( 130 loc, tyAttr, isConst, linkage, global.sym_name(), initAttr); 131 auto &gr = g.getInitializerRegion(); 132 rewriter.inlineRegionBefore(global.region(), gr, gr.end()); 133 if (!gr.empty()) { 134 // Replace insert_on_range with a constant dense attribute if the 135 // initialization is on the full range. 136 auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>(); 137 for (auto insertOp : insertOnRangeOps) { 138 if (isFullRange(insertOp.coor(), insertOp.getType())) { 139 auto seqTyAttr = convertType(insertOp.getType()); 140 auto *op = insertOp.val().getDefiningOp(); 141 auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op); 142 if (!constant) { 143 auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op); 144 if (!convertOp) 145 continue; 146 constant = cast<mlir::arith::ConstantOp>( 147 convertOp.value().getDefiningOp()); 148 } 149 mlir::Type vecType = mlir::VectorType::get( 150 insertOp.getType().getShape(), constant.getType()); 151 auto denseAttr = mlir::DenseElementsAttr::get( 152 vecType.cast<ShapedType>(), constant.value()); 153 rewriter.setInsertionPointAfter(insertOp); 154 rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>( 155 insertOp, seqTyAttr, denseAttr); 156 } 157 } 158 } 159 rewriter.eraseOp(global); 160 return success(); 161 } 162 163 bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const { 164 auto extents = seqTy.getShape(); 165 if (indexes.size() / 2 != extents.size()) 166 return false; 167 for (unsigned i = 0; i < indexes.size(); i += 2) { 168 if (indexes[i].cast<IntegerAttr>().getInt() != 0) 169 return false; 170 if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1) 171 return false; 172 } 173 return true; 174 } 175 176 // TODO: String comparaison should be avoided. Replace linkName with an 177 // enumeration. 178 mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const { 179 if (optLinkage.hasValue()) { 180 auto name = optLinkage.getValue(); 181 if (name == "internal") 182 return mlir::LLVM::Linkage::Internal; 183 if (name == "linkonce") 184 return mlir::LLVM::Linkage::Linkonce; 185 if (name == "common") 186 return mlir::LLVM::Linkage::Common; 187 if (name == "weak") 188 return mlir::LLVM::Linkage::Weak; 189 } 190 return mlir::LLVM::Linkage::External; 191 } 192 }; 193 194 template <typename OP> 195 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select, 196 typename OP::Adaptor adaptor, 197 mlir::ConversionPatternRewriter &rewriter) { 198 unsigned conds = select.getNumConditions(); 199 auto cases = select.getCases().getValue(); 200 mlir::Value selector = adaptor.selector(); 201 auto loc = select.getLoc(); 202 assert(conds > 0 && "select must have cases"); 203 204 llvm::SmallVector<mlir::Block *> destinations; 205 llvm::SmallVector<mlir::ValueRange> destinationsOperands; 206 mlir::Block *defaultDestination; 207 mlir::ValueRange defaultOperands; 208 llvm::SmallVector<int32_t> caseValues; 209 210 for (unsigned t = 0; t != conds; ++t) { 211 mlir::Block *dest = select.getSuccessor(t); 212 auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t); 213 const mlir::Attribute &attr = cases[t]; 214 if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) { 215 destinations.push_back(dest); 216 destinationsOperands.push_back(destOps.hasValue() ? *destOps 217 : ValueRange()); 218 caseValues.push_back(intAttr.getInt()); 219 continue; 220 } 221 assert(attr.template dyn_cast_or_null<mlir::UnitAttr>()); 222 assert((t + 1 == conds) && "unit must be last"); 223 defaultDestination = dest; 224 defaultOperands = destOps.hasValue() ? *destOps : ValueRange(); 225 } 226 227 // LLVM::SwitchOp takes a i32 type for the selector. 228 if (select.getSelector().getType() != rewriter.getI32Type()) 229 selector = 230 rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector); 231 232 rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>( 233 select, selector, 234 /*defaultDestination=*/defaultDestination, 235 /*defaultOperands=*/defaultOperands, 236 /*caseValues=*/caseValues, 237 /*caseDestinations=*/destinations, 238 /*caseOperands=*/destinationsOperands, 239 /*branchWeights=*/ArrayRef<int32_t>()); 240 } 241 242 /// conversion of fir::SelectOp to an if-then-else ladder 243 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> { 244 using FIROpConversion::FIROpConversion; 245 246 mlir::LogicalResult 247 matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor, 248 mlir::ConversionPatternRewriter &rewriter) const override { 249 selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter); 250 return success(); 251 } 252 }; 253 254 /// conversion of fir::SelectRankOp to an if-then-else ladder 255 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> { 256 using FIROpConversion::FIROpConversion; 257 258 mlir::LogicalResult 259 matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor, 260 mlir::ConversionPatternRewriter &rewriter) const override { 261 selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter); 262 return success(); 263 } 264 }; 265 266 // convert to LLVM IR dialect `undef` 267 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> { 268 using FIROpConversion::FIROpConversion; 269 270 mlir::LogicalResult 271 matchAndRewrite(fir::UndefOp undef, OpAdaptor, 272 mlir::ConversionPatternRewriter &rewriter) const override { 273 rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>( 274 undef, convertType(undef.getType())); 275 return success(); 276 } 277 }; 278 279 // convert to LLVM IR dialect `unreachable` 280 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> { 281 using FIROpConversion::FIROpConversion; 282 283 mlir::LogicalResult 284 matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor, 285 mlir::ConversionPatternRewriter &rewriter) const override { 286 rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach); 287 return success(); 288 } 289 }; 290 291 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> { 292 using FIROpConversion::FIROpConversion; 293 294 mlir::LogicalResult 295 matchAndRewrite(fir::ZeroOp zero, OpAdaptor, 296 mlir::ConversionPatternRewriter &rewriter) const override { 297 auto ty = convertType(zero.getType()); 298 if (ty.isa<mlir::LLVM::LLVMPointerType>()) { 299 rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty); 300 } else if (ty.isa<mlir::IntegerType>()) { 301 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 302 zero, ty, mlir::IntegerAttr::get(zero.getType(), 0)); 303 } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) { 304 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 305 zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0)); 306 } else { 307 // TODO: create ConstantAggregateZero for FIR aggregate/array types. 308 return rewriter.notifyMatchFailure( 309 zero, 310 "conversion of fir.zero with aggregate type not implemented yet"); 311 } 312 return success(); 313 } 314 }; 315 316 // Code shared between insert_value and extract_value Ops. 317 struct ValueOpCommon { 318 // Translate the arguments pertaining to any multidimensional array to 319 // row-major order for LLVM-IR. 320 static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs, 321 mlir::Type ty) { 322 assert(ty && "type is null"); 323 const auto end = attrs.size(); 324 for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) { 325 if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 326 const auto dim = getDimension(seq); 327 if (dim > 1) { 328 auto ub = std::min(i + dim, end); 329 std::reverse(attrs.begin() + i, attrs.begin() + ub); 330 i += dim - 1; 331 } 332 ty = getArrayElementType(seq); 333 } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) { 334 ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()]; 335 } else { 336 llvm_unreachable("index into invalid type"); 337 } 338 } 339 } 340 341 static llvm::SmallVector<mlir::Attribute> 342 collectIndices(mlir::ConversionPatternRewriter &rewriter, 343 mlir::ArrayAttr arrAttr) { 344 llvm::SmallVector<mlir::Attribute> attrs; 345 for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) { 346 if (i->isa<mlir::IntegerAttr>()) { 347 attrs.push_back(*i); 348 } else { 349 auto fieldName = i->cast<mlir::StringAttr>().getValue(); 350 ++i; 351 auto ty = i->cast<mlir::TypeAttr>().getValue(); 352 auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName); 353 attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index)); 354 } 355 } 356 return attrs; 357 } 358 359 private: 360 static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) { 361 unsigned result = 1; 362 for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>(); 363 eleTy; 364 eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>()) 365 ++result; 366 return result; 367 } 368 369 static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) { 370 auto eleTy = ty.getElementType(); 371 while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>()) 372 eleTy = arrTy.getElementType(); 373 return eleTy; 374 } 375 }; 376 377 /// Extract a subobject value from an ssa-value of aggregate type 378 struct ExtractValueOpConversion 379 : public FIROpAndTypeConversion<fir::ExtractValueOp>, 380 public ValueOpCommon { 381 using FIROpAndTypeConversion::FIROpAndTypeConversion; 382 383 mlir::LogicalResult 384 doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor, 385 mlir::ConversionPatternRewriter &rewriter) const override { 386 auto attrs = collectIndices(rewriter, extractVal.coor()); 387 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 388 auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs); 389 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>( 390 extractVal, ty, adaptor.getOperands()[0], position); 391 return success(); 392 } 393 }; 394 395 /// InsertValue is the generalized instruction for the composition of new 396 /// aggregate type values. 397 struct InsertValueOpConversion 398 : public FIROpAndTypeConversion<fir::InsertValueOp>, 399 public ValueOpCommon { 400 using FIROpAndTypeConversion::FIROpAndTypeConversion; 401 402 mlir::LogicalResult 403 doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor, 404 mlir::ConversionPatternRewriter &rewriter) const override { 405 auto attrs = collectIndices(rewriter, insertVal.coor()); 406 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 407 auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs); 408 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 409 insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1], 410 position); 411 return success(); 412 } 413 }; 414 415 /// InsertOnRange inserts a value into a sequence over a range of offsets. 416 struct InsertOnRangeOpConversion 417 : public FIROpAndTypeConversion<fir::InsertOnRangeOp> { 418 using FIROpAndTypeConversion::FIROpAndTypeConversion; 419 420 // Increments an array of subscripts in a row major fasion. 421 void incrementSubscripts(const SmallVector<uint64_t> &dims, 422 SmallVector<uint64_t> &subscripts) const { 423 for (size_t i = dims.size(); i > 0; --i) { 424 if (++subscripts[i - 1] < dims[i - 1]) { 425 return; 426 } 427 subscripts[i - 1] = 0; 428 } 429 } 430 431 mlir::LogicalResult 432 doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor, 433 mlir::ConversionPatternRewriter &rewriter) const override { 434 435 llvm::SmallVector<uint64_t> dims; 436 auto type = adaptor.getOperands()[0].getType(); 437 438 // Iteratively extract the array dimensions from the type. 439 while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 440 dims.push_back(t.getNumElements()); 441 type = t.getElementType(); 442 } 443 444 SmallVector<uint64_t> lBounds; 445 SmallVector<uint64_t> uBounds; 446 447 // Extract integer value from the attribute 448 SmallVector<int64_t> coordinates = llvm::to_vector<4>( 449 llvm::map_range(range.coor(), [](Attribute a) -> int64_t { 450 return a.cast<IntegerAttr>().getInt(); 451 })); 452 453 // Unzip the upper and lower bound and convert to a row major format. 454 for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) { 455 uBounds.push_back(*i++); 456 lBounds.push_back(*i); 457 } 458 459 auto &subscripts = lBounds; 460 auto loc = range.getLoc(); 461 mlir::Value lastOp = adaptor.getOperands()[0]; 462 mlir::Value insertVal = adaptor.getOperands()[1]; 463 464 auto i64Ty = rewriter.getI64Type(); 465 while (subscripts != uBounds) { 466 // Convert uint64_t's to Attribute's. 467 SmallVector<mlir::Attribute> subscriptAttrs; 468 for (const auto &subscript : subscripts) 469 subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript)); 470 lastOp = rewriter.create<mlir::LLVM::InsertValueOp>( 471 loc, ty, lastOp, insertVal, 472 ArrayAttr::get(range.getContext(), subscriptAttrs)); 473 474 incrementSubscripts(dims, subscripts); 475 } 476 477 // Convert uint64_t's to Attribute's. 478 SmallVector<mlir::Attribute> subscriptAttrs; 479 for (const auto &subscript : subscripts) 480 subscriptAttrs.push_back( 481 IntegerAttr::get(rewriter.getI64Type(), subscript)); 482 mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs); 483 484 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 485 range, ty, lastOp, insertVal, 486 ArrayAttr::get(range.getContext(), arrayRef)); 487 488 return success(); 489 } 490 }; 491 492 static mlir::Type getComplexEleTy(mlir::Type complex) { 493 if (auto cc = complex.dyn_cast<mlir::ComplexType>()) 494 return cc.getElementType(); 495 return complex.cast<fir::ComplexType>().getElementType(); 496 } 497 498 // 499 // Primitive operations on Complex types 500 // 501 502 /// Generate inline code for complex addition/subtraction 503 template <typename LLVMOP, typename OPTY> 504 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds, 505 mlir::ConversionPatternRewriter &rewriter, 506 fir::LLVMTypeConverter &lowering) { 507 mlir::Value a = opnds[0]; 508 mlir::Value b = opnds[1]; 509 auto loc = sumop.getLoc(); 510 auto ctx = sumop.getContext(); 511 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 512 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 513 mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType())); 514 mlir::Type ty = lowering.convertType(sumop.getType()); 515 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 516 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 517 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 518 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 519 auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1); 520 auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1); 521 auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 522 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0); 523 return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1); 524 } 525 526 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> { 527 using FIROpConversion::FIROpConversion; 528 529 mlir::LogicalResult 530 matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor, 531 mlir::ConversionPatternRewriter &rewriter) const override { 532 // given: (x + iy) + (x' + iy') 533 // result: (x + x') + i(y + y') 534 auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(), 535 rewriter, lowerTy()); 536 rewriter.replaceOp(addc, r.getResult()); 537 return success(); 538 } 539 }; 540 541 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> { 542 using FIROpConversion::FIROpConversion; 543 544 mlir::LogicalResult 545 matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor, 546 mlir::ConversionPatternRewriter &rewriter) const override { 547 // given: (x + iy) - (x' + iy') 548 // result: (x - x') + i(y - y') 549 auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(), 550 rewriter, lowerTy()); 551 rewriter.replaceOp(subc, r.getResult()); 552 return success(); 553 } 554 }; 555 556 /// Inlined complex multiply 557 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> { 558 using FIROpConversion::FIROpConversion; 559 560 mlir::LogicalResult 561 matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor, 562 mlir::ConversionPatternRewriter &rewriter) const override { 563 // TODO: Can we use a call to __muldc3 ? 564 // given: (x + iy) * (x' + iy') 565 // result: (xx'-yy')+i(xy'+yx') 566 mlir::Value a = adaptor.getOperands()[0]; 567 mlir::Value b = adaptor.getOperands()[1]; 568 auto loc = mulc.getLoc(); 569 auto *ctx = mulc.getContext(); 570 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 571 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 572 mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType())); 573 mlir::Type ty = convertType(mulc.getType()); 574 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 575 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 576 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 577 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 578 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 579 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 580 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 581 auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx); 582 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 583 auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy); 584 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 585 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 586 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 587 rewriter.replaceOp(mulc, r0.getResult()); 588 return success(); 589 } 590 }; 591 592 /// Inlined complex division 593 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> { 594 using FIROpConversion::FIROpConversion; 595 596 mlir::LogicalResult 597 matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor, 598 mlir::ConversionPatternRewriter &rewriter) const override { 599 // TODO: Can we use a call to __divdc3 instead? 600 // Just generate inline code for now. 601 // given: (x + iy) / (x' + iy') 602 // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y' 603 mlir::Value a = adaptor.getOperands()[0]; 604 mlir::Value b = adaptor.getOperands()[1]; 605 auto loc = divc.getLoc(); 606 auto *ctx = divc.getContext(); 607 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 608 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 609 mlir::Type eleTy = convertType(getComplexEleTy(divc.getType())); 610 mlir::Type ty = convertType(divc.getType()); 611 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 612 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 613 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 614 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 615 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 616 auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1); 617 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 618 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 619 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 620 auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1); 621 auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1); 622 auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy); 623 auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy); 624 auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d); 625 auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d); 626 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 627 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 628 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 629 rewriter.replaceOp(divc, r0.getResult()); 630 return success(); 631 } 632 }; 633 634 /// Inlined complex negation 635 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> { 636 using FIROpConversion::FIROpConversion; 637 638 mlir::LogicalResult 639 matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor, 640 mlir::ConversionPatternRewriter &rewriter) const override { 641 // given: -(x + iy) 642 // result: -x - iy 643 auto *ctxt = neg.getContext(); 644 auto eleTy = convertType(getComplexEleTy(neg.getType())); 645 auto ty = convertType(neg.getType()); 646 auto loc = neg.getLoc(); 647 mlir::Value o0 = adaptor.getOperands()[0]; 648 auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0)); 649 auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1)); 650 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0); 651 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1); 652 auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp); 653 auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip); 654 auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0); 655 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1); 656 return success(); 657 } 658 }; 659 660 } // namespace 661 662 namespace { 663 /// Convert FIR dialect to LLVM dialect 664 /// 665 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An 666 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect. 667 /// 668 /// This pass is not complete yet. We are upstreaming it in small patches. 669 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> { 670 public: 671 mlir::ModuleOp getModule() { return getOperation(); } 672 673 void runOnOperation() override final { 674 auto *context = getModule().getContext(); 675 fir::LLVMTypeConverter typeConverter{getModule()}; 676 mlir::OwningRewritePatternList pattern(context); 677 pattern.insert<AddcOpConversion, AddrOfOpConversion, CallOpConversion, 678 DivcOpConversion, ExtractValueOpConversion, 679 HasValueOpConversion, GlobalOpConversion, 680 InsertOnRangeOpConversion, InsertValueOpConversion, 681 NegcOpConversion, MulcOpConversion, SelectOpConversion, 682 SelectRankOpConversion, SubcOpConversion, UndefOpConversion, 683 UnreachableOpConversion, ZeroOpConversion>(typeConverter); 684 mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern); 685 mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter, 686 pattern); 687 mlir::ConversionTarget target{*context}; 688 target.addLegalDialect<mlir::LLVM::LLVMDialect>(); 689 690 // required NOPs for applying a full conversion 691 target.addLegalOp<mlir::ModuleOp>(); 692 693 // apply the patterns 694 if (mlir::failed(mlir::applyFullConversion(getModule(), target, 695 std::move(pattern)))) { 696 signalPassFailure(); 697 } 698 } 699 }; 700 } // namespace 701 702 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() { 703 return std::make_unique<FIRToLLVMLowering>(); 704 } 705