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