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