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 "flang/Optimizer/Support/TypeCode.h" 19 #include "mlir/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.h" 20 #include "mlir/Conversion/LLVMCommon/Pattern.h" 21 #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h" 22 #include "mlir/IR/BuiltinTypes.h" 23 #include "mlir/IR/Matchers.h" 24 #include "mlir/Pass/Pass.h" 25 #include "llvm/ADT/ArrayRef.h" 26 27 #define DEBUG_TYPE "flang-codegen" 28 29 // fir::LLVMTypeConverter for converting to LLVM IR dialect types. 30 #include "TypeConverter.h" 31 32 // TODO: This should really be recovered from the specified target. 33 static constexpr unsigned defaultAlign = 8; 34 35 /// `fir.box` attribute values as defined for CFI_attribute_t in 36 /// flang/ISO_Fortran_binding.h. 37 static constexpr unsigned kAttrPointer = CFI_attribute_pointer; 38 static constexpr unsigned kAttrAllocatable = CFI_attribute_allocatable; 39 40 static mlir::LLVM::ConstantOp 41 genConstantIndex(mlir::Location loc, mlir::Type ity, 42 mlir::ConversionPatternRewriter &rewriter, 43 std::int64_t offset) { 44 auto cattr = rewriter.getI64IntegerAttr(offset); 45 return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr); 46 } 47 48 static Block *createBlock(mlir::ConversionPatternRewriter &rewriter, 49 mlir::Block *insertBefore) { 50 assert(insertBefore && "expected valid insertion block"); 51 return rewriter.createBlock(insertBefore->getParent(), 52 mlir::Region::iterator(insertBefore)); 53 } 54 55 namespace { 56 /// FIR conversion pattern template 57 template <typename FromOp> 58 class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> { 59 public: 60 explicit FIROpConversion(fir::LLVMTypeConverter &lowering) 61 : mlir::ConvertOpToLLVMPattern<FromOp>(lowering) {} 62 63 protected: 64 mlir::Type convertType(mlir::Type ty) const { 65 return lowerTy().convertType(ty); 66 } 67 68 mlir::LLVM::ConstantOp 69 genI32Constant(mlir::Location loc, mlir::ConversionPatternRewriter &rewriter, 70 int value) const { 71 mlir::Type i32Ty = rewriter.getI32Type(); 72 mlir::IntegerAttr attr = rewriter.getI32IntegerAttr(value); 73 return rewriter.create<mlir::LLVM::ConstantOp>(loc, i32Ty, attr); 74 } 75 76 mlir::LLVM::ConstantOp 77 genConstantOffset(mlir::Location loc, 78 mlir::ConversionPatternRewriter &rewriter, 79 int offset) const { 80 mlir::Type ity = lowerTy().offsetType(); 81 mlir::IntegerAttr cattr = rewriter.getI32IntegerAttr(offset); 82 return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr); 83 } 84 85 /// Construct code sequence to extract the specifc value from a `fir.box`. 86 mlir::Value getValueFromBox(mlir::Location loc, mlir::Value box, 87 mlir::Type resultTy, 88 mlir::ConversionPatternRewriter &rewriter, 89 unsigned boxValue) const { 90 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 91 mlir::LLVM::ConstantOp cValuePos = 92 genConstantOffset(loc, rewriter, boxValue); 93 auto pty = mlir::LLVM::LLVMPointerType::get(resultTy); 94 auto p = rewriter.create<mlir::LLVM::GEPOp>( 95 loc, pty, mlir::ValueRange{box, c0, cValuePos}); 96 return rewriter.create<mlir::LLVM::LoadOp>(loc, resultTy, p); 97 } 98 99 /// Method to construct code sequence to get the triple for dimension `dim` 100 /// from a box. 101 SmallVector<mlir::Value, 3> 102 getDimsFromBox(mlir::Location loc, ArrayRef<mlir::Type> retTys, 103 mlir::Value box, mlir::Value dim, 104 mlir::ConversionPatternRewriter &rewriter) const { 105 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 106 mlir::LLVM::ConstantOp cDims = 107 genConstantOffset(loc, rewriter, kDimsPosInBox); 108 mlir::LLVM::LoadOp l0 = 109 loadFromOffset(loc, box, c0, cDims, dim, 0, retTys[0], rewriter); 110 mlir::LLVM::LoadOp l1 = 111 loadFromOffset(loc, box, c0, cDims, dim, 1, retTys[1], rewriter); 112 mlir::LLVM::LoadOp l2 = 113 loadFromOffset(loc, box, c0, cDims, dim, 2, retTys[2], rewriter); 114 return {l0.getResult(), l1.getResult(), l2.getResult()}; 115 } 116 117 mlir::LLVM::LoadOp 118 loadFromOffset(mlir::Location loc, mlir::Value a, mlir::LLVM::ConstantOp c0, 119 mlir::LLVM::ConstantOp cDims, mlir::Value dim, int off, 120 mlir::Type ty, 121 mlir::ConversionPatternRewriter &rewriter) const { 122 auto pty = mlir::LLVM::LLVMPointerType::get(ty); 123 mlir::LLVM::ConstantOp c = genConstantOffset(loc, rewriter, off); 124 mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, a, c0, cDims, dim, c); 125 return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p); 126 } 127 128 /// Read base address from a fir.box. Returned address has type ty. 129 mlir::Value 130 loadBaseAddrFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box, 131 mlir::ConversionPatternRewriter &rewriter) const { 132 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 133 mlir::LLVM::ConstantOp cAddr = 134 genConstantOffset(loc, rewriter, kAddrPosInBox); 135 auto pty = mlir::LLVM::LLVMPointerType::get(ty); 136 mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cAddr); 137 return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p); 138 } 139 140 mlir::Value 141 loadElementSizeFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box, 142 mlir::ConversionPatternRewriter &rewriter) const { 143 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 144 mlir::LLVM::ConstantOp cElemLen = 145 genConstantOffset(loc, rewriter, kElemLenPosInBox); 146 auto pty = mlir::LLVM::LLVMPointerType::get(ty); 147 mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cElemLen); 148 return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p); 149 } 150 151 // Load the attribute from the \p box and perform a check against \p maskValue 152 // The final comparison is implemented as `(attribute & maskValue) != 0`. 153 mlir::Value genBoxAttributeCheck(mlir::Location loc, mlir::Value box, 154 mlir::ConversionPatternRewriter &rewriter, 155 unsigned maskValue) const { 156 mlir::Type attrTy = rewriter.getI32Type(); 157 mlir::Value attribute = 158 getValueFromBox(loc, box, attrTy, rewriter, kAttributePosInBox); 159 mlir::LLVM::ConstantOp attrMask = 160 genConstantOffset(loc, rewriter, maskValue); 161 auto maskRes = 162 rewriter.create<mlir::LLVM::AndOp>(loc, attrTy, attribute, attrMask); 163 mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 164 return rewriter.create<mlir::LLVM::ICmpOp>( 165 loc, mlir::LLVM::ICmpPredicate::ne, maskRes, c0); 166 } 167 168 // Get the element type given an LLVM type that is of the form 169 // [llvm.ptr](array|struct|vector)+ and the provided indexes. 170 static mlir::Type getBoxEleTy(mlir::Type type, 171 llvm::ArrayRef<unsigned> indexes) { 172 if (auto t = type.dyn_cast<mlir::LLVM::LLVMPointerType>()) 173 type = t.getElementType(); 174 for (auto i : indexes) { 175 if (auto t = type.dyn_cast<mlir::LLVM::LLVMStructType>()) { 176 assert(!t.isOpaque() && i < t.getBody().size()); 177 type = t.getBody()[i]; 178 } else if (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 179 type = t.getElementType(); 180 } else if (auto t = type.dyn_cast<mlir::VectorType>()) { 181 type = t.getElementType(); 182 } else { 183 fir::emitFatalError(mlir::UnknownLoc::get(type.getContext()), 184 "request for invalid box element type"); 185 } 186 } 187 return type; 188 } 189 190 template <typename... ARGS> 191 mlir::LLVM::GEPOp genGEP(mlir::Location loc, mlir::Type ty, 192 mlir::ConversionPatternRewriter &rewriter, 193 mlir::Value base, ARGS... args) const { 194 SmallVector<mlir::Value> cv{args...}; 195 return rewriter.create<mlir::LLVM::GEPOp>(loc, ty, base, cv); 196 } 197 198 /// Perform an extension or truncation as needed on an integer value. Lowering 199 /// to the specific target may involve some sign-extending or truncation of 200 /// values, particularly to fit them from abstract box types to the 201 /// appropriate reified structures. 202 mlir::Value integerCast(mlir::Location loc, 203 mlir::ConversionPatternRewriter &rewriter, 204 mlir::Type ty, mlir::Value val) const { 205 auto valTy = val.getType(); 206 // If the value was not yet lowered, lower its type so that it can 207 // be used in getPrimitiveTypeSizeInBits. 208 if (!valTy.isa<mlir::IntegerType>()) 209 valTy = convertType(valTy); 210 auto toSize = mlir::LLVM::getPrimitiveTypeSizeInBits(ty); 211 auto fromSize = mlir::LLVM::getPrimitiveTypeSizeInBits(valTy); 212 if (toSize < fromSize) 213 return rewriter.create<mlir::LLVM::TruncOp>(loc, ty, val); 214 if (toSize > fromSize) 215 return rewriter.create<mlir::LLVM::SExtOp>(loc, ty, val); 216 return val; 217 } 218 219 fir::LLVMTypeConverter &lowerTy() const { 220 return *static_cast<fir::LLVMTypeConverter *>(this->getTypeConverter()); 221 } 222 }; 223 224 /// FIR conversion pattern template 225 template <typename FromOp> 226 class FIROpAndTypeConversion : public FIROpConversion<FromOp> { 227 public: 228 using FIROpConversion<FromOp>::FIROpConversion; 229 using OpAdaptor = typename FromOp::Adaptor; 230 231 mlir::LogicalResult 232 matchAndRewrite(FromOp op, OpAdaptor adaptor, 233 mlir::ConversionPatternRewriter &rewriter) const final { 234 mlir::Type ty = this->convertType(op.getType()); 235 return doRewrite(op, ty, adaptor, rewriter); 236 } 237 238 virtual mlir::LogicalResult 239 doRewrite(FromOp addr, mlir::Type ty, OpAdaptor adaptor, 240 mlir::ConversionPatternRewriter &rewriter) const = 0; 241 }; 242 243 /// Create value signaling an absent optional argument in a call, e.g. 244 /// `fir.absent !fir.ref<i64>` --> `llvm.mlir.null : !llvm.ptr<i64>` 245 struct AbsentOpConversion : public FIROpConversion<fir::AbsentOp> { 246 using FIROpConversion::FIROpConversion; 247 248 mlir::LogicalResult 249 matchAndRewrite(fir::AbsentOp absent, OpAdaptor, 250 mlir::ConversionPatternRewriter &rewriter) const override { 251 mlir::Type ty = convertType(absent.getType()); 252 mlir::Location loc = absent.getLoc(); 253 254 if (absent.getType().isa<fir::BoxCharType>()) { 255 auto structTy = ty.cast<mlir::LLVM::LLVMStructType>(); 256 assert(!structTy.isOpaque() && !structTy.getBody().empty()); 257 auto undefStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 258 auto nullField = 259 rewriter.create<mlir::LLVM::NullOp>(loc, structTy.getBody()[0]); 260 mlir::MLIRContext *ctx = absent.getContext(); 261 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 262 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 263 absent, ty, undefStruct, nullField, c0); 264 } else { 265 rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(absent, ty); 266 } 267 return success(); 268 } 269 }; 270 271 // Lower `fir.address_of` operation to `llvm.address_of` operation. 272 struct AddrOfOpConversion : public FIROpConversion<fir::AddrOfOp> { 273 using FIROpConversion::FIROpConversion; 274 275 mlir::LogicalResult 276 matchAndRewrite(fir::AddrOfOp addr, OpAdaptor adaptor, 277 mlir::ConversionPatternRewriter &rewriter) const override { 278 auto ty = convertType(addr.getType()); 279 rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>( 280 addr, ty, addr.symbol().getRootReference().getValue()); 281 return success(); 282 } 283 }; 284 } // namespace 285 286 /// Lookup the function to compute the memory size of this parametric derived 287 /// type. The size of the object may depend on the LEN type parameters of the 288 /// derived type. 289 static mlir::LLVM::LLVMFuncOp 290 getDependentTypeMemSizeFn(fir::RecordType recTy, fir::AllocaOp op, 291 mlir::ConversionPatternRewriter &rewriter) { 292 auto module = op->getParentOfType<mlir::ModuleOp>(); 293 std::string name = recTy.getName().str() + "P.mem.size"; 294 return module.lookupSymbol<mlir::LLVM::LLVMFuncOp>(name); 295 } 296 297 namespace { 298 /// convert to LLVM IR dialect `alloca` 299 struct AllocaOpConversion : public FIROpConversion<fir::AllocaOp> { 300 using FIROpConversion::FIROpConversion; 301 302 mlir::LogicalResult 303 matchAndRewrite(fir::AllocaOp alloc, OpAdaptor adaptor, 304 mlir::ConversionPatternRewriter &rewriter) const override { 305 mlir::ValueRange operands = adaptor.getOperands(); 306 auto loc = alloc.getLoc(); 307 mlir::Type ity = lowerTy().indexType(); 308 unsigned i = 0; 309 mlir::Value size = genConstantIndex(loc, ity, rewriter, 1).getResult(); 310 mlir::Type ty = convertType(alloc.getType()); 311 mlir::Type resultTy = ty; 312 if (alloc.hasLenParams()) { 313 unsigned end = alloc.numLenParams(); 314 llvm::SmallVector<mlir::Value> lenParams; 315 for (; i < end; ++i) 316 lenParams.push_back(operands[i]); 317 mlir::Type scalarType = fir::unwrapSequenceType(alloc.getInType()); 318 if (auto chrTy = scalarType.dyn_cast<fir::CharacterType>()) { 319 fir::CharacterType rawCharTy = fir::CharacterType::getUnknownLen( 320 chrTy.getContext(), chrTy.getFKind()); 321 ty = mlir::LLVM::LLVMPointerType::get(convertType(rawCharTy)); 322 assert(end == 1); 323 size = integerCast(loc, rewriter, ity, lenParams[0]); 324 } else if (auto recTy = scalarType.dyn_cast<fir::RecordType>()) { 325 mlir::LLVM::LLVMFuncOp memSizeFn = 326 getDependentTypeMemSizeFn(recTy, alloc, rewriter); 327 if (!memSizeFn) 328 emitError(loc, "did not find allocation function"); 329 mlir::NamedAttribute attr = rewriter.getNamedAttr( 330 "callee", mlir::SymbolRefAttr::get(memSizeFn)); 331 auto call = rewriter.create<mlir::LLVM::CallOp>( 332 loc, ity, lenParams, llvm::ArrayRef<mlir::NamedAttribute>{attr}); 333 size = call.getResult(0); 334 ty = mlir::LLVM::LLVMPointerType::get( 335 mlir::IntegerType::get(alloc.getContext(), 8)); 336 } else { 337 return emitError(loc, "unexpected type ") 338 << scalarType << " with type parameters"; 339 } 340 } 341 if (alloc.hasShapeOperands()) { 342 mlir::Type allocEleTy = fir::unwrapRefType(alloc.getType()); 343 // Scale the size by constant factors encoded in the array type. 344 if (auto seqTy = allocEleTy.dyn_cast<fir::SequenceType>()) { 345 fir::SequenceType::Extent constSize = 1; 346 for (auto extent : seqTy.getShape()) 347 if (extent != fir::SequenceType::getUnknownExtent()) 348 constSize *= extent; 349 mlir::Value constVal{ 350 genConstantIndex(loc, ity, rewriter, constSize).getResult()}; 351 size = rewriter.create<mlir::LLVM::MulOp>(loc, ity, size, constVal); 352 } 353 unsigned end = operands.size(); 354 for (; i < end; ++i) 355 size = rewriter.create<mlir::LLVM::MulOp>( 356 loc, ity, size, integerCast(loc, rewriter, ity, operands[i])); 357 } 358 if (ty == resultTy) { 359 // Do not emit the bitcast if ty and resultTy are the same. 360 rewriter.replaceOpWithNewOp<mlir::LLVM::AllocaOp>(alloc, ty, size, 361 alloc->getAttrs()); 362 } else { 363 auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, ty, size, 364 alloc->getAttrs()); 365 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(alloc, resultTy, al); 366 } 367 return success(); 368 } 369 }; 370 371 /// Lower `fir.box_addr` to the sequence of operations to extract the first 372 /// element of the box. 373 struct BoxAddrOpConversion : public FIROpConversion<fir::BoxAddrOp> { 374 using FIROpConversion::FIROpConversion; 375 376 mlir::LogicalResult 377 matchAndRewrite(fir::BoxAddrOp boxaddr, OpAdaptor adaptor, 378 mlir::ConversionPatternRewriter &rewriter) const override { 379 mlir::Value a = adaptor.getOperands()[0]; 380 auto loc = boxaddr.getLoc(); 381 mlir::Type ty = convertType(boxaddr.getType()); 382 if (auto argty = boxaddr.val().getType().dyn_cast<fir::BoxType>()) { 383 rewriter.replaceOp(boxaddr, loadBaseAddrFromBox(loc, ty, a, rewriter)); 384 } else { 385 auto c0attr = rewriter.getI32IntegerAttr(0); 386 auto c0 = mlir::ArrayAttr::get(boxaddr.getContext(), c0attr); 387 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(boxaddr, ty, a, 388 c0); 389 } 390 return success(); 391 } 392 }; 393 394 /// Lower `fir.box_dims` to a sequence of operations to extract the requested 395 /// dimension infomartion from the boxed value. 396 /// Result in a triple set of GEPs and loads. 397 struct BoxDimsOpConversion : public FIROpConversion<fir::BoxDimsOp> { 398 using FIROpConversion::FIROpConversion; 399 400 mlir::LogicalResult 401 matchAndRewrite(fir::BoxDimsOp boxdims, OpAdaptor adaptor, 402 mlir::ConversionPatternRewriter &rewriter) const override { 403 SmallVector<mlir::Type, 3> resultTypes = { 404 convertType(boxdims.getResult(0).getType()), 405 convertType(boxdims.getResult(1).getType()), 406 convertType(boxdims.getResult(2).getType()), 407 }; 408 auto results = 409 getDimsFromBox(boxdims.getLoc(), resultTypes, adaptor.getOperands()[0], 410 adaptor.getOperands()[1], rewriter); 411 rewriter.replaceOp(boxdims, results); 412 return success(); 413 } 414 }; 415 416 /// Lower `fir.box_elesize` to a sequence of operations ro extract the size of 417 /// an element in the boxed value. 418 struct BoxEleSizeOpConversion : public FIROpConversion<fir::BoxEleSizeOp> { 419 using FIROpConversion::FIROpConversion; 420 421 mlir::LogicalResult 422 matchAndRewrite(fir::BoxEleSizeOp boxelesz, OpAdaptor adaptor, 423 mlir::ConversionPatternRewriter &rewriter) const override { 424 mlir::Value a = adaptor.getOperands()[0]; 425 auto loc = boxelesz.getLoc(); 426 auto ty = convertType(boxelesz.getType()); 427 auto elemSize = getValueFromBox(loc, a, ty, rewriter, kElemLenPosInBox); 428 rewriter.replaceOp(boxelesz, elemSize); 429 return success(); 430 } 431 }; 432 433 /// Lower `fir.box_isalloc` to a sequence of operations to determine if the 434 /// boxed value was from an ALLOCATABLE entity. 435 struct BoxIsAllocOpConversion : public FIROpConversion<fir::BoxIsAllocOp> { 436 using FIROpConversion::FIROpConversion; 437 438 mlir::LogicalResult 439 matchAndRewrite(fir::BoxIsAllocOp boxisalloc, OpAdaptor adaptor, 440 mlir::ConversionPatternRewriter &rewriter) const override { 441 mlir::Value box = adaptor.getOperands()[0]; 442 auto loc = boxisalloc.getLoc(); 443 mlir::Value check = 444 genBoxAttributeCheck(loc, box, rewriter, kAttrAllocatable); 445 rewriter.replaceOp(boxisalloc, check); 446 return success(); 447 } 448 }; 449 450 /// Lower `fir.box_isarray` to a sequence of operations to determine if the 451 /// boxed is an array. 452 struct BoxIsArrayOpConversion : public FIROpConversion<fir::BoxIsArrayOp> { 453 using FIROpConversion::FIROpConversion; 454 455 mlir::LogicalResult 456 matchAndRewrite(fir::BoxIsArrayOp boxisarray, OpAdaptor adaptor, 457 mlir::ConversionPatternRewriter &rewriter) const override { 458 mlir::Value a = adaptor.getOperands()[0]; 459 auto loc = boxisarray.getLoc(); 460 auto rank = 461 getValueFromBox(loc, a, rewriter.getI32Type(), rewriter, kRankPosInBox); 462 auto c0 = genConstantOffset(loc, rewriter, 0); 463 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>( 464 boxisarray, mlir::LLVM::ICmpPredicate::ne, rank, c0); 465 return success(); 466 } 467 }; 468 469 /// Lower `fir.box_isptr` to a sequence of operations to determined if the 470 /// boxed value was from a POINTER entity. 471 struct BoxIsPtrOpConversion : public FIROpConversion<fir::BoxIsPtrOp> { 472 using FIROpConversion::FIROpConversion; 473 474 mlir::LogicalResult 475 matchAndRewrite(fir::BoxIsPtrOp boxisptr, OpAdaptor adaptor, 476 mlir::ConversionPatternRewriter &rewriter) const override { 477 mlir::Value box = adaptor.getOperands()[0]; 478 auto loc = boxisptr.getLoc(); 479 mlir::Value check = genBoxAttributeCheck(loc, box, rewriter, kAttrPointer); 480 rewriter.replaceOp(boxisptr, check); 481 return success(); 482 } 483 }; 484 485 /// Lower `fir.box_rank` to the sequence of operation to extract the rank from 486 /// the box. 487 struct BoxRankOpConversion : public FIROpConversion<fir::BoxRankOp> { 488 using FIROpConversion::FIROpConversion; 489 490 mlir::LogicalResult 491 matchAndRewrite(fir::BoxRankOp boxrank, OpAdaptor adaptor, 492 mlir::ConversionPatternRewriter &rewriter) const override { 493 mlir::Value a = adaptor.getOperands()[0]; 494 auto loc = boxrank.getLoc(); 495 mlir::Type ty = convertType(boxrank.getType()); 496 auto result = getValueFromBox(loc, a, ty, rewriter, kRankPosInBox); 497 rewriter.replaceOp(boxrank, result); 498 return success(); 499 } 500 }; 501 502 /// Lower `fir.string_lit` to LLVM IR dialect operation. 503 struct StringLitOpConversion : public FIROpConversion<fir::StringLitOp> { 504 using FIROpConversion::FIROpConversion; 505 506 mlir::LogicalResult 507 matchAndRewrite(fir::StringLitOp constop, OpAdaptor adaptor, 508 mlir::ConversionPatternRewriter &rewriter) const override { 509 auto ty = convertType(constop.getType()); 510 auto attr = constop.getValue(); 511 if (attr.isa<mlir::StringAttr>()) { 512 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(constop, ty, attr); 513 return success(); 514 } 515 516 auto arr = attr.cast<mlir::ArrayAttr>(); 517 auto charTy = constop.getType().cast<fir::CharacterType>(); 518 unsigned bits = lowerTy().characterBitsize(charTy); 519 mlir::Type intTy = rewriter.getIntegerType(bits); 520 auto attrs = llvm::map_range( 521 arr.getValue(), [intTy, bits](mlir::Attribute attr) -> Attribute { 522 return mlir::IntegerAttr::get( 523 intTy, 524 attr.cast<mlir::IntegerAttr>().getValue().sextOrTrunc(bits)); 525 }); 526 mlir::Type vecType = mlir::VectorType::get(arr.size(), intTy); 527 auto denseAttr = mlir::DenseElementsAttr::get( 528 vecType.cast<mlir::ShapedType>(), llvm::to_vector<8>(attrs)); 529 rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(constop, ty, 530 denseAttr); 531 return success(); 532 } 533 }; 534 535 /// Lower `fir.box_tdesc` to the sequence of operations to extract the type 536 /// descriptor from the box. 537 struct BoxTypeDescOpConversion : public FIROpConversion<fir::BoxTypeDescOp> { 538 using FIROpConversion::FIROpConversion; 539 540 mlir::LogicalResult 541 matchAndRewrite(fir::BoxTypeDescOp boxtypedesc, OpAdaptor adaptor, 542 mlir::ConversionPatternRewriter &rewriter) const override { 543 mlir::Value box = adaptor.getOperands()[0]; 544 auto loc = boxtypedesc.getLoc(); 545 mlir::Type typeTy = 546 fir::getDescFieldTypeModel<kTypePosInBox>()(boxtypedesc.getContext()); 547 auto result = getValueFromBox(loc, box, typeTy, rewriter, kTypePosInBox); 548 auto typePtrTy = mlir::LLVM::LLVMPointerType::get(typeTy); 549 rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(boxtypedesc, typePtrTy, 550 result); 551 return success(); 552 } 553 }; 554 555 // `fir.call` -> `llvm.call` 556 struct CallOpConversion : public FIROpConversion<fir::CallOp> { 557 using FIROpConversion::FIROpConversion; 558 559 mlir::LogicalResult 560 matchAndRewrite(fir::CallOp call, OpAdaptor adaptor, 561 mlir::ConversionPatternRewriter &rewriter) const override { 562 SmallVector<mlir::Type> resultTys; 563 for (auto r : call.getResults()) 564 resultTys.push_back(convertType(r.getType())); 565 rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>( 566 call, resultTys, adaptor.getOperands(), call->getAttrs()); 567 return success(); 568 } 569 }; 570 571 static mlir::Type getComplexEleTy(mlir::Type complex) { 572 if (auto cc = complex.dyn_cast<mlir::ComplexType>()) 573 return cc.getElementType(); 574 return complex.cast<fir::ComplexType>().getElementType(); 575 } 576 577 /// Compare complex values 578 /// 579 /// Per 10.1, the only comparisons available are .EQ. (oeq) and .NE. (une). 580 /// 581 /// For completeness, all other comparison are done on the real component only. 582 struct CmpcOpConversion : public FIROpConversion<fir::CmpcOp> { 583 using FIROpConversion::FIROpConversion; 584 585 mlir::LogicalResult 586 matchAndRewrite(fir::CmpcOp cmp, OpAdaptor adaptor, 587 mlir::ConversionPatternRewriter &rewriter) const override { 588 mlir::ValueRange operands = adaptor.getOperands(); 589 mlir::MLIRContext *ctxt = cmp.getContext(); 590 mlir::Type eleTy = convertType(getComplexEleTy(cmp.lhs().getType())); 591 mlir::Type resTy = convertType(cmp.getType()); 592 mlir::Location loc = cmp.getLoc(); 593 auto pos0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0)); 594 SmallVector<mlir::Value, 2> rp{rewriter.create<mlir::LLVM::ExtractValueOp>( 595 loc, eleTy, operands[0], pos0), 596 rewriter.create<mlir::LLVM::ExtractValueOp>( 597 loc, eleTy, operands[1], pos0)}; 598 auto rcp = 599 rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, rp, cmp->getAttrs()); 600 auto pos1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1)); 601 SmallVector<mlir::Value, 2> ip{rewriter.create<mlir::LLVM::ExtractValueOp>( 602 loc, eleTy, operands[0], pos1), 603 rewriter.create<mlir::LLVM::ExtractValueOp>( 604 loc, eleTy, operands[1], pos1)}; 605 auto icp = 606 rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, ip, cmp->getAttrs()); 607 SmallVector<mlir::Value, 2> cp{rcp, icp}; 608 switch (cmp.getPredicate()) { 609 case mlir::arith::CmpFPredicate::OEQ: // .EQ. 610 rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(cmp, resTy, cp); 611 break; 612 case mlir::arith::CmpFPredicate::UNE: // .NE. 613 rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(cmp, resTy, cp); 614 break; 615 default: 616 rewriter.replaceOp(cmp, rcp.getResult()); 617 break; 618 } 619 return success(); 620 } 621 }; 622 623 /// Lower complex constants 624 struct ConstcOpConversion : public FIROpConversion<fir::ConstcOp> { 625 using FIROpConversion::FIROpConversion; 626 627 mlir::LogicalResult 628 matchAndRewrite(fir::ConstcOp conc, OpAdaptor, 629 mlir::ConversionPatternRewriter &rewriter) const override { 630 mlir::Location loc = conc.getLoc(); 631 mlir::MLIRContext *ctx = conc.getContext(); 632 mlir::Type ty = convertType(conc.getType()); 633 mlir::Type ety = convertType(getComplexEleTy(conc.getType())); 634 auto realFloatAttr = mlir::FloatAttr::get(ety, getValue(conc.getReal())); 635 auto realPart = 636 rewriter.create<mlir::LLVM::ConstantOp>(loc, ety, realFloatAttr); 637 auto imFloatAttr = mlir::FloatAttr::get(ety, getValue(conc.getImaginary())); 638 auto imPart = 639 rewriter.create<mlir::LLVM::ConstantOp>(loc, ety, imFloatAttr); 640 auto realIndex = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 641 auto imIndex = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 642 auto undef = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 643 auto setReal = rewriter.create<mlir::LLVM::InsertValueOp>( 644 loc, ty, undef, realPart, realIndex); 645 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(conc, ty, setReal, 646 imPart, imIndex); 647 return success(); 648 } 649 650 inline APFloat getValue(mlir::Attribute attr) const { 651 return attr.cast<fir::RealAttr>().getValue(); 652 } 653 }; 654 655 /// convert value of from-type to value of to-type 656 struct ConvertOpConversion : public FIROpConversion<fir::ConvertOp> { 657 using FIROpConversion::FIROpConversion; 658 659 static bool isFloatingPointTy(mlir::Type ty) { 660 return ty.isa<mlir::FloatType>(); 661 } 662 663 mlir::LogicalResult 664 matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor, 665 mlir::ConversionPatternRewriter &rewriter) const override { 666 auto fromTy = convertType(convert.value().getType()); 667 auto toTy = convertType(convert.res().getType()); 668 mlir::Value op0 = adaptor.getOperands()[0]; 669 if (fromTy == toTy) { 670 rewriter.replaceOp(convert, op0); 671 return success(); 672 } 673 auto loc = convert.getLoc(); 674 auto convertFpToFp = [&](mlir::Value val, unsigned fromBits, 675 unsigned toBits, mlir::Type toTy) -> mlir::Value { 676 if (fromBits == toBits) { 677 // TODO: Converting between two floating-point representations with the 678 // same bitwidth is not allowed for now. 679 mlir::emitError(loc, 680 "cannot implicitly convert between two floating-point " 681 "representations of the same bitwidth"); 682 return {}; 683 } 684 if (fromBits > toBits) 685 return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val); 686 return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val); 687 }; 688 // Complex to complex conversion. 689 if (fir::isa_complex(convert.value().getType()) && 690 fir::isa_complex(convert.res().getType())) { 691 // Special case: handle the conversion of a complex such that both the 692 // real and imaginary parts are converted together. 693 auto zero = mlir::ArrayAttr::get(convert.getContext(), 694 rewriter.getI32IntegerAttr(0)); 695 auto one = mlir::ArrayAttr::get(convert.getContext(), 696 rewriter.getI32IntegerAttr(1)); 697 auto ty = convertType(getComplexEleTy(convert.value().getType())); 698 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, zero); 699 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, one); 700 auto nt = convertType(getComplexEleTy(convert.res().getType())); 701 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty); 702 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt); 703 auto rc = convertFpToFp(rp, fromBits, toBits, nt); 704 auto ic = convertFpToFp(ip, fromBits, toBits, nt); 705 auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy); 706 auto i1 = 707 rewriter.create<mlir::LLVM::InsertValueOp>(loc, toTy, un, rc, zero); 708 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, toTy, i1, 709 ic, one); 710 return mlir::success(); 711 } 712 // Floating point to floating point conversion. 713 if (isFloatingPointTy(fromTy)) { 714 if (isFloatingPointTy(toTy)) { 715 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy); 716 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy); 717 auto v = convertFpToFp(op0, fromBits, toBits, toTy); 718 rewriter.replaceOp(convert, v); 719 return mlir::success(); 720 } 721 if (toTy.isa<mlir::IntegerType>()) { 722 rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0); 723 return mlir::success(); 724 } 725 } else if (fromTy.isa<mlir::IntegerType>()) { 726 // Integer to integer conversion. 727 if (toTy.isa<mlir::IntegerType>()) { 728 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy); 729 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy); 730 assert(fromBits != toBits); 731 if (fromBits > toBits) { 732 rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0); 733 return mlir::success(); 734 } 735 rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0); 736 return mlir::success(); 737 } 738 // Integer to floating point conversion. 739 if (isFloatingPointTy(toTy)) { 740 rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0); 741 return mlir::success(); 742 } 743 // Integer to pointer conversion. 744 if (toTy.isa<mlir::LLVM::LLVMPointerType>()) { 745 rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0); 746 return mlir::success(); 747 } 748 } else if (fromTy.isa<mlir::LLVM::LLVMPointerType>()) { 749 // Pointer to integer conversion. 750 if (toTy.isa<mlir::IntegerType>()) { 751 rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0); 752 return mlir::success(); 753 } 754 // Pointer to pointer conversion. 755 if (toTy.isa<mlir::LLVM::LLVMPointerType>()) { 756 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0); 757 return mlir::success(); 758 } 759 } 760 return emitError(loc) << "cannot convert " << fromTy << " to " << toTy; 761 } 762 }; 763 764 /// Lower `fir.dispatch` operation. A virtual call to a method in a dispatch 765 /// table. 766 struct DispatchOpConversion : public FIROpConversion<fir::DispatchOp> { 767 using FIROpConversion::FIROpConversion; 768 769 mlir::LogicalResult 770 matchAndRewrite(fir::DispatchOp dispatch, OpAdaptor adaptor, 771 mlir::ConversionPatternRewriter &rewriter) const override { 772 return rewriter.notifyMatchFailure( 773 dispatch, "fir.dispatch codegen is not implemented yet"); 774 } 775 }; 776 777 /// Lower `fir.dispatch_table` operation. The dispatch table for a Fortran 778 /// derived type. 779 struct DispatchTableOpConversion 780 : public FIROpConversion<fir::DispatchTableOp> { 781 using FIROpConversion::FIROpConversion; 782 783 mlir::LogicalResult 784 matchAndRewrite(fir::DispatchTableOp dispTab, OpAdaptor adaptor, 785 mlir::ConversionPatternRewriter &rewriter) const override { 786 return rewriter.notifyMatchFailure( 787 dispTab, "fir.dispatch_table codegen is not implemented yet"); 788 } 789 }; 790 791 /// Lower `fir.dt_entry` operation. An entry in a dispatch table; binds a 792 /// method-name to a function. 793 struct DTEntryOpConversion : public FIROpConversion<fir::DTEntryOp> { 794 using FIROpConversion::FIROpConversion; 795 796 mlir::LogicalResult 797 matchAndRewrite(fir::DTEntryOp dtEnt, OpAdaptor adaptor, 798 mlir::ConversionPatternRewriter &rewriter) const override { 799 return rewriter.notifyMatchFailure( 800 dtEnt, "fir.dt_entry codegen is not implemented yet"); 801 } 802 }; 803 804 /// Lower `fir.global_len` operation. 805 struct GlobalLenOpConversion : public FIROpConversion<fir::GlobalLenOp> { 806 using FIROpConversion::FIROpConversion; 807 808 mlir::LogicalResult 809 matchAndRewrite(fir::GlobalLenOp globalLen, OpAdaptor adaptor, 810 mlir::ConversionPatternRewriter &rewriter) const override { 811 return rewriter.notifyMatchFailure( 812 globalLen, "fir.global_len codegen is not implemented yet"); 813 } 814 }; 815 816 /// Lower `fir.gentypedesc` to a global constant. 817 struct GenTypeDescOpConversion : public FIROpConversion<fir::GenTypeDescOp> { 818 using FIROpConversion::FIROpConversion; 819 820 mlir::LogicalResult 821 matchAndRewrite(fir::GenTypeDescOp gentypedesc, OpAdaptor adaptor, 822 mlir::ConversionPatternRewriter &rewriter) const override { 823 return rewriter.notifyMatchFailure( 824 gentypedesc, "fir.fir.gentypedesc codegen is not implemented yet"); 825 } 826 }; 827 828 /// Lower `fir.has_value` operation to `llvm.return` operation. 829 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> { 830 using FIROpConversion::FIROpConversion; 831 832 mlir::LogicalResult 833 matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor, 834 mlir::ConversionPatternRewriter &rewriter) const override { 835 rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands()); 836 return success(); 837 } 838 }; 839 840 /// Lower `fir.global` operation to `llvm.global` operation. 841 /// `fir.insert_on_range` operations are replaced with constant dense attribute 842 /// if they are applied on the full range. 843 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> { 844 using FIROpConversion::FIROpConversion; 845 846 mlir::LogicalResult 847 matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor, 848 mlir::ConversionPatternRewriter &rewriter) const override { 849 auto tyAttr = convertType(global.getType()); 850 if (global.getType().isa<fir::BoxType>()) 851 tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType(); 852 auto loc = global.getLoc(); 853 mlir::Attribute initAttr{}; 854 if (global.initVal()) 855 initAttr = global.initVal().getValue(); 856 auto linkage = convertLinkage(global.linkName()); 857 auto isConst = global.constant().hasValue(); 858 auto g = rewriter.create<mlir::LLVM::GlobalOp>( 859 loc, tyAttr, isConst, linkage, global.sym_name(), initAttr); 860 auto &gr = g.getInitializerRegion(); 861 rewriter.inlineRegionBefore(global.region(), gr, gr.end()); 862 if (!gr.empty()) { 863 // Replace insert_on_range with a constant dense attribute if the 864 // initialization is on the full range. 865 auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>(); 866 for (auto insertOp : insertOnRangeOps) { 867 if (isFullRange(insertOp.coor(), insertOp.getType())) { 868 auto seqTyAttr = convertType(insertOp.getType()); 869 auto *op = insertOp.val().getDefiningOp(); 870 auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op); 871 if (!constant) { 872 auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op); 873 if (!convertOp) 874 continue; 875 constant = cast<mlir::arith::ConstantOp>( 876 convertOp.value().getDefiningOp()); 877 } 878 mlir::Type vecType = mlir::VectorType::get( 879 insertOp.getType().getShape(), constant.getType()); 880 auto denseAttr = mlir::DenseElementsAttr::get( 881 vecType.cast<ShapedType>(), constant.value()); 882 rewriter.setInsertionPointAfter(insertOp); 883 rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>( 884 insertOp, seqTyAttr, denseAttr); 885 } 886 } 887 } 888 rewriter.eraseOp(global); 889 return success(); 890 } 891 892 bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const { 893 auto extents = seqTy.getShape(); 894 if (indexes.size() / 2 != extents.size()) 895 return false; 896 for (unsigned i = 0; i < indexes.size(); i += 2) { 897 if (indexes[i].cast<IntegerAttr>().getInt() != 0) 898 return false; 899 if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1) 900 return false; 901 } 902 return true; 903 } 904 905 // TODO: String comparaison should be avoided. Replace linkName with an 906 // enumeration. 907 mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const { 908 if (optLinkage.hasValue()) { 909 auto name = optLinkage.getValue(); 910 if (name == "internal") 911 return mlir::LLVM::Linkage::Internal; 912 if (name == "linkonce") 913 return mlir::LLVM::Linkage::Linkonce; 914 if (name == "common") 915 return mlir::LLVM::Linkage::Common; 916 if (name == "weak") 917 return mlir::LLVM::Linkage::Weak; 918 } 919 return mlir::LLVM::Linkage::External; 920 } 921 }; 922 923 void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest, 924 Optional<mlir::ValueRange> destOps, 925 mlir::ConversionPatternRewriter &rewriter, 926 mlir::Block *newBlock) { 927 if (destOps.hasValue()) 928 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, destOps.getValue(), 929 newBlock, mlir::ValueRange()); 930 else 931 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock); 932 } 933 934 template <typename A, typename B> 935 void genBrOp(A caseOp, mlir::Block *dest, Optional<B> destOps, 936 mlir::ConversionPatternRewriter &rewriter) { 937 if (destOps.hasValue()) 938 rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, destOps.getValue(), 939 dest); 940 else 941 rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, llvm::None, dest); 942 } 943 944 void genCaseLadderStep(mlir::Location loc, mlir::Value cmp, mlir::Block *dest, 945 Optional<mlir::ValueRange> destOps, 946 mlir::ConversionPatternRewriter &rewriter) { 947 auto *thisBlock = rewriter.getInsertionBlock(); 948 auto *newBlock = createBlock(rewriter, dest); 949 rewriter.setInsertionPointToEnd(thisBlock); 950 genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock); 951 rewriter.setInsertionPointToEnd(newBlock); 952 } 953 954 /// Conversion of `fir.select_case` 955 /// 956 /// The `fir.select_case` operation is converted to a if-then-else ladder. 957 /// Depending on the case condition type, one or several comparison and 958 /// conditional branching can be generated. 959 /// 960 /// A a point value case such as `case(4)`, a lower bound case such as 961 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a 962 /// simple comparison between the selector value and the constant value in the 963 /// case. The block associated with the case condition is then executed if 964 /// the comparison succeed otherwise it branch to the next block with the 965 /// comparison for the the next case conditon. 966 /// 967 /// A closed interval case condition such as `case(7:10)` is converted with a 968 /// first comparison and conditional branching for the lower bound. If 969 /// successful, it branch to a second block with the comparison for the 970 /// upper bound in the same case condition. 971 /// 972 /// TODO: lowering of CHARACTER type cases is not handled yet. 973 struct SelectCaseOpConversion : public FIROpConversion<fir::SelectCaseOp> { 974 using FIROpConversion::FIROpConversion; 975 976 mlir::LogicalResult 977 matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor, 978 mlir::ConversionPatternRewriter &rewriter) const override { 979 unsigned conds = caseOp.getNumConditions(); 980 llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue(); 981 // Type can be CHARACTER, INTEGER, or LOGICAL (C1145) 982 LLVM_ATTRIBUTE_UNUSED auto ty = caseOp.getSelector().getType(); 983 if (ty.isa<fir::CharacterType>()) 984 return rewriter.notifyMatchFailure(caseOp, 985 "conversion of fir.select_case with " 986 "character type not implemented yet"); 987 mlir::Value selector = caseOp.getSelector(adaptor.getOperands()); 988 auto loc = caseOp.getLoc(); 989 for (unsigned t = 0; t != conds; ++t) { 990 mlir::Block *dest = caseOp.getSuccessor(t); 991 llvm::Optional<mlir::ValueRange> destOps = 992 caseOp.getSuccessorOperands(adaptor.getOperands(), t); 993 llvm::Optional<mlir::ValueRange> cmpOps = 994 *caseOp.getCompareOperands(adaptor.getOperands(), t); 995 mlir::Value caseArg = *(cmpOps.getValue().begin()); 996 mlir::Attribute attr = cases[t]; 997 if (attr.isa<fir::PointIntervalAttr>()) { 998 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 999 loc, mlir::LLVM::ICmpPredicate::eq, selector, caseArg); 1000 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 1001 continue; 1002 } 1003 if (attr.isa<fir::LowerBoundAttr>()) { 1004 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 1005 loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector); 1006 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 1007 continue; 1008 } 1009 if (attr.isa<fir::UpperBoundAttr>()) { 1010 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 1011 loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg); 1012 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 1013 continue; 1014 } 1015 if (attr.isa<fir::ClosedIntervalAttr>()) { 1016 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 1017 loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector); 1018 auto *thisBlock = rewriter.getInsertionBlock(); 1019 auto *newBlock1 = createBlock(rewriter, dest); 1020 auto *newBlock2 = createBlock(rewriter, dest); 1021 rewriter.setInsertionPointToEnd(thisBlock); 1022 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, newBlock1, newBlock2); 1023 rewriter.setInsertionPointToEnd(newBlock1); 1024 mlir::Value caseArg0 = *(cmpOps.getValue().begin() + 1); 1025 auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>( 1026 loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg0); 1027 genCondBrOp(loc, cmp0, dest, destOps, rewriter, newBlock2); 1028 rewriter.setInsertionPointToEnd(newBlock2); 1029 continue; 1030 } 1031 assert(attr.isa<mlir::UnitAttr>()); 1032 assert((t + 1 == conds) && "unit must be last"); 1033 genBrOp(caseOp, dest, destOps, rewriter); 1034 } 1035 return success(); 1036 } 1037 }; 1038 1039 template <typename OP> 1040 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select, 1041 typename OP::Adaptor adaptor, 1042 mlir::ConversionPatternRewriter &rewriter) { 1043 unsigned conds = select.getNumConditions(); 1044 auto cases = select.getCases().getValue(); 1045 mlir::Value selector = adaptor.selector(); 1046 auto loc = select.getLoc(); 1047 assert(conds > 0 && "select must have cases"); 1048 1049 llvm::SmallVector<mlir::Block *> destinations; 1050 llvm::SmallVector<mlir::ValueRange> destinationsOperands; 1051 mlir::Block *defaultDestination; 1052 mlir::ValueRange defaultOperands; 1053 llvm::SmallVector<int32_t> caseValues; 1054 1055 for (unsigned t = 0; t != conds; ++t) { 1056 mlir::Block *dest = select.getSuccessor(t); 1057 auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t); 1058 const mlir::Attribute &attr = cases[t]; 1059 if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) { 1060 destinations.push_back(dest); 1061 destinationsOperands.push_back(destOps.hasValue() ? *destOps 1062 : ValueRange()); 1063 caseValues.push_back(intAttr.getInt()); 1064 continue; 1065 } 1066 assert(attr.template dyn_cast_or_null<mlir::UnitAttr>()); 1067 assert((t + 1 == conds) && "unit must be last"); 1068 defaultDestination = dest; 1069 defaultOperands = destOps.hasValue() ? *destOps : ValueRange(); 1070 } 1071 1072 // LLVM::SwitchOp takes a i32 type for the selector. 1073 if (select.getSelector().getType() != rewriter.getI32Type()) 1074 selector = 1075 rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector); 1076 1077 rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>( 1078 select, selector, 1079 /*defaultDestination=*/defaultDestination, 1080 /*defaultOperands=*/defaultOperands, 1081 /*caseValues=*/caseValues, 1082 /*caseDestinations=*/destinations, 1083 /*caseOperands=*/destinationsOperands, 1084 /*branchWeights=*/ArrayRef<int32_t>()); 1085 } 1086 1087 /// conversion of fir::SelectOp to an if-then-else ladder 1088 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> { 1089 using FIROpConversion::FIROpConversion; 1090 1091 mlir::LogicalResult 1092 matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor, 1093 mlir::ConversionPatternRewriter &rewriter) const override { 1094 selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter); 1095 return success(); 1096 } 1097 }; 1098 1099 /// `fir.load` --> `llvm.load` 1100 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> { 1101 using FIROpConversion::FIROpConversion; 1102 1103 mlir::LogicalResult 1104 matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor, 1105 mlir::ConversionPatternRewriter &rewriter) const override { 1106 // fir.box is a special case because it is considered as an ssa values in 1107 // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box> 1108 // and fir.box end up being the same llvm types and loading a 1109 // fir.ref<fir.box> is actually a no op in LLVM. 1110 if (load.getType().isa<fir::BoxType>()) { 1111 rewriter.replaceOp(load, adaptor.getOperands()[0]); 1112 } else { 1113 mlir::Type ty = convertType(load.getType()); 1114 ArrayRef<NamedAttribute> at = load->getAttrs(); 1115 rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>( 1116 load, ty, adaptor.getOperands(), at); 1117 } 1118 return success(); 1119 } 1120 }; 1121 1122 /// Lower `fir.select_type` to LLVM IR dialect. 1123 struct SelectTypeOpConversion : public FIROpConversion<fir::SelectTypeOp> { 1124 using FIROpConversion::FIROpConversion; 1125 1126 mlir::LogicalResult 1127 matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor, 1128 mlir::ConversionPatternRewriter &rewriter) const override { 1129 return rewriter.notifyMatchFailure( 1130 select, "fir.select_type codegen is not implemented yet"); 1131 } 1132 }; 1133 1134 /// conversion of fir::SelectRankOp to an if-then-else ladder 1135 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> { 1136 using FIROpConversion::FIROpConversion; 1137 1138 mlir::LogicalResult 1139 matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor, 1140 mlir::ConversionPatternRewriter &rewriter) const override { 1141 selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter); 1142 return success(); 1143 } 1144 }; 1145 1146 /// `fir.store` --> `llvm.store` 1147 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> { 1148 using FIROpConversion::FIROpConversion; 1149 1150 mlir::LogicalResult 1151 matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor, 1152 mlir::ConversionPatternRewriter &rewriter) const override { 1153 if (store.value().getType().isa<fir::BoxType>()) { 1154 // fir.box value is actually in memory, load it first before storing it. 1155 mlir::Location loc = store.getLoc(); 1156 mlir::Type boxPtrTy = adaptor.getOperands()[0].getType(); 1157 auto val = rewriter.create<mlir::LLVM::LoadOp>( 1158 loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(), 1159 adaptor.getOperands()[0]); 1160 rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 1161 store, val, adaptor.getOperands()[1]); 1162 } else { 1163 rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 1164 store, adaptor.getOperands()[0], adaptor.getOperands()[1]); 1165 } 1166 return success(); 1167 } 1168 }; 1169 1170 /// convert to LLVM IR dialect `undef` 1171 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> { 1172 using FIROpConversion::FIROpConversion; 1173 1174 mlir::LogicalResult 1175 matchAndRewrite(fir::UndefOp undef, OpAdaptor, 1176 mlir::ConversionPatternRewriter &rewriter) const override { 1177 rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>( 1178 undef, convertType(undef.getType())); 1179 return success(); 1180 } 1181 }; 1182 1183 /// `fir.unreachable` --> `llvm.unreachable` 1184 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> { 1185 using FIROpConversion::FIROpConversion; 1186 1187 mlir::LogicalResult 1188 matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor, 1189 mlir::ConversionPatternRewriter &rewriter) const override { 1190 rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach); 1191 return success(); 1192 } 1193 }; 1194 1195 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> { 1196 using FIROpConversion::FIROpConversion; 1197 1198 mlir::LogicalResult 1199 matchAndRewrite(fir::ZeroOp zero, OpAdaptor, 1200 mlir::ConversionPatternRewriter &rewriter) const override { 1201 auto ty = convertType(zero.getType()); 1202 if (ty.isa<mlir::LLVM::LLVMPointerType>()) { 1203 rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty); 1204 } else if (ty.isa<mlir::IntegerType>()) { 1205 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 1206 zero, ty, mlir::IntegerAttr::get(zero.getType(), 0)); 1207 } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) { 1208 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 1209 zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0)); 1210 } else { 1211 // TODO: create ConstantAggregateZero for FIR aggregate/array types. 1212 return rewriter.notifyMatchFailure( 1213 zero, 1214 "conversion of fir.zero with aggregate type not implemented yet"); 1215 } 1216 return success(); 1217 } 1218 }; 1219 1220 /// Common base class for embox to descriptor conversion. 1221 template <typename OP> 1222 struct EmboxCommonConversion : public FIROpConversion<OP> { 1223 using FIROpConversion<OP>::FIROpConversion; 1224 1225 // Find the LLVMFuncOp in whose entry block the alloca should be inserted. 1226 // The order to find the LLVMFuncOp is as follows: 1227 // 1. The parent operation of the current block if it is a LLVMFuncOp. 1228 // 2. The first ancestor that is a LLVMFuncOp. 1229 mlir::LLVM::LLVMFuncOp 1230 getFuncForAllocaInsert(mlir::ConversionPatternRewriter &rewriter) const { 1231 mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp(); 1232 return mlir::isa<mlir::LLVM::LLVMFuncOp>(parentOp) 1233 ? mlir::cast<mlir::LLVM::LLVMFuncOp>(parentOp) 1234 : parentOp->getParentOfType<mlir::LLVM::LLVMFuncOp>(); 1235 } 1236 1237 // Generate an alloca of size 1 and type \p toTy. 1238 mlir::LLVM::AllocaOp 1239 genAllocaWithType(mlir::Location loc, mlir::Type toTy, unsigned alignment, 1240 mlir::ConversionPatternRewriter &rewriter) const { 1241 auto thisPt = rewriter.saveInsertionPoint(); 1242 mlir::LLVM::LLVMFuncOp func = getFuncForAllocaInsert(rewriter); 1243 rewriter.setInsertionPointToStart(&func.front()); 1244 auto size = this->genI32Constant(loc, rewriter, 1); 1245 auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, toTy, size, alignment); 1246 rewriter.restoreInsertionPoint(thisPt); 1247 return al; 1248 } 1249 1250 static int getCFIAttr(fir::BoxType boxTy) { 1251 auto eleTy = boxTy.getEleTy(); 1252 if (eleTy.isa<fir::PointerType>()) 1253 return CFI_attribute_pointer; 1254 if (eleTy.isa<fir::HeapType>()) 1255 return CFI_attribute_allocatable; 1256 return CFI_attribute_other; 1257 } 1258 1259 static fir::RecordType unwrapIfDerived(fir::BoxType boxTy) { 1260 return fir::unwrapSequenceType(fir::dyn_cast_ptrOrBoxEleTy(boxTy)) 1261 .template dyn_cast<fir::RecordType>(); 1262 } 1263 static bool isDerivedTypeWithLenParams(fir::BoxType boxTy) { 1264 auto recTy = unwrapIfDerived(boxTy); 1265 return recTy && recTy.getNumLenParams() > 0; 1266 } 1267 static bool isDerivedType(fir::BoxType boxTy) { 1268 return unwrapIfDerived(boxTy) != nullptr; 1269 } 1270 1271 // Get the element size and CFI type code of the boxed value. 1272 std::tuple<mlir::Value, mlir::Value> getSizeAndTypeCode( 1273 mlir::Location loc, mlir::ConversionPatternRewriter &rewriter, 1274 mlir::Type boxEleTy, mlir::ValueRange lenParams = {}) const { 1275 auto doInteger = 1276 [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> { 1277 int typeCode = fir::integerBitsToTypeCode(width); 1278 return {this->genConstantOffset(loc, rewriter, width / 8), 1279 this->genConstantOffset(loc, rewriter, typeCode)}; 1280 }; 1281 auto doLogical = 1282 [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> { 1283 int typeCode = fir::logicalBitsToTypeCode(width); 1284 return {this->genConstantOffset(loc, rewriter, width / 8), 1285 this->genConstantOffset(loc, rewriter, typeCode)}; 1286 }; 1287 auto doFloat = [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> { 1288 int typeCode = fir::realBitsToTypeCode(width); 1289 return {this->genConstantOffset(loc, rewriter, width / 8), 1290 this->genConstantOffset(loc, rewriter, typeCode)}; 1291 }; 1292 auto doComplex = 1293 [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> { 1294 auto typeCode = fir::complexBitsToTypeCode(width); 1295 return {this->genConstantOffset(loc, rewriter, width / 8 * 2), 1296 this->genConstantOffset(loc, rewriter, typeCode)}; 1297 }; 1298 auto doCharacter = 1299 [&](unsigned width, 1300 mlir::Value len) -> std::tuple<mlir::Value, mlir::Value> { 1301 auto typeCode = fir::characterBitsToTypeCode(width); 1302 auto typeCodeVal = this->genConstantOffset(loc, rewriter, typeCode); 1303 if (width == 8) 1304 return {len, typeCodeVal}; 1305 auto byteWidth = this->genConstantOffset(loc, rewriter, width / 8); 1306 auto i64Ty = mlir::IntegerType::get(&this->lowerTy().getContext(), 64); 1307 auto size = 1308 rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, byteWidth, len); 1309 return {size, typeCodeVal}; 1310 }; 1311 auto getKindMap = [&]() -> fir::KindMapping & { 1312 return this->lowerTy().getKindMap(); 1313 }; 1314 // Pointer-like types. 1315 if (auto eleTy = fir::dyn_cast_ptrEleTy(boxEleTy)) 1316 boxEleTy = eleTy; 1317 // Integer types. 1318 if (fir::isa_integer(boxEleTy)) { 1319 if (auto ty = boxEleTy.dyn_cast<mlir::IntegerType>()) 1320 return doInteger(ty.getWidth()); 1321 auto ty = boxEleTy.cast<fir::IntegerType>(); 1322 return doInteger(getKindMap().getIntegerBitsize(ty.getFKind())); 1323 } 1324 // Floating point types. 1325 if (fir::isa_real(boxEleTy)) { 1326 if (auto ty = boxEleTy.dyn_cast<mlir::FloatType>()) 1327 return doFloat(ty.getWidth()); 1328 auto ty = boxEleTy.cast<fir::RealType>(); 1329 return doFloat(getKindMap().getRealBitsize(ty.getFKind())); 1330 } 1331 // Complex types. 1332 if (fir::isa_complex(boxEleTy)) { 1333 if (auto ty = boxEleTy.dyn_cast<mlir::ComplexType>()) 1334 return doComplex( 1335 ty.getElementType().cast<mlir::FloatType>().getWidth()); 1336 auto ty = boxEleTy.cast<fir::ComplexType>(); 1337 return doComplex(getKindMap().getRealBitsize(ty.getFKind())); 1338 } 1339 // Character types. 1340 if (auto ty = boxEleTy.dyn_cast<fir::CharacterType>()) { 1341 auto charWidth = getKindMap().getCharacterBitsize(ty.getFKind()); 1342 if (ty.getLen() != fir::CharacterType::unknownLen()) { 1343 auto len = this->genConstantOffset(loc, rewriter, ty.getLen()); 1344 return doCharacter(charWidth, len); 1345 } 1346 assert(!lenParams.empty()); 1347 return doCharacter(charWidth, lenParams.back()); 1348 } 1349 // Logical type. 1350 if (auto ty = boxEleTy.dyn_cast<fir::LogicalType>()) 1351 return doLogical(getKindMap().getLogicalBitsize(ty.getFKind())); 1352 // Array types. 1353 if (auto seqTy = boxEleTy.dyn_cast<fir::SequenceType>()) 1354 return getSizeAndTypeCode(loc, rewriter, seqTy.getEleTy(), lenParams); 1355 // Derived-type types. 1356 if (boxEleTy.isa<fir::RecordType>()) { 1357 auto ptrTy = mlir::LLVM::LLVMPointerType::get( 1358 this->lowerTy().convertType(boxEleTy)); 1359 auto nullPtr = rewriter.create<mlir::LLVM::NullOp>(loc, ptrTy); 1360 auto one = 1361 genConstantIndex(loc, this->lowerTy().offsetType(), rewriter, 1); 1362 auto gep = rewriter.create<mlir::LLVM::GEPOp>( 1363 loc, ptrTy, mlir::ValueRange{nullPtr, one}); 1364 auto eleSize = rewriter.create<mlir::LLVM::PtrToIntOp>( 1365 loc, this->lowerTy().indexType(), gep); 1366 return {eleSize, 1367 this->genConstantOffset(loc, rewriter, fir::derivedToTypeCode())}; 1368 } 1369 // Reference type. 1370 if (fir::isa_ref_type(boxEleTy)) { 1371 // FIXME: use the target pointer size rather than sizeof(void*) 1372 return {this->genConstantOffset(loc, rewriter, sizeof(void *)), 1373 this->genConstantOffset(loc, rewriter, CFI_type_cptr)}; 1374 } 1375 fir::emitFatalError(loc, "unhandled type in fir.box code generation"); 1376 } 1377 1378 /// Basic pattern to write a field in the descriptor 1379 mlir::Value insertField(mlir::ConversionPatternRewriter &rewriter, 1380 mlir::Location loc, mlir::Value dest, 1381 ArrayRef<unsigned> fldIndexes, mlir::Value value, 1382 bool bitcast = false) const { 1383 auto boxTy = dest.getType(); 1384 auto fldTy = this->getBoxEleTy(boxTy, fldIndexes); 1385 if (bitcast) 1386 value = rewriter.create<mlir::LLVM::BitcastOp>(loc, fldTy, value); 1387 else 1388 value = this->integerCast(loc, rewriter, fldTy, value); 1389 SmallVector<mlir::Attribute, 2> attrs; 1390 for (auto i : fldIndexes) 1391 attrs.push_back(rewriter.getI32IntegerAttr(i)); 1392 auto indexesAttr = mlir::ArrayAttr::get(rewriter.getContext(), attrs); 1393 return rewriter.create<mlir::LLVM::InsertValueOp>(loc, boxTy, dest, value, 1394 indexesAttr); 1395 } 1396 1397 inline mlir::Value 1398 insertBaseAddress(mlir::ConversionPatternRewriter &rewriter, 1399 mlir::Location loc, mlir::Value dest, 1400 mlir::Value base) const { 1401 return insertField(rewriter, loc, dest, {0}, base, /*bitCast=*/true); 1402 } 1403 1404 /// Get the address of the type descriptor global variable that was created by 1405 /// lowering for derived type \p recType. 1406 template <typename BOX> 1407 mlir::Value 1408 getTypeDescriptor(BOX box, mlir::ConversionPatternRewriter &rewriter, 1409 mlir::Location loc, fir::RecordType recType) const { 1410 std::string name = recType.getLoweredName(); 1411 auto module = box->template getParentOfType<mlir::ModuleOp>(); 1412 if (auto global = module.template lookupSymbol<fir::GlobalOp>(name)) { 1413 auto ty = mlir::LLVM::LLVMPointerType::get( 1414 this->lowerTy().convertType(global.getType())); 1415 return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty, 1416 global.sym_name()); 1417 } 1418 if (auto global = 1419 module.template lookupSymbol<mlir::LLVM::GlobalOp>(name)) { 1420 // The global may have already been translated to LLVM. 1421 auto ty = mlir::LLVM::LLVMPointerType::get(global.getType()); 1422 return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty, 1423 global.sym_name()); 1424 } 1425 // The global does not exist in the current translation unit, but may be 1426 // defined elsewhere (e.g., type defined in a module). 1427 // For now, create a extern_weak symbol (will become nullptr if unresolved) 1428 // to support generating code without the front-end generated symbols. 1429 // These could be made available_externally to require the symbols to be 1430 // defined elsewhere and to cause link-time failure otherwise. 1431 auto i8Ty = rewriter.getIntegerType(8); 1432 mlir::OpBuilder modBuilder(module.getBodyRegion()); 1433 // TODO: The symbol should be lowered to constant in lowering, they are read 1434 // only. 1435 modBuilder.create<mlir::LLVM::GlobalOp>(loc, i8Ty, /*isConstant=*/false, 1436 mlir::LLVM::Linkage::ExternWeak, 1437 name, mlir::Attribute{}); 1438 auto ty = mlir::LLVM::LLVMPointerType::get(i8Ty); 1439 return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty, name); 1440 } 1441 1442 template <typename BOX> 1443 std::tuple<fir::BoxType, mlir::Value, mlir::Value> 1444 consDescriptorPrefix(BOX box, mlir::ConversionPatternRewriter &rewriter, 1445 unsigned rank, mlir::ValueRange lenParams) const { 1446 auto loc = box.getLoc(); 1447 auto boxTy = box.getType().template dyn_cast<fir::BoxType>(); 1448 auto convTy = this->lowerTy().convertBoxType(boxTy, rank); 1449 auto llvmBoxPtrTy = convTy.template cast<mlir::LLVM::LLVMPointerType>(); 1450 auto llvmBoxTy = llvmBoxPtrTy.getElementType(); 1451 mlir::Value descriptor = 1452 rewriter.create<mlir::LLVM::UndefOp>(loc, llvmBoxTy); 1453 1454 llvm::SmallVector<mlir::Value> typeparams = lenParams; 1455 if constexpr (!std::is_same_v<BOX, fir::EmboxOp>) { 1456 if (!box.substr().empty() && fir::hasDynamicSize(boxTy.getEleTy())) 1457 typeparams.push_back(box.substr()[1]); 1458 } 1459 1460 // Write each of the fields with the appropriate values 1461 auto [eleSize, cfiTy] = 1462 getSizeAndTypeCode(loc, rewriter, boxTy.getEleTy(), typeparams); 1463 descriptor = 1464 insertField(rewriter, loc, descriptor, {kElemLenPosInBox}, eleSize); 1465 descriptor = insertField(rewriter, loc, descriptor, {kVersionPosInBox}, 1466 this->genI32Constant(loc, rewriter, CFI_VERSION)); 1467 descriptor = insertField(rewriter, loc, descriptor, {kRankPosInBox}, 1468 this->genI32Constant(loc, rewriter, rank)); 1469 descriptor = insertField(rewriter, loc, descriptor, {kTypePosInBox}, cfiTy); 1470 descriptor = 1471 insertField(rewriter, loc, descriptor, {kAttributePosInBox}, 1472 this->genI32Constant(loc, rewriter, getCFIAttr(boxTy))); 1473 const bool hasAddendum = isDerivedType(boxTy); 1474 descriptor = 1475 insertField(rewriter, loc, descriptor, {kF18AddendumPosInBox}, 1476 this->genI32Constant(loc, rewriter, hasAddendum ? 1 : 0)); 1477 1478 if (hasAddendum) { 1479 auto isArray = 1480 fir::dyn_cast_ptrOrBoxEleTy(boxTy).template isa<fir::SequenceType>(); 1481 unsigned typeDescFieldId = isArray ? kOptTypePtrPosInBox : kDimsPosInBox; 1482 auto typeDesc = 1483 getTypeDescriptor(box, rewriter, loc, unwrapIfDerived(boxTy)); 1484 descriptor = 1485 insertField(rewriter, loc, descriptor, {typeDescFieldId}, typeDesc, 1486 /*bitCast=*/true); 1487 } 1488 1489 return {boxTy, descriptor, eleSize}; 1490 } 1491 1492 /// If the embox is not in a globalOp body, allocate storage for the box; 1493 /// store the value inside and return the generated alloca. Return the input 1494 /// value otherwise. 1495 mlir::Value 1496 placeInMemoryIfNotGlobalInit(mlir::ConversionPatternRewriter &rewriter, 1497 mlir::Location loc, mlir::Value boxValue) const { 1498 auto *thisBlock = rewriter.getInsertionBlock(); 1499 if (thisBlock && mlir::isa<mlir::LLVM::GlobalOp>(thisBlock->getParentOp())) 1500 return boxValue; 1501 auto boxPtrTy = mlir::LLVM::LLVMPointerType::get(boxValue.getType()); 1502 auto alloca = genAllocaWithType(loc, boxPtrTy, defaultAlign, rewriter); 1503 rewriter.create<mlir::LLVM::StoreOp>(loc, boxValue, alloca); 1504 return alloca; 1505 } 1506 }; 1507 1508 /// Create a generic box on a memory reference. This conversions lowers the 1509 /// abstract box to the appropriate, initialized descriptor. 1510 struct EmboxOpConversion : public EmboxCommonConversion<fir::EmboxOp> { 1511 using EmboxCommonConversion::EmboxCommonConversion; 1512 1513 mlir::LogicalResult 1514 matchAndRewrite(fir::EmboxOp embox, OpAdaptor adaptor, 1515 mlir::ConversionPatternRewriter &rewriter) const override { 1516 assert(!embox.getShape() && "There should be no dims on this embox op"); 1517 auto [boxTy, dest, eleSize] = 1518 consDescriptorPrefix(embox, rewriter, /*rank=*/0, 1519 /*lenParams=*/adaptor.getOperands().drop_front(1)); 1520 dest = insertBaseAddress(rewriter, embox.getLoc(), dest, 1521 adaptor.getOperands()[0]); 1522 if (isDerivedTypeWithLenParams(boxTy)) 1523 return rewriter.notifyMatchFailure( 1524 embox, "fir.embox codegen of derived with length parameters not " 1525 "implemented yet"); 1526 auto result = placeInMemoryIfNotGlobalInit(rewriter, embox.getLoc(), dest); 1527 rewriter.replaceOp(embox, result); 1528 return success(); 1529 } 1530 }; 1531 1532 // Code shared between insert_value and extract_value Ops. 1533 struct ValueOpCommon { 1534 // Translate the arguments pertaining to any multidimensional array to 1535 // row-major order for LLVM-IR. 1536 static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs, 1537 mlir::Type ty) { 1538 assert(ty && "type is null"); 1539 const auto end = attrs.size(); 1540 for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) { 1541 if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 1542 const auto dim = getDimension(seq); 1543 if (dim > 1) { 1544 auto ub = std::min(i + dim, end); 1545 std::reverse(attrs.begin() + i, attrs.begin() + ub); 1546 i += dim - 1; 1547 } 1548 ty = getArrayElementType(seq); 1549 } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) { 1550 ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()]; 1551 } else { 1552 llvm_unreachable("index into invalid type"); 1553 } 1554 } 1555 } 1556 1557 static llvm::SmallVector<mlir::Attribute> 1558 collectIndices(mlir::ConversionPatternRewriter &rewriter, 1559 mlir::ArrayAttr arrAttr) { 1560 llvm::SmallVector<mlir::Attribute> attrs; 1561 for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) { 1562 if (i->isa<mlir::IntegerAttr>()) { 1563 attrs.push_back(*i); 1564 } else { 1565 auto fieldName = i->cast<mlir::StringAttr>().getValue(); 1566 ++i; 1567 auto ty = i->cast<mlir::TypeAttr>().getValue(); 1568 auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName); 1569 attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index)); 1570 } 1571 } 1572 return attrs; 1573 } 1574 1575 private: 1576 static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) { 1577 unsigned result = 1; 1578 for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>(); 1579 eleTy; 1580 eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>()) 1581 ++result; 1582 return result; 1583 } 1584 1585 static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) { 1586 auto eleTy = ty.getElementType(); 1587 while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>()) 1588 eleTy = arrTy.getElementType(); 1589 return eleTy; 1590 } 1591 }; 1592 1593 /// Extract a subobject value from an ssa-value of aggregate type 1594 struct ExtractValueOpConversion 1595 : public FIROpAndTypeConversion<fir::ExtractValueOp>, 1596 public ValueOpCommon { 1597 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1598 1599 mlir::LogicalResult 1600 doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor, 1601 mlir::ConversionPatternRewriter &rewriter) const override { 1602 auto attrs = collectIndices(rewriter, extractVal.coor()); 1603 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 1604 auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs); 1605 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>( 1606 extractVal, ty, adaptor.getOperands()[0], position); 1607 return success(); 1608 } 1609 }; 1610 1611 /// InsertValue is the generalized instruction for the composition of new 1612 /// aggregate type values. 1613 struct InsertValueOpConversion 1614 : public FIROpAndTypeConversion<fir::InsertValueOp>, 1615 public ValueOpCommon { 1616 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1617 1618 mlir::LogicalResult 1619 doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor, 1620 mlir::ConversionPatternRewriter &rewriter) const override { 1621 auto attrs = collectIndices(rewriter, insertVal.coor()); 1622 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 1623 auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs); 1624 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1625 insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1], 1626 position); 1627 return success(); 1628 } 1629 }; 1630 1631 /// InsertOnRange inserts a value into a sequence over a range of offsets. 1632 struct InsertOnRangeOpConversion 1633 : public FIROpAndTypeConversion<fir::InsertOnRangeOp> { 1634 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1635 1636 // Increments an array of subscripts in a row major fasion. 1637 void incrementSubscripts(const SmallVector<uint64_t> &dims, 1638 SmallVector<uint64_t> &subscripts) const { 1639 for (size_t i = dims.size(); i > 0; --i) { 1640 if (++subscripts[i - 1] < dims[i - 1]) { 1641 return; 1642 } 1643 subscripts[i - 1] = 0; 1644 } 1645 } 1646 1647 mlir::LogicalResult 1648 doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor, 1649 mlir::ConversionPatternRewriter &rewriter) const override { 1650 1651 llvm::SmallVector<uint64_t> dims; 1652 auto type = adaptor.getOperands()[0].getType(); 1653 1654 // Iteratively extract the array dimensions from the type. 1655 while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 1656 dims.push_back(t.getNumElements()); 1657 type = t.getElementType(); 1658 } 1659 1660 SmallVector<uint64_t> lBounds; 1661 SmallVector<uint64_t> uBounds; 1662 1663 // Extract integer value from the attribute 1664 SmallVector<int64_t> coordinates = llvm::to_vector<4>( 1665 llvm::map_range(range.coor(), [](Attribute a) -> int64_t { 1666 return a.cast<IntegerAttr>().getInt(); 1667 })); 1668 1669 // Unzip the upper and lower bound and convert to a row major format. 1670 for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) { 1671 uBounds.push_back(*i++); 1672 lBounds.push_back(*i); 1673 } 1674 1675 auto &subscripts = lBounds; 1676 auto loc = range.getLoc(); 1677 mlir::Value lastOp = adaptor.getOperands()[0]; 1678 mlir::Value insertVal = adaptor.getOperands()[1]; 1679 1680 auto i64Ty = rewriter.getI64Type(); 1681 while (subscripts != uBounds) { 1682 // Convert uint64_t's to Attribute's. 1683 SmallVector<mlir::Attribute> subscriptAttrs; 1684 for (const auto &subscript : subscripts) 1685 subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript)); 1686 lastOp = rewriter.create<mlir::LLVM::InsertValueOp>( 1687 loc, ty, lastOp, insertVal, 1688 ArrayAttr::get(range.getContext(), subscriptAttrs)); 1689 1690 incrementSubscripts(dims, subscripts); 1691 } 1692 1693 // Convert uint64_t's to Attribute's. 1694 SmallVector<mlir::Attribute> subscriptAttrs; 1695 for (const auto &subscript : subscripts) 1696 subscriptAttrs.push_back( 1697 IntegerAttr::get(rewriter.getI64Type(), subscript)); 1698 mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs); 1699 1700 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1701 range, ty, lastOp, insertVal, 1702 ArrayAttr::get(range.getContext(), arrayRef)); 1703 1704 return success(); 1705 } 1706 }; 1707 1708 // 1709 // Primitive operations on Complex types 1710 // 1711 1712 /// Generate inline code for complex addition/subtraction 1713 template <typename LLVMOP, typename OPTY> 1714 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds, 1715 mlir::ConversionPatternRewriter &rewriter, 1716 fir::LLVMTypeConverter &lowering) { 1717 mlir::Value a = opnds[0]; 1718 mlir::Value b = opnds[1]; 1719 auto loc = sumop.getLoc(); 1720 auto ctx = sumop.getContext(); 1721 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1722 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1723 mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType())); 1724 mlir::Type ty = lowering.convertType(sumop.getType()); 1725 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1726 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1727 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1728 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1729 auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1); 1730 auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1); 1731 auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1732 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0); 1733 return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1); 1734 } 1735 1736 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> { 1737 using FIROpConversion::FIROpConversion; 1738 1739 mlir::LogicalResult 1740 matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor, 1741 mlir::ConversionPatternRewriter &rewriter) const override { 1742 // given: (x + iy) + (x' + iy') 1743 // result: (x + x') + i(y + y') 1744 auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(), 1745 rewriter, lowerTy()); 1746 rewriter.replaceOp(addc, r.getResult()); 1747 return success(); 1748 } 1749 }; 1750 1751 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> { 1752 using FIROpConversion::FIROpConversion; 1753 1754 mlir::LogicalResult 1755 matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor, 1756 mlir::ConversionPatternRewriter &rewriter) const override { 1757 // given: (x + iy) - (x' + iy') 1758 // result: (x - x') + i(y - y') 1759 auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(), 1760 rewriter, lowerTy()); 1761 rewriter.replaceOp(subc, r.getResult()); 1762 return success(); 1763 } 1764 }; 1765 1766 /// Inlined complex multiply 1767 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> { 1768 using FIROpConversion::FIROpConversion; 1769 1770 mlir::LogicalResult 1771 matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor, 1772 mlir::ConversionPatternRewriter &rewriter) const override { 1773 // TODO: Can we use a call to __muldc3 ? 1774 // given: (x + iy) * (x' + iy') 1775 // result: (xx'-yy')+i(xy'+yx') 1776 mlir::Value a = adaptor.getOperands()[0]; 1777 mlir::Value b = adaptor.getOperands()[1]; 1778 auto loc = mulc.getLoc(); 1779 auto *ctx = mulc.getContext(); 1780 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1781 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1782 mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType())); 1783 mlir::Type ty = convertType(mulc.getType()); 1784 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1785 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1786 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1787 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1788 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 1789 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 1790 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 1791 auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx); 1792 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 1793 auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy); 1794 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1795 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 1796 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 1797 rewriter.replaceOp(mulc, r0.getResult()); 1798 return success(); 1799 } 1800 }; 1801 1802 /// Inlined complex division 1803 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> { 1804 using FIROpConversion::FIROpConversion; 1805 1806 mlir::LogicalResult 1807 matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor, 1808 mlir::ConversionPatternRewriter &rewriter) const override { 1809 // TODO: Can we use a call to __divdc3 instead? 1810 // Just generate inline code for now. 1811 // given: (x + iy) / (x' + iy') 1812 // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y' 1813 mlir::Value a = adaptor.getOperands()[0]; 1814 mlir::Value b = adaptor.getOperands()[1]; 1815 auto loc = divc.getLoc(); 1816 auto *ctx = divc.getContext(); 1817 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1818 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1819 mlir::Type eleTy = convertType(getComplexEleTy(divc.getType())); 1820 mlir::Type ty = convertType(divc.getType()); 1821 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1822 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1823 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1824 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1825 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 1826 auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1); 1827 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 1828 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 1829 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 1830 auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1); 1831 auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1); 1832 auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy); 1833 auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy); 1834 auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d); 1835 auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d); 1836 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1837 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 1838 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 1839 rewriter.replaceOp(divc, r0.getResult()); 1840 return success(); 1841 } 1842 }; 1843 1844 /// Inlined complex negation 1845 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> { 1846 using FIROpConversion::FIROpConversion; 1847 1848 mlir::LogicalResult 1849 matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor, 1850 mlir::ConversionPatternRewriter &rewriter) const override { 1851 // given: -(x + iy) 1852 // result: -x - iy 1853 auto *ctxt = neg.getContext(); 1854 auto eleTy = convertType(getComplexEleTy(neg.getType())); 1855 auto ty = convertType(neg.getType()); 1856 auto loc = neg.getLoc(); 1857 mlir::Value o0 = adaptor.getOperands()[0]; 1858 auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0)); 1859 auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1)); 1860 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0); 1861 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1); 1862 auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp); 1863 auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip); 1864 auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0); 1865 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1); 1866 return success(); 1867 } 1868 }; 1869 1870 /// Conversion pattern for operation that must be dead. The information in these 1871 /// operations is used by other operation. At this point they should not have 1872 /// anymore uses. 1873 /// These operations are normally dead after the pre-codegen pass. 1874 template <typename FromOp> 1875 struct MustBeDeadConversion : public FIROpConversion<FromOp> { 1876 explicit MustBeDeadConversion(fir::LLVMTypeConverter &lowering) 1877 : FIROpConversion<FromOp>(lowering) {} 1878 using OpAdaptor = typename FromOp::Adaptor; 1879 1880 mlir::LogicalResult 1881 matchAndRewrite(FromOp op, OpAdaptor adaptor, 1882 mlir::ConversionPatternRewriter &rewriter) const final { 1883 if (!op->getUses().empty()) 1884 return rewriter.notifyMatchFailure(op, "op must be dead"); 1885 rewriter.eraseOp(op); 1886 return success(); 1887 } 1888 }; 1889 1890 struct ShapeOpConversion : public MustBeDeadConversion<fir::ShapeOp> { 1891 using MustBeDeadConversion::MustBeDeadConversion; 1892 }; 1893 1894 struct ShapeShiftOpConversion : public MustBeDeadConversion<fir::ShapeShiftOp> { 1895 using MustBeDeadConversion::MustBeDeadConversion; 1896 }; 1897 1898 struct ShiftOpConversion : public MustBeDeadConversion<fir::ShiftOp> { 1899 using MustBeDeadConversion::MustBeDeadConversion; 1900 }; 1901 1902 struct SliceOpConversion : public MustBeDeadConversion<fir::SliceOp> { 1903 using MustBeDeadConversion::MustBeDeadConversion; 1904 }; 1905 1906 /// `fir.is_present` --> 1907 /// ``` 1908 /// %0 = llvm.mlir.constant(0 : i64) 1909 /// %1 = llvm.ptrtoint %0 1910 /// %2 = llvm.icmp "ne" %1, %0 : i64 1911 /// ``` 1912 struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> { 1913 using FIROpConversion::FIROpConversion; 1914 1915 mlir::LogicalResult 1916 matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor, 1917 mlir::ConversionPatternRewriter &rewriter) const override { 1918 mlir::Type idxTy = lowerTy().indexType(); 1919 mlir::Location loc = isPresent.getLoc(); 1920 auto ptr = adaptor.getOperands()[0]; 1921 1922 if (isPresent.val().getType().isa<fir::BoxCharType>()) { 1923 auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>(); 1924 assert(!structTy.isOpaque() && !structTy.getBody().empty()); 1925 1926 mlir::Type ty = structTy.getBody()[0]; 1927 mlir::MLIRContext *ctx = isPresent.getContext(); 1928 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1929 ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0); 1930 } 1931 mlir::LLVM::ConstantOp c0 = 1932 genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0); 1933 auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr); 1934 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>( 1935 isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0); 1936 1937 return success(); 1938 } 1939 }; 1940 1941 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of 1942 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element 1943 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd 1944 /// element is the length of the character buffer (`#n`). 1945 struct EmboxCharOpConversion : public FIROpConversion<fir::EmboxCharOp> { 1946 using FIROpConversion::FIROpConversion; 1947 1948 mlir::LogicalResult 1949 matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor, 1950 mlir::ConversionPatternRewriter &rewriter) const override { 1951 mlir::ValueRange operands = adaptor.getOperands(); 1952 MLIRContext *ctx = emboxChar.getContext(); 1953 1954 mlir::Value charBuffer = operands[0]; 1955 mlir::Value charBufferLen = operands[1]; 1956 1957 mlir::Location loc = emboxChar.getLoc(); 1958 mlir::Type llvmStructTy = convertType(emboxChar.getType()); 1959 auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy); 1960 1961 mlir::Type lenTy = 1962 llvmStructTy.cast<mlir::LLVM::LLVMStructType>().getBody()[1]; 1963 mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen); 1964 1965 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1966 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1967 auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>( 1968 loc, llvmStructTy, llvmStruct, charBuffer, c0); 1969 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1970 emboxChar, llvmStructTy, insertBufferOp, lenAfterCast, c1); 1971 1972 return success(); 1973 } 1974 }; 1975 1976 /// Construct an `llvm.extractvalue` instruction. It will return value at 1977 /// element \p x from \p tuple. 1978 mlir::LLVM::ExtractValueOp 1979 genExtractValueWithIndex(mlir::Location loc, mlir::Value tuple, mlir::Type ty, 1980 mlir::ConversionPatternRewriter &rewriter, 1981 mlir::MLIRContext *ctx, int x) { 1982 auto cx = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(x)); 1983 auto xty = ty.cast<mlir::LLVM::LLVMStructType>().getBody()[x]; 1984 return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, xty, tuple, cx); 1985 } 1986 1987 /// Convert `!fir.boxchar_len` to `!llvm.extractvalue` for the 2nd part of the 1988 /// boxchar. 1989 struct BoxCharLenOpConversion : public FIROpConversion<fir::BoxCharLenOp> { 1990 using FIROpConversion::FIROpConversion; 1991 1992 mlir::LogicalResult 1993 matchAndRewrite(fir::BoxCharLenOp boxCharLen, OpAdaptor adaptor, 1994 mlir::ConversionPatternRewriter &rewriter) const override { 1995 mlir::Value boxChar = adaptor.getOperands()[0]; 1996 mlir::Location loc = boxChar.getLoc(); 1997 mlir::MLIRContext *ctx = boxChar.getContext(); 1998 mlir::Type returnValTy = boxCharLen.getResult().getType(); 1999 2000 constexpr int boxcharLenIdx = 1; 2001 mlir::LLVM::ExtractValueOp len = genExtractValueWithIndex( 2002 loc, boxChar, boxChar.getType(), rewriter, ctx, boxcharLenIdx); 2003 mlir::Value lenAfterCast = integerCast(loc, rewriter, returnValTy, len); 2004 rewriter.replaceOp(boxCharLen, lenAfterCast); 2005 2006 return success(); 2007 } 2008 }; 2009 2010 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for 2011 /// the character buffer and one for the buffer length. 2012 struct UnboxCharOpConversion : public FIROpConversion<fir::UnboxCharOp> { 2013 using FIROpConversion::FIROpConversion; 2014 2015 mlir::LogicalResult 2016 matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor, 2017 mlir::ConversionPatternRewriter &rewriter) const override { 2018 MLIRContext *ctx = unboxchar.getContext(); 2019 2020 mlir::Type lenTy = convertType(unboxchar.getType(1)); 2021 mlir::Value tuple = adaptor.getOperands()[0]; 2022 mlir::Type tupleTy = tuple.getType(); 2023 2024 mlir::Location loc = unboxchar.getLoc(); 2025 mlir::Value ptrToBuffer = 2026 genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 0); 2027 2028 mlir::LLVM::ExtractValueOp len = 2029 genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 1); 2030 mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len); 2031 2032 rewriter.replaceOp(unboxchar, 2033 ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast}); 2034 return success(); 2035 } 2036 }; 2037 2038 } // namespace 2039 2040 namespace { 2041 /// Convert FIR dialect to LLVM dialect 2042 /// 2043 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An 2044 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect. 2045 /// 2046 /// This pass is not complete yet. We are upstreaming it in small patches. 2047 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> { 2048 public: 2049 mlir::ModuleOp getModule() { return getOperation(); } 2050 2051 void runOnOperation() override final { 2052 auto mod = getModule(); 2053 if (!forcedTargetTriple.empty()) { 2054 fir::setTargetTriple(mod, forcedTargetTriple); 2055 } 2056 2057 auto *context = getModule().getContext(); 2058 fir::LLVMTypeConverter typeConverter{getModule()}; 2059 mlir::OwningRewritePatternList pattern(context); 2060 pattern.insert< 2061 AbsentOpConversion, AddcOpConversion, AddrOfOpConversion, 2062 AllocaOpConversion, BoxAddrOpConversion, BoxCharLenOpConversion, 2063 BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion, 2064 BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxRankOpConversion, 2065 BoxTypeDescOpConversion, CallOpConversion, CmpcOpConversion, 2066 ConstcOpConversion, ConvertOpConversion, DispatchOpConversion, 2067 DispatchTableOpConversion, DTEntryOpConversion, DivcOpConversion, 2068 EmboxOpConversion, EmboxCharOpConversion, ExtractValueOpConversion, 2069 HasValueOpConversion, GenTypeDescOpConversion, GlobalLenOpConversion, 2070 GlobalOpConversion, InsertOnRangeOpConversion, InsertValueOpConversion, 2071 IsPresentOpConversion, LoadOpConversion, NegcOpConversion, 2072 MulcOpConversion, SelectCaseOpConversion, SelectOpConversion, 2073 SelectRankOpConversion, SelectTypeOpConversion, ShapeOpConversion, 2074 ShapeShiftOpConversion, ShiftOpConversion, SliceOpConversion, 2075 StoreOpConversion, StringLitOpConversion, SubcOpConversion, 2076 UnboxCharOpConversion, UndefOpConversion, UnreachableOpConversion, 2077 ZeroOpConversion>(typeConverter); 2078 mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern); 2079 mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter, 2080 pattern); 2081 mlir::ConversionTarget target{*context}; 2082 target.addLegalDialect<mlir::LLVM::LLVMDialect>(); 2083 2084 // required NOPs for applying a full conversion 2085 target.addLegalOp<mlir::ModuleOp>(); 2086 2087 // apply the patterns 2088 if (mlir::failed(mlir::applyFullConversion(getModule(), target, 2089 std::move(pattern)))) { 2090 signalPassFailure(); 2091 } 2092 } 2093 }; 2094 } // namespace 2095 2096 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() { 2097 return std::make_unique<FIRToLLVMLowering>(); 2098 } 2099