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.dispatch` operation. A virtual call to a method in a dispatch 564 /// table. 565 struct DispatchOpConversion : public FIROpConversion<fir::DispatchOp> { 566 using FIROpConversion::FIROpConversion; 567 568 mlir::LogicalResult 569 matchAndRewrite(fir::DispatchOp dispatch, OpAdaptor adaptor, 570 mlir::ConversionPatternRewriter &rewriter) const override { 571 return rewriter.notifyMatchFailure( 572 dispatch, "fir.dispatch codegen is not implemented yet"); 573 } 574 }; 575 576 /// Lower `fir.dispatch_table` operation. The dispatch table for a Fortran 577 /// derived type. 578 struct DispatchTableOpConversion 579 : public FIROpConversion<fir::DispatchTableOp> { 580 using FIROpConversion::FIROpConversion; 581 582 mlir::LogicalResult 583 matchAndRewrite(fir::DispatchTableOp dispTab, OpAdaptor adaptor, 584 mlir::ConversionPatternRewriter &rewriter) const override { 585 return rewriter.notifyMatchFailure( 586 dispTab, "fir.dispatch_table codegen is not implemented yet"); 587 } 588 }; 589 590 /// Lower `fir.dt_entry` operation. An entry in a dispatch table; binds a 591 /// method-name to a function. 592 struct DTEntryOpConversion : public FIROpConversion<fir::DTEntryOp> { 593 using FIROpConversion::FIROpConversion; 594 595 mlir::LogicalResult 596 matchAndRewrite(fir::DTEntryOp dtEnt, OpAdaptor adaptor, 597 mlir::ConversionPatternRewriter &rewriter) const override { 598 return rewriter.notifyMatchFailure( 599 dtEnt, "fir.dt_entry codegen is not implemented yet"); 600 } 601 }; 602 603 /// Lower `fir.has_value` operation to `llvm.return` operation. 604 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> { 605 using FIROpConversion::FIROpConversion; 606 607 mlir::LogicalResult 608 matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor, 609 mlir::ConversionPatternRewriter &rewriter) const override { 610 rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands()); 611 return success(); 612 } 613 }; 614 615 /// Lower `fir.global` operation to `llvm.global` operation. 616 /// `fir.insert_on_range` operations are replaced with constant dense attribute 617 /// if they are applied on the full range. 618 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> { 619 using FIROpConversion::FIROpConversion; 620 621 mlir::LogicalResult 622 matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor, 623 mlir::ConversionPatternRewriter &rewriter) const override { 624 auto tyAttr = convertType(global.getType()); 625 if (global.getType().isa<fir::BoxType>()) 626 tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType(); 627 auto loc = global.getLoc(); 628 mlir::Attribute initAttr{}; 629 if (global.initVal()) 630 initAttr = global.initVal().getValue(); 631 auto linkage = convertLinkage(global.linkName()); 632 auto isConst = global.constant().hasValue(); 633 auto g = rewriter.create<mlir::LLVM::GlobalOp>( 634 loc, tyAttr, isConst, linkage, global.sym_name(), initAttr); 635 auto &gr = g.getInitializerRegion(); 636 rewriter.inlineRegionBefore(global.region(), gr, gr.end()); 637 if (!gr.empty()) { 638 // Replace insert_on_range with a constant dense attribute if the 639 // initialization is on the full range. 640 auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>(); 641 for (auto insertOp : insertOnRangeOps) { 642 if (isFullRange(insertOp.coor(), insertOp.getType())) { 643 auto seqTyAttr = convertType(insertOp.getType()); 644 auto *op = insertOp.val().getDefiningOp(); 645 auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op); 646 if (!constant) { 647 auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op); 648 if (!convertOp) 649 continue; 650 constant = cast<mlir::arith::ConstantOp>( 651 convertOp.value().getDefiningOp()); 652 } 653 mlir::Type vecType = mlir::VectorType::get( 654 insertOp.getType().getShape(), constant.getType()); 655 auto denseAttr = mlir::DenseElementsAttr::get( 656 vecType.cast<ShapedType>(), constant.value()); 657 rewriter.setInsertionPointAfter(insertOp); 658 rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>( 659 insertOp, seqTyAttr, denseAttr); 660 } 661 } 662 } 663 rewriter.eraseOp(global); 664 return success(); 665 } 666 667 bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const { 668 auto extents = seqTy.getShape(); 669 if (indexes.size() / 2 != extents.size()) 670 return false; 671 for (unsigned i = 0; i < indexes.size(); i += 2) { 672 if (indexes[i].cast<IntegerAttr>().getInt() != 0) 673 return false; 674 if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1) 675 return false; 676 } 677 return true; 678 } 679 680 // TODO: String comparaison should be avoided. Replace linkName with an 681 // enumeration. 682 mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const { 683 if (optLinkage.hasValue()) { 684 auto name = optLinkage.getValue(); 685 if (name == "internal") 686 return mlir::LLVM::Linkage::Internal; 687 if (name == "linkonce") 688 return mlir::LLVM::Linkage::Linkonce; 689 if (name == "common") 690 return mlir::LLVM::Linkage::Common; 691 if (name == "weak") 692 return mlir::LLVM::Linkage::Weak; 693 } 694 return mlir::LLVM::Linkage::External; 695 } 696 }; 697 698 template <typename OP> 699 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select, 700 typename OP::Adaptor adaptor, 701 mlir::ConversionPatternRewriter &rewriter) { 702 unsigned conds = select.getNumConditions(); 703 auto cases = select.getCases().getValue(); 704 mlir::Value selector = adaptor.selector(); 705 auto loc = select.getLoc(); 706 assert(conds > 0 && "select must have cases"); 707 708 llvm::SmallVector<mlir::Block *> destinations; 709 llvm::SmallVector<mlir::ValueRange> destinationsOperands; 710 mlir::Block *defaultDestination; 711 mlir::ValueRange defaultOperands; 712 llvm::SmallVector<int32_t> caseValues; 713 714 for (unsigned t = 0; t != conds; ++t) { 715 mlir::Block *dest = select.getSuccessor(t); 716 auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t); 717 const mlir::Attribute &attr = cases[t]; 718 if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) { 719 destinations.push_back(dest); 720 destinationsOperands.push_back(destOps.hasValue() ? *destOps 721 : ValueRange()); 722 caseValues.push_back(intAttr.getInt()); 723 continue; 724 } 725 assert(attr.template dyn_cast_or_null<mlir::UnitAttr>()); 726 assert((t + 1 == conds) && "unit must be last"); 727 defaultDestination = dest; 728 defaultOperands = destOps.hasValue() ? *destOps : ValueRange(); 729 } 730 731 // LLVM::SwitchOp takes a i32 type for the selector. 732 if (select.getSelector().getType() != rewriter.getI32Type()) 733 selector = 734 rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector); 735 736 rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>( 737 select, selector, 738 /*defaultDestination=*/defaultDestination, 739 /*defaultOperands=*/defaultOperands, 740 /*caseValues=*/caseValues, 741 /*caseDestinations=*/destinations, 742 /*caseOperands=*/destinationsOperands, 743 /*branchWeights=*/ArrayRef<int32_t>()); 744 } 745 746 /// conversion of fir::SelectOp to an if-then-else ladder 747 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> { 748 using FIROpConversion::FIROpConversion; 749 750 mlir::LogicalResult 751 matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor, 752 mlir::ConversionPatternRewriter &rewriter) const override { 753 selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter); 754 return success(); 755 } 756 }; 757 758 /// `fir.load` --> `llvm.load` 759 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> { 760 using FIROpConversion::FIROpConversion; 761 762 mlir::LogicalResult 763 matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor, 764 mlir::ConversionPatternRewriter &rewriter) const override { 765 // fir.box is a special case because it is considered as an ssa values in 766 // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box> 767 // and fir.box end up being the same llvm types and loading a 768 // fir.ref<fir.box> is actually a no op in LLVM. 769 if (load.getType().isa<fir::BoxType>()) { 770 rewriter.replaceOp(load, adaptor.getOperands()[0]); 771 } else { 772 mlir::Type ty = convertType(load.getType()); 773 ArrayRef<NamedAttribute> at = load->getAttrs(); 774 rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>( 775 load, ty, adaptor.getOperands(), at); 776 } 777 return success(); 778 } 779 }; 780 781 /// conversion of fir::SelectRankOp to an if-then-else ladder 782 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> { 783 using FIROpConversion::FIROpConversion; 784 785 mlir::LogicalResult 786 matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor, 787 mlir::ConversionPatternRewriter &rewriter) const override { 788 selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter); 789 return success(); 790 } 791 }; 792 793 /// `fir.store` --> `llvm.store` 794 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> { 795 using FIROpConversion::FIROpConversion; 796 797 mlir::LogicalResult 798 matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor, 799 mlir::ConversionPatternRewriter &rewriter) const override { 800 if (store.value().getType().isa<fir::BoxType>()) { 801 // fir.box value is actually in memory, load it first before storing it. 802 mlir::Location loc = store.getLoc(); 803 mlir::Type boxPtrTy = adaptor.getOperands()[0].getType(); 804 auto val = rewriter.create<mlir::LLVM::LoadOp>( 805 loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(), 806 adaptor.getOperands()[0]); 807 rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 808 store, val, adaptor.getOperands()[1]); 809 } else { 810 rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 811 store, adaptor.getOperands()[0], adaptor.getOperands()[1]); 812 } 813 return success(); 814 } 815 }; 816 817 /// convert to LLVM IR dialect `undef` 818 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> { 819 using FIROpConversion::FIROpConversion; 820 821 mlir::LogicalResult 822 matchAndRewrite(fir::UndefOp undef, OpAdaptor, 823 mlir::ConversionPatternRewriter &rewriter) const override { 824 rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>( 825 undef, convertType(undef.getType())); 826 return success(); 827 } 828 }; 829 830 /// `fir.unreachable` --> `llvm.unreachable` 831 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> { 832 using FIROpConversion::FIROpConversion; 833 834 mlir::LogicalResult 835 matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor, 836 mlir::ConversionPatternRewriter &rewriter) const override { 837 rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach); 838 return success(); 839 } 840 }; 841 842 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> { 843 using FIROpConversion::FIROpConversion; 844 845 mlir::LogicalResult 846 matchAndRewrite(fir::ZeroOp zero, OpAdaptor, 847 mlir::ConversionPatternRewriter &rewriter) const override { 848 auto ty = convertType(zero.getType()); 849 if (ty.isa<mlir::LLVM::LLVMPointerType>()) { 850 rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty); 851 } else if (ty.isa<mlir::IntegerType>()) { 852 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 853 zero, ty, mlir::IntegerAttr::get(zero.getType(), 0)); 854 } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) { 855 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 856 zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0)); 857 } else { 858 // TODO: create ConstantAggregateZero for FIR aggregate/array types. 859 return rewriter.notifyMatchFailure( 860 zero, 861 "conversion of fir.zero with aggregate type not implemented yet"); 862 } 863 return success(); 864 } 865 }; 866 867 // Code shared between insert_value and extract_value Ops. 868 struct ValueOpCommon { 869 // Translate the arguments pertaining to any multidimensional array to 870 // row-major order for LLVM-IR. 871 static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs, 872 mlir::Type ty) { 873 assert(ty && "type is null"); 874 const auto end = attrs.size(); 875 for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) { 876 if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 877 const auto dim = getDimension(seq); 878 if (dim > 1) { 879 auto ub = std::min(i + dim, end); 880 std::reverse(attrs.begin() + i, attrs.begin() + ub); 881 i += dim - 1; 882 } 883 ty = getArrayElementType(seq); 884 } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) { 885 ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()]; 886 } else { 887 llvm_unreachable("index into invalid type"); 888 } 889 } 890 } 891 892 static llvm::SmallVector<mlir::Attribute> 893 collectIndices(mlir::ConversionPatternRewriter &rewriter, 894 mlir::ArrayAttr arrAttr) { 895 llvm::SmallVector<mlir::Attribute> attrs; 896 for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) { 897 if (i->isa<mlir::IntegerAttr>()) { 898 attrs.push_back(*i); 899 } else { 900 auto fieldName = i->cast<mlir::StringAttr>().getValue(); 901 ++i; 902 auto ty = i->cast<mlir::TypeAttr>().getValue(); 903 auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName); 904 attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index)); 905 } 906 } 907 return attrs; 908 } 909 910 private: 911 static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) { 912 unsigned result = 1; 913 for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>(); 914 eleTy; 915 eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>()) 916 ++result; 917 return result; 918 } 919 920 static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) { 921 auto eleTy = ty.getElementType(); 922 while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>()) 923 eleTy = arrTy.getElementType(); 924 return eleTy; 925 } 926 }; 927 928 /// Extract a subobject value from an ssa-value of aggregate type 929 struct ExtractValueOpConversion 930 : public FIROpAndTypeConversion<fir::ExtractValueOp>, 931 public ValueOpCommon { 932 using FIROpAndTypeConversion::FIROpAndTypeConversion; 933 934 mlir::LogicalResult 935 doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor, 936 mlir::ConversionPatternRewriter &rewriter) const override { 937 auto attrs = collectIndices(rewriter, extractVal.coor()); 938 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 939 auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs); 940 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>( 941 extractVal, ty, adaptor.getOperands()[0], position); 942 return success(); 943 } 944 }; 945 946 /// InsertValue is the generalized instruction for the composition of new 947 /// aggregate type values. 948 struct InsertValueOpConversion 949 : public FIROpAndTypeConversion<fir::InsertValueOp>, 950 public ValueOpCommon { 951 using FIROpAndTypeConversion::FIROpAndTypeConversion; 952 953 mlir::LogicalResult 954 doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor, 955 mlir::ConversionPatternRewriter &rewriter) const override { 956 auto attrs = collectIndices(rewriter, insertVal.coor()); 957 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 958 auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs); 959 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 960 insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1], 961 position); 962 return success(); 963 } 964 }; 965 966 /// InsertOnRange inserts a value into a sequence over a range of offsets. 967 struct InsertOnRangeOpConversion 968 : public FIROpAndTypeConversion<fir::InsertOnRangeOp> { 969 using FIROpAndTypeConversion::FIROpAndTypeConversion; 970 971 // Increments an array of subscripts in a row major fasion. 972 void incrementSubscripts(const SmallVector<uint64_t> &dims, 973 SmallVector<uint64_t> &subscripts) const { 974 for (size_t i = dims.size(); i > 0; --i) { 975 if (++subscripts[i - 1] < dims[i - 1]) { 976 return; 977 } 978 subscripts[i - 1] = 0; 979 } 980 } 981 982 mlir::LogicalResult 983 doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor, 984 mlir::ConversionPatternRewriter &rewriter) const override { 985 986 llvm::SmallVector<uint64_t> dims; 987 auto type = adaptor.getOperands()[0].getType(); 988 989 // Iteratively extract the array dimensions from the type. 990 while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 991 dims.push_back(t.getNumElements()); 992 type = t.getElementType(); 993 } 994 995 SmallVector<uint64_t> lBounds; 996 SmallVector<uint64_t> uBounds; 997 998 // Extract integer value from the attribute 999 SmallVector<int64_t> coordinates = llvm::to_vector<4>( 1000 llvm::map_range(range.coor(), [](Attribute a) -> int64_t { 1001 return a.cast<IntegerAttr>().getInt(); 1002 })); 1003 1004 // Unzip the upper and lower bound and convert to a row major format. 1005 for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) { 1006 uBounds.push_back(*i++); 1007 lBounds.push_back(*i); 1008 } 1009 1010 auto &subscripts = lBounds; 1011 auto loc = range.getLoc(); 1012 mlir::Value lastOp = adaptor.getOperands()[0]; 1013 mlir::Value insertVal = adaptor.getOperands()[1]; 1014 1015 auto i64Ty = rewriter.getI64Type(); 1016 while (subscripts != uBounds) { 1017 // Convert uint64_t's to Attribute's. 1018 SmallVector<mlir::Attribute> subscriptAttrs; 1019 for (const auto &subscript : subscripts) 1020 subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript)); 1021 lastOp = rewriter.create<mlir::LLVM::InsertValueOp>( 1022 loc, ty, lastOp, insertVal, 1023 ArrayAttr::get(range.getContext(), subscriptAttrs)); 1024 1025 incrementSubscripts(dims, subscripts); 1026 } 1027 1028 // Convert uint64_t's to Attribute's. 1029 SmallVector<mlir::Attribute> subscriptAttrs; 1030 for (const auto &subscript : subscripts) 1031 subscriptAttrs.push_back( 1032 IntegerAttr::get(rewriter.getI64Type(), subscript)); 1033 mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs); 1034 1035 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1036 range, ty, lastOp, insertVal, 1037 ArrayAttr::get(range.getContext(), arrayRef)); 1038 1039 return success(); 1040 } 1041 }; 1042 1043 // 1044 // Primitive operations on Complex types 1045 // 1046 1047 /// Generate inline code for complex addition/subtraction 1048 template <typename LLVMOP, typename OPTY> 1049 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds, 1050 mlir::ConversionPatternRewriter &rewriter, 1051 fir::LLVMTypeConverter &lowering) { 1052 mlir::Value a = opnds[0]; 1053 mlir::Value b = opnds[1]; 1054 auto loc = sumop.getLoc(); 1055 auto ctx = sumop.getContext(); 1056 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1057 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1058 mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType())); 1059 mlir::Type ty = lowering.convertType(sumop.getType()); 1060 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1061 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1062 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1063 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1064 auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1); 1065 auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1); 1066 auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1067 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0); 1068 return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1); 1069 } 1070 1071 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> { 1072 using FIROpConversion::FIROpConversion; 1073 1074 mlir::LogicalResult 1075 matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor, 1076 mlir::ConversionPatternRewriter &rewriter) const override { 1077 // given: (x + iy) + (x' + iy') 1078 // result: (x + x') + i(y + y') 1079 auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(), 1080 rewriter, lowerTy()); 1081 rewriter.replaceOp(addc, r.getResult()); 1082 return success(); 1083 } 1084 }; 1085 1086 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> { 1087 using FIROpConversion::FIROpConversion; 1088 1089 mlir::LogicalResult 1090 matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor, 1091 mlir::ConversionPatternRewriter &rewriter) const override { 1092 // given: (x + iy) - (x' + iy') 1093 // result: (x - x') + i(y - y') 1094 auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(), 1095 rewriter, lowerTy()); 1096 rewriter.replaceOp(subc, r.getResult()); 1097 return success(); 1098 } 1099 }; 1100 1101 /// Inlined complex multiply 1102 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> { 1103 using FIROpConversion::FIROpConversion; 1104 1105 mlir::LogicalResult 1106 matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor, 1107 mlir::ConversionPatternRewriter &rewriter) const override { 1108 // TODO: Can we use a call to __muldc3 ? 1109 // given: (x + iy) * (x' + iy') 1110 // result: (xx'-yy')+i(xy'+yx') 1111 mlir::Value a = adaptor.getOperands()[0]; 1112 mlir::Value b = adaptor.getOperands()[1]; 1113 auto loc = mulc.getLoc(); 1114 auto *ctx = mulc.getContext(); 1115 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1116 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1117 mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType())); 1118 mlir::Type ty = convertType(mulc.getType()); 1119 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1120 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1121 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1122 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1123 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 1124 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 1125 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 1126 auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx); 1127 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 1128 auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy); 1129 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1130 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 1131 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 1132 rewriter.replaceOp(mulc, r0.getResult()); 1133 return success(); 1134 } 1135 }; 1136 1137 /// Inlined complex division 1138 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> { 1139 using FIROpConversion::FIROpConversion; 1140 1141 mlir::LogicalResult 1142 matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor, 1143 mlir::ConversionPatternRewriter &rewriter) const override { 1144 // TODO: Can we use a call to __divdc3 instead? 1145 // Just generate inline code for now. 1146 // given: (x + iy) / (x' + iy') 1147 // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y' 1148 mlir::Value a = adaptor.getOperands()[0]; 1149 mlir::Value b = adaptor.getOperands()[1]; 1150 auto loc = divc.getLoc(); 1151 auto *ctx = divc.getContext(); 1152 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1153 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1154 mlir::Type eleTy = convertType(getComplexEleTy(divc.getType())); 1155 mlir::Type ty = convertType(divc.getType()); 1156 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1157 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1158 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1159 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1160 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 1161 auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1); 1162 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 1163 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 1164 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 1165 auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1); 1166 auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1); 1167 auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy); 1168 auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy); 1169 auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d); 1170 auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d); 1171 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1172 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 1173 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 1174 rewriter.replaceOp(divc, r0.getResult()); 1175 return success(); 1176 } 1177 }; 1178 1179 /// Inlined complex negation 1180 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> { 1181 using FIROpConversion::FIROpConversion; 1182 1183 mlir::LogicalResult 1184 matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor, 1185 mlir::ConversionPatternRewriter &rewriter) const override { 1186 // given: -(x + iy) 1187 // result: -x - iy 1188 auto *ctxt = neg.getContext(); 1189 auto eleTy = convertType(getComplexEleTy(neg.getType())); 1190 auto ty = convertType(neg.getType()); 1191 auto loc = neg.getLoc(); 1192 mlir::Value o0 = adaptor.getOperands()[0]; 1193 auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0)); 1194 auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1)); 1195 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0); 1196 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1); 1197 auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp); 1198 auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip); 1199 auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0); 1200 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1); 1201 return success(); 1202 } 1203 }; 1204 1205 } // namespace 1206 1207 namespace { 1208 /// Convert FIR dialect to LLVM dialect 1209 /// 1210 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An 1211 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect. 1212 /// 1213 /// This pass is not complete yet. We are upstreaming it in small patches. 1214 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> { 1215 public: 1216 mlir::ModuleOp getModule() { return getOperation(); } 1217 1218 void runOnOperation() override final { 1219 auto mod = getModule(); 1220 if (!forcedTargetTriple.empty()) { 1221 fir::setTargetTriple(mod, forcedTargetTriple); 1222 } 1223 1224 auto *context = getModule().getContext(); 1225 fir::LLVMTypeConverter typeConverter{getModule()}; 1226 mlir::OwningRewritePatternList pattern(context); 1227 pattern.insert< 1228 AddcOpConversion, AddrOfOpConversion, AllocaOpConversion, 1229 BoxAddrOpConversion, BoxDimsOpConversion, BoxEleSizeOpConversion, 1230 BoxIsAllocOpConversion, BoxIsArrayOpConversion, BoxIsPtrOpConversion, 1231 BoxRankOpConversion, CallOpConversion, ConvertOpConversion, 1232 DispatchOpConversion, DispatchTableOpConversion, DTEntryOpConversion, 1233 DivcOpConversion, ExtractValueOpConversion, HasValueOpConversion, 1234 GlobalOpConversion, InsertOnRangeOpConversion, InsertValueOpConversion, 1235 LoadOpConversion, NegcOpConversion, MulcOpConversion, 1236 SelectOpConversion, SelectRankOpConversion, StoreOpConversion, 1237 SubcOpConversion, UndefOpConversion, UnreachableOpConversion, 1238 ZeroOpConversion>(typeConverter); 1239 mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern); 1240 mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter, 1241 pattern); 1242 mlir::ConversionTarget target{*context}; 1243 target.addLegalDialect<mlir::LLVM::LLVMDialect>(); 1244 1245 // required NOPs for applying a full conversion 1246 target.addLegalOp<mlir::ModuleOp>(); 1247 1248 // apply the patterns 1249 if (mlir::failed(mlir::applyFullConversion(getModule(), target, 1250 std::move(pattern)))) { 1251 signalPassFailure(); 1252 } 1253 } 1254 }; 1255 } // namespace 1256 1257 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() { 1258 return std::make_unique<FIRToLLVMLowering>(); 1259 } 1260