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