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