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