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 static mlir::Type getComplexEleTy(mlir::Type complex) { 100 if (auto cc = complex.dyn_cast<mlir::ComplexType>()) 101 return cc.getElementType(); 102 return complex.cast<fir::ComplexType>().getElementType(); 103 } 104 105 /// convert value of from-type to value of to-type 106 struct ConvertOpConversion : public FIROpConversion<fir::ConvertOp> { 107 using FIROpConversion::FIROpConversion; 108 109 static bool isFloatingPointTy(mlir::Type ty) { 110 return ty.isa<mlir::FloatType>(); 111 } 112 113 mlir::LogicalResult 114 matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor, 115 mlir::ConversionPatternRewriter &rewriter) const override { 116 auto fromTy = convertType(convert.value().getType()); 117 auto toTy = convertType(convert.res().getType()); 118 mlir::Value op0 = adaptor.getOperands()[0]; 119 if (fromTy == toTy) { 120 rewriter.replaceOp(convert, op0); 121 return success(); 122 } 123 auto loc = convert.getLoc(); 124 auto convertFpToFp = [&](mlir::Value val, unsigned fromBits, 125 unsigned toBits, mlir::Type toTy) -> mlir::Value { 126 if (fromBits == toBits) { 127 // TODO: Converting between two floating-point representations with the 128 // same bitwidth is not allowed for now. 129 mlir::emitError(loc, 130 "cannot implicitly convert between two floating-point " 131 "representations of the same bitwidth"); 132 return {}; 133 } 134 if (fromBits > toBits) 135 return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val); 136 return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val); 137 }; 138 // Complex to complex conversion. 139 if (fir::isa_complex(convert.value().getType()) && 140 fir::isa_complex(convert.res().getType())) { 141 // Special case: handle the conversion of a complex such that both the 142 // real and imaginary parts are converted together. 143 auto zero = mlir::ArrayAttr::get(convert.getContext(), 144 rewriter.getI32IntegerAttr(0)); 145 auto one = mlir::ArrayAttr::get(convert.getContext(), 146 rewriter.getI32IntegerAttr(1)); 147 auto ty = convertType(getComplexEleTy(convert.value().getType())); 148 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, zero); 149 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, one); 150 auto nt = convertType(getComplexEleTy(convert.res().getType())); 151 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty); 152 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt); 153 auto rc = convertFpToFp(rp, fromBits, toBits, nt); 154 auto ic = convertFpToFp(ip, fromBits, toBits, nt); 155 auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy); 156 auto i1 = 157 rewriter.create<mlir::LLVM::InsertValueOp>(loc, toTy, un, rc, zero); 158 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, toTy, i1, 159 ic, one); 160 return mlir::success(); 161 } 162 // Floating point to floating point conversion. 163 if (isFloatingPointTy(fromTy)) { 164 if (isFloatingPointTy(toTy)) { 165 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy); 166 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy); 167 auto v = convertFpToFp(op0, fromBits, toBits, toTy); 168 rewriter.replaceOp(convert, v); 169 return mlir::success(); 170 } 171 if (toTy.isa<mlir::IntegerType>()) { 172 rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0); 173 return mlir::success(); 174 } 175 } else if (fromTy.isa<mlir::IntegerType>()) { 176 // Integer to integer conversion. 177 if (toTy.isa<mlir::IntegerType>()) { 178 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy); 179 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy); 180 assert(fromBits != toBits); 181 if (fromBits > toBits) { 182 rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0); 183 return mlir::success(); 184 } 185 rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0); 186 return mlir::success(); 187 } 188 // Integer to floating point conversion. 189 if (isFloatingPointTy(toTy)) { 190 rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0); 191 return mlir::success(); 192 } 193 // Integer to pointer conversion. 194 if (toTy.isa<mlir::LLVM::LLVMPointerType>()) { 195 rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0); 196 return mlir::success(); 197 } 198 } else if (fromTy.isa<mlir::LLVM::LLVMPointerType>()) { 199 // Pointer to integer conversion. 200 if (toTy.isa<mlir::IntegerType>()) { 201 rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0); 202 return mlir::success(); 203 } 204 // Pointer to pointer conversion. 205 if (toTy.isa<mlir::LLVM::LLVMPointerType>()) { 206 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0); 207 return mlir::success(); 208 } 209 } 210 return emitError(loc) << "cannot convert " << fromTy << " to " << toTy; 211 } 212 }; 213 214 /// Lower `fir.has_value` operation to `llvm.return` operation. 215 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> { 216 using FIROpConversion::FIROpConversion; 217 218 mlir::LogicalResult 219 matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor, 220 mlir::ConversionPatternRewriter &rewriter) const override { 221 rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands()); 222 return success(); 223 } 224 }; 225 226 /// Lower `fir.global` operation to `llvm.global` operation. 227 /// `fir.insert_on_range` operations are replaced with constant dense attribute 228 /// if they are applied on the full range. 229 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> { 230 using FIROpConversion::FIROpConversion; 231 232 mlir::LogicalResult 233 matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor, 234 mlir::ConversionPatternRewriter &rewriter) const override { 235 auto tyAttr = convertType(global.getType()); 236 if (global.getType().isa<fir::BoxType>()) 237 tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType(); 238 auto loc = global.getLoc(); 239 mlir::Attribute initAttr{}; 240 if (global.initVal()) 241 initAttr = global.initVal().getValue(); 242 auto linkage = convertLinkage(global.linkName()); 243 auto isConst = global.constant().hasValue(); 244 auto g = rewriter.create<mlir::LLVM::GlobalOp>( 245 loc, tyAttr, isConst, linkage, global.sym_name(), initAttr); 246 auto &gr = g.getInitializerRegion(); 247 rewriter.inlineRegionBefore(global.region(), gr, gr.end()); 248 if (!gr.empty()) { 249 // Replace insert_on_range with a constant dense attribute if the 250 // initialization is on the full range. 251 auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>(); 252 for (auto insertOp : insertOnRangeOps) { 253 if (isFullRange(insertOp.coor(), insertOp.getType())) { 254 auto seqTyAttr = convertType(insertOp.getType()); 255 auto *op = insertOp.val().getDefiningOp(); 256 auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op); 257 if (!constant) { 258 auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op); 259 if (!convertOp) 260 continue; 261 constant = cast<mlir::arith::ConstantOp>( 262 convertOp.value().getDefiningOp()); 263 } 264 mlir::Type vecType = mlir::VectorType::get( 265 insertOp.getType().getShape(), constant.getType()); 266 auto denseAttr = mlir::DenseElementsAttr::get( 267 vecType.cast<ShapedType>(), constant.value()); 268 rewriter.setInsertionPointAfter(insertOp); 269 rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>( 270 insertOp, seqTyAttr, denseAttr); 271 } 272 } 273 } 274 rewriter.eraseOp(global); 275 return success(); 276 } 277 278 bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const { 279 auto extents = seqTy.getShape(); 280 if (indexes.size() / 2 != extents.size()) 281 return false; 282 for (unsigned i = 0; i < indexes.size(); i += 2) { 283 if (indexes[i].cast<IntegerAttr>().getInt() != 0) 284 return false; 285 if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1) 286 return false; 287 } 288 return true; 289 } 290 291 // TODO: String comparaison should be avoided. Replace linkName with an 292 // enumeration. 293 mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const { 294 if (optLinkage.hasValue()) { 295 auto name = optLinkage.getValue(); 296 if (name == "internal") 297 return mlir::LLVM::Linkage::Internal; 298 if (name == "linkonce") 299 return mlir::LLVM::Linkage::Linkonce; 300 if (name == "common") 301 return mlir::LLVM::Linkage::Common; 302 if (name == "weak") 303 return mlir::LLVM::Linkage::Weak; 304 } 305 return mlir::LLVM::Linkage::External; 306 } 307 }; 308 309 template <typename OP> 310 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select, 311 typename OP::Adaptor adaptor, 312 mlir::ConversionPatternRewriter &rewriter) { 313 unsigned conds = select.getNumConditions(); 314 auto cases = select.getCases().getValue(); 315 mlir::Value selector = adaptor.selector(); 316 auto loc = select.getLoc(); 317 assert(conds > 0 && "select must have cases"); 318 319 llvm::SmallVector<mlir::Block *> destinations; 320 llvm::SmallVector<mlir::ValueRange> destinationsOperands; 321 mlir::Block *defaultDestination; 322 mlir::ValueRange defaultOperands; 323 llvm::SmallVector<int32_t> caseValues; 324 325 for (unsigned t = 0; t != conds; ++t) { 326 mlir::Block *dest = select.getSuccessor(t); 327 auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t); 328 const mlir::Attribute &attr = cases[t]; 329 if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) { 330 destinations.push_back(dest); 331 destinationsOperands.push_back(destOps.hasValue() ? *destOps 332 : ValueRange()); 333 caseValues.push_back(intAttr.getInt()); 334 continue; 335 } 336 assert(attr.template dyn_cast_or_null<mlir::UnitAttr>()); 337 assert((t + 1 == conds) && "unit must be last"); 338 defaultDestination = dest; 339 defaultOperands = destOps.hasValue() ? *destOps : ValueRange(); 340 } 341 342 // LLVM::SwitchOp takes a i32 type for the selector. 343 if (select.getSelector().getType() != rewriter.getI32Type()) 344 selector = 345 rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector); 346 347 rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>( 348 select, selector, 349 /*defaultDestination=*/defaultDestination, 350 /*defaultOperands=*/defaultOperands, 351 /*caseValues=*/caseValues, 352 /*caseDestinations=*/destinations, 353 /*caseOperands=*/destinationsOperands, 354 /*branchWeights=*/ArrayRef<int32_t>()); 355 } 356 357 /// conversion of fir::SelectOp to an if-then-else ladder 358 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> { 359 using FIROpConversion::FIROpConversion; 360 361 mlir::LogicalResult 362 matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor, 363 mlir::ConversionPatternRewriter &rewriter) const override { 364 selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter); 365 return success(); 366 } 367 }; 368 369 /// conversion of fir::SelectRankOp to an if-then-else ladder 370 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> { 371 using FIROpConversion::FIROpConversion; 372 373 mlir::LogicalResult 374 matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor, 375 mlir::ConversionPatternRewriter &rewriter) const override { 376 selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter); 377 return success(); 378 } 379 }; 380 381 // convert to LLVM IR dialect `undef` 382 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> { 383 using FIROpConversion::FIROpConversion; 384 385 mlir::LogicalResult 386 matchAndRewrite(fir::UndefOp undef, OpAdaptor, 387 mlir::ConversionPatternRewriter &rewriter) const override { 388 rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>( 389 undef, convertType(undef.getType())); 390 return success(); 391 } 392 }; 393 394 // convert to LLVM IR dialect `unreachable` 395 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> { 396 using FIROpConversion::FIROpConversion; 397 398 mlir::LogicalResult 399 matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor, 400 mlir::ConversionPatternRewriter &rewriter) const override { 401 rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach); 402 return success(); 403 } 404 }; 405 406 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> { 407 using FIROpConversion::FIROpConversion; 408 409 mlir::LogicalResult 410 matchAndRewrite(fir::ZeroOp zero, OpAdaptor, 411 mlir::ConversionPatternRewriter &rewriter) const override { 412 auto ty = convertType(zero.getType()); 413 if (ty.isa<mlir::LLVM::LLVMPointerType>()) { 414 rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty); 415 } else if (ty.isa<mlir::IntegerType>()) { 416 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 417 zero, ty, mlir::IntegerAttr::get(zero.getType(), 0)); 418 } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) { 419 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 420 zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0)); 421 } else { 422 // TODO: create ConstantAggregateZero for FIR aggregate/array types. 423 return rewriter.notifyMatchFailure( 424 zero, 425 "conversion of fir.zero with aggregate type not implemented yet"); 426 } 427 return success(); 428 } 429 }; 430 431 // Code shared between insert_value and extract_value Ops. 432 struct ValueOpCommon { 433 // Translate the arguments pertaining to any multidimensional array to 434 // row-major order for LLVM-IR. 435 static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs, 436 mlir::Type ty) { 437 assert(ty && "type is null"); 438 const auto end = attrs.size(); 439 for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) { 440 if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 441 const auto dim = getDimension(seq); 442 if (dim > 1) { 443 auto ub = std::min(i + dim, end); 444 std::reverse(attrs.begin() + i, attrs.begin() + ub); 445 i += dim - 1; 446 } 447 ty = getArrayElementType(seq); 448 } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) { 449 ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()]; 450 } else { 451 llvm_unreachable("index into invalid type"); 452 } 453 } 454 } 455 456 static llvm::SmallVector<mlir::Attribute> 457 collectIndices(mlir::ConversionPatternRewriter &rewriter, 458 mlir::ArrayAttr arrAttr) { 459 llvm::SmallVector<mlir::Attribute> attrs; 460 for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) { 461 if (i->isa<mlir::IntegerAttr>()) { 462 attrs.push_back(*i); 463 } else { 464 auto fieldName = i->cast<mlir::StringAttr>().getValue(); 465 ++i; 466 auto ty = i->cast<mlir::TypeAttr>().getValue(); 467 auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName); 468 attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index)); 469 } 470 } 471 return attrs; 472 } 473 474 private: 475 static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) { 476 unsigned result = 1; 477 for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>(); 478 eleTy; 479 eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>()) 480 ++result; 481 return result; 482 } 483 484 static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) { 485 auto eleTy = ty.getElementType(); 486 while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>()) 487 eleTy = arrTy.getElementType(); 488 return eleTy; 489 } 490 }; 491 492 /// Extract a subobject value from an ssa-value of aggregate type 493 struct ExtractValueOpConversion 494 : public FIROpAndTypeConversion<fir::ExtractValueOp>, 495 public ValueOpCommon { 496 using FIROpAndTypeConversion::FIROpAndTypeConversion; 497 498 mlir::LogicalResult 499 doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor, 500 mlir::ConversionPatternRewriter &rewriter) const override { 501 auto attrs = collectIndices(rewriter, extractVal.coor()); 502 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 503 auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs); 504 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>( 505 extractVal, ty, adaptor.getOperands()[0], position); 506 return success(); 507 } 508 }; 509 510 /// InsertValue is the generalized instruction for the composition of new 511 /// aggregate type values. 512 struct InsertValueOpConversion 513 : public FIROpAndTypeConversion<fir::InsertValueOp>, 514 public ValueOpCommon { 515 using FIROpAndTypeConversion::FIROpAndTypeConversion; 516 517 mlir::LogicalResult 518 doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor, 519 mlir::ConversionPatternRewriter &rewriter) const override { 520 auto attrs = collectIndices(rewriter, insertVal.coor()); 521 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 522 auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs); 523 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 524 insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1], 525 position); 526 return success(); 527 } 528 }; 529 530 /// InsertOnRange inserts a value into a sequence over a range of offsets. 531 struct InsertOnRangeOpConversion 532 : public FIROpAndTypeConversion<fir::InsertOnRangeOp> { 533 using FIROpAndTypeConversion::FIROpAndTypeConversion; 534 535 // Increments an array of subscripts in a row major fasion. 536 void incrementSubscripts(const SmallVector<uint64_t> &dims, 537 SmallVector<uint64_t> &subscripts) const { 538 for (size_t i = dims.size(); i > 0; --i) { 539 if (++subscripts[i - 1] < dims[i - 1]) { 540 return; 541 } 542 subscripts[i - 1] = 0; 543 } 544 } 545 546 mlir::LogicalResult 547 doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor, 548 mlir::ConversionPatternRewriter &rewriter) const override { 549 550 llvm::SmallVector<uint64_t> dims; 551 auto type = adaptor.getOperands()[0].getType(); 552 553 // Iteratively extract the array dimensions from the type. 554 while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 555 dims.push_back(t.getNumElements()); 556 type = t.getElementType(); 557 } 558 559 SmallVector<uint64_t> lBounds; 560 SmallVector<uint64_t> uBounds; 561 562 // Extract integer value from the attribute 563 SmallVector<int64_t> coordinates = llvm::to_vector<4>( 564 llvm::map_range(range.coor(), [](Attribute a) -> int64_t { 565 return a.cast<IntegerAttr>().getInt(); 566 })); 567 568 // Unzip the upper and lower bound and convert to a row major format. 569 for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) { 570 uBounds.push_back(*i++); 571 lBounds.push_back(*i); 572 } 573 574 auto &subscripts = lBounds; 575 auto loc = range.getLoc(); 576 mlir::Value lastOp = adaptor.getOperands()[0]; 577 mlir::Value insertVal = adaptor.getOperands()[1]; 578 579 auto i64Ty = rewriter.getI64Type(); 580 while (subscripts != uBounds) { 581 // Convert uint64_t's to Attribute's. 582 SmallVector<mlir::Attribute> subscriptAttrs; 583 for (const auto &subscript : subscripts) 584 subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript)); 585 lastOp = rewriter.create<mlir::LLVM::InsertValueOp>( 586 loc, ty, lastOp, insertVal, 587 ArrayAttr::get(range.getContext(), subscriptAttrs)); 588 589 incrementSubscripts(dims, subscripts); 590 } 591 592 // Convert uint64_t's to Attribute's. 593 SmallVector<mlir::Attribute> subscriptAttrs; 594 for (const auto &subscript : subscripts) 595 subscriptAttrs.push_back( 596 IntegerAttr::get(rewriter.getI64Type(), subscript)); 597 mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs); 598 599 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 600 range, ty, lastOp, insertVal, 601 ArrayAttr::get(range.getContext(), arrayRef)); 602 603 return success(); 604 } 605 }; 606 607 // 608 // Primitive operations on Complex types 609 // 610 611 /// Generate inline code for complex addition/subtraction 612 template <typename LLVMOP, typename OPTY> 613 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds, 614 mlir::ConversionPatternRewriter &rewriter, 615 fir::LLVMTypeConverter &lowering) { 616 mlir::Value a = opnds[0]; 617 mlir::Value b = opnds[1]; 618 auto loc = sumop.getLoc(); 619 auto ctx = sumop.getContext(); 620 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 621 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 622 mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType())); 623 mlir::Type ty = lowering.convertType(sumop.getType()); 624 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 625 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 626 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 627 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 628 auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1); 629 auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1); 630 auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 631 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0); 632 return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1); 633 } 634 635 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> { 636 using FIROpConversion::FIROpConversion; 637 638 mlir::LogicalResult 639 matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor, 640 mlir::ConversionPatternRewriter &rewriter) const override { 641 // given: (x + iy) + (x' + iy') 642 // result: (x + x') + i(y + y') 643 auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(), 644 rewriter, lowerTy()); 645 rewriter.replaceOp(addc, r.getResult()); 646 return success(); 647 } 648 }; 649 650 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> { 651 using FIROpConversion::FIROpConversion; 652 653 mlir::LogicalResult 654 matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor, 655 mlir::ConversionPatternRewriter &rewriter) const override { 656 // given: (x + iy) - (x' + iy') 657 // result: (x - x') + i(y - y') 658 auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(), 659 rewriter, lowerTy()); 660 rewriter.replaceOp(subc, r.getResult()); 661 return success(); 662 } 663 }; 664 665 /// Inlined complex multiply 666 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> { 667 using FIROpConversion::FIROpConversion; 668 669 mlir::LogicalResult 670 matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor, 671 mlir::ConversionPatternRewriter &rewriter) const override { 672 // TODO: Can we use a call to __muldc3 ? 673 // given: (x + iy) * (x' + iy') 674 // result: (xx'-yy')+i(xy'+yx') 675 mlir::Value a = adaptor.getOperands()[0]; 676 mlir::Value b = adaptor.getOperands()[1]; 677 auto loc = mulc.getLoc(); 678 auto *ctx = mulc.getContext(); 679 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 680 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 681 mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType())); 682 mlir::Type ty = convertType(mulc.getType()); 683 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 684 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 685 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 686 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 687 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 688 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 689 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 690 auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx); 691 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 692 auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy); 693 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 694 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 695 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 696 rewriter.replaceOp(mulc, r0.getResult()); 697 return success(); 698 } 699 }; 700 701 /// Inlined complex division 702 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> { 703 using FIROpConversion::FIROpConversion; 704 705 mlir::LogicalResult 706 matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor, 707 mlir::ConversionPatternRewriter &rewriter) const override { 708 // TODO: Can we use a call to __divdc3 instead? 709 // Just generate inline code for now. 710 // given: (x + iy) / (x' + iy') 711 // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y' 712 mlir::Value a = adaptor.getOperands()[0]; 713 mlir::Value b = adaptor.getOperands()[1]; 714 auto loc = divc.getLoc(); 715 auto *ctx = divc.getContext(); 716 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 717 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 718 mlir::Type eleTy = convertType(getComplexEleTy(divc.getType())); 719 mlir::Type ty = convertType(divc.getType()); 720 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 721 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 722 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 723 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 724 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 725 auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1); 726 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 727 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 728 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 729 auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1); 730 auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1); 731 auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy); 732 auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy); 733 auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d); 734 auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d); 735 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 736 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 737 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 738 rewriter.replaceOp(divc, r0.getResult()); 739 return success(); 740 } 741 }; 742 743 /// Inlined complex negation 744 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> { 745 using FIROpConversion::FIROpConversion; 746 747 mlir::LogicalResult 748 matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor, 749 mlir::ConversionPatternRewriter &rewriter) const override { 750 // given: -(x + iy) 751 // result: -x - iy 752 auto *ctxt = neg.getContext(); 753 auto eleTy = convertType(getComplexEleTy(neg.getType())); 754 auto ty = convertType(neg.getType()); 755 auto loc = neg.getLoc(); 756 mlir::Value o0 = adaptor.getOperands()[0]; 757 auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0)); 758 auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1)); 759 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0); 760 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1); 761 auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp); 762 auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip); 763 auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0); 764 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1); 765 return success(); 766 } 767 }; 768 769 } // namespace 770 771 namespace { 772 /// Convert FIR dialect to LLVM dialect 773 /// 774 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An 775 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect. 776 /// 777 /// This pass is not complete yet. We are upstreaming it in small patches. 778 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> { 779 public: 780 mlir::ModuleOp getModule() { return getOperation(); } 781 782 void runOnOperation() override final { 783 auto mod = getModule(); 784 if (!forcedTargetTriple.empty()) { 785 fir::setTargetTriple(mod, forcedTargetTriple); 786 } 787 788 auto *context = getModule().getContext(); 789 fir::LLVMTypeConverter typeConverter{getModule()}; 790 mlir::OwningRewritePatternList pattern(context); 791 pattern 792 .insert<AddcOpConversion, AddrOfOpConversion, CallOpConversion, 793 ConvertOpConversion, DivcOpConversion, ExtractValueOpConversion, 794 HasValueOpConversion, GlobalOpConversion, 795 InsertOnRangeOpConversion, InsertValueOpConversion, 796 NegcOpConversion, MulcOpConversion, SelectOpConversion, 797 SelectRankOpConversion, SubcOpConversion, UndefOpConversion, 798 UnreachableOpConversion, ZeroOpConversion>(typeConverter); 799 mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern); 800 mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter, 801 pattern); 802 mlir::ConversionTarget target{*context}; 803 target.addLegalDialect<mlir::LLVM::LLVMDialect>(); 804 805 // required NOPs for applying a full conversion 806 target.addLegalOp<mlir::ModuleOp>(); 807 808 // apply the patterns 809 if (mlir::failed(mlir::applyFullConversion(getModule(), target, 810 std::move(pattern)))) { 811 signalPassFailure(); 812 } 813 } 814 }; 815 } // namespace 816 817 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() { 818 return std::make_unique<FIRToLLVMLowering>(); 819 } 820