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 /// Create value signaling an absent optional argument in a call, e.g. 210 /// `fir.absent !fir.ref<i64>` --> `llvm.mlir.null : !llvm.ptr<i64>` 211 struct AbsentOpConversion : public FIROpConversion<fir::AbsentOp> { 212 using FIROpConversion::FIROpConversion; 213 214 mlir::LogicalResult 215 matchAndRewrite(fir::AbsentOp absent, OpAdaptor, 216 mlir::ConversionPatternRewriter &rewriter) const override { 217 mlir::Type ty = convertType(absent.getType()); 218 mlir::Location loc = absent.getLoc(); 219 220 if (absent.getType().isa<fir::BoxCharType>()) { 221 auto structTy = ty.cast<mlir::LLVM::LLVMStructType>(); 222 assert(!structTy.isOpaque() && !structTy.getBody().empty()); 223 auto undefStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 224 auto nullField = 225 rewriter.create<mlir::LLVM::NullOp>(loc, structTy.getBody()[0]); 226 mlir::MLIRContext *ctx = absent.getContext(); 227 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 228 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 229 absent, ty, undefStruct, nullField, c0); 230 } else { 231 rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(absent, ty); 232 } 233 return success(); 234 } 235 }; 236 237 // Lower `fir.address_of` operation to `llvm.address_of` operation. 238 struct AddrOfOpConversion : public FIROpConversion<fir::AddrOfOp> { 239 using FIROpConversion::FIROpConversion; 240 241 mlir::LogicalResult 242 matchAndRewrite(fir::AddrOfOp addr, OpAdaptor adaptor, 243 mlir::ConversionPatternRewriter &rewriter) const override { 244 auto ty = convertType(addr.getType()); 245 rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>( 246 addr, ty, addr.symbol().getRootReference().getValue()); 247 return success(); 248 } 249 }; 250 } // namespace 251 252 /// Lookup the function to compute the memory size of this parametric derived 253 /// type. The size of the object may depend on the LEN type parameters of the 254 /// derived type. 255 static mlir::LLVM::LLVMFuncOp 256 getDependentTypeMemSizeFn(fir::RecordType recTy, fir::AllocaOp op, 257 mlir::ConversionPatternRewriter &rewriter) { 258 auto module = op->getParentOfType<mlir::ModuleOp>(); 259 std::string name = recTy.getName().str() + "P.mem.size"; 260 return module.lookupSymbol<mlir::LLVM::LLVMFuncOp>(name); 261 } 262 263 namespace { 264 /// convert to LLVM IR dialect `alloca` 265 struct AllocaOpConversion : public FIROpConversion<fir::AllocaOp> { 266 using FIROpConversion::FIROpConversion; 267 268 mlir::LogicalResult 269 matchAndRewrite(fir::AllocaOp alloc, OpAdaptor adaptor, 270 mlir::ConversionPatternRewriter &rewriter) const override { 271 mlir::ValueRange operands = adaptor.getOperands(); 272 auto loc = alloc.getLoc(); 273 mlir::Type ity = lowerTy().indexType(); 274 unsigned i = 0; 275 mlir::Value size = genConstantIndex(loc, ity, rewriter, 1).getResult(); 276 mlir::Type ty = convertType(alloc.getType()); 277 mlir::Type resultTy = ty; 278 if (alloc.hasLenParams()) { 279 unsigned end = alloc.numLenParams(); 280 llvm::SmallVector<mlir::Value> lenParams; 281 for (; i < end; ++i) 282 lenParams.push_back(operands[i]); 283 mlir::Type scalarType = fir::unwrapSequenceType(alloc.getInType()); 284 if (auto chrTy = scalarType.dyn_cast<fir::CharacterType>()) { 285 fir::CharacterType rawCharTy = fir::CharacterType::getUnknownLen( 286 chrTy.getContext(), chrTy.getFKind()); 287 ty = mlir::LLVM::LLVMPointerType::get(convertType(rawCharTy)); 288 assert(end == 1); 289 size = integerCast(loc, rewriter, ity, lenParams[0]); 290 } else if (auto recTy = scalarType.dyn_cast<fir::RecordType>()) { 291 mlir::LLVM::LLVMFuncOp memSizeFn = 292 getDependentTypeMemSizeFn(recTy, alloc, rewriter); 293 if (!memSizeFn) 294 emitError(loc, "did not find allocation function"); 295 mlir::NamedAttribute attr = rewriter.getNamedAttr( 296 "callee", mlir::SymbolRefAttr::get(memSizeFn)); 297 auto call = rewriter.create<mlir::LLVM::CallOp>( 298 loc, ity, lenParams, llvm::ArrayRef<mlir::NamedAttribute>{attr}); 299 size = call.getResult(0); 300 ty = mlir::LLVM::LLVMPointerType::get( 301 mlir::IntegerType::get(alloc.getContext(), 8)); 302 } else { 303 return emitError(loc, "unexpected type ") 304 << scalarType << " with type parameters"; 305 } 306 } 307 if (alloc.hasShapeOperands()) { 308 mlir::Type allocEleTy = fir::unwrapRefType(alloc.getType()); 309 // Scale the size by constant factors encoded in the array type. 310 if (auto seqTy = allocEleTy.dyn_cast<fir::SequenceType>()) { 311 fir::SequenceType::Extent constSize = 1; 312 for (auto extent : seqTy.getShape()) 313 if (extent != fir::SequenceType::getUnknownExtent()) 314 constSize *= extent; 315 mlir::Value constVal{ 316 genConstantIndex(loc, ity, rewriter, constSize).getResult()}; 317 size = rewriter.create<mlir::LLVM::MulOp>(loc, ity, size, constVal); 318 } 319 unsigned end = operands.size(); 320 for (; i < end; ++i) 321 size = rewriter.create<mlir::LLVM::MulOp>( 322 loc, ity, size, integerCast(loc, rewriter, ity, operands[i])); 323 } 324 if (ty == resultTy) { 325 // Do not emit the bitcast if ty and resultTy are the same. 326 rewriter.replaceOpWithNewOp<mlir::LLVM::AllocaOp>(alloc, ty, size, 327 alloc->getAttrs()); 328 } else { 329 auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, ty, size, 330 alloc->getAttrs()); 331 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(alloc, resultTy, al); 332 } 333 return success(); 334 } 335 }; 336 337 /// Lower `fir.box_addr` to the sequence of operations to extract the first 338 /// element of the box. 339 struct BoxAddrOpConversion : public FIROpConversion<fir::BoxAddrOp> { 340 using FIROpConversion::FIROpConversion; 341 342 mlir::LogicalResult 343 matchAndRewrite(fir::BoxAddrOp boxaddr, OpAdaptor adaptor, 344 mlir::ConversionPatternRewriter &rewriter) const override { 345 mlir::Value a = adaptor.getOperands()[0]; 346 auto loc = boxaddr.getLoc(); 347 mlir::Type ty = convertType(boxaddr.getType()); 348 if (auto argty = boxaddr.val().getType().dyn_cast<fir::BoxType>()) { 349 rewriter.replaceOp(boxaddr, loadBaseAddrFromBox(loc, ty, a, rewriter)); 350 } else { 351 auto c0attr = rewriter.getI32IntegerAttr(0); 352 auto c0 = mlir::ArrayAttr::get(boxaddr.getContext(), c0attr); 353 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(boxaddr, ty, a, 354 c0); 355 } 356 return success(); 357 } 358 }; 359 360 /// Lower `fir.box_dims` to a sequence of operations to extract the requested 361 /// dimension infomartion from the boxed value. 362 /// Result in a triple set of GEPs and loads. 363 struct BoxDimsOpConversion : public FIROpConversion<fir::BoxDimsOp> { 364 using FIROpConversion::FIROpConversion; 365 366 mlir::LogicalResult 367 matchAndRewrite(fir::BoxDimsOp boxdims, OpAdaptor adaptor, 368 mlir::ConversionPatternRewriter &rewriter) const override { 369 SmallVector<mlir::Type, 3> resultTypes = { 370 convertType(boxdims.getResult(0).getType()), 371 convertType(boxdims.getResult(1).getType()), 372 convertType(boxdims.getResult(2).getType()), 373 }; 374 auto results = 375 getDimsFromBox(boxdims.getLoc(), resultTypes, adaptor.getOperands()[0], 376 adaptor.getOperands()[1], rewriter); 377 rewriter.replaceOp(boxdims, results); 378 return success(); 379 } 380 }; 381 382 /// Lower `fir.box_elesize` to a sequence of operations ro extract the size of 383 /// an element in the boxed value. 384 struct BoxEleSizeOpConversion : public FIROpConversion<fir::BoxEleSizeOp> { 385 using FIROpConversion::FIROpConversion; 386 387 mlir::LogicalResult 388 matchAndRewrite(fir::BoxEleSizeOp boxelesz, OpAdaptor adaptor, 389 mlir::ConversionPatternRewriter &rewriter) const override { 390 mlir::Value a = adaptor.getOperands()[0]; 391 auto loc = boxelesz.getLoc(); 392 auto ty = convertType(boxelesz.getType()); 393 auto elemSize = getValueFromBox(loc, a, ty, rewriter, kElemLenPosInBox); 394 rewriter.replaceOp(boxelesz, elemSize); 395 return success(); 396 } 397 }; 398 399 /// Lower `fir.box_isalloc` to a sequence of operations to determine if the 400 /// boxed value was from an ALLOCATABLE entity. 401 struct BoxIsAllocOpConversion : public FIROpConversion<fir::BoxIsAllocOp> { 402 using FIROpConversion::FIROpConversion; 403 404 mlir::LogicalResult 405 matchAndRewrite(fir::BoxIsAllocOp boxisalloc, OpAdaptor adaptor, 406 mlir::ConversionPatternRewriter &rewriter) const override { 407 mlir::Value box = adaptor.getOperands()[0]; 408 auto loc = boxisalloc.getLoc(); 409 mlir::Value check = 410 genBoxAttributeCheck(loc, box, rewriter, kAttrAllocatable); 411 rewriter.replaceOp(boxisalloc, check); 412 return success(); 413 } 414 }; 415 416 /// Lower `fir.box_isarray` to a sequence of operations to determine if the 417 /// boxed is an array. 418 struct BoxIsArrayOpConversion : public FIROpConversion<fir::BoxIsArrayOp> { 419 using FIROpConversion::FIROpConversion; 420 421 mlir::LogicalResult 422 matchAndRewrite(fir::BoxIsArrayOp boxisarray, OpAdaptor adaptor, 423 mlir::ConversionPatternRewriter &rewriter) const override { 424 mlir::Value a = adaptor.getOperands()[0]; 425 auto loc = boxisarray.getLoc(); 426 auto rank = 427 getValueFromBox(loc, a, rewriter.getI32Type(), rewriter, kRankPosInBox); 428 auto c0 = genConstantOffset(loc, rewriter, 0); 429 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>( 430 boxisarray, mlir::LLVM::ICmpPredicate::ne, rank, c0); 431 return success(); 432 } 433 }; 434 435 /// Lower `fir.box_isptr` to a sequence of operations to determined if the 436 /// boxed value was from a POINTER entity. 437 struct BoxIsPtrOpConversion : public FIROpConversion<fir::BoxIsPtrOp> { 438 using FIROpConversion::FIROpConversion; 439 440 mlir::LogicalResult 441 matchAndRewrite(fir::BoxIsPtrOp boxisptr, OpAdaptor adaptor, 442 mlir::ConversionPatternRewriter &rewriter) const override { 443 mlir::Value box = adaptor.getOperands()[0]; 444 auto loc = boxisptr.getLoc(); 445 mlir::Value check = genBoxAttributeCheck(loc, box, rewriter, kAttrPointer); 446 rewriter.replaceOp(boxisptr, check); 447 return success(); 448 } 449 }; 450 451 /// Lower `fir.box_rank` to the sequence of operation to extract the rank from 452 /// the box. 453 struct BoxRankOpConversion : public FIROpConversion<fir::BoxRankOp> { 454 using FIROpConversion::FIROpConversion; 455 456 mlir::LogicalResult 457 matchAndRewrite(fir::BoxRankOp boxrank, OpAdaptor adaptor, 458 mlir::ConversionPatternRewriter &rewriter) const override { 459 mlir::Value a = adaptor.getOperands()[0]; 460 auto loc = boxrank.getLoc(); 461 mlir::Type ty = convertType(boxrank.getType()); 462 auto result = getValueFromBox(loc, a, ty, rewriter, kRankPosInBox); 463 rewriter.replaceOp(boxrank, result); 464 return success(); 465 } 466 }; 467 468 // `fir.call` -> `llvm.call` 469 struct CallOpConversion : public FIROpConversion<fir::CallOp> { 470 using FIROpConversion::FIROpConversion; 471 472 mlir::LogicalResult 473 matchAndRewrite(fir::CallOp call, OpAdaptor adaptor, 474 mlir::ConversionPatternRewriter &rewriter) const override { 475 SmallVector<mlir::Type> resultTys; 476 for (auto r : call.getResults()) 477 resultTys.push_back(convertType(r.getType())); 478 rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>( 479 call, resultTys, adaptor.getOperands(), call->getAttrs()); 480 return success(); 481 } 482 }; 483 484 static mlir::Type getComplexEleTy(mlir::Type complex) { 485 if (auto cc = complex.dyn_cast<mlir::ComplexType>()) 486 return cc.getElementType(); 487 return complex.cast<fir::ComplexType>().getElementType(); 488 } 489 490 /// convert value of from-type to value of to-type 491 struct ConvertOpConversion : public FIROpConversion<fir::ConvertOp> { 492 using FIROpConversion::FIROpConversion; 493 494 static bool isFloatingPointTy(mlir::Type ty) { 495 return ty.isa<mlir::FloatType>(); 496 } 497 498 mlir::LogicalResult 499 matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor, 500 mlir::ConversionPatternRewriter &rewriter) const override { 501 auto fromTy = convertType(convert.value().getType()); 502 auto toTy = convertType(convert.res().getType()); 503 mlir::Value op0 = adaptor.getOperands()[0]; 504 if (fromTy == toTy) { 505 rewriter.replaceOp(convert, op0); 506 return success(); 507 } 508 auto loc = convert.getLoc(); 509 auto convertFpToFp = [&](mlir::Value val, unsigned fromBits, 510 unsigned toBits, mlir::Type toTy) -> mlir::Value { 511 if (fromBits == toBits) { 512 // TODO: Converting between two floating-point representations with the 513 // same bitwidth is not allowed for now. 514 mlir::emitError(loc, 515 "cannot implicitly convert between two floating-point " 516 "representations of the same bitwidth"); 517 return {}; 518 } 519 if (fromBits > toBits) 520 return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val); 521 return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val); 522 }; 523 // Complex to complex conversion. 524 if (fir::isa_complex(convert.value().getType()) && 525 fir::isa_complex(convert.res().getType())) { 526 // Special case: handle the conversion of a complex such that both the 527 // real and imaginary parts are converted together. 528 auto zero = mlir::ArrayAttr::get(convert.getContext(), 529 rewriter.getI32IntegerAttr(0)); 530 auto one = mlir::ArrayAttr::get(convert.getContext(), 531 rewriter.getI32IntegerAttr(1)); 532 auto ty = convertType(getComplexEleTy(convert.value().getType())); 533 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, zero); 534 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, one); 535 auto nt = convertType(getComplexEleTy(convert.res().getType())); 536 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty); 537 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt); 538 auto rc = convertFpToFp(rp, fromBits, toBits, nt); 539 auto ic = convertFpToFp(ip, fromBits, toBits, nt); 540 auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy); 541 auto i1 = 542 rewriter.create<mlir::LLVM::InsertValueOp>(loc, toTy, un, rc, zero); 543 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, toTy, i1, 544 ic, one); 545 return mlir::success(); 546 } 547 // Floating point to floating point conversion. 548 if (isFloatingPointTy(fromTy)) { 549 if (isFloatingPointTy(toTy)) { 550 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy); 551 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy); 552 auto v = convertFpToFp(op0, fromBits, toBits, toTy); 553 rewriter.replaceOp(convert, v); 554 return mlir::success(); 555 } 556 if (toTy.isa<mlir::IntegerType>()) { 557 rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0); 558 return mlir::success(); 559 } 560 } else if (fromTy.isa<mlir::IntegerType>()) { 561 // Integer to integer conversion. 562 if (toTy.isa<mlir::IntegerType>()) { 563 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy); 564 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy); 565 assert(fromBits != toBits); 566 if (fromBits > toBits) { 567 rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0); 568 return mlir::success(); 569 } 570 rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0); 571 return mlir::success(); 572 } 573 // Integer to floating point conversion. 574 if (isFloatingPointTy(toTy)) { 575 rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0); 576 return mlir::success(); 577 } 578 // Integer to pointer conversion. 579 if (toTy.isa<mlir::LLVM::LLVMPointerType>()) { 580 rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0); 581 return mlir::success(); 582 } 583 } else if (fromTy.isa<mlir::LLVM::LLVMPointerType>()) { 584 // Pointer to integer conversion. 585 if (toTy.isa<mlir::IntegerType>()) { 586 rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0); 587 return mlir::success(); 588 } 589 // Pointer to pointer conversion. 590 if (toTy.isa<mlir::LLVM::LLVMPointerType>()) { 591 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0); 592 return mlir::success(); 593 } 594 } 595 return emitError(loc) << "cannot convert " << fromTy << " to " << toTy; 596 } 597 }; 598 599 /// Lower `fir.dispatch` operation. A virtual call to a method in a dispatch 600 /// table. 601 struct DispatchOpConversion : public FIROpConversion<fir::DispatchOp> { 602 using FIROpConversion::FIROpConversion; 603 604 mlir::LogicalResult 605 matchAndRewrite(fir::DispatchOp dispatch, OpAdaptor adaptor, 606 mlir::ConversionPatternRewriter &rewriter) const override { 607 return rewriter.notifyMatchFailure( 608 dispatch, "fir.dispatch codegen is not implemented yet"); 609 } 610 }; 611 612 /// Lower `fir.dispatch_table` operation. The dispatch table for a Fortran 613 /// derived type. 614 struct DispatchTableOpConversion 615 : public FIROpConversion<fir::DispatchTableOp> { 616 using FIROpConversion::FIROpConversion; 617 618 mlir::LogicalResult 619 matchAndRewrite(fir::DispatchTableOp dispTab, OpAdaptor adaptor, 620 mlir::ConversionPatternRewriter &rewriter) const override { 621 return rewriter.notifyMatchFailure( 622 dispTab, "fir.dispatch_table codegen is not implemented yet"); 623 } 624 }; 625 626 /// Lower `fir.dt_entry` operation. An entry in a dispatch table; binds a 627 /// method-name to a function. 628 struct DTEntryOpConversion : public FIROpConversion<fir::DTEntryOp> { 629 using FIROpConversion::FIROpConversion; 630 631 mlir::LogicalResult 632 matchAndRewrite(fir::DTEntryOp dtEnt, OpAdaptor adaptor, 633 mlir::ConversionPatternRewriter &rewriter) const override { 634 return rewriter.notifyMatchFailure( 635 dtEnt, "fir.dt_entry codegen is not implemented yet"); 636 } 637 }; 638 639 /// Lower `fir.global_len` operation. 640 struct GlobalLenOpConversion : public FIROpConversion<fir::GlobalLenOp> { 641 using FIROpConversion::FIROpConversion; 642 643 mlir::LogicalResult 644 matchAndRewrite(fir::GlobalLenOp globalLen, OpAdaptor adaptor, 645 mlir::ConversionPatternRewriter &rewriter) const override { 646 return rewriter.notifyMatchFailure( 647 globalLen, "fir.global_len codegen is not implemented yet"); 648 } 649 }; 650 651 /// Lower `fir.has_value` operation to `llvm.return` operation. 652 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> { 653 using FIROpConversion::FIROpConversion; 654 655 mlir::LogicalResult 656 matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor, 657 mlir::ConversionPatternRewriter &rewriter) const override { 658 rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands()); 659 return success(); 660 } 661 }; 662 663 /// Lower `fir.global` operation to `llvm.global` operation. 664 /// `fir.insert_on_range` operations are replaced with constant dense attribute 665 /// if they are applied on the full range. 666 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> { 667 using FIROpConversion::FIROpConversion; 668 669 mlir::LogicalResult 670 matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor, 671 mlir::ConversionPatternRewriter &rewriter) const override { 672 auto tyAttr = convertType(global.getType()); 673 if (global.getType().isa<fir::BoxType>()) 674 tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType(); 675 auto loc = global.getLoc(); 676 mlir::Attribute initAttr{}; 677 if (global.initVal()) 678 initAttr = global.initVal().getValue(); 679 auto linkage = convertLinkage(global.linkName()); 680 auto isConst = global.constant().hasValue(); 681 auto g = rewriter.create<mlir::LLVM::GlobalOp>( 682 loc, tyAttr, isConst, linkage, global.sym_name(), initAttr); 683 auto &gr = g.getInitializerRegion(); 684 rewriter.inlineRegionBefore(global.region(), gr, gr.end()); 685 if (!gr.empty()) { 686 // Replace insert_on_range with a constant dense attribute if the 687 // initialization is on the full range. 688 auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>(); 689 for (auto insertOp : insertOnRangeOps) { 690 if (isFullRange(insertOp.coor(), insertOp.getType())) { 691 auto seqTyAttr = convertType(insertOp.getType()); 692 auto *op = insertOp.val().getDefiningOp(); 693 auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op); 694 if (!constant) { 695 auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op); 696 if (!convertOp) 697 continue; 698 constant = cast<mlir::arith::ConstantOp>( 699 convertOp.value().getDefiningOp()); 700 } 701 mlir::Type vecType = mlir::VectorType::get( 702 insertOp.getType().getShape(), constant.getType()); 703 auto denseAttr = mlir::DenseElementsAttr::get( 704 vecType.cast<ShapedType>(), constant.value()); 705 rewriter.setInsertionPointAfter(insertOp); 706 rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>( 707 insertOp, seqTyAttr, denseAttr); 708 } 709 } 710 } 711 rewriter.eraseOp(global); 712 return success(); 713 } 714 715 bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const { 716 auto extents = seqTy.getShape(); 717 if (indexes.size() / 2 != extents.size()) 718 return false; 719 for (unsigned i = 0; i < indexes.size(); i += 2) { 720 if (indexes[i].cast<IntegerAttr>().getInt() != 0) 721 return false; 722 if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1) 723 return false; 724 } 725 return true; 726 } 727 728 // TODO: String comparaison should be avoided. Replace linkName with an 729 // enumeration. 730 mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const { 731 if (optLinkage.hasValue()) { 732 auto name = optLinkage.getValue(); 733 if (name == "internal") 734 return mlir::LLVM::Linkage::Internal; 735 if (name == "linkonce") 736 return mlir::LLVM::Linkage::Linkonce; 737 if (name == "common") 738 return mlir::LLVM::Linkage::Common; 739 if (name == "weak") 740 return mlir::LLVM::Linkage::Weak; 741 } 742 return mlir::LLVM::Linkage::External; 743 } 744 }; 745 746 void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest, 747 Optional<mlir::ValueRange> destOps, 748 mlir::ConversionPatternRewriter &rewriter, 749 mlir::Block *newBlock) { 750 if (destOps.hasValue()) 751 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, destOps.getValue(), 752 newBlock, mlir::ValueRange()); 753 else 754 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock); 755 } 756 757 template <typename A, typename B> 758 void genBrOp(A caseOp, mlir::Block *dest, Optional<B> destOps, 759 mlir::ConversionPatternRewriter &rewriter) { 760 if (destOps.hasValue()) 761 rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, destOps.getValue(), 762 dest); 763 else 764 rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, llvm::None, dest); 765 } 766 767 void genCaseLadderStep(mlir::Location loc, mlir::Value cmp, mlir::Block *dest, 768 Optional<mlir::ValueRange> destOps, 769 mlir::ConversionPatternRewriter &rewriter) { 770 auto *thisBlock = rewriter.getInsertionBlock(); 771 auto *newBlock = createBlock(rewriter, dest); 772 rewriter.setInsertionPointToEnd(thisBlock); 773 genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock); 774 rewriter.setInsertionPointToEnd(newBlock); 775 } 776 777 /// Conversion of `fir.select_case` 778 /// 779 /// The `fir.select_case` operation is converted to a if-then-else ladder. 780 /// Depending on the case condition type, one or several comparison and 781 /// conditional branching can be generated. 782 /// 783 /// A a point value case such as `case(4)`, a lower bound case such as 784 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a 785 /// simple comparison between the selector value and the constant value in the 786 /// case. The block associated with the case condition is then executed if 787 /// the comparison succeed otherwise it branch to the next block with the 788 /// comparison for the the next case conditon. 789 /// 790 /// A closed interval case condition such as `case(7:10)` is converted with a 791 /// first comparison and conditional branching for the lower bound. If 792 /// successful, it branch to a second block with the comparison for the 793 /// upper bound in the same case condition. 794 /// 795 /// TODO: lowering of CHARACTER type cases is not handled yet. 796 struct SelectCaseOpConversion : public FIROpConversion<fir::SelectCaseOp> { 797 using FIROpConversion::FIROpConversion; 798 799 mlir::LogicalResult 800 matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor, 801 mlir::ConversionPatternRewriter &rewriter) const override { 802 unsigned conds = caseOp.getNumConditions(); 803 llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue(); 804 // Type can be CHARACTER, INTEGER, or LOGICAL (C1145) 805 LLVM_ATTRIBUTE_UNUSED auto ty = caseOp.getSelector().getType(); 806 if (ty.isa<fir::CharacterType>()) 807 return rewriter.notifyMatchFailure(caseOp, 808 "conversion of fir.select_case with " 809 "character type not implemented yet"); 810 mlir::Value selector = caseOp.getSelector(adaptor.getOperands()); 811 auto loc = caseOp.getLoc(); 812 for (unsigned t = 0; t != conds; ++t) { 813 mlir::Block *dest = caseOp.getSuccessor(t); 814 llvm::Optional<mlir::ValueRange> destOps = 815 caseOp.getSuccessorOperands(adaptor.getOperands(), t); 816 llvm::Optional<mlir::ValueRange> cmpOps = 817 *caseOp.getCompareOperands(adaptor.getOperands(), t); 818 mlir::Value caseArg = *(cmpOps.getValue().begin()); 819 mlir::Attribute attr = cases[t]; 820 if (attr.isa<fir::PointIntervalAttr>()) { 821 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 822 loc, mlir::LLVM::ICmpPredicate::eq, selector, caseArg); 823 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 824 continue; 825 } 826 if (attr.isa<fir::LowerBoundAttr>()) { 827 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 828 loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector); 829 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 830 continue; 831 } 832 if (attr.isa<fir::UpperBoundAttr>()) { 833 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 834 loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg); 835 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 836 continue; 837 } 838 if (attr.isa<fir::ClosedIntervalAttr>()) { 839 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 840 loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector); 841 auto *thisBlock = rewriter.getInsertionBlock(); 842 auto *newBlock1 = createBlock(rewriter, dest); 843 auto *newBlock2 = createBlock(rewriter, dest); 844 rewriter.setInsertionPointToEnd(thisBlock); 845 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, newBlock1, newBlock2); 846 rewriter.setInsertionPointToEnd(newBlock1); 847 mlir::Value caseArg0 = *(cmpOps.getValue().begin() + 1); 848 auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>( 849 loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg0); 850 genCondBrOp(loc, cmp0, dest, destOps, rewriter, newBlock2); 851 rewriter.setInsertionPointToEnd(newBlock2); 852 continue; 853 } 854 assert(attr.isa<mlir::UnitAttr>()); 855 assert((t + 1 == conds) && "unit must be last"); 856 genBrOp(caseOp, dest, destOps, rewriter); 857 } 858 return success(); 859 } 860 }; 861 862 template <typename OP> 863 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select, 864 typename OP::Adaptor adaptor, 865 mlir::ConversionPatternRewriter &rewriter) { 866 unsigned conds = select.getNumConditions(); 867 auto cases = select.getCases().getValue(); 868 mlir::Value selector = adaptor.selector(); 869 auto loc = select.getLoc(); 870 assert(conds > 0 && "select must have cases"); 871 872 llvm::SmallVector<mlir::Block *> destinations; 873 llvm::SmallVector<mlir::ValueRange> destinationsOperands; 874 mlir::Block *defaultDestination; 875 mlir::ValueRange defaultOperands; 876 llvm::SmallVector<int32_t> caseValues; 877 878 for (unsigned t = 0; t != conds; ++t) { 879 mlir::Block *dest = select.getSuccessor(t); 880 auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t); 881 const mlir::Attribute &attr = cases[t]; 882 if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) { 883 destinations.push_back(dest); 884 destinationsOperands.push_back(destOps.hasValue() ? *destOps 885 : ValueRange()); 886 caseValues.push_back(intAttr.getInt()); 887 continue; 888 } 889 assert(attr.template dyn_cast_or_null<mlir::UnitAttr>()); 890 assert((t + 1 == conds) && "unit must be last"); 891 defaultDestination = dest; 892 defaultOperands = destOps.hasValue() ? *destOps : ValueRange(); 893 } 894 895 // LLVM::SwitchOp takes a i32 type for the selector. 896 if (select.getSelector().getType() != rewriter.getI32Type()) 897 selector = 898 rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector); 899 900 rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>( 901 select, selector, 902 /*defaultDestination=*/defaultDestination, 903 /*defaultOperands=*/defaultOperands, 904 /*caseValues=*/caseValues, 905 /*caseDestinations=*/destinations, 906 /*caseOperands=*/destinationsOperands, 907 /*branchWeights=*/ArrayRef<int32_t>()); 908 } 909 910 /// conversion of fir::SelectOp to an if-then-else ladder 911 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> { 912 using FIROpConversion::FIROpConversion; 913 914 mlir::LogicalResult 915 matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor, 916 mlir::ConversionPatternRewriter &rewriter) const override { 917 selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter); 918 return success(); 919 } 920 }; 921 922 /// `fir.load` --> `llvm.load` 923 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> { 924 using FIROpConversion::FIROpConversion; 925 926 mlir::LogicalResult 927 matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor, 928 mlir::ConversionPatternRewriter &rewriter) const override { 929 // fir.box is a special case because it is considered as an ssa values in 930 // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box> 931 // and fir.box end up being the same llvm types and loading a 932 // fir.ref<fir.box> is actually a no op in LLVM. 933 if (load.getType().isa<fir::BoxType>()) { 934 rewriter.replaceOp(load, adaptor.getOperands()[0]); 935 } else { 936 mlir::Type ty = convertType(load.getType()); 937 ArrayRef<NamedAttribute> at = load->getAttrs(); 938 rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>( 939 load, ty, adaptor.getOperands(), at); 940 } 941 return success(); 942 } 943 }; 944 945 /// Lower `fir.select_type` to LLVM IR dialect. 946 struct SelectTypeOpConversion : public FIROpConversion<fir::SelectTypeOp> { 947 using FIROpConversion::FIROpConversion; 948 949 mlir::LogicalResult 950 matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor, 951 mlir::ConversionPatternRewriter &rewriter) const override { 952 return rewriter.notifyMatchFailure( 953 select, "fir.select_type codegen is not implemented yet"); 954 } 955 }; 956 957 /// conversion of fir::SelectRankOp to an if-then-else ladder 958 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> { 959 using FIROpConversion::FIROpConversion; 960 961 mlir::LogicalResult 962 matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor, 963 mlir::ConversionPatternRewriter &rewriter) const override { 964 selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter); 965 return success(); 966 } 967 }; 968 969 /// `fir.store` --> `llvm.store` 970 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> { 971 using FIROpConversion::FIROpConversion; 972 973 mlir::LogicalResult 974 matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor, 975 mlir::ConversionPatternRewriter &rewriter) const override { 976 if (store.value().getType().isa<fir::BoxType>()) { 977 // fir.box value is actually in memory, load it first before storing it. 978 mlir::Location loc = store.getLoc(); 979 mlir::Type boxPtrTy = adaptor.getOperands()[0].getType(); 980 auto val = rewriter.create<mlir::LLVM::LoadOp>( 981 loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(), 982 adaptor.getOperands()[0]); 983 rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 984 store, val, adaptor.getOperands()[1]); 985 } else { 986 rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 987 store, adaptor.getOperands()[0], adaptor.getOperands()[1]); 988 } 989 return success(); 990 } 991 }; 992 993 /// convert to LLVM IR dialect `undef` 994 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> { 995 using FIROpConversion::FIROpConversion; 996 997 mlir::LogicalResult 998 matchAndRewrite(fir::UndefOp undef, OpAdaptor, 999 mlir::ConversionPatternRewriter &rewriter) const override { 1000 rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>( 1001 undef, convertType(undef.getType())); 1002 return success(); 1003 } 1004 }; 1005 1006 /// `fir.unreachable` --> `llvm.unreachable` 1007 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> { 1008 using FIROpConversion::FIROpConversion; 1009 1010 mlir::LogicalResult 1011 matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor, 1012 mlir::ConversionPatternRewriter &rewriter) const override { 1013 rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach); 1014 return success(); 1015 } 1016 }; 1017 1018 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> { 1019 using FIROpConversion::FIROpConversion; 1020 1021 mlir::LogicalResult 1022 matchAndRewrite(fir::ZeroOp zero, OpAdaptor, 1023 mlir::ConversionPatternRewriter &rewriter) const override { 1024 auto ty = convertType(zero.getType()); 1025 if (ty.isa<mlir::LLVM::LLVMPointerType>()) { 1026 rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty); 1027 } else if (ty.isa<mlir::IntegerType>()) { 1028 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 1029 zero, ty, mlir::IntegerAttr::get(zero.getType(), 0)); 1030 } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) { 1031 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 1032 zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0)); 1033 } else { 1034 // TODO: create ConstantAggregateZero for FIR aggregate/array types. 1035 return rewriter.notifyMatchFailure( 1036 zero, 1037 "conversion of fir.zero with aggregate type not implemented yet"); 1038 } 1039 return success(); 1040 } 1041 }; 1042 1043 // Code shared between insert_value and extract_value Ops. 1044 struct ValueOpCommon { 1045 // Translate the arguments pertaining to any multidimensional array to 1046 // row-major order for LLVM-IR. 1047 static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs, 1048 mlir::Type ty) { 1049 assert(ty && "type is null"); 1050 const auto end = attrs.size(); 1051 for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) { 1052 if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 1053 const auto dim = getDimension(seq); 1054 if (dim > 1) { 1055 auto ub = std::min(i + dim, end); 1056 std::reverse(attrs.begin() + i, attrs.begin() + ub); 1057 i += dim - 1; 1058 } 1059 ty = getArrayElementType(seq); 1060 } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) { 1061 ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()]; 1062 } else { 1063 llvm_unreachable("index into invalid type"); 1064 } 1065 } 1066 } 1067 1068 static llvm::SmallVector<mlir::Attribute> 1069 collectIndices(mlir::ConversionPatternRewriter &rewriter, 1070 mlir::ArrayAttr arrAttr) { 1071 llvm::SmallVector<mlir::Attribute> attrs; 1072 for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) { 1073 if (i->isa<mlir::IntegerAttr>()) { 1074 attrs.push_back(*i); 1075 } else { 1076 auto fieldName = i->cast<mlir::StringAttr>().getValue(); 1077 ++i; 1078 auto ty = i->cast<mlir::TypeAttr>().getValue(); 1079 auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName); 1080 attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index)); 1081 } 1082 } 1083 return attrs; 1084 } 1085 1086 private: 1087 static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) { 1088 unsigned result = 1; 1089 for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>(); 1090 eleTy; 1091 eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>()) 1092 ++result; 1093 return result; 1094 } 1095 1096 static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) { 1097 auto eleTy = ty.getElementType(); 1098 while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>()) 1099 eleTy = arrTy.getElementType(); 1100 return eleTy; 1101 } 1102 }; 1103 1104 /// Extract a subobject value from an ssa-value of aggregate type 1105 struct ExtractValueOpConversion 1106 : public FIROpAndTypeConversion<fir::ExtractValueOp>, 1107 public ValueOpCommon { 1108 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1109 1110 mlir::LogicalResult 1111 doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor, 1112 mlir::ConversionPatternRewriter &rewriter) const override { 1113 auto attrs = collectIndices(rewriter, extractVal.coor()); 1114 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 1115 auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs); 1116 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>( 1117 extractVal, ty, adaptor.getOperands()[0], position); 1118 return success(); 1119 } 1120 }; 1121 1122 /// InsertValue is the generalized instruction for the composition of new 1123 /// aggregate type values. 1124 struct InsertValueOpConversion 1125 : public FIROpAndTypeConversion<fir::InsertValueOp>, 1126 public ValueOpCommon { 1127 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1128 1129 mlir::LogicalResult 1130 doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor, 1131 mlir::ConversionPatternRewriter &rewriter) const override { 1132 auto attrs = collectIndices(rewriter, insertVal.coor()); 1133 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 1134 auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs); 1135 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1136 insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1], 1137 position); 1138 return success(); 1139 } 1140 }; 1141 1142 /// InsertOnRange inserts a value into a sequence over a range of offsets. 1143 struct InsertOnRangeOpConversion 1144 : public FIROpAndTypeConversion<fir::InsertOnRangeOp> { 1145 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1146 1147 // Increments an array of subscripts in a row major fasion. 1148 void incrementSubscripts(const SmallVector<uint64_t> &dims, 1149 SmallVector<uint64_t> &subscripts) const { 1150 for (size_t i = dims.size(); i > 0; --i) { 1151 if (++subscripts[i - 1] < dims[i - 1]) { 1152 return; 1153 } 1154 subscripts[i - 1] = 0; 1155 } 1156 } 1157 1158 mlir::LogicalResult 1159 doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor, 1160 mlir::ConversionPatternRewriter &rewriter) const override { 1161 1162 llvm::SmallVector<uint64_t> dims; 1163 auto type = adaptor.getOperands()[0].getType(); 1164 1165 // Iteratively extract the array dimensions from the type. 1166 while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 1167 dims.push_back(t.getNumElements()); 1168 type = t.getElementType(); 1169 } 1170 1171 SmallVector<uint64_t> lBounds; 1172 SmallVector<uint64_t> uBounds; 1173 1174 // Extract integer value from the attribute 1175 SmallVector<int64_t> coordinates = llvm::to_vector<4>( 1176 llvm::map_range(range.coor(), [](Attribute a) -> int64_t { 1177 return a.cast<IntegerAttr>().getInt(); 1178 })); 1179 1180 // Unzip the upper and lower bound and convert to a row major format. 1181 for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) { 1182 uBounds.push_back(*i++); 1183 lBounds.push_back(*i); 1184 } 1185 1186 auto &subscripts = lBounds; 1187 auto loc = range.getLoc(); 1188 mlir::Value lastOp = adaptor.getOperands()[0]; 1189 mlir::Value insertVal = adaptor.getOperands()[1]; 1190 1191 auto i64Ty = rewriter.getI64Type(); 1192 while (subscripts != uBounds) { 1193 // Convert uint64_t's to Attribute's. 1194 SmallVector<mlir::Attribute> subscriptAttrs; 1195 for (const auto &subscript : subscripts) 1196 subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript)); 1197 lastOp = rewriter.create<mlir::LLVM::InsertValueOp>( 1198 loc, ty, lastOp, insertVal, 1199 ArrayAttr::get(range.getContext(), subscriptAttrs)); 1200 1201 incrementSubscripts(dims, subscripts); 1202 } 1203 1204 // Convert uint64_t's to Attribute's. 1205 SmallVector<mlir::Attribute> subscriptAttrs; 1206 for (const auto &subscript : subscripts) 1207 subscriptAttrs.push_back( 1208 IntegerAttr::get(rewriter.getI64Type(), subscript)); 1209 mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs); 1210 1211 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1212 range, ty, lastOp, insertVal, 1213 ArrayAttr::get(range.getContext(), arrayRef)); 1214 1215 return success(); 1216 } 1217 }; 1218 1219 // 1220 // Primitive operations on Complex types 1221 // 1222 1223 /// Generate inline code for complex addition/subtraction 1224 template <typename LLVMOP, typename OPTY> 1225 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds, 1226 mlir::ConversionPatternRewriter &rewriter, 1227 fir::LLVMTypeConverter &lowering) { 1228 mlir::Value a = opnds[0]; 1229 mlir::Value b = opnds[1]; 1230 auto loc = sumop.getLoc(); 1231 auto ctx = sumop.getContext(); 1232 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1233 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1234 mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType())); 1235 mlir::Type ty = lowering.convertType(sumop.getType()); 1236 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1237 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1238 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1239 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1240 auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1); 1241 auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1); 1242 auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1243 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0); 1244 return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1); 1245 } 1246 1247 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> { 1248 using FIROpConversion::FIROpConversion; 1249 1250 mlir::LogicalResult 1251 matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor, 1252 mlir::ConversionPatternRewriter &rewriter) const override { 1253 // given: (x + iy) + (x' + iy') 1254 // result: (x + x') + i(y + y') 1255 auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(), 1256 rewriter, lowerTy()); 1257 rewriter.replaceOp(addc, r.getResult()); 1258 return success(); 1259 } 1260 }; 1261 1262 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> { 1263 using FIROpConversion::FIROpConversion; 1264 1265 mlir::LogicalResult 1266 matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor, 1267 mlir::ConversionPatternRewriter &rewriter) const override { 1268 // given: (x + iy) - (x' + iy') 1269 // result: (x - x') + i(y - y') 1270 auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(), 1271 rewriter, lowerTy()); 1272 rewriter.replaceOp(subc, r.getResult()); 1273 return success(); 1274 } 1275 }; 1276 1277 /// Inlined complex multiply 1278 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> { 1279 using FIROpConversion::FIROpConversion; 1280 1281 mlir::LogicalResult 1282 matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor, 1283 mlir::ConversionPatternRewriter &rewriter) const override { 1284 // TODO: Can we use a call to __muldc3 ? 1285 // given: (x + iy) * (x' + iy') 1286 // result: (xx'-yy')+i(xy'+yx') 1287 mlir::Value a = adaptor.getOperands()[0]; 1288 mlir::Value b = adaptor.getOperands()[1]; 1289 auto loc = mulc.getLoc(); 1290 auto *ctx = mulc.getContext(); 1291 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1292 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1293 mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType())); 1294 mlir::Type ty = convertType(mulc.getType()); 1295 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1296 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1297 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1298 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1299 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 1300 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 1301 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 1302 auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx); 1303 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 1304 auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy); 1305 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1306 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 1307 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 1308 rewriter.replaceOp(mulc, r0.getResult()); 1309 return success(); 1310 } 1311 }; 1312 1313 /// Inlined complex division 1314 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> { 1315 using FIROpConversion::FIROpConversion; 1316 1317 mlir::LogicalResult 1318 matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor, 1319 mlir::ConversionPatternRewriter &rewriter) const override { 1320 // TODO: Can we use a call to __divdc3 instead? 1321 // Just generate inline code for now. 1322 // given: (x + iy) / (x' + iy') 1323 // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y' 1324 mlir::Value a = adaptor.getOperands()[0]; 1325 mlir::Value b = adaptor.getOperands()[1]; 1326 auto loc = divc.getLoc(); 1327 auto *ctx = divc.getContext(); 1328 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1329 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1330 mlir::Type eleTy = convertType(getComplexEleTy(divc.getType())); 1331 mlir::Type ty = convertType(divc.getType()); 1332 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1333 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1334 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1335 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1336 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 1337 auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1); 1338 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 1339 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 1340 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 1341 auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1); 1342 auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1); 1343 auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy); 1344 auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy); 1345 auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d); 1346 auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d); 1347 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1348 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 1349 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 1350 rewriter.replaceOp(divc, r0.getResult()); 1351 return success(); 1352 } 1353 }; 1354 1355 /// Inlined complex negation 1356 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> { 1357 using FIROpConversion::FIROpConversion; 1358 1359 mlir::LogicalResult 1360 matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor, 1361 mlir::ConversionPatternRewriter &rewriter) const override { 1362 // given: -(x + iy) 1363 // result: -x - iy 1364 auto *ctxt = neg.getContext(); 1365 auto eleTy = convertType(getComplexEleTy(neg.getType())); 1366 auto ty = convertType(neg.getType()); 1367 auto loc = neg.getLoc(); 1368 mlir::Value o0 = adaptor.getOperands()[0]; 1369 auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0)); 1370 auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1)); 1371 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0); 1372 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1); 1373 auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp); 1374 auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip); 1375 auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0); 1376 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1); 1377 return success(); 1378 } 1379 }; 1380 1381 /// `fir.is_present` --> 1382 /// ``` 1383 /// %0 = llvm.mlir.constant(0 : i64) 1384 /// %1 = llvm.ptrtoint %0 1385 /// %2 = llvm.icmp "ne" %1, %0 : i64 1386 /// ``` 1387 struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> { 1388 using FIROpConversion::FIROpConversion; 1389 1390 mlir::LogicalResult 1391 matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor, 1392 mlir::ConversionPatternRewriter &rewriter) const override { 1393 mlir::Type idxTy = lowerTy().indexType(); 1394 mlir::Location loc = isPresent.getLoc(); 1395 auto ptr = adaptor.getOperands()[0]; 1396 1397 if (isPresent.val().getType().isa<fir::BoxCharType>()) { 1398 auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>(); 1399 assert(!structTy.isOpaque() && !structTy.getBody().empty()); 1400 1401 mlir::Type ty = structTy.getBody()[0]; 1402 mlir::MLIRContext *ctx = isPresent.getContext(); 1403 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1404 ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0); 1405 } 1406 mlir::LLVM::ConstantOp c0 = 1407 genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0); 1408 auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr); 1409 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>( 1410 isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0); 1411 1412 return success(); 1413 } 1414 }; 1415 1416 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of 1417 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element 1418 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd 1419 /// element is the length of the character buffer (`#n`). 1420 struct EmboxCharOpConversion : public FIROpConversion<fir::EmboxCharOp> { 1421 using FIROpConversion::FIROpConversion; 1422 1423 mlir::LogicalResult 1424 matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor, 1425 mlir::ConversionPatternRewriter &rewriter) const override { 1426 mlir::ValueRange operands = adaptor.getOperands(); 1427 MLIRContext *ctx = emboxChar.getContext(); 1428 1429 mlir::Value charBuffer = operands[0]; 1430 mlir::Value charBufferLen = operands[1]; 1431 1432 mlir::Location loc = emboxChar.getLoc(); 1433 mlir::Type llvmStructTy = convertType(emboxChar.getType()); 1434 auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy); 1435 1436 mlir::Type lenTy = 1437 llvmStructTy.cast<mlir::LLVM::LLVMStructType>().getBody()[1]; 1438 mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen); 1439 1440 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1441 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1442 auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>( 1443 loc, llvmStructTy, llvmStruct, charBuffer, c0); 1444 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1445 emboxChar, llvmStructTy, insertBufferOp, lenAfterCast, c1); 1446 1447 return success(); 1448 } 1449 }; 1450 1451 /// Construct an `llvm.extractvalue` instruction. It will return value at 1452 /// element \p x from \p tuple. 1453 mlir::LLVM::ExtractValueOp 1454 genExtractValueWithIndex(mlir::Location loc, mlir::Value tuple, mlir::Type ty, 1455 mlir::ConversionPatternRewriter &rewriter, 1456 mlir::MLIRContext *ctx, int x) { 1457 auto cx = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(x)); 1458 auto xty = ty.cast<mlir::LLVM::LLVMStructType>().getBody()[x]; 1459 return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, xty, tuple, cx); 1460 } 1461 1462 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for 1463 /// the character buffer and one for the buffer length. 1464 struct UnboxCharOpConversion : public FIROpConversion<fir::UnboxCharOp> { 1465 using FIROpConversion::FIROpConversion; 1466 1467 mlir::LogicalResult 1468 matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor, 1469 mlir::ConversionPatternRewriter &rewriter) const override { 1470 MLIRContext *ctx = unboxchar.getContext(); 1471 1472 mlir::Type lenTy = convertType(unboxchar.getType(1)); 1473 mlir::Value tuple = adaptor.getOperands()[0]; 1474 mlir::Type tupleTy = tuple.getType(); 1475 1476 mlir::Location loc = unboxchar.getLoc(); 1477 mlir::Value ptrToBuffer = 1478 genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 0); 1479 1480 mlir::LLVM::ExtractValueOp len = 1481 genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 1); 1482 mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len); 1483 1484 rewriter.replaceOp(unboxchar, 1485 ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast}); 1486 return success(); 1487 } 1488 }; 1489 1490 } // namespace 1491 1492 namespace { 1493 /// Convert FIR dialect to LLVM dialect 1494 /// 1495 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An 1496 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect. 1497 /// 1498 /// This pass is not complete yet. We are upstreaming it in small patches. 1499 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> { 1500 public: 1501 mlir::ModuleOp getModule() { return getOperation(); } 1502 1503 void runOnOperation() override final { 1504 auto mod = getModule(); 1505 if (!forcedTargetTriple.empty()) { 1506 fir::setTargetTriple(mod, forcedTargetTriple); 1507 } 1508 1509 auto *context = getModule().getContext(); 1510 fir::LLVMTypeConverter typeConverter{getModule()}; 1511 mlir::OwningRewritePatternList pattern(context); 1512 pattern.insert< 1513 AbsentOpConversion, AddcOpConversion, AddrOfOpConversion, 1514 AllocaOpConversion, BoxAddrOpConversion, BoxDimsOpConversion, 1515 BoxEleSizeOpConversion, BoxIsAllocOpConversion, BoxIsArrayOpConversion, 1516 BoxIsPtrOpConversion, BoxRankOpConversion, CallOpConversion, 1517 ConvertOpConversion, DispatchOpConversion, DispatchTableOpConversion, 1518 DTEntryOpConversion, DivcOpConversion, EmboxCharOpConversion, 1519 ExtractValueOpConversion, HasValueOpConversion, GlobalLenOpConversion, 1520 GlobalOpConversion, InsertOnRangeOpConversion, InsertValueOpConversion, 1521 IsPresentOpConversion, LoadOpConversion, NegcOpConversion, 1522 MulcOpConversion, SelectCaseOpConversion, SelectOpConversion, 1523 SelectRankOpConversion, SelectTypeOpConversion, StoreOpConversion, 1524 SubcOpConversion, UnboxCharOpConversion, UndefOpConversion, 1525 UnreachableOpConversion, ZeroOpConversion>(typeConverter); 1526 mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern); 1527 mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter, 1528 pattern); 1529 mlir::ConversionTarget target{*context}; 1530 target.addLegalDialect<mlir::LLVM::LLVMDialect>(); 1531 1532 // required NOPs for applying a full conversion 1533 target.addLegalOp<mlir::ModuleOp>(); 1534 1535 // apply the patterns 1536 if (mlir::failed(mlir::applyFullConversion(getModule(), target, 1537 std::move(pattern)))) { 1538 signalPassFailure(); 1539 } 1540 } 1541 }; 1542 } // namespace 1543 1544 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() { 1545 return std::make_unique<FIRToLLVMLowering>(); 1546 } 1547