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