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.boxproc_host` operation. Extracts the host pointer from the 536 /// boxproc. 537 /// TODO: Part of supporting Fortran 2003 procedure pointers. 538 struct BoxProcHostOpConversion : public FIROpConversion<fir::BoxProcHostOp> { 539 using FIROpConversion::FIROpConversion; 540 541 mlir::LogicalResult 542 matchAndRewrite(fir::BoxProcHostOp boxprochost, OpAdaptor adaptor, 543 mlir::ConversionPatternRewriter &rewriter) const override { 544 return rewriter.notifyMatchFailure( 545 boxprochost, "fir.boxproc_host codegen is not implemented yet"); 546 } 547 }; 548 549 /// Lower `fir.box_tdesc` to the sequence of operations to extract the type 550 /// descriptor from the box. 551 struct BoxTypeDescOpConversion : public FIROpConversion<fir::BoxTypeDescOp> { 552 using FIROpConversion::FIROpConversion; 553 554 mlir::LogicalResult 555 matchAndRewrite(fir::BoxTypeDescOp boxtypedesc, OpAdaptor adaptor, 556 mlir::ConversionPatternRewriter &rewriter) const override { 557 mlir::Value box = adaptor.getOperands()[0]; 558 auto loc = boxtypedesc.getLoc(); 559 mlir::Type typeTy = 560 fir::getDescFieldTypeModel<kTypePosInBox>()(boxtypedesc.getContext()); 561 auto result = getValueFromBox(loc, box, typeTy, rewriter, kTypePosInBox); 562 auto typePtrTy = mlir::LLVM::LLVMPointerType::get(typeTy); 563 rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(boxtypedesc, typePtrTy, 564 result); 565 return success(); 566 } 567 }; 568 569 // `fir.call` -> `llvm.call` 570 struct CallOpConversion : public FIROpConversion<fir::CallOp> { 571 using FIROpConversion::FIROpConversion; 572 573 mlir::LogicalResult 574 matchAndRewrite(fir::CallOp call, OpAdaptor adaptor, 575 mlir::ConversionPatternRewriter &rewriter) const override { 576 SmallVector<mlir::Type> resultTys; 577 for (auto r : call.getResults()) 578 resultTys.push_back(convertType(r.getType())); 579 rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>( 580 call, resultTys, adaptor.getOperands(), call->getAttrs()); 581 return success(); 582 } 583 }; 584 585 static mlir::Type getComplexEleTy(mlir::Type complex) { 586 if (auto cc = complex.dyn_cast<mlir::ComplexType>()) 587 return cc.getElementType(); 588 return complex.cast<fir::ComplexType>().getElementType(); 589 } 590 591 /// Compare complex values 592 /// 593 /// Per 10.1, the only comparisons available are .EQ. (oeq) and .NE. (une). 594 /// 595 /// For completeness, all other comparison are done on the real component only. 596 struct CmpcOpConversion : public FIROpConversion<fir::CmpcOp> { 597 using FIROpConversion::FIROpConversion; 598 599 mlir::LogicalResult 600 matchAndRewrite(fir::CmpcOp cmp, OpAdaptor adaptor, 601 mlir::ConversionPatternRewriter &rewriter) const override { 602 mlir::ValueRange operands = adaptor.getOperands(); 603 mlir::MLIRContext *ctxt = cmp.getContext(); 604 mlir::Type eleTy = convertType(getComplexEleTy(cmp.lhs().getType())); 605 mlir::Type resTy = convertType(cmp.getType()); 606 mlir::Location loc = cmp.getLoc(); 607 auto pos0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0)); 608 SmallVector<mlir::Value, 2> rp{rewriter.create<mlir::LLVM::ExtractValueOp>( 609 loc, eleTy, operands[0], pos0), 610 rewriter.create<mlir::LLVM::ExtractValueOp>( 611 loc, eleTy, operands[1], pos0)}; 612 auto rcp = 613 rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, rp, cmp->getAttrs()); 614 auto pos1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1)); 615 SmallVector<mlir::Value, 2> ip{rewriter.create<mlir::LLVM::ExtractValueOp>( 616 loc, eleTy, operands[0], pos1), 617 rewriter.create<mlir::LLVM::ExtractValueOp>( 618 loc, eleTy, operands[1], pos1)}; 619 auto icp = 620 rewriter.create<mlir::LLVM::FCmpOp>(loc, resTy, ip, cmp->getAttrs()); 621 SmallVector<mlir::Value, 2> cp{rcp, icp}; 622 switch (cmp.getPredicate()) { 623 case mlir::arith::CmpFPredicate::OEQ: // .EQ. 624 rewriter.replaceOpWithNewOp<mlir::LLVM::AndOp>(cmp, resTy, cp); 625 break; 626 case mlir::arith::CmpFPredicate::UNE: // .NE. 627 rewriter.replaceOpWithNewOp<mlir::LLVM::OrOp>(cmp, resTy, cp); 628 break; 629 default: 630 rewriter.replaceOp(cmp, rcp.getResult()); 631 break; 632 } 633 return success(); 634 } 635 }; 636 637 /// Lower complex constants 638 struct ConstcOpConversion : public FIROpConversion<fir::ConstcOp> { 639 using FIROpConversion::FIROpConversion; 640 641 mlir::LogicalResult 642 matchAndRewrite(fir::ConstcOp conc, OpAdaptor, 643 mlir::ConversionPatternRewriter &rewriter) const override { 644 mlir::Location loc = conc.getLoc(); 645 mlir::MLIRContext *ctx = conc.getContext(); 646 mlir::Type ty = convertType(conc.getType()); 647 mlir::Type ety = convertType(getComplexEleTy(conc.getType())); 648 auto realFloatAttr = mlir::FloatAttr::get(ety, getValue(conc.getReal())); 649 auto realPart = 650 rewriter.create<mlir::LLVM::ConstantOp>(loc, ety, realFloatAttr); 651 auto imFloatAttr = mlir::FloatAttr::get(ety, getValue(conc.getImaginary())); 652 auto imPart = 653 rewriter.create<mlir::LLVM::ConstantOp>(loc, ety, imFloatAttr); 654 auto realIndex = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 655 auto imIndex = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 656 auto undef = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 657 auto setReal = rewriter.create<mlir::LLVM::InsertValueOp>( 658 loc, ty, undef, realPart, realIndex); 659 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(conc, ty, setReal, 660 imPart, imIndex); 661 return success(); 662 } 663 664 inline APFloat getValue(mlir::Attribute attr) const { 665 return attr.cast<fir::RealAttr>().getValue(); 666 } 667 }; 668 669 /// convert value of from-type to value of to-type 670 struct ConvertOpConversion : public FIROpConversion<fir::ConvertOp> { 671 using FIROpConversion::FIROpConversion; 672 673 static bool isFloatingPointTy(mlir::Type ty) { 674 return ty.isa<mlir::FloatType>(); 675 } 676 677 mlir::LogicalResult 678 matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor, 679 mlir::ConversionPatternRewriter &rewriter) const override { 680 auto fromTy = convertType(convert.value().getType()); 681 auto toTy = convertType(convert.res().getType()); 682 mlir::Value op0 = adaptor.getOperands()[0]; 683 if (fromTy == toTy) { 684 rewriter.replaceOp(convert, op0); 685 return success(); 686 } 687 auto loc = convert.getLoc(); 688 auto convertFpToFp = [&](mlir::Value val, unsigned fromBits, 689 unsigned toBits, mlir::Type toTy) -> mlir::Value { 690 if (fromBits == toBits) { 691 // TODO: Converting between two floating-point representations with the 692 // same bitwidth is not allowed for now. 693 mlir::emitError(loc, 694 "cannot implicitly convert between two floating-point " 695 "representations of the same bitwidth"); 696 return {}; 697 } 698 if (fromBits > toBits) 699 return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val); 700 return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val); 701 }; 702 // Complex to complex conversion. 703 if (fir::isa_complex(convert.value().getType()) && 704 fir::isa_complex(convert.res().getType())) { 705 // Special case: handle the conversion of a complex such that both the 706 // real and imaginary parts are converted together. 707 auto zero = mlir::ArrayAttr::get(convert.getContext(), 708 rewriter.getI32IntegerAttr(0)); 709 auto one = mlir::ArrayAttr::get(convert.getContext(), 710 rewriter.getI32IntegerAttr(1)); 711 auto ty = convertType(getComplexEleTy(convert.value().getType())); 712 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, zero); 713 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, one); 714 auto nt = convertType(getComplexEleTy(convert.res().getType())); 715 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty); 716 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt); 717 auto rc = convertFpToFp(rp, fromBits, toBits, nt); 718 auto ic = convertFpToFp(ip, fromBits, toBits, nt); 719 auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy); 720 auto i1 = 721 rewriter.create<mlir::LLVM::InsertValueOp>(loc, toTy, un, rc, zero); 722 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, toTy, i1, 723 ic, one); 724 return mlir::success(); 725 } 726 // Floating point to floating point conversion. 727 if (isFloatingPointTy(fromTy)) { 728 if (isFloatingPointTy(toTy)) { 729 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy); 730 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy); 731 auto v = convertFpToFp(op0, fromBits, toBits, toTy); 732 rewriter.replaceOp(convert, v); 733 return mlir::success(); 734 } 735 if (toTy.isa<mlir::IntegerType>()) { 736 rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0); 737 return mlir::success(); 738 } 739 } else if (fromTy.isa<mlir::IntegerType>()) { 740 // Integer to integer conversion. 741 if (toTy.isa<mlir::IntegerType>()) { 742 auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy); 743 auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy); 744 assert(fromBits != toBits); 745 if (fromBits > toBits) { 746 rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0); 747 return mlir::success(); 748 } 749 rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0); 750 return mlir::success(); 751 } 752 // Integer to floating point conversion. 753 if (isFloatingPointTy(toTy)) { 754 rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0); 755 return mlir::success(); 756 } 757 // Integer to pointer conversion. 758 if (toTy.isa<mlir::LLVM::LLVMPointerType>()) { 759 rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0); 760 return mlir::success(); 761 } 762 } else if (fromTy.isa<mlir::LLVM::LLVMPointerType>()) { 763 // Pointer to integer conversion. 764 if (toTy.isa<mlir::IntegerType>()) { 765 rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0); 766 return mlir::success(); 767 } 768 // Pointer to pointer conversion. 769 if (toTy.isa<mlir::LLVM::LLVMPointerType>()) { 770 rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0); 771 return mlir::success(); 772 } 773 } 774 return emitError(loc) << "cannot convert " << fromTy << " to " << toTy; 775 } 776 }; 777 778 /// Lower `fir.dispatch` operation. A virtual call to a method in a dispatch 779 /// table. 780 struct DispatchOpConversion : public FIROpConversion<fir::DispatchOp> { 781 using FIROpConversion::FIROpConversion; 782 783 mlir::LogicalResult 784 matchAndRewrite(fir::DispatchOp dispatch, OpAdaptor adaptor, 785 mlir::ConversionPatternRewriter &rewriter) const override { 786 return rewriter.notifyMatchFailure( 787 dispatch, "fir.dispatch codegen is not implemented yet"); 788 } 789 }; 790 791 /// Lower `fir.dispatch_table` operation. The dispatch table for a Fortran 792 /// derived type. 793 struct DispatchTableOpConversion 794 : public FIROpConversion<fir::DispatchTableOp> { 795 using FIROpConversion::FIROpConversion; 796 797 mlir::LogicalResult 798 matchAndRewrite(fir::DispatchTableOp dispTab, OpAdaptor adaptor, 799 mlir::ConversionPatternRewriter &rewriter) const override { 800 return rewriter.notifyMatchFailure( 801 dispTab, "fir.dispatch_table codegen is not implemented yet"); 802 } 803 }; 804 805 /// Lower `fir.dt_entry` operation. An entry in a dispatch table; binds a 806 /// method-name to a function. 807 struct DTEntryOpConversion : public FIROpConversion<fir::DTEntryOp> { 808 using FIROpConversion::FIROpConversion; 809 810 mlir::LogicalResult 811 matchAndRewrite(fir::DTEntryOp dtEnt, OpAdaptor adaptor, 812 mlir::ConversionPatternRewriter &rewriter) const override { 813 return rewriter.notifyMatchFailure( 814 dtEnt, "fir.dt_entry codegen is not implemented yet"); 815 } 816 }; 817 818 /// Lower `fir.global_len` operation. 819 struct GlobalLenOpConversion : public FIROpConversion<fir::GlobalLenOp> { 820 using FIROpConversion::FIROpConversion; 821 822 mlir::LogicalResult 823 matchAndRewrite(fir::GlobalLenOp globalLen, OpAdaptor adaptor, 824 mlir::ConversionPatternRewriter &rewriter) const override { 825 return rewriter.notifyMatchFailure( 826 globalLen, "fir.global_len codegen is not implemented yet"); 827 } 828 }; 829 830 /// Lower `fir.gentypedesc` to a global constant. 831 struct GenTypeDescOpConversion : public FIROpConversion<fir::GenTypeDescOp> { 832 using FIROpConversion::FIROpConversion; 833 834 mlir::LogicalResult 835 matchAndRewrite(fir::GenTypeDescOp gentypedesc, OpAdaptor adaptor, 836 mlir::ConversionPatternRewriter &rewriter) const override { 837 return rewriter.notifyMatchFailure( 838 gentypedesc, "fir.fir.gentypedesc codegen is not implemented yet"); 839 } 840 }; 841 842 /// Convert `fir.end` 843 struct FirEndOpConversion : public FIROpConversion<fir::FirEndOp> { 844 using FIROpConversion::FIROpConversion; 845 846 mlir::LogicalResult 847 matchAndRewrite(fir::FirEndOp firEnd, OpAdaptor, 848 mlir::ConversionPatternRewriter &rewriter) const override { 849 return rewriter.notifyMatchFailure( 850 firEnd, "fir.end codegen is not implemented yet"); 851 } 852 }; 853 854 /// Lower `fir.has_value` operation to `llvm.return` operation. 855 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> { 856 using FIROpConversion::FIROpConversion; 857 858 mlir::LogicalResult 859 matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor, 860 mlir::ConversionPatternRewriter &rewriter) const override { 861 rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands()); 862 return success(); 863 } 864 }; 865 866 /// Lower `fir.global` operation to `llvm.global` operation. 867 /// `fir.insert_on_range` operations are replaced with constant dense attribute 868 /// if they are applied on the full range. 869 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> { 870 using FIROpConversion::FIROpConversion; 871 872 mlir::LogicalResult 873 matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor, 874 mlir::ConversionPatternRewriter &rewriter) const override { 875 auto tyAttr = convertType(global.getType()); 876 if (global.getType().isa<fir::BoxType>()) 877 tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType(); 878 auto loc = global.getLoc(); 879 mlir::Attribute initAttr{}; 880 if (global.initVal()) 881 initAttr = global.initVal().getValue(); 882 auto linkage = convertLinkage(global.linkName()); 883 auto isConst = global.constant().hasValue(); 884 auto g = rewriter.create<mlir::LLVM::GlobalOp>( 885 loc, tyAttr, isConst, linkage, global.sym_name(), initAttr); 886 auto &gr = g.getInitializerRegion(); 887 rewriter.inlineRegionBefore(global.region(), gr, gr.end()); 888 if (!gr.empty()) { 889 // Replace insert_on_range with a constant dense attribute if the 890 // initialization is on the full range. 891 auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>(); 892 for (auto insertOp : insertOnRangeOps) { 893 if (isFullRange(insertOp.coor(), insertOp.getType())) { 894 auto seqTyAttr = convertType(insertOp.getType()); 895 auto *op = insertOp.val().getDefiningOp(); 896 auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op); 897 if (!constant) { 898 auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op); 899 if (!convertOp) 900 continue; 901 constant = cast<mlir::arith::ConstantOp>( 902 convertOp.value().getDefiningOp()); 903 } 904 mlir::Type vecType = mlir::VectorType::get( 905 insertOp.getType().getShape(), constant.getType()); 906 auto denseAttr = mlir::DenseElementsAttr::get( 907 vecType.cast<ShapedType>(), constant.value()); 908 rewriter.setInsertionPointAfter(insertOp); 909 rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>( 910 insertOp, seqTyAttr, denseAttr); 911 } 912 } 913 } 914 rewriter.eraseOp(global); 915 return success(); 916 } 917 918 bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const { 919 auto extents = seqTy.getShape(); 920 if (indexes.size() / 2 != extents.size()) 921 return false; 922 for (unsigned i = 0; i < indexes.size(); i += 2) { 923 if (indexes[i].cast<IntegerAttr>().getInt() != 0) 924 return false; 925 if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1) 926 return false; 927 } 928 return true; 929 } 930 931 // TODO: String comparaison should be avoided. Replace linkName with an 932 // enumeration. 933 mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const { 934 if (optLinkage.hasValue()) { 935 auto name = optLinkage.getValue(); 936 if (name == "internal") 937 return mlir::LLVM::Linkage::Internal; 938 if (name == "linkonce") 939 return mlir::LLVM::Linkage::Linkonce; 940 if (name == "common") 941 return mlir::LLVM::Linkage::Common; 942 if (name == "weak") 943 return mlir::LLVM::Linkage::Weak; 944 } 945 return mlir::LLVM::Linkage::External; 946 } 947 }; 948 949 void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest, 950 Optional<mlir::ValueRange> destOps, 951 mlir::ConversionPatternRewriter &rewriter, 952 mlir::Block *newBlock) { 953 if (destOps.hasValue()) 954 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, destOps.getValue(), 955 newBlock, mlir::ValueRange()); 956 else 957 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock); 958 } 959 960 template <typename A, typename B> 961 void genBrOp(A caseOp, mlir::Block *dest, Optional<B> destOps, 962 mlir::ConversionPatternRewriter &rewriter) { 963 if (destOps.hasValue()) 964 rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, destOps.getValue(), 965 dest); 966 else 967 rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, llvm::None, dest); 968 } 969 970 void genCaseLadderStep(mlir::Location loc, mlir::Value cmp, mlir::Block *dest, 971 Optional<mlir::ValueRange> destOps, 972 mlir::ConversionPatternRewriter &rewriter) { 973 auto *thisBlock = rewriter.getInsertionBlock(); 974 auto *newBlock = createBlock(rewriter, dest); 975 rewriter.setInsertionPointToEnd(thisBlock); 976 genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock); 977 rewriter.setInsertionPointToEnd(newBlock); 978 } 979 980 /// Conversion of `fir.select_case` 981 /// 982 /// The `fir.select_case` operation is converted to a if-then-else ladder. 983 /// Depending on the case condition type, one or several comparison and 984 /// conditional branching can be generated. 985 /// 986 /// A a point value case such as `case(4)`, a lower bound case such as 987 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a 988 /// simple comparison between the selector value and the constant value in the 989 /// case. The block associated with the case condition is then executed if 990 /// the comparison succeed otherwise it branch to the next block with the 991 /// comparison for the the next case conditon. 992 /// 993 /// A closed interval case condition such as `case(7:10)` is converted with a 994 /// first comparison and conditional branching for the lower bound. If 995 /// successful, it branch to a second block with the comparison for the 996 /// upper bound in the same case condition. 997 /// 998 /// TODO: lowering of CHARACTER type cases is not handled yet. 999 struct SelectCaseOpConversion : public FIROpConversion<fir::SelectCaseOp> { 1000 using FIROpConversion::FIROpConversion; 1001 1002 mlir::LogicalResult 1003 matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor, 1004 mlir::ConversionPatternRewriter &rewriter) const override { 1005 unsigned conds = caseOp.getNumConditions(); 1006 llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue(); 1007 // Type can be CHARACTER, INTEGER, or LOGICAL (C1145) 1008 LLVM_ATTRIBUTE_UNUSED auto ty = caseOp.getSelector().getType(); 1009 if (ty.isa<fir::CharacterType>()) 1010 return rewriter.notifyMatchFailure(caseOp, 1011 "conversion of fir.select_case with " 1012 "character type not implemented yet"); 1013 mlir::Value selector = caseOp.getSelector(adaptor.getOperands()); 1014 auto loc = caseOp.getLoc(); 1015 for (unsigned t = 0; t != conds; ++t) { 1016 mlir::Block *dest = caseOp.getSuccessor(t); 1017 llvm::Optional<mlir::ValueRange> destOps = 1018 caseOp.getSuccessorOperands(adaptor.getOperands(), t); 1019 llvm::Optional<mlir::ValueRange> cmpOps = 1020 *caseOp.getCompareOperands(adaptor.getOperands(), t); 1021 mlir::Value caseArg = *(cmpOps.getValue().begin()); 1022 mlir::Attribute attr = cases[t]; 1023 if (attr.isa<fir::PointIntervalAttr>()) { 1024 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 1025 loc, mlir::LLVM::ICmpPredicate::eq, selector, caseArg); 1026 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 1027 continue; 1028 } 1029 if (attr.isa<fir::LowerBoundAttr>()) { 1030 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 1031 loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector); 1032 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 1033 continue; 1034 } 1035 if (attr.isa<fir::UpperBoundAttr>()) { 1036 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 1037 loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg); 1038 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 1039 continue; 1040 } 1041 if (attr.isa<fir::ClosedIntervalAttr>()) { 1042 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 1043 loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector); 1044 auto *thisBlock = rewriter.getInsertionBlock(); 1045 auto *newBlock1 = createBlock(rewriter, dest); 1046 auto *newBlock2 = createBlock(rewriter, dest); 1047 rewriter.setInsertionPointToEnd(thisBlock); 1048 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, newBlock1, newBlock2); 1049 rewriter.setInsertionPointToEnd(newBlock1); 1050 mlir::Value caseArg0 = *(cmpOps.getValue().begin() + 1); 1051 auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>( 1052 loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg0); 1053 genCondBrOp(loc, cmp0, dest, destOps, rewriter, newBlock2); 1054 rewriter.setInsertionPointToEnd(newBlock2); 1055 continue; 1056 } 1057 assert(attr.isa<mlir::UnitAttr>()); 1058 assert((t + 1 == conds) && "unit must be last"); 1059 genBrOp(caseOp, dest, destOps, rewriter); 1060 } 1061 return success(); 1062 } 1063 }; 1064 1065 template <typename OP> 1066 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select, 1067 typename OP::Adaptor adaptor, 1068 mlir::ConversionPatternRewriter &rewriter) { 1069 unsigned conds = select.getNumConditions(); 1070 auto cases = select.getCases().getValue(); 1071 mlir::Value selector = adaptor.selector(); 1072 auto loc = select.getLoc(); 1073 assert(conds > 0 && "select must have cases"); 1074 1075 llvm::SmallVector<mlir::Block *> destinations; 1076 llvm::SmallVector<mlir::ValueRange> destinationsOperands; 1077 mlir::Block *defaultDestination; 1078 mlir::ValueRange defaultOperands; 1079 llvm::SmallVector<int32_t> caseValues; 1080 1081 for (unsigned t = 0; t != conds; ++t) { 1082 mlir::Block *dest = select.getSuccessor(t); 1083 auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t); 1084 const mlir::Attribute &attr = cases[t]; 1085 if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) { 1086 destinations.push_back(dest); 1087 destinationsOperands.push_back(destOps.hasValue() ? *destOps 1088 : ValueRange()); 1089 caseValues.push_back(intAttr.getInt()); 1090 continue; 1091 } 1092 assert(attr.template dyn_cast_or_null<mlir::UnitAttr>()); 1093 assert((t + 1 == conds) && "unit must be last"); 1094 defaultDestination = dest; 1095 defaultOperands = destOps.hasValue() ? *destOps : ValueRange(); 1096 } 1097 1098 // LLVM::SwitchOp takes a i32 type for the selector. 1099 if (select.getSelector().getType() != rewriter.getI32Type()) 1100 selector = 1101 rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector); 1102 1103 rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>( 1104 select, selector, 1105 /*defaultDestination=*/defaultDestination, 1106 /*defaultOperands=*/defaultOperands, 1107 /*caseValues=*/caseValues, 1108 /*caseDestinations=*/destinations, 1109 /*caseOperands=*/destinationsOperands, 1110 /*branchWeights=*/ArrayRef<int32_t>()); 1111 } 1112 1113 /// conversion of fir::SelectOp to an if-then-else ladder 1114 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> { 1115 using FIROpConversion::FIROpConversion; 1116 1117 mlir::LogicalResult 1118 matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor, 1119 mlir::ConversionPatternRewriter &rewriter) const override { 1120 selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter); 1121 return success(); 1122 } 1123 }; 1124 1125 /// `fir.load` --> `llvm.load` 1126 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> { 1127 using FIROpConversion::FIROpConversion; 1128 1129 mlir::LogicalResult 1130 matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor, 1131 mlir::ConversionPatternRewriter &rewriter) const override { 1132 // fir.box is a special case because it is considered as an ssa values in 1133 // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box> 1134 // and fir.box end up being the same llvm types and loading a 1135 // fir.ref<fir.box> is actually a no op in LLVM. 1136 if (load.getType().isa<fir::BoxType>()) { 1137 rewriter.replaceOp(load, adaptor.getOperands()[0]); 1138 } else { 1139 mlir::Type ty = convertType(load.getType()); 1140 ArrayRef<NamedAttribute> at = load->getAttrs(); 1141 rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>( 1142 load, ty, adaptor.getOperands(), at); 1143 } 1144 return success(); 1145 } 1146 }; 1147 1148 /// Lower `fir.select_type` to LLVM IR dialect. 1149 struct SelectTypeOpConversion : public FIROpConversion<fir::SelectTypeOp> { 1150 using FIROpConversion::FIROpConversion; 1151 1152 mlir::LogicalResult 1153 matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor, 1154 mlir::ConversionPatternRewriter &rewriter) const override { 1155 return rewriter.notifyMatchFailure( 1156 select, "fir.select_type codegen is not implemented yet"); 1157 } 1158 }; 1159 1160 /// conversion of fir::SelectRankOp to an if-then-else ladder 1161 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> { 1162 using FIROpConversion::FIROpConversion; 1163 1164 mlir::LogicalResult 1165 matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor, 1166 mlir::ConversionPatternRewriter &rewriter) const override { 1167 selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter); 1168 return success(); 1169 } 1170 }; 1171 1172 /// `fir.store` --> `llvm.store` 1173 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> { 1174 using FIROpConversion::FIROpConversion; 1175 1176 mlir::LogicalResult 1177 matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor, 1178 mlir::ConversionPatternRewriter &rewriter) const override { 1179 if (store.value().getType().isa<fir::BoxType>()) { 1180 // fir.box value is actually in memory, load it first before storing it. 1181 mlir::Location loc = store.getLoc(); 1182 mlir::Type boxPtrTy = adaptor.getOperands()[0].getType(); 1183 auto val = rewriter.create<mlir::LLVM::LoadOp>( 1184 loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(), 1185 adaptor.getOperands()[0]); 1186 rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 1187 store, val, adaptor.getOperands()[1]); 1188 } else { 1189 rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 1190 store, adaptor.getOperands()[0], adaptor.getOperands()[1]); 1191 } 1192 return success(); 1193 } 1194 }; 1195 1196 /// convert to LLVM IR dialect `undef` 1197 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> { 1198 using FIROpConversion::FIROpConversion; 1199 1200 mlir::LogicalResult 1201 matchAndRewrite(fir::UndefOp undef, OpAdaptor, 1202 mlir::ConversionPatternRewriter &rewriter) const override { 1203 rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>( 1204 undef, convertType(undef.getType())); 1205 return success(); 1206 } 1207 }; 1208 1209 /// `fir.unreachable` --> `llvm.unreachable` 1210 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> { 1211 using FIROpConversion::FIROpConversion; 1212 1213 mlir::LogicalResult 1214 matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor, 1215 mlir::ConversionPatternRewriter &rewriter) const override { 1216 rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach); 1217 return success(); 1218 } 1219 }; 1220 1221 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> { 1222 using FIROpConversion::FIROpConversion; 1223 1224 mlir::LogicalResult 1225 matchAndRewrite(fir::ZeroOp zero, OpAdaptor, 1226 mlir::ConversionPatternRewriter &rewriter) const override { 1227 auto ty = convertType(zero.getType()); 1228 if (ty.isa<mlir::LLVM::LLVMPointerType>()) { 1229 rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty); 1230 } else if (ty.isa<mlir::IntegerType>()) { 1231 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 1232 zero, ty, mlir::IntegerAttr::get(zero.getType(), 0)); 1233 } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) { 1234 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 1235 zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0)); 1236 } else { 1237 // TODO: create ConstantAggregateZero for FIR aggregate/array types. 1238 return rewriter.notifyMatchFailure( 1239 zero, 1240 "conversion of fir.zero with aggregate type not implemented yet"); 1241 } 1242 return success(); 1243 } 1244 }; 1245 1246 /// Common base class for embox to descriptor conversion. 1247 template <typename OP> 1248 struct EmboxCommonConversion : public FIROpConversion<OP> { 1249 using FIROpConversion<OP>::FIROpConversion; 1250 1251 // Find the LLVMFuncOp in whose entry block the alloca should be inserted. 1252 // The order to find the LLVMFuncOp is as follows: 1253 // 1. The parent operation of the current block if it is a LLVMFuncOp. 1254 // 2. The first ancestor that is a LLVMFuncOp. 1255 mlir::LLVM::LLVMFuncOp 1256 getFuncForAllocaInsert(mlir::ConversionPatternRewriter &rewriter) const { 1257 mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp(); 1258 return mlir::isa<mlir::LLVM::LLVMFuncOp>(parentOp) 1259 ? mlir::cast<mlir::LLVM::LLVMFuncOp>(parentOp) 1260 : parentOp->getParentOfType<mlir::LLVM::LLVMFuncOp>(); 1261 } 1262 1263 // Generate an alloca of size 1 and type \p toTy. 1264 mlir::LLVM::AllocaOp 1265 genAllocaWithType(mlir::Location loc, mlir::Type toTy, unsigned alignment, 1266 mlir::ConversionPatternRewriter &rewriter) const { 1267 auto thisPt = rewriter.saveInsertionPoint(); 1268 mlir::LLVM::LLVMFuncOp func = getFuncForAllocaInsert(rewriter); 1269 rewriter.setInsertionPointToStart(&func.front()); 1270 auto size = this->genI32Constant(loc, rewriter, 1); 1271 auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, toTy, size, alignment); 1272 rewriter.restoreInsertionPoint(thisPt); 1273 return al; 1274 } 1275 1276 static int getCFIAttr(fir::BoxType boxTy) { 1277 auto eleTy = boxTy.getEleTy(); 1278 if (eleTy.isa<fir::PointerType>()) 1279 return CFI_attribute_pointer; 1280 if (eleTy.isa<fir::HeapType>()) 1281 return CFI_attribute_allocatable; 1282 return CFI_attribute_other; 1283 } 1284 1285 static fir::RecordType unwrapIfDerived(fir::BoxType boxTy) { 1286 return fir::unwrapSequenceType(fir::dyn_cast_ptrOrBoxEleTy(boxTy)) 1287 .template dyn_cast<fir::RecordType>(); 1288 } 1289 static bool isDerivedTypeWithLenParams(fir::BoxType boxTy) { 1290 auto recTy = unwrapIfDerived(boxTy); 1291 return recTy && recTy.getNumLenParams() > 0; 1292 } 1293 static bool isDerivedType(fir::BoxType boxTy) { 1294 return unwrapIfDerived(boxTy) != nullptr; 1295 } 1296 1297 // Get the element size and CFI type code of the boxed value. 1298 std::tuple<mlir::Value, mlir::Value> getSizeAndTypeCode( 1299 mlir::Location loc, mlir::ConversionPatternRewriter &rewriter, 1300 mlir::Type boxEleTy, mlir::ValueRange lenParams = {}) const { 1301 auto doInteger = 1302 [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> { 1303 int typeCode = fir::integerBitsToTypeCode(width); 1304 return {this->genConstantOffset(loc, rewriter, width / 8), 1305 this->genConstantOffset(loc, rewriter, typeCode)}; 1306 }; 1307 auto doLogical = 1308 [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> { 1309 int typeCode = fir::logicalBitsToTypeCode(width); 1310 return {this->genConstantOffset(loc, rewriter, width / 8), 1311 this->genConstantOffset(loc, rewriter, typeCode)}; 1312 }; 1313 auto doFloat = [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> { 1314 int typeCode = fir::realBitsToTypeCode(width); 1315 return {this->genConstantOffset(loc, rewriter, width / 8), 1316 this->genConstantOffset(loc, rewriter, typeCode)}; 1317 }; 1318 auto doComplex = 1319 [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> { 1320 auto typeCode = fir::complexBitsToTypeCode(width); 1321 return {this->genConstantOffset(loc, rewriter, width / 8 * 2), 1322 this->genConstantOffset(loc, rewriter, typeCode)}; 1323 }; 1324 auto doCharacter = 1325 [&](unsigned width, 1326 mlir::Value len) -> std::tuple<mlir::Value, mlir::Value> { 1327 auto typeCode = fir::characterBitsToTypeCode(width); 1328 auto typeCodeVal = this->genConstantOffset(loc, rewriter, typeCode); 1329 if (width == 8) 1330 return {len, typeCodeVal}; 1331 auto byteWidth = this->genConstantOffset(loc, rewriter, width / 8); 1332 auto i64Ty = mlir::IntegerType::get(&this->lowerTy().getContext(), 64); 1333 auto size = 1334 rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, byteWidth, len); 1335 return {size, typeCodeVal}; 1336 }; 1337 auto getKindMap = [&]() -> fir::KindMapping & { 1338 return this->lowerTy().getKindMap(); 1339 }; 1340 // Pointer-like types. 1341 if (auto eleTy = fir::dyn_cast_ptrEleTy(boxEleTy)) 1342 boxEleTy = eleTy; 1343 // Integer types. 1344 if (fir::isa_integer(boxEleTy)) { 1345 if (auto ty = boxEleTy.dyn_cast<mlir::IntegerType>()) 1346 return doInteger(ty.getWidth()); 1347 auto ty = boxEleTy.cast<fir::IntegerType>(); 1348 return doInteger(getKindMap().getIntegerBitsize(ty.getFKind())); 1349 } 1350 // Floating point types. 1351 if (fir::isa_real(boxEleTy)) { 1352 if (auto ty = boxEleTy.dyn_cast<mlir::FloatType>()) 1353 return doFloat(ty.getWidth()); 1354 auto ty = boxEleTy.cast<fir::RealType>(); 1355 return doFloat(getKindMap().getRealBitsize(ty.getFKind())); 1356 } 1357 // Complex types. 1358 if (fir::isa_complex(boxEleTy)) { 1359 if (auto ty = boxEleTy.dyn_cast<mlir::ComplexType>()) 1360 return doComplex( 1361 ty.getElementType().cast<mlir::FloatType>().getWidth()); 1362 auto ty = boxEleTy.cast<fir::ComplexType>(); 1363 return doComplex(getKindMap().getRealBitsize(ty.getFKind())); 1364 } 1365 // Character types. 1366 if (auto ty = boxEleTy.dyn_cast<fir::CharacterType>()) { 1367 auto charWidth = getKindMap().getCharacterBitsize(ty.getFKind()); 1368 if (ty.getLen() != fir::CharacterType::unknownLen()) { 1369 auto len = this->genConstantOffset(loc, rewriter, ty.getLen()); 1370 return doCharacter(charWidth, len); 1371 } 1372 assert(!lenParams.empty()); 1373 return doCharacter(charWidth, lenParams.back()); 1374 } 1375 // Logical type. 1376 if (auto ty = boxEleTy.dyn_cast<fir::LogicalType>()) 1377 return doLogical(getKindMap().getLogicalBitsize(ty.getFKind())); 1378 // Array types. 1379 if (auto seqTy = boxEleTy.dyn_cast<fir::SequenceType>()) 1380 return getSizeAndTypeCode(loc, rewriter, seqTy.getEleTy(), lenParams); 1381 // Derived-type types. 1382 if (boxEleTy.isa<fir::RecordType>()) { 1383 auto ptrTy = mlir::LLVM::LLVMPointerType::get( 1384 this->lowerTy().convertType(boxEleTy)); 1385 auto nullPtr = rewriter.create<mlir::LLVM::NullOp>(loc, ptrTy); 1386 auto one = 1387 genConstantIndex(loc, this->lowerTy().offsetType(), rewriter, 1); 1388 auto gep = rewriter.create<mlir::LLVM::GEPOp>( 1389 loc, ptrTy, mlir::ValueRange{nullPtr, one}); 1390 auto eleSize = rewriter.create<mlir::LLVM::PtrToIntOp>( 1391 loc, this->lowerTy().indexType(), gep); 1392 return {eleSize, 1393 this->genConstantOffset(loc, rewriter, fir::derivedToTypeCode())}; 1394 } 1395 // Reference type. 1396 if (fir::isa_ref_type(boxEleTy)) { 1397 // FIXME: use the target pointer size rather than sizeof(void*) 1398 return {this->genConstantOffset(loc, rewriter, sizeof(void *)), 1399 this->genConstantOffset(loc, rewriter, CFI_type_cptr)}; 1400 } 1401 fir::emitFatalError(loc, "unhandled type in fir.box code generation"); 1402 } 1403 1404 /// Basic pattern to write a field in the descriptor 1405 mlir::Value insertField(mlir::ConversionPatternRewriter &rewriter, 1406 mlir::Location loc, mlir::Value dest, 1407 ArrayRef<unsigned> fldIndexes, mlir::Value value, 1408 bool bitcast = false) const { 1409 auto boxTy = dest.getType(); 1410 auto fldTy = this->getBoxEleTy(boxTy, fldIndexes); 1411 if (bitcast) 1412 value = rewriter.create<mlir::LLVM::BitcastOp>(loc, fldTy, value); 1413 else 1414 value = this->integerCast(loc, rewriter, fldTy, value); 1415 SmallVector<mlir::Attribute, 2> attrs; 1416 for (auto i : fldIndexes) 1417 attrs.push_back(rewriter.getI32IntegerAttr(i)); 1418 auto indexesAttr = mlir::ArrayAttr::get(rewriter.getContext(), attrs); 1419 return rewriter.create<mlir::LLVM::InsertValueOp>(loc, boxTy, dest, value, 1420 indexesAttr); 1421 } 1422 1423 inline mlir::Value 1424 insertBaseAddress(mlir::ConversionPatternRewriter &rewriter, 1425 mlir::Location loc, mlir::Value dest, 1426 mlir::Value base) const { 1427 return insertField(rewriter, loc, dest, {0}, base, /*bitCast=*/true); 1428 } 1429 1430 /// Get the address of the type descriptor global variable that was created by 1431 /// lowering for derived type \p recType. 1432 template <typename BOX> 1433 mlir::Value 1434 getTypeDescriptor(BOX box, mlir::ConversionPatternRewriter &rewriter, 1435 mlir::Location loc, fir::RecordType recType) const { 1436 std::string name = recType.getLoweredName(); 1437 auto module = box->template getParentOfType<mlir::ModuleOp>(); 1438 if (auto global = module.template lookupSymbol<fir::GlobalOp>(name)) { 1439 auto ty = mlir::LLVM::LLVMPointerType::get( 1440 this->lowerTy().convertType(global.getType())); 1441 return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty, 1442 global.sym_name()); 1443 } 1444 if (auto global = 1445 module.template lookupSymbol<mlir::LLVM::GlobalOp>(name)) { 1446 // The global may have already been translated to LLVM. 1447 auto ty = mlir::LLVM::LLVMPointerType::get(global.getType()); 1448 return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty, 1449 global.sym_name()); 1450 } 1451 // The global does not exist in the current translation unit, but may be 1452 // defined elsewhere (e.g., type defined in a module). 1453 // For now, create a extern_weak symbol (will become nullptr if unresolved) 1454 // to support generating code without the front-end generated symbols. 1455 // These could be made available_externally to require the symbols to be 1456 // defined elsewhere and to cause link-time failure otherwise. 1457 auto i8Ty = rewriter.getIntegerType(8); 1458 mlir::OpBuilder modBuilder(module.getBodyRegion()); 1459 // TODO: The symbol should be lowered to constant in lowering, they are read 1460 // only. 1461 modBuilder.create<mlir::LLVM::GlobalOp>(loc, i8Ty, /*isConstant=*/false, 1462 mlir::LLVM::Linkage::ExternWeak, 1463 name, mlir::Attribute{}); 1464 auto ty = mlir::LLVM::LLVMPointerType::get(i8Ty); 1465 return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty, name); 1466 } 1467 1468 template <typename BOX> 1469 std::tuple<fir::BoxType, mlir::Value, mlir::Value> 1470 consDescriptorPrefix(BOX box, mlir::ConversionPatternRewriter &rewriter, 1471 unsigned rank, mlir::ValueRange lenParams) const { 1472 auto loc = box.getLoc(); 1473 auto boxTy = box.getType().template dyn_cast<fir::BoxType>(); 1474 auto convTy = this->lowerTy().convertBoxType(boxTy, rank); 1475 auto llvmBoxPtrTy = convTy.template cast<mlir::LLVM::LLVMPointerType>(); 1476 auto llvmBoxTy = llvmBoxPtrTy.getElementType(); 1477 mlir::Value descriptor = 1478 rewriter.create<mlir::LLVM::UndefOp>(loc, llvmBoxTy); 1479 1480 llvm::SmallVector<mlir::Value> typeparams = lenParams; 1481 if constexpr (!std::is_same_v<BOX, fir::EmboxOp>) { 1482 if (!box.substr().empty() && fir::hasDynamicSize(boxTy.getEleTy())) 1483 typeparams.push_back(box.substr()[1]); 1484 } 1485 1486 // Write each of the fields with the appropriate values 1487 auto [eleSize, cfiTy] = 1488 getSizeAndTypeCode(loc, rewriter, boxTy.getEleTy(), typeparams); 1489 descriptor = 1490 insertField(rewriter, loc, descriptor, {kElemLenPosInBox}, eleSize); 1491 descriptor = insertField(rewriter, loc, descriptor, {kVersionPosInBox}, 1492 this->genI32Constant(loc, rewriter, CFI_VERSION)); 1493 descriptor = insertField(rewriter, loc, descriptor, {kRankPosInBox}, 1494 this->genI32Constant(loc, rewriter, rank)); 1495 descriptor = insertField(rewriter, loc, descriptor, {kTypePosInBox}, cfiTy); 1496 descriptor = 1497 insertField(rewriter, loc, descriptor, {kAttributePosInBox}, 1498 this->genI32Constant(loc, rewriter, getCFIAttr(boxTy))); 1499 const bool hasAddendum = isDerivedType(boxTy); 1500 descriptor = 1501 insertField(rewriter, loc, descriptor, {kF18AddendumPosInBox}, 1502 this->genI32Constant(loc, rewriter, hasAddendum ? 1 : 0)); 1503 1504 if (hasAddendum) { 1505 auto isArray = 1506 fir::dyn_cast_ptrOrBoxEleTy(boxTy).template isa<fir::SequenceType>(); 1507 unsigned typeDescFieldId = isArray ? kOptTypePtrPosInBox : kDimsPosInBox; 1508 auto typeDesc = 1509 getTypeDescriptor(box, rewriter, loc, unwrapIfDerived(boxTy)); 1510 descriptor = 1511 insertField(rewriter, loc, descriptor, {typeDescFieldId}, typeDesc, 1512 /*bitCast=*/true); 1513 } 1514 1515 return {boxTy, descriptor, eleSize}; 1516 } 1517 1518 /// If the embox is not in a globalOp body, allocate storage for the box; 1519 /// store the value inside and return the generated alloca. Return the input 1520 /// value otherwise. 1521 mlir::Value 1522 placeInMemoryIfNotGlobalInit(mlir::ConversionPatternRewriter &rewriter, 1523 mlir::Location loc, mlir::Value boxValue) const { 1524 auto *thisBlock = rewriter.getInsertionBlock(); 1525 if (thisBlock && mlir::isa<mlir::LLVM::GlobalOp>(thisBlock->getParentOp())) 1526 return boxValue; 1527 auto boxPtrTy = mlir::LLVM::LLVMPointerType::get(boxValue.getType()); 1528 auto alloca = genAllocaWithType(loc, boxPtrTy, defaultAlign, rewriter); 1529 rewriter.create<mlir::LLVM::StoreOp>(loc, boxValue, alloca); 1530 return alloca; 1531 } 1532 }; 1533 1534 /// Create a generic box on a memory reference. This conversions lowers the 1535 /// abstract box to the appropriate, initialized descriptor. 1536 struct EmboxOpConversion : public EmboxCommonConversion<fir::EmboxOp> { 1537 using EmboxCommonConversion::EmboxCommonConversion; 1538 1539 mlir::LogicalResult 1540 matchAndRewrite(fir::EmboxOp embox, OpAdaptor adaptor, 1541 mlir::ConversionPatternRewriter &rewriter) const override { 1542 assert(!embox.getShape() && "There should be no dims on this embox op"); 1543 auto [boxTy, dest, eleSize] = 1544 consDescriptorPrefix(embox, rewriter, /*rank=*/0, 1545 /*lenParams=*/adaptor.getOperands().drop_front(1)); 1546 dest = insertBaseAddress(rewriter, embox.getLoc(), dest, 1547 adaptor.getOperands()[0]); 1548 if (isDerivedTypeWithLenParams(boxTy)) 1549 return rewriter.notifyMatchFailure( 1550 embox, "fir.embox codegen of derived with length parameters not " 1551 "implemented yet"); 1552 auto result = placeInMemoryIfNotGlobalInit(rewriter, embox.getLoc(), dest); 1553 rewriter.replaceOp(embox, result); 1554 return success(); 1555 } 1556 }; 1557 1558 /// Lower `fir.emboxproc` operation. Creates a procedure box. 1559 /// TODO: Part of supporting Fortran 2003 procedure pointers. 1560 struct EmboxProcOpConversion : public FIROpConversion<fir::EmboxProcOp> { 1561 using FIROpConversion::FIROpConversion; 1562 1563 mlir::LogicalResult 1564 matchAndRewrite(fir::EmboxProcOp emboxproc, OpAdaptor adaptor, 1565 mlir::ConversionPatternRewriter &rewriter) const override { 1566 return rewriter.notifyMatchFailure( 1567 emboxproc, "fir.emboxproc codegen is not implemented yet"); 1568 } 1569 }; 1570 1571 1572 // Code shared between insert_value and extract_value Ops. 1573 struct ValueOpCommon { 1574 // Translate the arguments pertaining to any multidimensional array to 1575 // row-major order for LLVM-IR. 1576 static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs, 1577 mlir::Type ty) { 1578 assert(ty && "type is null"); 1579 const auto end = attrs.size(); 1580 for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) { 1581 if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 1582 const auto dim = getDimension(seq); 1583 if (dim > 1) { 1584 auto ub = std::min(i + dim, end); 1585 std::reverse(attrs.begin() + i, attrs.begin() + ub); 1586 i += dim - 1; 1587 } 1588 ty = getArrayElementType(seq); 1589 } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) { 1590 ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()]; 1591 } else { 1592 llvm_unreachable("index into invalid type"); 1593 } 1594 } 1595 } 1596 1597 static llvm::SmallVector<mlir::Attribute> 1598 collectIndices(mlir::ConversionPatternRewriter &rewriter, 1599 mlir::ArrayAttr arrAttr) { 1600 llvm::SmallVector<mlir::Attribute> attrs; 1601 for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) { 1602 if (i->isa<mlir::IntegerAttr>()) { 1603 attrs.push_back(*i); 1604 } else { 1605 auto fieldName = i->cast<mlir::StringAttr>().getValue(); 1606 ++i; 1607 auto ty = i->cast<mlir::TypeAttr>().getValue(); 1608 auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName); 1609 attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index)); 1610 } 1611 } 1612 return attrs; 1613 } 1614 1615 private: 1616 static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) { 1617 unsigned result = 1; 1618 for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>(); 1619 eleTy; 1620 eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>()) 1621 ++result; 1622 return result; 1623 } 1624 1625 static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) { 1626 auto eleTy = ty.getElementType(); 1627 while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>()) 1628 eleTy = arrTy.getElementType(); 1629 return eleTy; 1630 } 1631 }; 1632 1633 /// Extract a subobject value from an ssa-value of aggregate type 1634 struct ExtractValueOpConversion 1635 : public FIROpAndTypeConversion<fir::ExtractValueOp>, 1636 public ValueOpCommon { 1637 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1638 1639 mlir::LogicalResult 1640 doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor, 1641 mlir::ConversionPatternRewriter &rewriter) const override { 1642 auto attrs = collectIndices(rewriter, extractVal.coor()); 1643 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 1644 auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs); 1645 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>( 1646 extractVal, ty, adaptor.getOperands()[0], position); 1647 return success(); 1648 } 1649 }; 1650 1651 /// InsertValue is the generalized instruction for the composition of new 1652 /// aggregate type values. 1653 struct InsertValueOpConversion 1654 : public FIROpAndTypeConversion<fir::InsertValueOp>, 1655 public ValueOpCommon { 1656 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1657 1658 mlir::LogicalResult 1659 doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor, 1660 mlir::ConversionPatternRewriter &rewriter) const override { 1661 auto attrs = collectIndices(rewriter, insertVal.coor()); 1662 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 1663 auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs); 1664 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1665 insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1], 1666 position); 1667 return success(); 1668 } 1669 }; 1670 1671 /// InsertOnRange inserts a value into a sequence over a range of offsets. 1672 struct InsertOnRangeOpConversion 1673 : public FIROpAndTypeConversion<fir::InsertOnRangeOp> { 1674 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1675 1676 // Increments an array of subscripts in a row major fasion. 1677 void incrementSubscripts(const SmallVector<uint64_t> &dims, 1678 SmallVector<uint64_t> &subscripts) const { 1679 for (size_t i = dims.size(); i > 0; --i) { 1680 if (++subscripts[i - 1] < dims[i - 1]) { 1681 return; 1682 } 1683 subscripts[i - 1] = 0; 1684 } 1685 } 1686 1687 mlir::LogicalResult 1688 doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor, 1689 mlir::ConversionPatternRewriter &rewriter) const override { 1690 1691 llvm::SmallVector<uint64_t> dims; 1692 auto type = adaptor.getOperands()[0].getType(); 1693 1694 // Iteratively extract the array dimensions from the type. 1695 while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 1696 dims.push_back(t.getNumElements()); 1697 type = t.getElementType(); 1698 } 1699 1700 SmallVector<uint64_t> lBounds; 1701 SmallVector<uint64_t> uBounds; 1702 1703 // Extract integer value from the attribute 1704 SmallVector<int64_t> coordinates = llvm::to_vector<4>( 1705 llvm::map_range(range.coor(), [](Attribute a) -> int64_t { 1706 return a.cast<IntegerAttr>().getInt(); 1707 })); 1708 1709 // Unzip the upper and lower bound and convert to a row major format. 1710 for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) { 1711 uBounds.push_back(*i++); 1712 lBounds.push_back(*i); 1713 } 1714 1715 auto &subscripts = lBounds; 1716 auto loc = range.getLoc(); 1717 mlir::Value lastOp = adaptor.getOperands()[0]; 1718 mlir::Value insertVal = adaptor.getOperands()[1]; 1719 1720 auto i64Ty = rewriter.getI64Type(); 1721 while (subscripts != uBounds) { 1722 // Convert uint64_t's to Attribute's. 1723 SmallVector<mlir::Attribute> subscriptAttrs; 1724 for (const auto &subscript : subscripts) 1725 subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript)); 1726 lastOp = rewriter.create<mlir::LLVM::InsertValueOp>( 1727 loc, ty, lastOp, insertVal, 1728 ArrayAttr::get(range.getContext(), subscriptAttrs)); 1729 1730 incrementSubscripts(dims, subscripts); 1731 } 1732 1733 // Convert uint64_t's to Attribute's. 1734 SmallVector<mlir::Attribute> subscriptAttrs; 1735 for (const auto &subscript : subscripts) 1736 subscriptAttrs.push_back( 1737 IntegerAttr::get(rewriter.getI64Type(), subscript)); 1738 mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs); 1739 1740 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1741 range, ty, lastOp, insertVal, 1742 ArrayAttr::get(range.getContext(), arrayRef)); 1743 1744 return success(); 1745 } 1746 }; 1747 1748 // 1749 // Primitive operations on Complex types 1750 // 1751 1752 /// Generate inline code for complex addition/subtraction 1753 template <typename LLVMOP, typename OPTY> 1754 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds, 1755 mlir::ConversionPatternRewriter &rewriter, 1756 fir::LLVMTypeConverter &lowering) { 1757 mlir::Value a = opnds[0]; 1758 mlir::Value b = opnds[1]; 1759 auto loc = sumop.getLoc(); 1760 auto ctx = sumop.getContext(); 1761 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1762 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1763 mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType())); 1764 mlir::Type ty = lowering.convertType(sumop.getType()); 1765 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1766 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1767 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1768 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1769 auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1); 1770 auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1); 1771 auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1772 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0); 1773 return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1); 1774 } 1775 1776 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> { 1777 using FIROpConversion::FIROpConversion; 1778 1779 mlir::LogicalResult 1780 matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor, 1781 mlir::ConversionPatternRewriter &rewriter) const override { 1782 // given: (x + iy) + (x' + iy') 1783 // result: (x + x') + i(y + y') 1784 auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(), 1785 rewriter, lowerTy()); 1786 rewriter.replaceOp(addc, r.getResult()); 1787 return success(); 1788 } 1789 }; 1790 1791 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> { 1792 using FIROpConversion::FIROpConversion; 1793 1794 mlir::LogicalResult 1795 matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor, 1796 mlir::ConversionPatternRewriter &rewriter) const override { 1797 // given: (x + iy) - (x' + iy') 1798 // result: (x - x') + i(y - y') 1799 auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(), 1800 rewriter, lowerTy()); 1801 rewriter.replaceOp(subc, r.getResult()); 1802 return success(); 1803 } 1804 }; 1805 1806 /// Inlined complex multiply 1807 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> { 1808 using FIROpConversion::FIROpConversion; 1809 1810 mlir::LogicalResult 1811 matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor, 1812 mlir::ConversionPatternRewriter &rewriter) const override { 1813 // TODO: Can we use a call to __muldc3 ? 1814 // given: (x + iy) * (x' + iy') 1815 // result: (xx'-yy')+i(xy'+yx') 1816 mlir::Value a = adaptor.getOperands()[0]; 1817 mlir::Value b = adaptor.getOperands()[1]; 1818 auto loc = mulc.getLoc(); 1819 auto *ctx = mulc.getContext(); 1820 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1821 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1822 mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType())); 1823 mlir::Type ty = convertType(mulc.getType()); 1824 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1825 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1826 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1827 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1828 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 1829 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 1830 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 1831 auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx); 1832 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 1833 auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy); 1834 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1835 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 1836 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 1837 rewriter.replaceOp(mulc, r0.getResult()); 1838 return success(); 1839 } 1840 }; 1841 1842 /// Inlined complex division 1843 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> { 1844 using FIROpConversion::FIROpConversion; 1845 1846 mlir::LogicalResult 1847 matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor, 1848 mlir::ConversionPatternRewriter &rewriter) const override { 1849 // TODO: Can we use a call to __divdc3 instead? 1850 // Just generate inline code for now. 1851 // given: (x + iy) / (x' + iy') 1852 // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y' 1853 mlir::Value a = adaptor.getOperands()[0]; 1854 mlir::Value b = adaptor.getOperands()[1]; 1855 auto loc = divc.getLoc(); 1856 auto *ctx = divc.getContext(); 1857 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1858 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1859 mlir::Type eleTy = convertType(getComplexEleTy(divc.getType())); 1860 mlir::Type ty = convertType(divc.getType()); 1861 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1862 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1863 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1864 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1865 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 1866 auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1); 1867 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 1868 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 1869 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 1870 auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1); 1871 auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1); 1872 auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy); 1873 auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy); 1874 auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d); 1875 auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d); 1876 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1877 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 1878 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 1879 rewriter.replaceOp(divc, r0.getResult()); 1880 return success(); 1881 } 1882 }; 1883 1884 /// Inlined complex negation 1885 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> { 1886 using FIROpConversion::FIROpConversion; 1887 1888 mlir::LogicalResult 1889 matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor, 1890 mlir::ConversionPatternRewriter &rewriter) const override { 1891 // given: -(x + iy) 1892 // result: -x - iy 1893 auto *ctxt = neg.getContext(); 1894 auto eleTy = convertType(getComplexEleTy(neg.getType())); 1895 auto ty = convertType(neg.getType()); 1896 auto loc = neg.getLoc(); 1897 mlir::Value o0 = adaptor.getOperands()[0]; 1898 auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0)); 1899 auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1)); 1900 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0); 1901 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1); 1902 auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp); 1903 auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip); 1904 auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0); 1905 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1); 1906 return success(); 1907 } 1908 }; 1909 1910 /// Conversion pattern for operation that must be dead. The information in these 1911 /// operations is used by other operation. At this point they should not have 1912 /// anymore uses. 1913 /// These operations are normally dead after the pre-codegen pass. 1914 template <typename FromOp> 1915 struct MustBeDeadConversion : public FIROpConversion<FromOp> { 1916 explicit MustBeDeadConversion(fir::LLVMTypeConverter &lowering) 1917 : FIROpConversion<FromOp>(lowering) {} 1918 using OpAdaptor = typename FromOp::Adaptor; 1919 1920 mlir::LogicalResult 1921 matchAndRewrite(FromOp op, OpAdaptor adaptor, 1922 mlir::ConversionPatternRewriter &rewriter) const final { 1923 if (!op->getUses().empty()) 1924 return rewriter.notifyMatchFailure(op, "op must be dead"); 1925 rewriter.eraseOp(op); 1926 return success(); 1927 } 1928 }; 1929 1930 struct ShapeOpConversion : public MustBeDeadConversion<fir::ShapeOp> { 1931 using MustBeDeadConversion::MustBeDeadConversion; 1932 }; 1933 1934 struct ShapeShiftOpConversion : public MustBeDeadConversion<fir::ShapeShiftOp> { 1935 using MustBeDeadConversion::MustBeDeadConversion; 1936 }; 1937 1938 struct ShiftOpConversion : public MustBeDeadConversion<fir::ShiftOp> { 1939 using MustBeDeadConversion::MustBeDeadConversion; 1940 }; 1941 1942 struct SliceOpConversion : public MustBeDeadConversion<fir::SliceOp> { 1943 using MustBeDeadConversion::MustBeDeadConversion; 1944 }; 1945 1946 /// `fir.is_present` --> 1947 /// ``` 1948 /// %0 = llvm.mlir.constant(0 : i64) 1949 /// %1 = llvm.ptrtoint %0 1950 /// %2 = llvm.icmp "ne" %1, %0 : i64 1951 /// ``` 1952 struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> { 1953 using FIROpConversion::FIROpConversion; 1954 1955 mlir::LogicalResult 1956 matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor, 1957 mlir::ConversionPatternRewriter &rewriter) const override { 1958 mlir::Type idxTy = lowerTy().indexType(); 1959 mlir::Location loc = isPresent.getLoc(); 1960 auto ptr = adaptor.getOperands()[0]; 1961 1962 if (isPresent.val().getType().isa<fir::BoxCharType>()) { 1963 auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>(); 1964 assert(!structTy.isOpaque() && !structTy.getBody().empty()); 1965 1966 mlir::Type ty = structTy.getBody()[0]; 1967 mlir::MLIRContext *ctx = isPresent.getContext(); 1968 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1969 ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0); 1970 } 1971 mlir::LLVM::ConstantOp c0 = 1972 genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0); 1973 auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr); 1974 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>( 1975 isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0); 1976 1977 return success(); 1978 } 1979 }; 1980 1981 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of 1982 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element 1983 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd 1984 /// element is the length of the character buffer (`#n`). 1985 struct EmboxCharOpConversion : public FIROpConversion<fir::EmboxCharOp> { 1986 using FIROpConversion::FIROpConversion; 1987 1988 mlir::LogicalResult 1989 matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor, 1990 mlir::ConversionPatternRewriter &rewriter) const override { 1991 mlir::ValueRange operands = adaptor.getOperands(); 1992 MLIRContext *ctx = emboxChar.getContext(); 1993 1994 mlir::Value charBuffer = operands[0]; 1995 mlir::Value charBufferLen = operands[1]; 1996 1997 mlir::Location loc = emboxChar.getLoc(); 1998 mlir::Type llvmStructTy = convertType(emboxChar.getType()); 1999 auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy); 2000 2001 mlir::Type lenTy = 2002 llvmStructTy.cast<mlir::LLVM::LLVMStructType>().getBody()[1]; 2003 mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen); 2004 2005 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 2006 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 2007 auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>( 2008 loc, llvmStructTy, llvmStruct, charBuffer, c0); 2009 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 2010 emboxChar, llvmStructTy, insertBufferOp, lenAfterCast, c1); 2011 2012 return success(); 2013 } 2014 }; 2015 2016 /// Construct an `llvm.extractvalue` instruction. It will return value at 2017 /// element \p x from \p tuple. 2018 mlir::LLVM::ExtractValueOp 2019 genExtractValueWithIndex(mlir::Location loc, mlir::Value tuple, mlir::Type ty, 2020 mlir::ConversionPatternRewriter &rewriter, 2021 mlir::MLIRContext *ctx, int x) { 2022 auto cx = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(x)); 2023 auto xty = ty.cast<mlir::LLVM::LLVMStructType>().getBody()[x]; 2024 return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, xty, tuple, cx); 2025 } 2026 2027 /// Convert `!fir.boxchar_len` to `!llvm.extractvalue` for the 2nd part of the 2028 /// boxchar. 2029 struct BoxCharLenOpConversion : public FIROpConversion<fir::BoxCharLenOp> { 2030 using FIROpConversion::FIROpConversion; 2031 2032 mlir::LogicalResult 2033 matchAndRewrite(fir::BoxCharLenOp boxCharLen, OpAdaptor adaptor, 2034 mlir::ConversionPatternRewriter &rewriter) const override { 2035 mlir::Value boxChar = adaptor.getOperands()[0]; 2036 mlir::Location loc = boxChar.getLoc(); 2037 mlir::MLIRContext *ctx = boxChar.getContext(); 2038 mlir::Type returnValTy = boxCharLen.getResult().getType(); 2039 2040 constexpr int boxcharLenIdx = 1; 2041 mlir::LLVM::ExtractValueOp len = genExtractValueWithIndex( 2042 loc, boxChar, boxChar.getType(), rewriter, ctx, boxcharLenIdx); 2043 mlir::Value lenAfterCast = integerCast(loc, rewriter, returnValTy, len); 2044 rewriter.replaceOp(boxCharLen, lenAfterCast); 2045 2046 return success(); 2047 } 2048 }; 2049 2050 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for 2051 /// the character buffer and one for the buffer length. 2052 struct UnboxCharOpConversion : public FIROpConversion<fir::UnboxCharOp> { 2053 using FIROpConversion::FIROpConversion; 2054 2055 mlir::LogicalResult 2056 matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor, 2057 mlir::ConversionPatternRewriter &rewriter) const override { 2058 MLIRContext *ctx = unboxchar.getContext(); 2059 2060 mlir::Type lenTy = convertType(unboxchar.getType(1)); 2061 mlir::Value tuple = adaptor.getOperands()[0]; 2062 mlir::Type tupleTy = tuple.getType(); 2063 2064 mlir::Location loc = unboxchar.getLoc(); 2065 mlir::Value ptrToBuffer = 2066 genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 0); 2067 2068 mlir::LLVM::ExtractValueOp len = 2069 genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 1); 2070 mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len); 2071 2072 rewriter.replaceOp(unboxchar, 2073 ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast}); 2074 return success(); 2075 } 2076 }; 2077 2078 /// Lower `fir.unboxproc` operation. Unbox a procedure box value, yielding its 2079 /// components. 2080 /// TODO: Part of supporting Fortran 2003 procedure pointers. 2081 struct UnboxProcOpConversion : public FIROpConversion<fir::UnboxProcOp> { 2082 using FIROpConversion::FIROpConversion; 2083 2084 mlir::LogicalResult 2085 matchAndRewrite(fir::UnboxProcOp unboxproc, OpAdaptor adaptor, 2086 mlir::ConversionPatternRewriter &rewriter) const override { 2087 return rewriter.notifyMatchFailure( 2088 unboxproc, "fir.unboxproc codegen is not implemented yet"); 2089 } 2090 }; 2091 2092 /// Convert `fir.field_index`. The conversion depends on whether the size of 2093 /// the record is static or dynamic. 2094 struct FieldIndexOpConversion : public FIROpConversion<fir::FieldIndexOp> { 2095 using FIROpConversion::FIROpConversion; 2096 2097 // NB: most field references should be resolved by this point 2098 mlir::LogicalResult 2099 matchAndRewrite(fir::FieldIndexOp field, OpAdaptor adaptor, 2100 mlir::ConversionPatternRewriter &rewriter) const override { 2101 auto recTy = field.on_type().cast<fir::RecordType>(); 2102 unsigned index = recTy.getFieldIndex(field.field_id()); 2103 2104 if (!fir::hasDynamicSize(recTy)) { 2105 // Derived type has compile-time constant layout. Return index of the 2106 // component type in the parent type (to be used in GEP). 2107 rewriter.replaceOp(field, mlir::ValueRange{genConstantOffset( 2108 field.getLoc(), rewriter, index)}); 2109 return success(); 2110 } 2111 2112 // Derived type has compile-time constant layout. Call the compiler 2113 // generated function to determine the byte offset of the field at runtime. 2114 // This returns a non-constant. 2115 FlatSymbolRefAttr symAttr = mlir::SymbolRefAttr::get( 2116 field.getContext(), getOffsetMethodName(recTy, field.field_id())); 2117 NamedAttribute callAttr = rewriter.getNamedAttr("callee", symAttr); 2118 NamedAttribute fieldAttr = rewriter.getNamedAttr( 2119 "field", mlir::IntegerAttr::get(lowerTy().indexType(), index)); 2120 rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>( 2121 field, lowerTy().offsetType(), adaptor.getOperands(), 2122 llvm::ArrayRef<mlir::NamedAttribute>{callAttr, fieldAttr}); 2123 return success(); 2124 } 2125 2126 // Re-Construct the name of the compiler generated method that calculates the 2127 // offset 2128 inline static std::string getOffsetMethodName(fir::RecordType recTy, 2129 llvm::StringRef field) { 2130 return recTy.getName().str() + "P." + field.str() + ".offset"; 2131 } 2132 }; 2133 2134 } // namespace 2135 2136 namespace { 2137 /// Convert FIR dialect to LLVM dialect 2138 /// 2139 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An 2140 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect. 2141 /// 2142 /// This pass is not complete yet. We are upstreaming it in small patches. 2143 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> { 2144 public: 2145 mlir::ModuleOp getModule() { return getOperation(); } 2146 2147 void runOnOperation() override final { 2148 auto mod = getModule(); 2149 if (!forcedTargetTriple.empty()) { 2150 fir::setTargetTriple(mod, forcedTargetTriple); 2151 } 2152 2153 auto *context = getModule().getContext(); 2154 fir::LLVMTypeConverter typeConverter{getModule()}; 2155 mlir::OwningRewritePatternList pattern(context); 2156 pattern.insert< 2157 AbsentOpConversion, AddcOpConversion, AddrOfOpConversion, 2158 AllocaOpConversion, BoxAddrOpConversion, BoxCharLenOpConversion, 2159 BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion, 2160 BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxProcHostOpConversion, 2161 BoxRankOpConversion, BoxTypeDescOpConversion, CallOpConversion, 2162 CmpcOpConversion, ConstcOpConversion, ConvertOpConversion, 2163 DispatchOpConversion, DispatchTableOpConversion, DTEntryOpConversion, 2164 DivcOpConversion, EmboxOpConversion, EmboxCharOpConversion, 2165 EmboxProcOpConversion, ExtractValueOpConversion, FieldIndexOpConversion, 2166 FirEndOpConversion, HasValueOpConversion, GenTypeDescOpConversion, 2167 GlobalLenOpConversion, GlobalOpConversion, InsertOnRangeOpConversion, 2168 InsertValueOpConversion, IsPresentOpConversion, LoadOpConversion, 2169 NegcOpConversion, MulcOpConversion, SelectCaseOpConversion, 2170 SelectOpConversion, SelectRankOpConversion, SelectTypeOpConversion, 2171 ShapeOpConversion, ShapeShiftOpConversion, ShiftOpConversion, 2172 SliceOpConversion, StoreOpConversion, StringLitOpConversion, 2173 SubcOpConversion, UnboxCharOpConversion, UnboxProcOpConversion, 2174 UndefOpConversion, UnreachableOpConversion, ZeroOpConversion>( 2175 typeConverter); 2176 mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern); 2177 mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter, 2178 pattern); 2179 mlir::ConversionTarget target{*context}; 2180 target.addLegalDialect<mlir::LLVM::LLVMDialect>(); 2181 2182 // required NOPs for applying a full conversion 2183 target.addLegalOp<mlir::ModuleOp>(); 2184 2185 // apply the patterns 2186 if (mlir::failed(mlir::applyFullConversion(getModule(), target, 2187 std::move(pattern)))) { 2188 signalPassFailure(); 2189 } 2190 } 2191 }; 2192 } // namespace 2193 2194 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() { 2195 return std::make_unique<FIRToLLVMLowering>(); 2196 } 2197