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