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