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