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.no_reassoc` to LLVM IR dialect. 1149 /// TODO: how do we want to enforce this in LLVM-IR? Can we manipulate the fast 1150 /// math flags? 1151 struct NoReassocOpConversion : public FIROpConversion<fir::NoReassocOp> { 1152 using FIROpConversion::FIROpConversion; 1153 1154 mlir::LogicalResult 1155 matchAndRewrite(fir::NoReassocOp noreassoc, OpAdaptor adaptor, 1156 mlir::ConversionPatternRewriter &rewriter) const override { 1157 rewriter.replaceOp(noreassoc, adaptor.getOperands()[0]); 1158 return success(); 1159 } 1160 }; 1161 1162 /// Lower `fir.select_type` to LLVM IR dialect. 1163 struct SelectTypeOpConversion : public FIROpConversion<fir::SelectTypeOp> { 1164 using FIROpConversion::FIROpConversion; 1165 1166 mlir::LogicalResult 1167 matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor, 1168 mlir::ConversionPatternRewriter &rewriter) const override { 1169 return rewriter.notifyMatchFailure( 1170 select, "fir.select_type codegen is not implemented yet"); 1171 } 1172 }; 1173 1174 /// conversion of fir::SelectRankOp to an if-then-else ladder 1175 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> { 1176 using FIROpConversion::FIROpConversion; 1177 1178 mlir::LogicalResult 1179 matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor, 1180 mlir::ConversionPatternRewriter &rewriter) const override { 1181 selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter); 1182 return success(); 1183 } 1184 }; 1185 1186 /// `fir.store` --> `llvm.store` 1187 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> { 1188 using FIROpConversion::FIROpConversion; 1189 1190 mlir::LogicalResult 1191 matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor, 1192 mlir::ConversionPatternRewriter &rewriter) const override { 1193 if (store.value().getType().isa<fir::BoxType>()) { 1194 // fir.box value is actually in memory, load it first before storing it. 1195 mlir::Location loc = store.getLoc(); 1196 mlir::Type boxPtrTy = adaptor.getOperands()[0].getType(); 1197 auto val = rewriter.create<mlir::LLVM::LoadOp>( 1198 loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(), 1199 adaptor.getOperands()[0]); 1200 rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 1201 store, val, adaptor.getOperands()[1]); 1202 } else { 1203 rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 1204 store, adaptor.getOperands()[0], adaptor.getOperands()[1]); 1205 } 1206 return success(); 1207 } 1208 }; 1209 1210 /// convert to LLVM IR dialect `undef` 1211 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> { 1212 using FIROpConversion::FIROpConversion; 1213 1214 mlir::LogicalResult 1215 matchAndRewrite(fir::UndefOp undef, OpAdaptor, 1216 mlir::ConversionPatternRewriter &rewriter) const override { 1217 rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>( 1218 undef, convertType(undef.getType())); 1219 return success(); 1220 } 1221 }; 1222 1223 /// `fir.unreachable` --> `llvm.unreachable` 1224 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> { 1225 using FIROpConversion::FIROpConversion; 1226 1227 mlir::LogicalResult 1228 matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor, 1229 mlir::ConversionPatternRewriter &rewriter) const override { 1230 rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach); 1231 return success(); 1232 } 1233 }; 1234 1235 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> { 1236 using FIROpConversion::FIROpConversion; 1237 1238 mlir::LogicalResult 1239 matchAndRewrite(fir::ZeroOp zero, OpAdaptor, 1240 mlir::ConversionPatternRewriter &rewriter) const override { 1241 auto ty = convertType(zero.getType()); 1242 if (ty.isa<mlir::LLVM::LLVMPointerType>()) { 1243 rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty); 1244 } else if (ty.isa<mlir::IntegerType>()) { 1245 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 1246 zero, ty, mlir::IntegerAttr::get(zero.getType(), 0)); 1247 } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) { 1248 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 1249 zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0)); 1250 } else { 1251 // TODO: create ConstantAggregateZero for FIR aggregate/array types. 1252 return rewriter.notifyMatchFailure( 1253 zero, 1254 "conversion of fir.zero with aggregate type not implemented yet"); 1255 } 1256 return success(); 1257 } 1258 }; 1259 1260 /// Common base class for embox to descriptor conversion. 1261 template <typename OP> 1262 struct EmboxCommonConversion : public FIROpConversion<OP> { 1263 using FIROpConversion<OP>::FIROpConversion; 1264 1265 // Find the LLVMFuncOp in whose entry block the alloca should be inserted. 1266 // The order to find the LLVMFuncOp is as follows: 1267 // 1. The parent operation of the current block if it is a LLVMFuncOp. 1268 // 2. The first ancestor that is a LLVMFuncOp. 1269 mlir::LLVM::LLVMFuncOp 1270 getFuncForAllocaInsert(mlir::ConversionPatternRewriter &rewriter) const { 1271 mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp(); 1272 return mlir::isa<mlir::LLVM::LLVMFuncOp>(parentOp) 1273 ? mlir::cast<mlir::LLVM::LLVMFuncOp>(parentOp) 1274 : parentOp->getParentOfType<mlir::LLVM::LLVMFuncOp>(); 1275 } 1276 1277 // Generate an alloca of size 1 and type \p toTy. 1278 mlir::LLVM::AllocaOp 1279 genAllocaWithType(mlir::Location loc, mlir::Type toTy, unsigned alignment, 1280 mlir::ConversionPatternRewriter &rewriter) const { 1281 auto thisPt = rewriter.saveInsertionPoint(); 1282 mlir::LLVM::LLVMFuncOp func = getFuncForAllocaInsert(rewriter); 1283 rewriter.setInsertionPointToStart(&func.front()); 1284 auto size = this->genI32Constant(loc, rewriter, 1); 1285 auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, toTy, size, alignment); 1286 rewriter.restoreInsertionPoint(thisPt); 1287 return al; 1288 } 1289 1290 static int getCFIAttr(fir::BoxType boxTy) { 1291 auto eleTy = boxTy.getEleTy(); 1292 if (eleTy.isa<fir::PointerType>()) 1293 return CFI_attribute_pointer; 1294 if (eleTy.isa<fir::HeapType>()) 1295 return CFI_attribute_allocatable; 1296 return CFI_attribute_other; 1297 } 1298 1299 static fir::RecordType unwrapIfDerived(fir::BoxType boxTy) { 1300 return fir::unwrapSequenceType(fir::dyn_cast_ptrOrBoxEleTy(boxTy)) 1301 .template dyn_cast<fir::RecordType>(); 1302 } 1303 static bool isDerivedTypeWithLenParams(fir::BoxType boxTy) { 1304 auto recTy = unwrapIfDerived(boxTy); 1305 return recTy && recTy.getNumLenParams() > 0; 1306 } 1307 static bool isDerivedType(fir::BoxType boxTy) { 1308 return unwrapIfDerived(boxTy) != nullptr; 1309 } 1310 1311 // Get the element size and CFI type code of the boxed value. 1312 std::tuple<mlir::Value, mlir::Value> getSizeAndTypeCode( 1313 mlir::Location loc, mlir::ConversionPatternRewriter &rewriter, 1314 mlir::Type boxEleTy, mlir::ValueRange lenParams = {}) const { 1315 auto doInteger = 1316 [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> { 1317 int typeCode = fir::integerBitsToTypeCode(width); 1318 return {this->genConstantOffset(loc, rewriter, width / 8), 1319 this->genConstantOffset(loc, rewriter, typeCode)}; 1320 }; 1321 auto doLogical = 1322 [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> { 1323 int typeCode = fir::logicalBitsToTypeCode(width); 1324 return {this->genConstantOffset(loc, rewriter, width / 8), 1325 this->genConstantOffset(loc, rewriter, typeCode)}; 1326 }; 1327 auto doFloat = [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> { 1328 int typeCode = fir::realBitsToTypeCode(width); 1329 return {this->genConstantOffset(loc, rewriter, width / 8), 1330 this->genConstantOffset(loc, rewriter, typeCode)}; 1331 }; 1332 auto doComplex = 1333 [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> { 1334 auto typeCode = fir::complexBitsToTypeCode(width); 1335 return {this->genConstantOffset(loc, rewriter, width / 8 * 2), 1336 this->genConstantOffset(loc, rewriter, typeCode)}; 1337 }; 1338 auto doCharacter = 1339 [&](unsigned width, 1340 mlir::Value len) -> std::tuple<mlir::Value, mlir::Value> { 1341 auto typeCode = fir::characterBitsToTypeCode(width); 1342 auto typeCodeVal = this->genConstantOffset(loc, rewriter, typeCode); 1343 if (width == 8) 1344 return {len, typeCodeVal}; 1345 auto byteWidth = this->genConstantOffset(loc, rewriter, width / 8); 1346 auto i64Ty = mlir::IntegerType::get(&this->lowerTy().getContext(), 64); 1347 auto size = 1348 rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, byteWidth, len); 1349 return {size, typeCodeVal}; 1350 }; 1351 auto getKindMap = [&]() -> fir::KindMapping & { 1352 return this->lowerTy().getKindMap(); 1353 }; 1354 // Pointer-like types. 1355 if (auto eleTy = fir::dyn_cast_ptrEleTy(boxEleTy)) 1356 boxEleTy = eleTy; 1357 // Integer types. 1358 if (fir::isa_integer(boxEleTy)) { 1359 if (auto ty = boxEleTy.dyn_cast<mlir::IntegerType>()) 1360 return doInteger(ty.getWidth()); 1361 auto ty = boxEleTy.cast<fir::IntegerType>(); 1362 return doInteger(getKindMap().getIntegerBitsize(ty.getFKind())); 1363 } 1364 // Floating point types. 1365 if (fir::isa_real(boxEleTy)) { 1366 if (auto ty = boxEleTy.dyn_cast<mlir::FloatType>()) 1367 return doFloat(ty.getWidth()); 1368 auto ty = boxEleTy.cast<fir::RealType>(); 1369 return doFloat(getKindMap().getRealBitsize(ty.getFKind())); 1370 } 1371 // Complex types. 1372 if (fir::isa_complex(boxEleTy)) { 1373 if (auto ty = boxEleTy.dyn_cast<mlir::ComplexType>()) 1374 return doComplex( 1375 ty.getElementType().cast<mlir::FloatType>().getWidth()); 1376 auto ty = boxEleTy.cast<fir::ComplexType>(); 1377 return doComplex(getKindMap().getRealBitsize(ty.getFKind())); 1378 } 1379 // Character types. 1380 if (auto ty = boxEleTy.dyn_cast<fir::CharacterType>()) { 1381 auto charWidth = getKindMap().getCharacterBitsize(ty.getFKind()); 1382 if (ty.getLen() != fir::CharacterType::unknownLen()) { 1383 auto len = this->genConstantOffset(loc, rewriter, ty.getLen()); 1384 return doCharacter(charWidth, len); 1385 } 1386 assert(!lenParams.empty()); 1387 return doCharacter(charWidth, lenParams.back()); 1388 } 1389 // Logical type. 1390 if (auto ty = boxEleTy.dyn_cast<fir::LogicalType>()) 1391 return doLogical(getKindMap().getLogicalBitsize(ty.getFKind())); 1392 // Array types. 1393 if (auto seqTy = boxEleTy.dyn_cast<fir::SequenceType>()) 1394 return getSizeAndTypeCode(loc, rewriter, seqTy.getEleTy(), lenParams); 1395 // Derived-type types. 1396 if (boxEleTy.isa<fir::RecordType>()) { 1397 auto ptrTy = mlir::LLVM::LLVMPointerType::get( 1398 this->lowerTy().convertType(boxEleTy)); 1399 auto nullPtr = rewriter.create<mlir::LLVM::NullOp>(loc, ptrTy); 1400 auto one = 1401 genConstantIndex(loc, this->lowerTy().offsetType(), rewriter, 1); 1402 auto gep = rewriter.create<mlir::LLVM::GEPOp>( 1403 loc, ptrTy, mlir::ValueRange{nullPtr, one}); 1404 auto eleSize = rewriter.create<mlir::LLVM::PtrToIntOp>( 1405 loc, this->lowerTy().indexType(), gep); 1406 return {eleSize, 1407 this->genConstantOffset(loc, rewriter, fir::derivedToTypeCode())}; 1408 } 1409 // Reference type. 1410 if (fir::isa_ref_type(boxEleTy)) { 1411 // FIXME: use the target pointer size rather than sizeof(void*) 1412 return {this->genConstantOffset(loc, rewriter, sizeof(void *)), 1413 this->genConstantOffset(loc, rewriter, CFI_type_cptr)}; 1414 } 1415 fir::emitFatalError(loc, "unhandled type in fir.box code generation"); 1416 } 1417 1418 /// Basic pattern to write a field in the descriptor 1419 mlir::Value insertField(mlir::ConversionPatternRewriter &rewriter, 1420 mlir::Location loc, mlir::Value dest, 1421 ArrayRef<unsigned> fldIndexes, mlir::Value value, 1422 bool bitcast = false) const { 1423 auto boxTy = dest.getType(); 1424 auto fldTy = this->getBoxEleTy(boxTy, fldIndexes); 1425 if (bitcast) 1426 value = rewriter.create<mlir::LLVM::BitcastOp>(loc, fldTy, value); 1427 else 1428 value = this->integerCast(loc, rewriter, fldTy, value); 1429 SmallVector<mlir::Attribute, 2> attrs; 1430 for (auto i : fldIndexes) 1431 attrs.push_back(rewriter.getI32IntegerAttr(i)); 1432 auto indexesAttr = mlir::ArrayAttr::get(rewriter.getContext(), attrs); 1433 return rewriter.create<mlir::LLVM::InsertValueOp>(loc, boxTy, dest, value, 1434 indexesAttr); 1435 } 1436 1437 inline mlir::Value 1438 insertBaseAddress(mlir::ConversionPatternRewriter &rewriter, 1439 mlir::Location loc, mlir::Value dest, 1440 mlir::Value base) const { 1441 return insertField(rewriter, loc, dest, {0}, base, /*bitCast=*/true); 1442 } 1443 1444 /// Get the address of the type descriptor global variable that was created by 1445 /// lowering for derived type \p recType. 1446 template <typename BOX> 1447 mlir::Value 1448 getTypeDescriptor(BOX box, mlir::ConversionPatternRewriter &rewriter, 1449 mlir::Location loc, fir::RecordType recType) const { 1450 std::string name = recType.getLoweredName(); 1451 auto module = box->template getParentOfType<mlir::ModuleOp>(); 1452 if (auto global = module.template lookupSymbol<fir::GlobalOp>(name)) { 1453 auto ty = mlir::LLVM::LLVMPointerType::get( 1454 this->lowerTy().convertType(global.getType())); 1455 return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty, 1456 global.sym_name()); 1457 } 1458 if (auto global = 1459 module.template lookupSymbol<mlir::LLVM::GlobalOp>(name)) { 1460 // The global may have already been translated to LLVM. 1461 auto ty = mlir::LLVM::LLVMPointerType::get(global.getType()); 1462 return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty, 1463 global.sym_name()); 1464 } 1465 // The global does not exist in the current translation unit, but may be 1466 // defined elsewhere (e.g., type defined in a module). 1467 // For now, create a extern_weak symbol (will become nullptr if unresolved) 1468 // to support generating code without the front-end generated symbols. 1469 // These could be made available_externally to require the symbols to be 1470 // defined elsewhere and to cause link-time failure otherwise. 1471 auto i8Ty = rewriter.getIntegerType(8); 1472 mlir::OpBuilder modBuilder(module.getBodyRegion()); 1473 // TODO: The symbol should be lowered to constant in lowering, they are read 1474 // only. 1475 modBuilder.create<mlir::LLVM::GlobalOp>(loc, i8Ty, /*isConstant=*/false, 1476 mlir::LLVM::Linkage::ExternWeak, 1477 name, mlir::Attribute{}); 1478 auto ty = mlir::LLVM::LLVMPointerType::get(i8Ty); 1479 return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty, name); 1480 } 1481 1482 template <typename BOX> 1483 std::tuple<fir::BoxType, mlir::Value, mlir::Value> 1484 consDescriptorPrefix(BOX box, mlir::ConversionPatternRewriter &rewriter, 1485 unsigned rank, mlir::ValueRange lenParams) const { 1486 auto loc = box.getLoc(); 1487 auto boxTy = box.getType().template dyn_cast<fir::BoxType>(); 1488 auto convTy = this->lowerTy().convertBoxType(boxTy, rank); 1489 auto llvmBoxPtrTy = convTy.template cast<mlir::LLVM::LLVMPointerType>(); 1490 auto llvmBoxTy = llvmBoxPtrTy.getElementType(); 1491 mlir::Value descriptor = 1492 rewriter.create<mlir::LLVM::UndefOp>(loc, llvmBoxTy); 1493 1494 llvm::SmallVector<mlir::Value> typeparams = lenParams; 1495 if constexpr (!std::is_same_v<BOX, fir::EmboxOp>) { 1496 if (!box.substr().empty() && fir::hasDynamicSize(boxTy.getEleTy())) 1497 typeparams.push_back(box.substr()[1]); 1498 } 1499 1500 // Write each of the fields with the appropriate values 1501 auto [eleSize, cfiTy] = 1502 getSizeAndTypeCode(loc, rewriter, boxTy.getEleTy(), typeparams); 1503 descriptor = 1504 insertField(rewriter, loc, descriptor, {kElemLenPosInBox}, eleSize); 1505 descriptor = insertField(rewriter, loc, descriptor, {kVersionPosInBox}, 1506 this->genI32Constant(loc, rewriter, CFI_VERSION)); 1507 descriptor = insertField(rewriter, loc, descriptor, {kRankPosInBox}, 1508 this->genI32Constant(loc, rewriter, rank)); 1509 descriptor = insertField(rewriter, loc, descriptor, {kTypePosInBox}, cfiTy); 1510 descriptor = 1511 insertField(rewriter, loc, descriptor, {kAttributePosInBox}, 1512 this->genI32Constant(loc, rewriter, getCFIAttr(boxTy))); 1513 const bool hasAddendum = isDerivedType(boxTy); 1514 descriptor = 1515 insertField(rewriter, loc, descriptor, {kF18AddendumPosInBox}, 1516 this->genI32Constant(loc, rewriter, hasAddendum ? 1 : 0)); 1517 1518 if (hasAddendum) { 1519 auto isArray = 1520 fir::dyn_cast_ptrOrBoxEleTy(boxTy).template isa<fir::SequenceType>(); 1521 unsigned typeDescFieldId = isArray ? kOptTypePtrPosInBox : kDimsPosInBox; 1522 auto typeDesc = 1523 getTypeDescriptor(box, rewriter, loc, unwrapIfDerived(boxTy)); 1524 descriptor = 1525 insertField(rewriter, loc, descriptor, {typeDescFieldId}, typeDesc, 1526 /*bitCast=*/true); 1527 } 1528 1529 return {boxTy, descriptor, eleSize}; 1530 } 1531 1532 /// If the embox is not in a globalOp body, allocate storage for the box; 1533 /// store the value inside and return the generated alloca. Return the input 1534 /// value otherwise. 1535 mlir::Value 1536 placeInMemoryIfNotGlobalInit(mlir::ConversionPatternRewriter &rewriter, 1537 mlir::Location loc, mlir::Value boxValue) const { 1538 auto *thisBlock = rewriter.getInsertionBlock(); 1539 if (thisBlock && mlir::isa<mlir::LLVM::GlobalOp>(thisBlock->getParentOp())) 1540 return boxValue; 1541 auto boxPtrTy = mlir::LLVM::LLVMPointerType::get(boxValue.getType()); 1542 auto alloca = genAllocaWithType(loc, boxPtrTy, defaultAlign, rewriter); 1543 rewriter.create<mlir::LLVM::StoreOp>(loc, boxValue, alloca); 1544 return alloca; 1545 } 1546 }; 1547 1548 /// Create a generic box on a memory reference. This conversions lowers the 1549 /// abstract box to the appropriate, initialized descriptor. 1550 struct EmboxOpConversion : public EmboxCommonConversion<fir::EmboxOp> { 1551 using EmboxCommonConversion::EmboxCommonConversion; 1552 1553 mlir::LogicalResult 1554 matchAndRewrite(fir::EmboxOp embox, OpAdaptor adaptor, 1555 mlir::ConversionPatternRewriter &rewriter) const override { 1556 assert(!embox.getShape() && "There should be no dims on this embox op"); 1557 auto [boxTy, dest, eleSize] = 1558 consDescriptorPrefix(embox, rewriter, /*rank=*/0, 1559 /*lenParams=*/adaptor.getOperands().drop_front(1)); 1560 dest = insertBaseAddress(rewriter, embox.getLoc(), dest, 1561 adaptor.getOperands()[0]); 1562 if (isDerivedTypeWithLenParams(boxTy)) 1563 return rewriter.notifyMatchFailure( 1564 embox, "fir.embox codegen of derived with length parameters not " 1565 "implemented yet"); 1566 auto result = placeInMemoryIfNotGlobalInit(rewriter, embox.getLoc(), dest); 1567 rewriter.replaceOp(embox, result); 1568 return success(); 1569 } 1570 }; 1571 1572 /// Lower `fir.emboxproc` operation. Creates a procedure box. 1573 /// TODO: Part of supporting Fortran 2003 procedure pointers. 1574 struct EmboxProcOpConversion : public FIROpConversion<fir::EmboxProcOp> { 1575 using FIROpConversion::FIROpConversion; 1576 1577 mlir::LogicalResult 1578 matchAndRewrite(fir::EmboxProcOp emboxproc, OpAdaptor adaptor, 1579 mlir::ConversionPatternRewriter &rewriter) const override { 1580 return rewriter.notifyMatchFailure( 1581 emboxproc, "fir.emboxproc codegen is not implemented yet"); 1582 } 1583 }; 1584 1585 1586 // Code shared between insert_value and extract_value Ops. 1587 struct ValueOpCommon { 1588 // Translate the arguments pertaining to any multidimensional array to 1589 // row-major order for LLVM-IR. 1590 static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs, 1591 mlir::Type ty) { 1592 assert(ty && "type is null"); 1593 const auto end = attrs.size(); 1594 for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) { 1595 if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 1596 const auto dim = getDimension(seq); 1597 if (dim > 1) { 1598 auto ub = std::min(i + dim, end); 1599 std::reverse(attrs.begin() + i, attrs.begin() + ub); 1600 i += dim - 1; 1601 } 1602 ty = getArrayElementType(seq); 1603 } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) { 1604 ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()]; 1605 } else { 1606 llvm_unreachable("index into invalid type"); 1607 } 1608 } 1609 } 1610 1611 static llvm::SmallVector<mlir::Attribute> 1612 collectIndices(mlir::ConversionPatternRewriter &rewriter, 1613 mlir::ArrayAttr arrAttr) { 1614 llvm::SmallVector<mlir::Attribute> attrs; 1615 for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) { 1616 if (i->isa<mlir::IntegerAttr>()) { 1617 attrs.push_back(*i); 1618 } else { 1619 auto fieldName = i->cast<mlir::StringAttr>().getValue(); 1620 ++i; 1621 auto ty = i->cast<mlir::TypeAttr>().getValue(); 1622 auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName); 1623 attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index)); 1624 } 1625 } 1626 return attrs; 1627 } 1628 1629 private: 1630 static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) { 1631 unsigned result = 1; 1632 for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>(); 1633 eleTy; 1634 eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>()) 1635 ++result; 1636 return result; 1637 } 1638 1639 static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) { 1640 auto eleTy = ty.getElementType(); 1641 while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>()) 1642 eleTy = arrTy.getElementType(); 1643 return eleTy; 1644 } 1645 }; 1646 1647 /// Extract a subobject value from an ssa-value of aggregate type 1648 struct ExtractValueOpConversion 1649 : public FIROpAndTypeConversion<fir::ExtractValueOp>, 1650 public ValueOpCommon { 1651 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1652 1653 mlir::LogicalResult 1654 doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor, 1655 mlir::ConversionPatternRewriter &rewriter) const override { 1656 auto attrs = collectIndices(rewriter, extractVal.coor()); 1657 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 1658 auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs); 1659 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>( 1660 extractVal, ty, adaptor.getOperands()[0], position); 1661 return success(); 1662 } 1663 }; 1664 1665 /// InsertValue is the generalized instruction for the composition of new 1666 /// aggregate type values. 1667 struct InsertValueOpConversion 1668 : public FIROpAndTypeConversion<fir::InsertValueOp>, 1669 public ValueOpCommon { 1670 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1671 1672 mlir::LogicalResult 1673 doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor, 1674 mlir::ConversionPatternRewriter &rewriter) const override { 1675 auto attrs = collectIndices(rewriter, insertVal.coor()); 1676 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 1677 auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs); 1678 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1679 insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1], 1680 position); 1681 return success(); 1682 } 1683 }; 1684 1685 /// InsertOnRange inserts a value into a sequence over a range of offsets. 1686 struct InsertOnRangeOpConversion 1687 : public FIROpAndTypeConversion<fir::InsertOnRangeOp> { 1688 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1689 1690 // Increments an array of subscripts in a row major fasion. 1691 void incrementSubscripts(const SmallVector<uint64_t> &dims, 1692 SmallVector<uint64_t> &subscripts) const { 1693 for (size_t i = dims.size(); i > 0; --i) { 1694 if (++subscripts[i - 1] < dims[i - 1]) { 1695 return; 1696 } 1697 subscripts[i - 1] = 0; 1698 } 1699 } 1700 1701 mlir::LogicalResult 1702 doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor, 1703 mlir::ConversionPatternRewriter &rewriter) const override { 1704 1705 llvm::SmallVector<uint64_t> dims; 1706 auto type = adaptor.getOperands()[0].getType(); 1707 1708 // Iteratively extract the array dimensions from the type. 1709 while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 1710 dims.push_back(t.getNumElements()); 1711 type = t.getElementType(); 1712 } 1713 1714 SmallVector<uint64_t> lBounds; 1715 SmallVector<uint64_t> uBounds; 1716 1717 // Extract integer value from the attribute 1718 SmallVector<int64_t> coordinates = llvm::to_vector<4>( 1719 llvm::map_range(range.coor(), [](Attribute a) -> int64_t { 1720 return a.cast<IntegerAttr>().getInt(); 1721 })); 1722 1723 // Unzip the upper and lower bound and convert to a row major format. 1724 for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) { 1725 uBounds.push_back(*i++); 1726 lBounds.push_back(*i); 1727 } 1728 1729 auto &subscripts = lBounds; 1730 auto loc = range.getLoc(); 1731 mlir::Value lastOp = adaptor.getOperands()[0]; 1732 mlir::Value insertVal = adaptor.getOperands()[1]; 1733 1734 auto i64Ty = rewriter.getI64Type(); 1735 while (subscripts != uBounds) { 1736 // Convert uint64_t's to Attribute's. 1737 SmallVector<mlir::Attribute> subscriptAttrs; 1738 for (const auto &subscript : subscripts) 1739 subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript)); 1740 lastOp = rewriter.create<mlir::LLVM::InsertValueOp>( 1741 loc, ty, lastOp, insertVal, 1742 ArrayAttr::get(range.getContext(), subscriptAttrs)); 1743 1744 incrementSubscripts(dims, subscripts); 1745 } 1746 1747 // Convert uint64_t's to Attribute's. 1748 SmallVector<mlir::Attribute> subscriptAttrs; 1749 for (const auto &subscript : subscripts) 1750 subscriptAttrs.push_back( 1751 IntegerAttr::get(rewriter.getI64Type(), subscript)); 1752 mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs); 1753 1754 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1755 range, ty, lastOp, insertVal, 1756 ArrayAttr::get(range.getContext(), arrayRef)); 1757 1758 return success(); 1759 } 1760 }; 1761 1762 // 1763 // Primitive operations on Complex types 1764 // 1765 1766 /// Generate inline code for complex addition/subtraction 1767 template <typename LLVMOP, typename OPTY> 1768 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds, 1769 mlir::ConversionPatternRewriter &rewriter, 1770 fir::LLVMTypeConverter &lowering) { 1771 mlir::Value a = opnds[0]; 1772 mlir::Value b = opnds[1]; 1773 auto loc = sumop.getLoc(); 1774 auto ctx = sumop.getContext(); 1775 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1776 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1777 mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType())); 1778 mlir::Type ty = lowering.convertType(sumop.getType()); 1779 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1780 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1781 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1782 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1783 auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1); 1784 auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1); 1785 auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1786 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0); 1787 return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1); 1788 } 1789 1790 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> { 1791 using FIROpConversion::FIROpConversion; 1792 1793 mlir::LogicalResult 1794 matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor, 1795 mlir::ConversionPatternRewriter &rewriter) const override { 1796 // given: (x + iy) + (x' + iy') 1797 // result: (x + x') + i(y + y') 1798 auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(), 1799 rewriter, lowerTy()); 1800 rewriter.replaceOp(addc, r.getResult()); 1801 return success(); 1802 } 1803 }; 1804 1805 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> { 1806 using FIROpConversion::FIROpConversion; 1807 1808 mlir::LogicalResult 1809 matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor, 1810 mlir::ConversionPatternRewriter &rewriter) const override { 1811 // given: (x + iy) - (x' + iy') 1812 // result: (x - x') + i(y - y') 1813 auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(), 1814 rewriter, lowerTy()); 1815 rewriter.replaceOp(subc, r.getResult()); 1816 return success(); 1817 } 1818 }; 1819 1820 /// Inlined complex multiply 1821 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> { 1822 using FIROpConversion::FIROpConversion; 1823 1824 mlir::LogicalResult 1825 matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor, 1826 mlir::ConversionPatternRewriter &rewriter) const override { 1827 // TODO: Can we use a call to __muldc3 ? 1828 // given: (x + iy) * (x' + iy') 1829 // result: (xx'-yy')+i(xy'+yx') 1830 mlir::Value a = adaptor.getOperands()[0]; 1831 mlir::Value b = adaptor.getOperands()[1]; 1832 auto loc = mulc.getLoc(); 1833 auto *ctx = mulc.getContext(); 1834 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1835 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1836 mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType())); 1837 mlir::Type ty = convertType(mulc.getType()); 1838 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1839 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1840 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1841 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1842 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 1843 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 1844 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 1845 auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx); 1846 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 1847 auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy); 1848 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1849 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 1850 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 1851 rewriter.replaceOp(mulc, r0.getResult()); 1852 return success(); 1853 } 1854 }; 1855 1856 /// Inlined complex division 1857 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> { 1858 using FIROpConversion::FIROpConversion; 1859 1860 mlir::LogicalResult 1861 matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor, 1862 mlir::ConversionPatternRewriter &rewriter) const override { 1863 // TODO: Can we use a call to __divdc3 instead? 1864 // Just generate inline code for now. 1865 // given: (x + iy) / (x' + iy') 1866 // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y' 1867 mlir::Value a = adaptor.getOperands()[0]; 1868 mlir::Value b = adaptor.getOperands()[1]; 1869 auto loc = divc.getLoc(); 1870 auto *ctx = divc.getContext(); 1871 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1872 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1873 mlir::Type eleTy = convertType(getComplexEleTy(divc.getType())); 1874 mlir::Type ty = convertType(divc.getType()); 1875 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1876 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1877 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1878 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1879 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 1880 auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1); 1881 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 1882 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 1883 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 1884 auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1); 1885 auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1); 1886 auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy); 1887 auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy); 1888 auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d); 1889 auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d); 1890 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1891 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 1892 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 1893 rewriter.replaceOp(divc, r0.getResult()); 1894 return success(); 1895 } 1896 }; 1897 1898 /// Inlined complex negation 1899 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> { 1900 using FIROpConversion::FIROpConversion; 1901 1902 mlir::LogicalResult 1903 matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor, 1904 mlir::ConversionPatternRewriter &rewriter) const override { 1905 // given: -(x + iy) 1906 // result: -x - iy 1907 auto *ctxt = neg.getContext(); 1908 auto eleTy = convertType(getComplexEleTy(neg.getType())); 1909 auto ty = convertType(neg.getType()); 1910 auto loc = neg.getLoc(); 1911 mlir::Value o0 = adaptor.getOperands()[0]; 1912 auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0)); 1913 auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1)); 1914 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0); 1915 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1); 1916 auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp); 1917 auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip); 1918 auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0); 1919 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1); 1920 return success(); 1921 } 1922 }; 1923 1924 /// Conversion pattern for operation that must be dead. The information in these 1925 /// operations is used by other operation. At this point they should not have 1926 /// anymore uses. 1927 /// These operations are normally dead after the pre-codegen pass. 1928 template <typename FromOp> 1929 struct MustBeDeadConversion : public FIROpConversion<FromOp> { 1930 explicit MustBeDeadConversion(fir::LLVMTypeConverter &lowering) 1931 : FIROpConversion<FromOp>(lowering) {} 1932 using OpAdaptor = typename FromOp::Adaptor; 1933 1934 mlir::LogicalResult 1935 matchAndRewrite(FromOp op, OpAdaptor adaptor, 1936 mlir::ConversionPatternRewriter &rewriter) const final { 1937 if (!op->getUses().empty()) 1938 return rewriter.notifyMatchFailure(op, "op must be dead"); 1939 rewriter.eraseOp(op); 1940 return success(); 1941 } 1942 }; 1943 1944 struct ShapeOpConversion : public MustBeDeadConversion<fir::ShapeOp> { 1945 using MustBeDeadConversion::MustBeDeadConversion; 1946 }; 1947 1948 struct ShapeShiftOpConversion : public MustBeDeadConversion<fir::ShapeShiftOp> { 1949 using MustBeDeadConversion::MustBeDeadConversion; 1950 }; 1951 1952 struct ShiftOpConversion : public MustBeDeadConversion<fir::ShiftOp> { 1953 using MustBeDeadConversion::MustBeDeadConversion; 1954 }; 1955 1956 struct SliceOpConversion : public MustBeDeadConversion<fir::SliceOp> { 1957 using MustBeDeadConversion::MustBeDeadConversion; 1958 }; 1959 1960 /// `fir.is_present` --> 1961 /// ``` 1962 /// %0 = llvm.mlir.constant(0 : i64) 1963 /// %1 = llvm.ptrtoint %0 1964 /// %2 = llvm.icmp "ne" %1, %0 : i64 1965 /// ``` 1966 struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> { 1967 using FIROpConversion::FIROpConversion; 1968 1969 mlir::LogicalResult 1970 matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor, 1971 mlir::ConversionPatternRewriter &rewriter) const override { 1972 mlir::Type idxTy = lowerTy().indexType(); 1973 mlir::Location loc = isPresent.getLoc(); 1974 auto ptr = adaptor.getOperands()[0]; 1975 1976 if (isPresent.val().getType().isa<fir::BoxCharType>()) { 1977 auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>(); 1978 assert(!structTy.isOpaque() && !structTy.getBody().empty()); 1979 1980 mlir::Type ty = structTy.getBody()[0]; 1981 mlir::MLIRContext *ctx = isPresent.getContext(); 1982 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1983 ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0); 1984 } 1985 mlir::LLVM::ConstantOp c0 = 1986 genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0); 1987 auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr); 1988 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>( 1989 isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0); 1990 1991 return success(); 1992 } 1993 }; 1994 1995 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of 1996 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element 1997 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd 1998 /// element is the length of the character buffer (`#n`). 1999 struct EmboxCharOpConversion : public FIROpConversion<fir::EmboxCharOp> { 2000 using FIROpConversion::FIROpConversion; 2001 2002 mlir::LogicalResult 2003 matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor, 2004 mlir::ConversionPatternRewriter &rewriter) const override { 2005 mlir::ValueRange operands = adaptor.getOperands(); 2006 MLIRContext *ctx = emboxChar.getContext(); 2007 2008 mlir::Value charBuffer = operands[0]; 2009 mlir::Value charBufferLen = operands[1]; 2010 2011 mlir::Location loc = emboxChar.getLoc(); 2012 mlir::Type llvmStructTy = convertType(emboxChar.getType()); 2013 auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy); 2014 2015 mlir::Type lenTy = 2016 llvmStructTy.cast<mlir::LLVM::LLVMStructType>().getBody()[1]; 2017 mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen); 2018 2019 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 2020 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 2021 auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>( 2022 loc, llvmStructTy, llvmStruct, charBuffer, c0); 2023 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 2024 emboxChar, llvmStructTy, insertBufferOp, lenAfterCast, c1); 2025 2026 return success(); 2027 } 2028 }; 2029 2030 /// Construct an `llvm.extractvalue` instruction. It will return value at 2031 /// element \p x from \p tuple. 2032 mlir::LLVM::ExtractValueOp 2033 genExtractValueWithIndex(mlir::Location loc, mlir::Value tuple, mlir::Type ty, 2034 mlir::ConversionPatternRewriter &rewriter, 2035 mlir::MLIRContext *ctx, int x) { 2036 auto cx = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(x)); 2037 auto xty = ty.cast<mlir::LLVM::LLVMStructType>().getBody()[x]; 2038 return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, xty, tuple, cx); 2039 } 2040 2041 /// Convert `!fir.boxchar_len` to `!llvm.extractvalue` for the 2nd part of the 2042 /// boxchar. 2043 struct BoxCharLenOpConversion : public FIROpConversion<fir::BoxCharLenOp> { 2044 using FIROpConversion::FIROpConversion; 2045 2046 mlir::LogicalResult 2047 matchAndRewrite(fir::BoxCharLenOp boxCharLen, OpAdaptor adaptor, 2048 mlir::ConversionPatternRewriter &rewriter) const override { 2049 mlir::Value boxChar = adaptor.getOperands()[0]; 2050 mlir::Location loc = boxChar.getLoc(); 2051 mlir::MLIRContext *ctx = boxChar.getContext(); 2052 mlir::Type returnValTy = boxCharLen.getResult().getType(); 2053 2054 constexpr int boxcharLenIdx = 1; 2055 mlir::LLVM::ExtractValueOp len = genExtractValueWithIndex( 2056 loc, boxChar, boxChar.getType(), rewriter, ctx, boxcharLenIdx); 2057 mlir::Value lenAfterCast = integerCast(loc, rewriter, returnValTy, len); 2058 rewriter.replaceOp(boxCharLen, lenAfterCast); 2059 2060 return success(); 2061 } 2062 }; 2063 2064 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for 2065 /// the character buffer and one for the buffer length. 2066 struct UnboxCharOpConversion : public FIROpConversion<fir::UnboxCharOp> { 2067 using FIROpConversion::FIROpConversion; 2068 2069 mlir::LogicalResult 2070 matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor, 2071 mlir::ConversionPatternRewriter &rewriter) const override { 2072 MLIRContext *ctx = unboxchar.getContext(); 2073 2074 mlir::Type lenTy = convertType(unboxchar.getType(1)); 2075 mlir::Value tuple = adaptor.getOperands()[0]; 2076 mlir::Type tupleTy = tuple.getType(); 2077 2078 mlir::Location loc = unboxchar.getLoc(); 2079 mlir::Value ptrToBuffer = 2080 genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 0); 2081 2082 mlir::LLVM::ExtractValueOp len = 2083 genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 1); 2084 mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len); 2085 2086 rewriter.replaceOp(unboxchar, 2087 ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast}); 2088 return success(); 2089 } 2090 }; 2091 2092 /// Lower `fir.unboxproc` operation. Unbox a procedure box value, yielding its 2093 /// components. 2094 /// TODO: Part of supporting Fortran 2003 procedure pointers. 2095 struct UnboxProcOpConversion : public FIROpConversion<fir::UnboxProcOp> { 2096 using FIROpConversion::FIROpConversion; 2097 2098 mlir::LogicalResult 2099 matchAndRewrite(fir::UnboxProcOp unboxproc, OpAdaptor adaptor, 2100 mlir::ConversionPatternRewriter &rewriter) const override { 2101 return rewriter.notifyMatchFailure( 2102 unboxproc, "fir.unboxproc codegen is not implemented yet"); 2103 } 2104 }; 2105 2106 /// Convert `fir.field_index`. The conversion depends on whether the size of 2107 /// the record is static or dynamic. 2108 struct FieldIndexOpConversion : public FIROpConversion<fir::FieldIndexOp> { 2109 using FIROpConversion::FIROpConversion; 2110 2111 // NB: most field references should be resolved by this point 2112 mlir::LogicalResult 2113 matchAndRewrite(fir::FieldIndexOp field, OpAdaptor adaptor, 2114 mlir::ConversionPatternRewriter &rewriter) const override { 2115 auto recTy = field.on_type().cast<fir::RecordType>(); 2116 unsigned index = recTy.getFieldIndex(field.field_id()); 2117 2118 if (!fir::hasDynamicSize(recTy)) { 2119 // Derived type has compile-time constant layout. Return index of the 2120 // component type in the parent type (to be used in GEP). 2121 rewriter.replaceOp(field, mlir::ValueRange{genConstantOffset( 2122 field.getLoc(), rewriter, index)}); 2123 return success(); 2124 } 2125 2126 // Derived type has compile-time constant layout. Call the compiler 2127 // generated function to determine the byte offset of the field at runtime. 2128 // This returns a non-constant. 2129 FlatSymbolRefAttr symAttr = mlir::SymbolRefAttr::get( 2130 field.getContext(), getOffsetMethodName(recTy, field.field_id())); 2131 NamedAttribute callAttr = rewriter.getNamedAttr("callee", symAttr); 2132 NamedAttribute fieldAttr = rewriter.getNamedAttr( 2133 "field", mlir::IntegerAttr::get(lowerTy().indexType(), index)); 2134 rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>( 2135 field, lowerTy().offsetType(), adaptor.getOperands(), 2136 llvm::ArrayRef<mlir::NamedAttribute>{callAttr, fieldAttr}); 2137 return success(); 2138 } 2139 2140 // Re-Construct the name of the compiler generated method that calculates the 2141 // offset 2142 inline static std::string getOffsetMethodName(fir::RecordType recTy, 2143 llvm::StringRef field) { 2144 return recTy.getName().str() + "P." + field.str() + ".offset"; 2145 } 2146 }; 2147 2148 } // namespace 2149 2150 namespace { 2151 /// Convert FIR dialect to LLVM dialect 2152 /// 2153 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An 2154 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect. 2155 /// 2156 /// This pass is not complete yet. We are upstreaming it in small patches. 2157 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> { 2158 public: 2159 mlir::ModuleOp getModule() { return getOperation(); } 2160 2161 void runOnOperation() override final { 2162 auto mod = getModule(); 2163 if (!forcedTargetTriple.empty()) { 2164 fir::setTargetTriple(mod, forcedTargetTriple); 2165 } 2166 2167 auto *context = getModule().getContext(); 2168 fir::LLVMTypeConverter typeConverter{getModule()}; 2169 mlir::OwningRewritePatternList pattern(context); 2170 pattern.insert< 2171 AbsentOpConversion, AddcOpConversion, AddrOfOpConversion, 2172 AllocaOpConversion, BoxAddrOpConversion, BoxCharLenOpConversion, 2173 BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion, 2174 BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxProcHostOpConversion, 2175 BoxRankOpConversion, BoxTypeDescOpConversion, CallOpConversion, 2176 CmpcOpConversion, ConstcOpConversion, ConvertOpConversion, 2177 DispatchOpConversion, DispatchTableOpConversion, DTEntryOpConversion, 2178 DivcOpConversion, EmboxOpConversion, EmboxCharOpConversion, 2179 EmboxProcOpConversion, ExtractValueOpConversion, FieldIndexOpConversion, 2180 FirEndOpConversion, HasValueOpConversion, GenTypeDescOpConversion, 2181 GlobalLenOpConversion, GlobalOpConversion, InsertOnRangeOpConversion, 2182 InsertValueOpConversion, IsPresentOpConversion, LoadOpConversion, 2183 NegcOpConversion, NoReassocOpConversion, MulcOpConversion, 2184 SelectCaseOpConversion, SelectOpConversion, SelectRankOpConversion, 2185 SelectTypeOpConversion, ShapeOpConversion, ShapeShiftOpConversion, 2186 ShiftOpConversion, SliceOpConversion, StoreOpConversion, 2187 StringLitOpConversion, SubcOpConversion, UnboxCharOpConversion, 2188 UnboxProcOpConversion, UndefOpConversion, UnreachableOpConversion, 2189 ZeroOpConversion>(typeConverter); 2190 mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern); 2191 mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter, 2192 pattern); 2193 mlir::ConversionTarget target{*context}; 2194 target.addLegalDialect<mlir::LLVM::LLVMDialect>(); 2195 2196 // required NOPs for applying a full conversion 2197 target.addLegalOp<mlir::ModuleOp>(); 2198 2199 // apply the patterns 2200 if (mlir::failed(mlir::applyFullConversion(getModule(), target, 2201 std::move(pattern)))) { 2202 signalPassFailure(); 2203 } 2204 } 2205 }; 2206 } // namespace 2207 2208 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() { 2209 return std::make_unique<FIRToLLVMLowering>(); 2210 } 2211