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