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