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