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.gentypedesc` to a global constant. 831 struct GenTypeDescOpConversion : public FIROpConversion<fir::GenTypeDescOp> { 832 using FIROpConversion::FIROpConversion; 833 834 mlir::LogicalResult 835 matchAndRewrite(fir::GenTypeDescOp gentypedesc, OpAdaptor adaptor, 836 mlir::ConversionPatternRewriter &rewriter) const override { 837 return rewriter.notifyMatchFailure( 838 gentypedesc, "fir.fir.gentypedesc codegen is not implemented yet"); 839 } 840 }; 841 842 /// Lower `fir.has_value` operation to `llvm.return` operation. 843 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> { 844 using FIROpConversion::FIROpConversion; 845 846 mlir::LogicalResult 847 matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor, 848 mlir::ConversionPatternRewriter &rewriter) const override { 849 rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands()); 850 return success(); 851 } 852 }; 853 854 /// Lower `fir.global` operation to `llvm.global` operation. 855 /// `fir.insert_on_range` operations are replaced with constant dense attribute 856 /// if they are applied on the full range. 857 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> { 858 using FIROpConversion::FIROpConversion; 859 860 mlir::LogicalResult 861 matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor, 862 mlir::ConversionPatternRewriter &rewriter) const override { 863 auto tyAttr = convertType(global.getType()); 864 if (global.getType().isa<fir::BoxType>()) 865 tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType(); 866 auto loc = global.getLoc(); 867 mlir::Attribute initAttr{}; 868 if (global.initVal()) 869 initAttr = global.initVal().getValue(); 870 auto linkage = convertLinkage(global.linkName()); 871 auto isConst = global.constant().hasValue(); 872 auto g = rewriter.create<mlir::LLVM::GlobalOp>( 873 loc, tyAttr, isConst, linkage, global.sym_name(), initAttr); 874 auto &gr = g.getInitializerRegion(); 875 rewriter.inlineRegionBefore(global.region(), gr, gr.end()); 876 if (!gr.empty()) { 877 // Replace insert_on_range with a constant dense attribute if the 878 // initialization is on the full range. 879 auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>(); 880 for (auto insertOp : insertOnRangeOps) { 881 if (isFullRange(insertOp.coor(), insertOp.getType())) { 882 auto seqTyAttr = convertType(insertOp.getType()); 883 auto *op = insertOp.val().getDefiningOp(); 884 auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op); 885 if (!constant) { 886 auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op); 887 if (!convertOp) 888 continue; 889 constant = cast<mlir::arith::ConstantOp>( 890 convertOp.value().getDefiningOp()); 891 } 892 mlir::Type vecType = mlir::VectorType::get( 893 insertOp.getType().getShape(), constant.getType()); 894 auto denseAttr = mlir::DenseElementsAttr::get( 895 vecType.cast<ShapedType>(), constant.value()); 896 rewriter.setInsertionPointAfter(insertOp); 897 rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>( 898 insertOp, seqTyAttr, denseAttr); 899 } 900 } 901 } 902 rewriter.eraseOp(global); 903 return success(); 904 } 905 906 bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const { 907 auto extents = seqTy.getShape(); 908 if (indexes.size() / 2 != extents.size()) 909 return false; 910 for (unsigned i = 0; i < indexes.size(); i += 2) { 911 if (indexes[i].cast<IntegerAttr>().getInt() != 0) 912 return false; 913 if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1) 914 return false; 915 } 916 return true; 917 } 918 919 // TODO: String comparaison should be avoided. Replace linkName with an 920 // enumeration. 921 mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const { 922 if (optLinkage.hasValue()) { 923 auto name = optLinkage.getValue(); 924 if (name == "internal") 925 return mlir::LLVM::Linkage::Internal; 926 if (name == "linkonce") 927 return mlir::LLVM::Linkage::Linkonce; 928 if (name == "common") 929 return mlir::LLVM::Linkage::Common; 930 if (name == "weak") 931 return mlir::LLVM::Linkage::Weak; 932 } 933 return mlir::LLVM::Linkage::External; 934 } 935 }; 936 937 void genCondBrOp(mlir::Location loc, mlir::Value cmp, mlir::Block *dest, 938 Optional<mlir::ValueRange> destOps, 939 mlir::ConversionPatternRewriter &rewriter, 940 mlir::Block *newBlock) { 941 if (destOps.hasValue()) 942 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, destOps.getValue(), 943 newBlock, mlir::ValueRange()); 944 else 945 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, dest, newBlock); 946 } 947 948 template <typename A, typename B> 949 void genBrOp(A caseOp, mlir::Block *dest, Optional<B> destOps, 950 mlir::ConversionPatternRewriter &rewriter) { 951 if (destOps.hasValue()) 952 rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, destOps.getValue(), 953 dest); 954 else 955 rewriter.replaceOpWithNewOp<mlir::LLVM::BrOp>(caseOp, llvm::None, dest); 956 } 957 958 void genCaseLadderStep(mlir::Location loc, mlir::Value cmp, mlir::Block *dest, 959 Optional<mlir::ValueRange> destOps, 960 mlir::ConversionPatternRewriter &rewriter) { 961 auto *thisBlock = rewriter.getInsertionBlock(); 962 auto *newBlock = createBlock(rewriter, dest); 963 rewriter.setInsertionPointToEnd(thisBlock); 964 genCondBrOp(loc, cmp, dest, destOps, rewriter, newBlock); 965 rewriter.setInsertionPointToEnd(newBlock); 966 } 967 968 /// Conversion of `fir.select_case` 969 /// 970 /// The `fir.select_case` operation is converted to a if-then-else ladder. 971 /// Depending on the case condition type, one or several comparison and 972 /// conditional branching can be generated. 973 /// 974 /// A a point value case such as `case(4)`, a lower bound case such as 975 /// `case(5:)` or an upper bound case such as `case(:3)` are converted to a 976 /// simple comparison between the selector value and the constant value in the 977 /// case. The block associated with the case condition is then executed if 978 /// the comparison succeed otherwise it branch to the next block with the 979 /// comparison for the the next case conditon. 980 /// 981 /// A closed interval case condition such as `case(7:10)` is converted with a 982 /// first comparison and conditional branching for the lower bound. If 983 /// successful, it branch to a second block with the comparison for the 984 /// upper bound in the same case condition. 985 /// 986 /// TODO: lowering of CHARACTER type cases is not handled yet. 987 struct SelectCaseOpConversion : public FIROpConversion<fir::SelectCaseOp> { 988 using FIROpConversion::FIROpConversion; 989 990 mlir::LogicalResult 991 matchAndRewrite(fir::SelectCaseOp caseOp, OpAdaptor adaptor, 992 mlir::ConversionPatternRewriter &rewriter) const override { 993 unsigned conds = caseOp.getNumConditions(); 994 llvm::ArrayRef<mlir::Attribute> cases = caseOp.getCases().getValue(); 995 // Type can be CHARACTER, INTEGER, or LOGICAL (C1145) 996 LLVM_ATTRIBUTE_UNUSED auto ty = caseOp.getSelector().getType(); 997 if (ty.isa<fir::CharacterType>()) 998 return rewriter.notifyMatchFailure(caseOp, 999 "conversion of fir.select_case with " 1000 "character type not implemented yet"); 1001 mlir::Value selector = caseOp.getSelector(adaptor.getOperands()); 1002 auto loc = caseOp.getLoc(); 1003 for (unsigned t = 0; t != conds; ++t) { 1004 mlir::Block *dest = caseOp.getSuccessor(t); 1005 llvm::Optional<mlir::ValueRange> destOps = 1006 caseOp.getSuccessorOperands(adaptor.getOperands(), t); 1007 llvm::Optional<mlir::ValueRange> cmpOps = 1008 *caseOp.getCompareOperands(adaptor.getOperands(), t); 1009 mlir::Value caseArg = *(cmpOps.getValue().begin()); 1010 mlir::Attribute attr = cases[t]; 1011 if (attr.isa<fir::PointIntervalAttr>()) { 1012 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 1013 loc, mlir::LLVM::ICmpPredicate::eq, selector, caseArg); 1014 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 1015 continue; 1016 } 1017 if (attr.isa<fir::LowerBoundAttr>()) { 1018 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 1019 loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector); 1020 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 1021 continue; 1022 } 1023 if (attr.isa<fir::UpperBoundAttr>()) { 1024 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 1025 loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg); 1026 genCaseLadderStep(loc, cmp, dest, destOps, rewriter); 1027 continue; 1028 } 1029 if (attr.isa<fir::ClosedIntervalAttr>()) { 1030 auto cmp = rewriter.create<mlir::LLVM::ICmpOp>( 1031 loc, mlir::LLVM::ICmpPredicate::sle, caseArg, selector); 1032 auto *thisBlock = rewriter.getInsertionBlock(); 1033 auto *newBlock1 = createBlock(rewriter, dest); 1034 auto *newBlock2 = createBlock(rewriter, dest); 1035 rewriter.setInsertionPointToEnd(thisBlock); 1036 rewriter.create<mlir::LLVM::CondBrOp>(loc, cmp, newBlock1, newBlock2); 1037 rewriter.setInsertionPointToEnd(newBlock1); 1038 mlir::Value caseArg0 = *(cmpOps.getValue().begin() + 1); 1039 auto cmp0 = rewriter.create<mlir::LLVM::ICmpOp>( 1040 loc, mlir::LLVM::ICmpPredicate::sle, selector, caseArg0); 1041 genCondBrOp(loc, cmp0, dest, destOps, rewriter, newBlock2); 1042 rewriter.setInsertionPointToEnd(newBlock2); 1043 continue; 1044 } 1045 assert(attr.isa<mlir::UnitAttr>()); 1046 assert((t + 1 == conds) && "unit must be last"); 1047 genBrOp(caseOp, dest, destOps, rewriter); 1048 } 1049 return success(); 1050 } 1051 }; 1052 1053 template <typename OP> 1054 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select, 1055 typename OP::Adaptor adaptor, 1056 mlir::ConversionPatternRewriter &rewriter) { 1057 unsigned conds = select.getNumConditions(); 1058 auto cases = select.getCases().getValue(); 1059 mlir::Value selector = adaptor.selector(); 1060 auto loc = select.getLoc(); 1061 assert(conds > 0 && "select must have cases"); 1062 1063 llvm::SmallVector<mlir::Block *> destinations; 1064 llvm::SmallVector<mlir::ValueRange> destinationsOperands; 1065 mlir::Block *defaultDestination; 1066 mlir::ValueRange defaultOperands; 1067 llvm::SmallVector<int32_t> caseValues; 1068 1069 for (unsigned t = 0; t != conds; ++t) { 1070 mlir::Block *dest = select.getSuccessor(t); 1071 auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t); 1072 const mlir::Attribute &attr = cases[t]; 1073 if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) { 1074 destinations.push_back(dest); 1075 destinationsOperands.push_back(destOps.hasValue() ? *destOps 1076 : ValueRange()); 1077 caseValues.push_back(intAttr.getInt()); 1078 continue; 1079 } 1080 assert(attr.template dyn_cast_or_null<mlir::UnitAttr>()); 1081 assert((t + 1 == conds) && "unit must be last"); 1082 defaultDestination = dest; 1083 defaultOperands = destOps.hasValue() ? *destOps : ValueRange(); 1084 } 1085 1086 // LLVM::SwitchOp takes a i32 type for the selector. 1087 if (select.getSelector().getType() != rewriter.getI32Type()) 1088 selector = 1089 rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector); 1090 1091 rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>( 1092 select, selector, 1093 /*defaultDestination=*/defaultDestination, 1094 /*defaultOperands=*/defaultOperands, 1095 /*caseValues=*/caseValues, 1096 /*caseDestinations=*/destinations, 1097 /*caseOperands=*/destinationsOperands, 1098 /*branchWeights=*/ArrayRef<int32_t>()); 1099 } 1100 1101 /// conversion of fir::SelectOp to an if-then-else ladder 1102 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> { 1103 using FIROpConversion::FIROpConversion; 1104 1105 mlir::LogicalResult 1106 matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor, 1107 mlir::ConversionPatternRewriter &rewriter) const override { 1108 selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter); 1109 return success(); 1110 } 1111 }; 1112 1113 /// `fir.load` --> `llvm.load` 1114 struct LoadOpConversion : public FIROpConversion<fir::LoadOp> { 1115 using FIROpConversion::FIROpConversion; 1116 1117 mlir::LogicalResult 1118 matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor, 1119 mlir::ConversionPatternRewriter &rewriter) const override { 1120 // fir.box is a special case because it is considered as an ssa values in 1121 // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box> 1122 // and fir.box end up being the same llvm types and loading a 1123 // fir.ref<fir.box> is actually a no op in LLVM. 1124 if (load.getType().isa<fir::BoxType>()) { 1125 rewriter.replaceOp(load, adaptor.getOperands()[0]); 1126 } else { 1127 mlir::Type ty = convertType(load.getType()); 1128 ArrayRef<NamedAttribute> at = load->getAttrs(); 1129 rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>( 1130 load, ty, adaptor.getOperands(), at); 1131 } 1132 return success(); 1133 } 1134 }; 1135 1136 /// Lower `fir.select_type` to LLVM IR dialect. 1137 struct SelectTypeOpConversion : public FIROpConversion<fir::SelectTypeOp> { 1138 using FIROpConversion::FIROpConversion; 1139 1140 mlir::LogicalResult 1141 matchAndRewrite(fir::SelectTypeOp select, OpAdaptor adaptor, 1142 mlir::ConversionPatternRewriter &rewriter) const override { 1143 return rewriter.notifyMatchFailure( 1144 select, "fir.select_type codegen is not implemented yet"); 1145 } 1146 }; 1147 1148 /// conversion of fir::SelectRankOp to an if-then-else ladder 1149 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> { 1150 using FIROpConversion::FIROpConversion; 1151 1152 mlir::LogicalResult 1153 matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor, 1154 mlir::ConversionPatternRewriter &rewriter) const override { 1155 selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter); 1156 return success(); 1157 } 1158 }; 1159 1160 /// `fir.store` --> `llvm.store` 1161 struct StoreOpConversion : public FIROpConversion<fir::StoreOp> { 1162 using FIROpConversion::FIROpConversion; 1163 1164 mlir::LogicalResult 1165 matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor, 1166 mlir::ConversionPatternRewriter &rewriter) const override { 1167 if (store.value().getType().isa<fir::BoxType>()) { 1168 // fir.box value is actually in memory, load it first before storing it. 1169 mlir::Location loc = store.getLoc(); 1170 mlir::Type boxPtrTy = adaptor.getOperands()[0].getType(); 1171 auto val = rewriter.create<mlir::LLVM::LoadOp>( 1172 loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(), 1173 adaptor.getOperands()[0]); 1174 rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 1175 store, val, adaptor.getOperands()[1]); 1176 } else { 1177 rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 1178 store, adaptor.getOperands()[0], adaptor.getOperands()[1]); 1179 } 1180 return success(); 1181 } 1182 }; 1183 1184 /// convert to LLVM IR dialect `undef` 1185 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> { 1186 using FIROpConversion::FIROpConversion; 1187 1188 mlir::LogicalResult 1189 matchAndRewrite(fir::UndefOp undef, OpAdaptor, 1190 mlir::ConversionPatternRewriter &rewriter) const override { 1191 rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>( 1192 undef, convertType(undef.getType())); 1193 return success(); 1194 } 1195 }; 1196 1197 /// `fir.unreachable` --> `llvm.unreachable` 1198 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> { 1199 using FIROpConversion::FIROpConversion; 1200 1201 mlir::LogicalResult 1202 matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor, 1203 mlir::ConversionPatternRewriter &rewriter) const override { 1204 rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach); 1205 return success(); 1206 } 1207 }; 1208 1209 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> { 1210 using FIROpConversion::FIROpConversion; 1211 1212 mlir::LogicalResult 1213 matchAndRewrite(fir::ZeroOp zero, OpAdaptor, 1214 mlir::ConversionPatternRewriter &rewriter) const override { 1215 auto ty = convertType(zero.getType()); 1216 if (ty.isa<mlir::LLVM::LLVMPointerType>()) { 1217 rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty); 1218 } else if (ty.isa<mlir::IntegerType>()) { 1219 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 1220 zero, ty, mlir::IntegerAttr::get(zero.getType(), 0)); 1221 } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) { 1222 rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 1223 zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0)); 1224 } else { 1225 // TODO: create ConstantAggregateZero for FIR aggregate/array types. 1226 return rewriter.notifyMatchFailure( 1227 zero, 1228 "conversion of fir.zero with aggregate type not implemented yet"); 1229 } 1230 return success(); 1231 } 1232 }; 1233 1234 /// Common base class for embox to descriptor conversion. 1235 template <typename OP> 1236 struct EmboxCommonConversion : public FIROpConversion<OP> { 1237 using FIROpConversion<OP>::FIROpConversion; 1238 1239 // Find the LLVMFuncOp in whose entry block the alloca should be inserted. 1240 // The order to find the LLVMFuncOp is as follows: 1241 // 1. The parent operation of the current block if it is a LLVMFuncOp. 1242 // 2. The first ancestor that is a LLVMFuncOp. 1243 mlir::LLVM::LLVMFuncOp 1244 getFuncForAllocaInsert(mlir::ConversionPatternRewriter &rewriter) const { 1245 mlir::Operation *parentOp = rewriter.getInsertionBlock()->getParentOp(); 1246 return mlir::isa<mlir::LLVM::LLVMFuncOp>(parentOp) 1247 ? mlir::cast<mlir::LLVM::LLVMFuncOp>(parentOp) 1248 : parentOp->getParentOfType<mlir::LLVM::LLVMFuncOp>(); 1249 } 1250 1251 // Generate an alloca of size 1 and type \p toTy. 1252 mlir::LLVM::AllocaOp 1253 genAllocaWithType(mlir::Location loc, mlir::Type toTy, unsigned alignment, 1254 mlir::ConversionPatternRewriter &rewriter) const { 1255 auto thisPt = rewriter.saveInsertionPoint(); 1256 mlir::LLVM::LLVMFuncOp func = getFuncForAllocaInsert(rewriter); 1257 rewriter.setInsertionPointToStart(&func.front()); 1258 auto size = this->genI32Constant(loc, rewriter, 1); 1259 auto al = rewriter.create<mlir::LLVM::AllocaOp>(loc, toTy, size, alignment); 1260 rewriter.restoreInsertionPoint(thisPt); 1261 return al; 1262 } 1263 1264 static int getCFIAttr(fir::BoxType boxTy) { 1265 auto eleTy = boxTy.getEleTy(); 1266 if (eleTy.isa<fir::PointerType>()) 1267 return CFI_attribute_pointer; 1268 if (eleTy.isa<fir::HeapType>()) 1269 return CFI_attribute_allocatable; 1270 return CFI_attribute_other; 1271 } 1272 1273 static fir::RecordType unwrapIfDerived(fir::BoxType boxTy) { 1274 return fir::unwrapSequenceType(fir::dyn_cast_ptrOrBoxEleTy(boxTy)) 1275 .template dyn_cast<fir::RecordType>(); 1276 } 1277 static bool isDerivedTypeWithLenParams(fir::BoxType boxTy) { 1278 auto recTy = unwrapIfDerived(boxTy); 1279 return recTy && recTy.getNumLenParams() > 0; 1280 } 1281 static bool isDerivedType(fir::BoxType boxTy) { 1282 return unwrapIfDerived(boxTy) != nullptr; 1283 } 1284 1285 // Get the element size and CFI type code of the boxed value. 1286 std::tuple<mlir::Value, mlir::Value> getSizeAndTypeCode( 1287 mlir::Location loc, mlir::ConversionPatternRewriter &rewriter, 1288 mlir::Type boxEleTy, mlir::ValueRange lenParams = {}) const { 1289 auto doInteger = 1290 [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> { 1291 int typeCode = fir::integerBitsToTypeCode(width); 1292 return {this->genConstantOffset(loc, rewriter, width / 8), 1293 this->genConstantOffset(loc, rewriter, typeCode)}; 1294 }; 1295 auto doLogical = 1296 [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> { 1297 int typeCode = fir::logicalBitsToTypeCode(width); 1298 return {this->genConstantOffset(loc, rewriter, width / 8), 1299 this->genConstantOffset(loc, rewriter, typeCode)}; 1300 }; 1301 auto doFloat = [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> { 1302 int typeCode = fir::realBitsToTypeCode(width); 1303 return {this->genConstantOffset(loc, rewriter, width / 8), 1304 this->genConstantOffset(loc, rewriter, typeCode)}; 1305 }; 1306 auto doComplex = 1307 [&](unsigned width) -> std::tuple<mlir::Value, mlir::Value> { 1308 auto typeCode = fir::complexBitsToTypeCode(width); 1309 return {this->genConstantOffset(loc, rewriter, width / 8 * 2), 1310 this->genConstantOffset(loc, rewriter, typeCode)}; 1311 }; 1312 auto doCharacter = 1313 [&](unsigned width, 1314 mlir::Value len) -> std::tuple<mlir::Value, mlir::Value> { 1315 auto typeCode = fir::characterBitsToTypeCode(width); 1316 auto typeCodeVal = this->genConstantOffset(loc, rewriter, typeCode); 1317 if (width == 8) 1318 return {len, typeCodeVal}; 1319 auto byteWidth = this->genConstantOffset(loc, rewriter, width / 8); 1320 auto i64Ty = mlir::IntegerType::get(&this->lowerTy().getContext(), 64); 1321 auto size = 1322 rewriter.create<mlir::LLVM::MulOp>(loc, i64Ty, byteWidth, len); 1323 return {size, typeCodeVal}; 1324 }; 1325 auto getKindMap = [&]() -> fir::KindMapping & { 1326 return this->lowerTy().getKindMap(); 1327 }; 1328 // Pointer-like types. 1329 if (auto eleTy = fir::dyn_cast_ptrEleTy(boxEleTy)) 1330 boxEleTy = eleTy; 1331 // Integer types. 1332 if (fir::isa_integer(boxEleTy)) { 1333 if (auto ty = boxEleTy.dyn_cast<mlir::IntegerType>()) 1334 return doInteger(ty.getWidth()); 1335 auto ty = boxEleTy.cast<fir::IntegerType>(); 1336 return doInteger(getKindMap().getIntegerBitsize(ty.getFKind())); 1337 } 1338 // Floating point types. 1339 if (fir::isa_real(boxEleTy)) { 1340 if (auto ty = boxEleTy.dyn_cast<mlir::FloatType>()) 1341 return doFloat(ty.getWidth()); 1342 auto ty = boxEleTy.cast<fir::RealType>(); 1343 return doFloat(getKindMap().getRealBitsize(ty.getFKind())); 1344 } 1345 // Complex types. 1346 if (fir::isa_complex(boxEleTy)) { 1347 if (auto ty = boxEleTy.dyn_cast<mlir::ComplexType>()) 1348 return doComplex( 1349 ty.getElementType().cast<mlir::FloatType>().getWidth()); 1350 auto ty = boxEleTy.cast<fir::ComplexType>(); 1351 return doComplex(getKindMap().getRealBitsize(ty.getFKind())); 1352 } 1353 // Character types. 1354 if (auto ty = boxEleTy.dyn_cast<fir::CharacterType>()) { 1355 auto charWidth = getKindMap().getCharacterBitsize(ty.getFKind()); 1356 if (ty.getLen() != fir::CharacterType::unknownLen()) { 1357 auto len = this->genConstantOffset(loc, rewriter, ty.getLen()); 1358 return doCharacter(charWidth, len); 1359 } 1360 assert(!lenParams.empty()); 1361 return doCharacter(charWidth, lenParams.back()); 1362 } 1363 // Logical type. 1364 if (auto ty = boxEleTy.dyn_cast<fir::LogicalType>()) 1365 return doLogical(getKindMap().getLogicalBitsize(ty.getFKind())); 1366 // Array types. 1367 if (auto seqTy = boxEleTy.dyn_cast<fir::SequenceType>()) 1368 return getSizeAndTypeCode(loc, rewriter, seqTy.getEleTy(), lenParams); 1369 // Derived-type types. 1370 if (boxEleTy.isa<fir::RecordType>()) { 1371 auto ptrTy = mlir::LLVM::LLVMPointerType::get( 1372 this->lowerTy().convertType(boxEleTy)); 1373 auto nullPtr = rewriter.create<mlir::LLVM::NullOp>(loc, ptrTy); 1374 auto one = 1375 genConstantIndex(loc, this->lowerTy().offsetType(), rewriter, 1); 1376 auto gep = rewriter.create<mlir::LLVM::GEPOp>( 1377 loc, ptrTy, mlir::ValueRange{nullPtr, one}); 1378 auto eleSize = rewriter.create<mlir::LLVM::PtrToIntOp>( 1379 loc, this->lowerTy().indexType(), gep); 1380 return {eleSize, 1381 this->genConstantOffset(loc, rewriter, fir::derivedToTypeCode())}; 1382 } 1383 // Reference type. 1384 if (fir::isa_ref_type(boxEleTy)) { 1385 // FIXME: use the target pointer size rather than sizeof(void*) 1386 return {this->genConstantOffset(loc, rewriter, sizeof(void *)), 1387 this->genConstantOffset(loc, rewriter, CFI_type_cptr)}; 1388 } 1389 fir::emitFatalError(loc, "unhandled type in fir.box code generation"); 1390 } 1391 1392 /// Basic pattern to write a field in the descriptor 1393 mlir::Value insertField(mlir::ConversionPatternRewriter &rewriter, 1394 mlir::Location loc, mlir::Value dest, 1395 ArrayRef<unsigned> fldIndexes, mlir::Value value, 1396 bool bitcast = false) const { 1397 auto boxTy = dest.getType(); 1398 auto fldTy = this->getBoxEleTy(boxTy, fldIndexes); 1399 if (bitcast) 1400 value = rewriter.create<mlir::LLVM::BitcastOp>(loc, fldTy, value); 1401 else 1402 value = this->integerCast(loc, rewriter, fldTy, value); 1403 SmallVector<mlir::Attribute, 2> attrs; 1404 for (auto i : fldIndexes) 1405 attrs.push_back(rewriter.getI32IntegerAttr(i)); 1406 auto indexesAttr = mlir::ArrayAttr::get(rewriter.getContext(), attrs); 1407 return rewriter.create<mlir::LLVM::InsertValueOp>(loc, boxTy, dest, value, 1408 indexesAttr); 1409 } 1410 1411 inline mlir::Value 1412 insertBaseAddress(mlir::ConversionPatternRewriter &rewriter, 1413 mlir::Location loc, mlir::Value dest, 1414 mlir::Value base) const { 1415 return insertField(rewriter, loc, dest, {0}, base, /*bitCast=*/true); 1416 } 1417 1418 /// Get the address of the type descriptor global variable that was created by 1419 /// lowering for derived type \p recType. 1420 template <typename BOX> 1421 mlir::Value 1422 getTypeDescriptor(BOX box, mlir::ConversionPatternRewriter &rewriter, 1423 mlir::Location loc, fir::RecordType recType) const { 1424 std::string name = recType.getLoweredName(); 1425 auto module = box->template getParentOfType<mlir::ModuleOp>(); 1426 if (auto global = module.template lookupSymbol<fir::GlobalOp>(name)) { 1427 auto ty = mlir::LLVM::LLVMPointerType::get( 1428 this->lowerTy().convertType(global.getType())); 1429 return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty, 1430 global.sym_name()); 1431 } 1432 if (auto global = 1433 module.template lookupSymbol<mlir::LLVM::GlobalOp>(name)) { 1434 // The global may have already been translated to LLVM. 1435 auto ty = mlir::LLVM::LLVMPointerType::get(global.getType()); 1436 return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty, 1437 global.sym_name()); 1438 } 1439 // The global does not exist in the current translation unit, but may be 1440 // defined elsewhere (e.g., type defined in a module). 1441 // For now, create a extern_weak symbol (will become nullptr if unresolved) 1442 // to support generating code without the front-end generated symbols. 1443 // These could be made available_externally to require the symbols to be 1444 // defined elsewhere and to cause link-time failure otherwise. 1445 auto i8Ty = rewriter.getIntegerType(8); 1446 mlir::OpBuilder modBuilder(module.getBodyRegion()); 1447 // TODO: The symbol should be lowered to constant in lowering, they are read 1448 // only. 1449 modBuilder.create<mlir::LLVM::GlobalOp>(loc, i8Ty, /*isConstant=*/false, 1450 mlir::LLVM::Linkage::ExternWeak, 1451 name, mlir::Attribute{}); 1452 auto ty = mlir::LLVM::LLVMPointerType::get(i8Ty); 1453 return rewriter.create<mlir::LLVM::AddressOfOp>(loc, ty, name); 1454 } 1455 1456 template <typename BOX> 1457 std::tuple<fir::BoxType, mlir::Value, mlir::Value> 1458 consDescriptorPrefix(BOX box, mlir::ConversionPatternRewriter &rewriter, 1459 unsigned rank, mlir::ValueRange lenParams) const { 1460 auto loc = box.getLoc(); 1461 auto boxTy = box.getType().template dyn_cast<fir::BoxType>(); 1462 auto convTy = this->lowerTy().convertBoxType(boxTy, rank); 1463 auto llvmBoxPtrTy = convTy.template cast<mlir::LLVM::LLVMPointerType>(); 1464 auto llvmBoxTy = llvmBoxPtrTy.getElementType(); 1465 mlir::Value descriptor = 1466 rewriter.create<mlir::LLVM::UndefOp>(loc, llvmBoxTy); 1467 1468 llvm::SmallVector<mlir::Value> typeparams = lenParams; 1469 if constexpr (!std::is_same_v<BOX, fir::EmboxOp>) { 1470 if (!box.substr().empty() && fir::hasDynamicSize(boxTy.getEleTy())) 1471 typeparams.push_back(box.substr()[1]); 1472 } 1473 1474 // Write each of the fields with the appropriate values 1475 auto [eleSize, cfiTy] = 1476 getSizeAndTypeCode(loc, rewriter, boxTy.getEleTy(), typeparams); 1477 descriptor = 1478 insertField(rewriter, loc, descriptor, {kElemLenPosInBox}, eleSize); 1479 descriptor = insertField(rewriter, loc, descriptor, {kVersionPosInBox}, 1480 this->genI32Constant(loc, rewriter, CFI_VERSION)); 1481 descriptor = insertField(rewriter, loc, descriptor, {kRankPosInBox}, 1482 this->genI32Constant(loc, rewriter, rank)); 1483 descriptor = insertField(rewriter, loc, descriptor, {kTypePosInBox}, cfiTy); 1484 descriptor = 1485 insertField(rewriter, loc, descriptor, {kAttributePosInBox}, 1486 this->genI32Constant(loc, rewriter, getCFIAttr(boxTy))); 1487 const bool hasAddendum = isDerivedType(boxTy); 1488 descriptor = 1489 insertField(rewriter, loc, descriptor, {kF18AddendumPosInBox}, 1490 this->genI32Constant(loc, rewriter, hasAddendum ? 1 : 0)); 1491 1492 if (hasAddendum) { 1493 auto isArray = 1494 fir::dyn_cast_ptrOrBoxEleTy(boxTy).template isa<fir::SequenceType>(); 1495 unsigned typeDescFieldId = isArray ? kOptTypePtrPosInBox : kDimsPosInBox; 1496 auto typeDesc = 1497 getTypeDescriptor(box, rewriter, loc, unwrapIfDerived(boxTy)); 1498 descriptor = 1499 insertField(rewriter, loc, descriptor, {typeDescFieldId}, typeDesc, 1500 /*bitCast=*/true); 1501 } 1502 1503 return {boxTy, descriptor, eleSize}; 1504 } 1505 1506 /// If the embox is not in a globalOp body, allocate storage for the box; 1507 /// store the value inside and return the generated alloca. Return the input 1508 /// value otherwise. 1509 mlir::Value 1510 placeInMemoryIfNotGlobalInit(mlir::ConversionPatternRewriter &rewriter, 1511 mlir::Location loc, mlir::Value boxValue) const { 1512 auto *thisBlock = rewriter.getInsertionBlock(); 1513 if (thisBlock && mlir::isa<mlir::LLVM::GlobalOp>(thisBlock->getParentOp())) 1514 return boxValue; 1515 auto boxPtrTy = mlir::LLVM::LLVMPointerType::get(boxValue.getType()); 1516 auto alloca = genAllocaWithType(loc, boxPtrTy, defaultAlign, rewriter); 1517 rewriter.create<mlir::LLVM::StoreOp>(loc, boxValue, alloca); 1518 return alloca; 1519 } 1520 }; 1521 1522 /// Create a generic box on a memory reference. This conversions lowers the 1523 /// abstract box to the appropriate, initialized descriptor. 1524 struct EmboxOpConversion : public EmboxCommonConversion<fir::EmboxOp> { 1525 using EmboxCommonConversion::EmboxCommonConversion; 1526 1527 mlir::LogicalResult 1528 matchAndRewrite(fir::EmboxOp embox, OpAdaptor adaptor, 1529 mlir::ConversionPatternRewriter &rewriter) const override { 1530 assert(!embox.getShape() && "There should be no dims on this embox op"); 1531 auto [boxTy, dest, eleSize] = 1532 consDescriptorPrefix(embox, rewriter, /*rank=*/0, 1533 /*lenParams=*/adaptor.getOperands().drop_front(1)); 1534 dest = insertBaseAddress(rewriter, embox.getLoc(), dest, 1535 adaptor.getOperands()[0]); 1536 if (isDerivedTypeWithLenParams(boxTy)) 1537 return rewriter.notifyMatchFailure( 1538 embox, "fir.embox codegen of derived with length parameters not " 1539 "implemented yet"); 1540 auto result = placeInMemoryIfNotGlobalInit(rewriter, embox.getLoc(), dest); 1541 rewriter.replaceOp(embox, result); 1542 return success(); 1543 } 1544 }; 1545 1546 /// Lower `fir.emboxproc` operation. Creates a procedure box. 1547 /// TODO: Part of supporting Fortran 2003 procedure pointers. 1548 struct EmboxProcOpConversion : public FIROpConversion<fir::EmboxProcOp> { 1549 using FIROpConversion::FIROpConversion; 1550 1551 mlir::LogicalResult 1552 matchAndRewrite(fir::EmboxProcOp emboxproc, OpAdaptor adaptor, 1553 mlir::ConversionPatternRewriter &rewriter) const override { 1554 return rewriter.notifyMatchFailure( 1555 emboxproc, "fir.emboxproc codegen is not implemented yet"); 1556 } 1557 }; 1558 1559 1560 // Code shared between insert_value and extract_value Ops. 1561 struct ValueOpCommon { 1562 // Translate the arguments pertaining to any multidimensional array to 1563 // row-major order for LLVM-IR. 1564 static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs, 1565 mlir::Type ty) { 1566 assert(ty && "type is null"); 1567 const auto end = attrs.size(); 1568 for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) { 1569 if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 1570 const auto dim = getDimension(seq); 1571 if (dim > 1) { 1572 auto ub = std::min(i + dim, end); 1573 std::reverse(attrs.begin() + i, attrs.begin() + ub); 1574 i += dim - 1; 1575 } 1576 ty = getArrayElementType(seq); 1577 } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) { 1578 ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()]; 1579 } else { 1580 llvm_unreachable("index into invalid type"); 1581 } 1582 } 1583 } 1584 1585 static llvm::SmallVector<mlir::Attribute> 1586 collectIndices(mlir::ConversionPatternRewriter &rewriter, 1587 mlir::ArrayAttr arrAttr) { 1588 llvm::SmallVector<mlir::Attribute> attrs; 1589 for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) { 1590 if (i->isa<mlir::IntegerAttr>()) { 1591 attrs.push_back(*i); 1592 } else { 1593 auto fieldName = i->cast<mlir::StringAttr>().getValue(); 1594 ++i; 1595 auto ty = i->cast<mlir::TypeAttr>().getValue(); 1596 auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName); 1597 attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index)); 1598 } 1599 } 1600 return attrs; 1601 } 1602 1603 private: 1604 static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) { 1605 unsigned result = 1; 1606 for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>(); 1607 eleTy; 1608 eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>()) 1609 ++result; 1610 return result; 1611 } 1612 1613 static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) { 1614 auto eleTy = ty.getElementType(); 1615 while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>()) 1616 eleTy = arrTy.getElementType(); 1617 return eleTy; 1618 } 1619 }; 1620 1621 /// Extract a subobject value from an ssa-value of aggregate type 1622 struct ExtractValueOpConversion 1623 : public FIROpAndTypeConversion<fir::ExtractValueOp>, 1624 public ValueOpCommon { 1625 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1626 1627 mlir::LogicalResult 1628 doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor, 1629 mlir::ConversionPatternRewriter &rewriter) const override { 1630 auto attrs = collectIndices(rewriter, extractVal.coor()); 1631 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 1632 auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs); 1633 rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>( 1634 extractVal, ty, adaptor.getOperands()[0], position); 1635 return success(); 1636 } 1637 }; 1638 1639 /// InsertValue is the generalized instruction for the composition of new 1640 /// aggregate type values. 1641 struct InsertValueOpConversion 1642 : public FIROpAndTypeConversion<fir::InsertValueOp>, 1643 public ValueOpCommon { 1644 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1645 1646 mlir::LogicalResult 1647 doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor, 1648 mlir::ConversionPatternRewriter &rewriter) const override { 1649 auto attrs = collectIndices(rewriter, insertVal.coor()); 1650 toRowMajor(attrs, adaptor.getOperands()[0].getType()); 1651 auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs); 1652 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1653 insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1], 1654 position); 1655 return success(); 1656 } 1657 }; 1658 1659 /// InsertOnRange inserts a value into a sequence over a range of offsets. 1660 struct InsertOnRangeOpConversion 1661 : public FIROpAndTypeConversion<fir::InsertOnRangeOp> { 1662 using FIROpAndTypeConversion::FIROpAndTypeConversion; 1663 1664 // Increments an array of subscripts in a row major fasion. 1665 void incrementSubscripts(const SmallVector<uint64_t> &dims, 1666 SmallVector<uint64_t> &subscripts) const { 1667 for (size_t i = dims.size(); i > 0; --i) { 1668 if (++subscripts[i - 1] < dims[i - 1]) { 1669 return; 1670 } 1671 subscripts[i - 1] = 0; 1672 } 1673 } 1674 1675 mlir::LogicalResult 1676 doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor, 1677 mlir::ConversionPatternRewriter &rewriter) const override { 1678 1679 llvm::SmallVector<uint64_t> dims; 1680 auto type = adaptor.getOperands()[0].getType(); 1681 1682 // Iteratively extract the array dimensions from the type. 1683 while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 1684 dims.push_back(t.getNumElements()); 1685 type = t.getElementType(); 1686 } 1687 1688 SmallVector<uint64_t> lBounds; 1689 SmallVector<uint64_t> uBounds; 1690 1691 // Extract integer value from the attribute 1692 SmallVector<int64_t> coordinates = llvm::to_vector<4>( 1693 llvm::map_range(range.coor(), [](Attribute a) -> int64_t { 1694 return a.cast<IntegerAttr>().getInt(); 1695 })); 1696 1697 // Unzip the upper and lower bound and convert to a row major format. 1698 for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) { 1699 uBounds.push_back(*i++); 1700 lBounds.push_back(*i); 1701 } 1702 1703 auto &subscripts = lBounds; 1704 auto loc = range.getLoc(); 1705 mlir::Value lastOp = adaptor.getOperands()[0]; 1706 mlir::Value insertVal = adaptor.getOperands()[1]; 1707 1708 auto i64Ty = rewriter.getI64Type(); 1709 while (subscripts != uBounds) { 1710 // Convert uint64_t's to Attribute's. 1711 SmallVector<mlir::Attribute> subscriptAttrs; 1712 for (const auto &subscript : subscripts) 1713 subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript)); 1714 lastOp = rewriter.create<mlir::LLVM::InsertValueOp>( 1715 loc, ty, lastOp, insertVal, 1716 ArrayAttr::get(range.getContext(), subscriptAttrs)); 1717 1718 incrementSubscripts(dims, subscripts); 1719 } 1720 1721 // Convert uint64_t's to Attribute's. 1722 SmallVector<mlir::Attribute> subscriptAttrs; 1723 for (const auto &subscript : subscripts) 1724 subscriptAttrs.push_back( 1725 IntegerAttr::get(rewriter.getI64Type(), subscript)); 1726 mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs); 1727 1728 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1729 range, ty, lastOp, insertVal, 1730 ArrayAttr::get(range.getContext(), arrayRef)); 1731 1732 return success(); 1733 } 1734 }; 1735 1736 // 1737 // Primitive operations on Complex types 1738 // 1739 1740 /// Generate inline code for complex addition/subtraction 1741 template <typename LLVMOP, typename OPTY> 1742 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds, 1743 mlir::ConversionPatternRewriter &rewriter, 1744 fir::LLVMTypeConverter &lowering) { 1745 mlir::Value a = opnds[0]; 1746 mlir::Value b = opnds[1]; 1747 auto loc = sumop.getLoc(); 1748 auto ctx = sumop.getContext(); 1749 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1750 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1751 mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType())); 1752 mlir::Type ty = lowering.convertType(sumop.getType()); 1753 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1754 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1755 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1756 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1757 auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1); 1758 auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1); 1759 auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1760 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0); 1761 return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1); 1762 } 1763 1764 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> { 1765 using FIROpConversion::FIROpConversion; 1766 1767 mlir::LogicalResult 1768 matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor, 1769 mlir::ConversionPatternRewriter &rewriter) const override { 1770 // given: (x + iy) + (x' + iy') 1771 // result: (x + x') + i(y + y') 1772 auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(), 1773 rewriter, lowerTy()); 1774 rewriter.replaceOp(addc, r.getResult()); 1775 return success(); 1776 } 1777 }; 1778 1779 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> { 1780 using FIROpConversion::FIROpConversion; 1781 1782 mlir::LogicalResult 1783 matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor, 1784 mlir::ConversionPatternRewriter &rewriter) const override { 1785 // given: (x + iy) - (x' + iy') 1786 // result: (x - x') + i(y - y') 1787 auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(), 1788 rewriter, lowerTy()); 1789 rewriter.replaceOp(subc, r.getResult()); 1790 return success(); 1791 } 1792 }; 1793 1794 /// Inlined complex multiply 1795 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> { 1796 using FIROpConversion::FIROpConversion; 1797 1798 mlir::LogicalResult 1799 matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor, 1800 mlir::ConversionPatternRewriter &rewriter) const override { 1801 // TODO: Can we use a call to __muldc3 ? 1802 // given: (x + iy) * (x' + iy') 1803 // result: (xx'-yy')+i(xy'+yx') 1804 mlir::Value a = adaptor.getOperands()[0]; 1805 mlir::Value b = adaptor.getOperands()[1]; 1806 auto loc = mulc.getLoc(); 1807 auto *ctx = mulc.getContext(); 1808 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1809 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1810 mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType())); 1811 mlir::Type ty = convertType(mulc.getType()); 1812 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1813 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1814 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1815 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1816 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 1817 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 1818 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 1819 auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx); 1820 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 1821 auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy); 1822 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1823 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 1824 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 1825 rewriter.replaceOp(mulc, r0.getResult()); 1826 return success(); 1827 } 1828 }; 1829 1830 /// Inlined complex division 1831 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> { 1832 using FIROpConversion::FIROpConversion; 1833 1834 mlir::LogicalResult 1835 matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor, 1836 mlir::ConversionPatternRewriter &rewriter) const override { 1837 // TODO: Can we use a call to __divdc3 instead? 1838 // Just generate inline code for now. 1839 // given: (x + iy) / (x' + iy') 1840 // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y' 1841 mlir::Value a = adaptor.getOperands()[0]; 1842 mlir::Value b = adaptor.getOperands()[1]; 1843 auto loc = divc.getLoc(); 1844 auto *ctx = divc.getContext(); 1845 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1846 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1847 mlir::Type eleTy = convertType(getComplexEleTy(divc.getType())); 1848 mlir::Type ty = convertType(divc.getType()); 1849 auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 1850 auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 1851 auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 1852 auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 1853 auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 1854 auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1); 1855 auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 1856 auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 1857 auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 1858 auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1); 1859 auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1); 1860 auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy); 1861 auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy); 1862 auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d); 1863 auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d); 1864 auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 1865 auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 1866 auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 1867 rewriter.replaceOp(divc, r0.getResult()); 1868 return success(); 1869 } 1870 }; 1871 1872 /// Inlined complex negation 1873 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> { 1874 using FIROpConversion::FIROpConversion; 1875 1876 mlir::LogicalResult 1877 matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor, 1878 mlir::ConversionPatternRewriter &rewriter) const override { 1879 // given: -(x + iy) 1880 // result: -x - iy 1881 auto *ctxt = neg.getContext(); 1882 auto eleTy = convertType(getComplexEleTy(neg.getType())); 1883 auto ty = convertType(neg.getType()); 1884 auto loc = neg.getLoc(); 1885 mlir::Value o0 = adaptor.getOperands()[0]; 1886 auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0)); 1887 auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1)); 1888 auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0); 1889 auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1); 1890 auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp); 1891 auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip); 1892 auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0); 1893 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1); 1894 return success(); 1895 } 1896 }; 1897 1898 /// Conversion pattern for operation that must be dead. The information in these 1899 /// operations is used by other operation. At this point they should not have 1900 /// anymore uses. 1901 /// These operations are normally dead after the pre-codegen pass. 1902 template <typename FromOp> 1903 struct MustBeDeadConversion : public FIROpConversion<FromOp> { 1904 explicit MustBeDeadConversion(fir::LLVMTypeConverter &lowering) 1905 : FIROpConversion<FromOp>(lowering) {} 1906 using OpAdaptor = typename FromOp::Adaptor; 1907 1908 mlir::LogicalResult 1909 matchAndRewrite(FromOp op, OpAdaptor adaptor, 1910 mlir::ConversionPatternRewriter &rewriter) const final { 1911 if (!op->getUses().empty()) 1912 return rewriter.notifyMatchFailure(op, "op must be dead"); 1913 rewriter.eraseOp(op); 1914 return success(); 1915 } 1916 }; 1917 1918 struct ShapeOpConversion : public MustBeDeadConversion<fir::ShapeOp> { 1919 using MustBeDeadConversion::MustBeDeadConversion; 1920 }; 1921 1922 struct ShapeShiftOpConversion : public MustBeDeadConversion<fir::ShapeShiftOp> { 1923 using MustBeDeadConversion::MustBeDeadConversion; 1924 }; 1925 1926 struct ShiftOpConversion : public MustBeDeadConversion<fir::ShiftOp> { 1927 using MustBeDeadConversion::MustBeDeadConversion; 1928 }; 1929 1930 struct SliceOpConversion : public MustBeDeadConversion<fir::SliceOp> { 1931 using MustBeDeadConversion::MustBeDeadConversion; 1932 }; 1933 1934 /// `fir.is_present` --> 1935 /// ``` 1936 /// %0 = llvm.mlir.constant(0 : i64) 1937 /// %1 = llvm.ptrtoint %0 1938 /// %2 = llvm.icmp "ne" %1, %0 : i64 1939 /// ``` 1940 struct IsPresentOpConversion : public FIROpConversion<fir::IsPresentOp> { 1941 using FIROpConversion::FIROpConversion; 1942 1943 mlir::LogicalResult 1944 matchAndRewrite(fir::IsPresentOp isPresent, OpAdaptor adaptor, 1945 mlir::ConversionPatternRewriter &rewriter) const override { 1946 mlir::Type idxTy = lowerTy().indexType(); 1947 mlir::Location loc = isPresent.getLoc(); 1948 auto ptr = adaptor.getOperands()[0]; 1949 1950 if (isPresent.val().getType().isa<fir::BoxCharType>()) { 1951 auto structTy = ptr.getType().cast<mlir::LLVM::LLVMStructType>(); 1952 assert(!structTy.isOpaque() && !structTy.getBody().empty()); 1953 1954 mlir::Type ty = structTy.getBody()[0]; 1955 mlir::MLIRContext *ctx = isPresent.getContext(); 1956 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1957 ptr = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, ptr, c0); 1958 } 1959 mlir::LLVM::ConstantOp c0 = 1960 genConstantIndex(isPresent.getLoc(), idxTy, rewriter, 0); 1961 auto addr = rewriter.create<mlir::LLVM::PtrToIntOp>(loc, idxTy, ptr); 1962 rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>( 1963 isPresent, mlir::LLVM::ICmpPredicate::ne, addr, c0); 1964 1965 return success(); 1966 } 1967 }; 1968 1969 /// Convert `!fir.emboxchar<!fir.char<KIND, ?>, #n>` into a sequence of 1970 /// instructions that generate `!llvm.struct<(ptr<ik>, i64)>`. The 1st element 1971 /// in this struct is a pointer. Its type is determined from `KIND`. The 2nd 1972 /// element is the length of the character buffer (`#n`). 1973 struct EmboxCharOpConversion : public FIROpConversion<fir::EmboxCharOp> { 1974 using FIROpConversion::FIROpConversion; 1975 1976 mlir::LogicalResult 1977 matchAndRewrite(fir::EmboxCharOp emboxChar, OpAdaptor adaptor, 1978 mlir::ConversionPatternRewriter &rewriter) const override { 1979 mlir::ValueRange operands = adaptor.getOperands(); 1980 MLIRContext *ctx = emboxChar.getContext(); 1981 1982 mlir::Value charBuffer = operands[0]; 1983 mlir::Value charBufferLen = operands[1]; 1984 1985 mlir::Location loc = emboxChar.getLoc(); 1986 mlir::Type llvmStructTy = convertType(emboxChar.getType()); 1987 auto llvmStruct = rewriter.create<mlir::LLVM::UndefOp>(loc, llvmStructTy); 1988 1989 mlir::Type lenTy = 1990 llvmStructTy.cast<mlir::LLVM::LLVMStructType>().getBody()[1]; 1991 mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, charBufferLen); 1992 1993 auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 1994 auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 1995 auto insertBufferOp = rewriter.create<mlir::LLVM::InsertValueOp>( 1996 loc, llvmStructTy, llvmStruct, charBuffer, c0); 1997 rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 1998 emboxChar, llvmStructTy, insertBufferOp, lenAfterCast, c1); 1999 2000 return success(); 2001 } 2002 }; 2003 2004 /// Construct an `llvm.extractvalue` instruction. It will return value at 2005 /// element \p x from \p tuple. 2006 mlir::LLVM::ExtractValueOp 2007 genExtractValueWithIndex(mlir::Location loc, mlir::Value tuple, mlir::Type ty, 2008 mlir::ConversionPatternRewriter &rewriter, 2009 mlir::MLIRContext *ctx, int x) { 2010 auto cx = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(x)); 2011 auto xty = ty.cast<mlir::LLVM::LLVMStructType>().getBody()[x]; 2012 return rewriter.create<mlir::LLVM::ExtractValueOp>(loc, xty, tuple, cx); 2013 } 2014 2015 /// Convert `!fir.boxchar_len` to `!llvm.extractvalue` for the 2nd part of the 2016 /// boxchar. 2017 struct BoxCharLenOpConversion : public FIROpConversion<fir::BoxCharLenOp> { 2018 using FIROpConversion::FIROpConversion; 2019 2020 mlir::LogicalResult 2021 matchAndRewrite(fir::BoxCharLenOp boxCharLen, OpAdaptor adaptor, 2022 mlir::ConversionPatternRewriter &rewriter) const override { 2023 mlir::Value boxChar = adaptor.getOperands()[0]; 2024 mlir::Location loc = boxChar.getLoc(); 2025 mlir::MLIRContext *ctx = boxChar.getContext(); 2026 mlir::Type returnValTy = boxCharLen.getResult().getType(); 2027 2028 constexpr int boxcharLenIdx = 1; 2029 mlir::LLVM::ExtractValueOp len = genExtractValueWithIndex( 2030 loc, boxChar, boxChar.getType(), rewriter, ctx, boxcharLenIdx); 2031 mlir::Value lenAfterCast = integerCast(loc, rewriter, returnValTy, len); 2032 rewriter.replaceOp(boxCharLen, lenAfterCast); 2033 2034 return success(); 2035 } 2036 }; 2037 2038 /// Convert `fir.unboxchar` into two `llvm.extractvalue` instructions. One for 2039 /// the character buffer and one for the buffer length. 2040 struct UnboxCharOpConversion : public FIROpConversion<fir::UnboxCharOp> { 2041 using FIROpConversion::FIROpConversion; 2042 2043 mlir::LogicalResult 2044 matchAndRewrite(fir::UnboxCharOp unboxchar, OpAdaptor adaptor, 2045 mlir::ConversionPatternRewriter &rewriter) const override { 2046 MLIRContext *ctx = unboxchar.getContext(); 2047 2048 mlir::Type lenTy = convertType(unboxchar.getType(1)); 2049 mlir::Value tuple = adaptor.getOperands()[0]; 2050 mlir::Type tupleTy = tuple.getType(); 2051 2052 mlir::Location loc = unboxchar.getLoc(); 2053 mlir::Value ptrToBuffer = 2054 genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 0); 2055 2056 mlir::LLVM::ExtractValueOp len = 2057 genExtractValueWithIndex(loc, tuple, tupleTy, rewriter, ctx, 1); 2058 mlir::Value lenAfterCast = integerCast(loc, rewriter, lenTy, len); 2059 2060 rewriter.replaceOp(unboxchar, 2061 ArrayRef<mlir::Value>{ptrToBuffer, lenAfterCast}); 2062 return success(); 2063 } 2064 }; 2065 2066 /// Lower `fir.unboxproc` operation. Unbox a procedure box value, yielding its 2067 /// components. 2068 /// TODO: Part of supporting Fortran 2003 procedure pointers. 2069 struct UnboxProcOpConversion : public FIROpConversion<fir::UnboxProcOp> { 2070 using FIROpConversion::FIROpConversion; 2071 2072 mlir::LogicalResult 2073 matchAndRewrite(fir::UnboxProcOp unboxproc, OpAdaptor adaptor, 2074 mlir::ConversionPatternRewriter &rewriter) const override { 2075 return rewriter.notifyMatchFailure( 2076 unboxproc, "fir.unboxproc codegen is not implemented yet"); 2077 } 2078 }; 2079 2080 } // namespace 2081 2082 namespace { 2083 /// Convert FIR dialect to LLVM dialect 2084 /// 2085 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An 2086 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect. 2087 /// 2088 /// This pass is not complete yet. We are upstreaming it in small patches. 2089 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> { 2090 public: 2091 mlir::ModuleOp getModule() { return getOperation(); } 2092 2093 void runOnOperation() override final { 2094 auto mod = getModule(); 2095 if (!forcedTargetTriple.empty()) { 2096 fir::setTargetTriple(mod, forcedTargetTriple); 2097 } 2098 2099 auto *context = getModule().getContext(); 2100 fir::LLVMTypeConverter typeConverter{getModule()}; 2101 mlir::OwningRewritePatternList pattern(context); 2102 pattern.insert< 2103 AbsentOpConversion, AddcOpConversion, AddrOfOpConversion, 2104 AllocaOpConversion, BoxAddrOpConversion, BoxCharLenOpConversion, 2105 BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion, 2106 BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxProcHostOpConversion, 2107 BoxRankOpConversion, BoxTypeDescOpConversion, CallOpConversion, 2108 CmpcOpConversion, ConstcOpConversion, ConvertOpConversion, 2109 DispatchOpConversion, DispatchTableOpConversion, DTEntryOpConversion, 2110 DivcOpConversion, EmboxOpConversion, EmboxCharOpConversion, 2111 EmboxProcOpConversion, ExtractValueOpConversion, HasValueOpConversion, 2112 GenTypeDescOpConversion, GlobalLenOpConversion, GlobalOpConversion, 2113 InsertOnRangeOpConversion, InsertValueOpConversion, 2114 IsPresentOpConversion, LoadOpConversion, NegcOpConversion, 2115 MulcOpConversion, SelectCaseOpConversion, SelectOpConversion, 2116 SelectRankOpConversion, SelectTypeOpConversion, ShapeOpConversion, 2117 ShapeShiftOpConversion, ShiftOpConversion, SliceOpConversion, 2118 StoreOpConversion, StringLitOpConversion, SubcOpConversion, 2119 UnboxCharOpConversion, UnboxProcOpConversion, UndefOpConversion, 2120 UnreachableOpConversion, ZeroOpConversion>(typeConverter); 2121 mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern); 2122 mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter, 2123 pattern); 2124 mlir::ConversionTarget target{*context}; 2125 target.addLegalDialect<mlir::LLVM::LLVMDialect>(); 2126 2127 // required NOPs for applying a full conversion 2128 target.addLegalOp<mlir::ModuleOp>(); 2129 2130 // apply the patterns 2131 if (mlir::failed(mlir::applyFullConversion(getModule(), target, 2132 std::move(pattern)))) { 2133 signalPassFailure(); 2134 } 2135 } 2136 }; 2137 } // namespace 2138 2139 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() { 2140 return std::make_unique<FIRToLLVMLowering>(); 2141 } 2142