1044d5b5dSValentin Clement //===-- CodeGen.cpp -- bridge to lower to LLVM ----------------------------===// 2044d5b5dSValentin Clement // 3044d5b5dSValentin Clement // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4044d5b5dSValentin Clement // See https://llvm.org/LICENSE.txt for license information. 5044d5b5dSValentin Clement // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6044d5b5dSValentin Clement // 7044d5b5dSValentin Clement //===----------------------------------------------------------------------===// 8044d5b5dSValentin Clement // 9044d5b5dSValentin Clement // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ 10044d5b5dSValentin Clement // 11044d5b5dSValentin Clement //===----------------------------------------------------------------------===// 12044d5b5dSValentin Clement 13044d5b5dSValentin Clement #include "flang/Optimizer/CodeGen/CodeGen.h" 14044d5b5dSValentin Clement #include "PassDetail.h" 15*b6e44ecdSValentin Clement #include "flang/ISO_Fortran_binding.h" 16044d5b5dSValentin Clement #include "flang/Optimizer/Dialect/FIROps.h" 17044d5b5dSValentin Clement #include "flang/Optimizer/Dialect/FIRType.h" 187b5132daSValentin Clement #include "flang/Optimizer/Support/FIRContext.h" 19044d5b5dSValentin Clement #include "mlir/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.h" 20044d5b5dSValentin Clement #include "mlir/Conversion/LLVMCommon/Pattern.h" 21044d5b5dSValentin Clement #include "mlir/Conversion/LLVMCommon/TypeConverter.h" 22044d5b5dSValentin Clement #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h" 23044d5b5dSValentin Clement #include "mlir/IR/BuiltinTypes.h" 243ae8e442SValentin Clement #include "mlir/IR/Matchers.h" 25044d5b5dSValentin Clement #include "mlir/Pass/Pass.h" 26044d5b5dSValentin Clement #include "llvm/ADT/ArrayRef.h" 27044d5b5dSValentin Clement 28044d5b5dSValentin Clement #define DEBUG_TYPE "flang-codegen" 29044d5b5dSValentin Clement 30044d5b5dSValentin Clement // fir::LLVMTypeConverter for converting to LLVM IR dialect types. 31044d5b5dSValentin Clement #include "TypeConverter.h" 32044d5b5dSValentin Clement 33*b6e44ecdSValentin Clement /// `fir.box` attribute values as defined for CFI_attribute_t in 34*b6e44ecdSValentin Clement /// flang/ISO_Fortran_binding.h. 35*b6e44ecdSValentin Clement static constexpr unsigned kAttrPointer = CFI_attribute_pointer; 36*b6e44ecdSValentin Clement static constexpr unsigned kAttrAllocatable = CFI_attribute_allocatable; 37*b6e44ecdSValentin Clement 38044d5b5dSValentin Clement namespace { 39044d5b5dSValentin Clement /// FIR conversion pattern template 40044d5b5dSValentin Clement template <typename FromOp> 41044d5b5dSValentin Clement class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> { 42044d5b5dSValentin Clement public: 43044d5b5dSValentin Clement explicit FIROpConversion(fir::LLVMTypeConverter &lowering) 44044d5b5dSValentin Clement : mlir::ConvertOpToLLVMPattern<FromOp>(lowering) {} 45044d5b5dSValentin Clement 46044d5b5dSValentin Clement protected: 47044d5b5dSValentin Clement mlir::Type convertType(mlir::Type ty) const { 48044d5b5dSValentin Clement return lowerTy().convertType(ty); 49044d5b5dSValentin Clement } 50044d5b5dSValentin Clement 51df3b9810SValentin Clement mlir::LLVM::ConstantOp 52df3b9810SValentin Clement genConstantOffset(mlir::Location loc, 53df3b9810SValentin Clement mlir::ConversionPatternRewriter &rewriter, 54df3b9810SValentin Clement int offset) const { 55df3b9810SValentin Clement auto ity = lowerTy().offsetType(); 56df3b9810SValentin Clement auto cattr = rewriter.getI32IntegerAttr(offset); 57df3b9810SValentin Clement return rewriter.create<mlir::LLVM::ConstantOp>(loc, ity, cattr); 58df3b9810SValentin Clement } 59df3b9810SValentin Clement 60*b6e44ecdSValentin Clement /// Construct code sequence to extract the specifc value from a `fir.box`. 61*b6e44ecdSValentin Clement mlir::Value getValueFromBox(mlir::Location loc, mlir::Value box, 62df3b9810SValentin Clement mlir::Type resultTy, 63*b6e44ecdSValentin Clement mlir::ConversionPatternRewriter &rewriter, 64*b6e44ecdSValentin Clement unsigned boxValue) const { 65df3b9810SValentin Clement mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 66*b6e44ecdSValentin Clement mlir::LLVM::ConstantOp cValuePos = 67*b6e44ecdSValentin Clement genConstantOffset(loc, rewriter, boxValue); 68df3b9810SValentin Clement auto pty = mlir::LLVM::LLVMPointerType::get(resultTy); 69df3b9810SValentin Clement auto p = rewriter.create<mlir::LLVM::GEPOp>( 70*b6e44ecdSValentin Clement loc, pty, mlir::ValueRange{box, c0, cValuePos}); 71df3b9810SValentin Clement return rewriter.create<mlir::LLVM::LoadOp>(loc, resultTy, p); 72df3b9810SValentin Clement } 73df3b9810SValentin Clement 74df3b9810SValentin Clement /// Method to construct code sequence to get the triple for dimension `dim` 75df3b9810SValentin Clement /// from a box. 76df3b9810SValentin Clement SmallVector<mlir::Value, 3> 77df3b9810SValentin Clement getDimsFromBox(mlir::Location loc, ArrayRef<mlir::Type> retTys, 78df3b9810SValentin Clement mlir::Value box, mlir::Value dim, 79df3b9810SValentin Clement mlir::ConversionPatternRewriter &rewriter) const { 80df3b9810SValentin Clement mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 81df3b9810SValentin Clement mlir::LLVM::ConstantOp cDims = 82df3b9810SValentin Clement genConstantOffset(loc, rewriter, kDimsPosInBox); 83df3b9810SValentin Clement mlir::LLVM::LoadOp l0 = 84df3b9810SValentin Clement loadFromOffset(loc, box, c0, cDims, dim, 0, retTys[0], rewriter); 85df3b9810SValentin Clement mlir::LLVM::LoadOp l1 = 86df3b9810SValentin Clement loadFromOffset(loc, box, c0, cDims, dim, 1, retTys[1], rewriter); 87df3b9810SValentin Clement mlir::LLVM::LoadOp l2 = 88df3b9810SValentin Clement loadFromOffset(loc, box, c0, cDims, dim, 2, retTys[2], rewriter); 89df3b9810SValentin Clement return {l0.getResult(), l1.getResult(), l2.getResult()}; 90df3b9810SValentin Clement } 91df3b9810SValentin Clement 92df3b9810SValentin Clement mlir::LLVM::LoadOp 93df3b9810SValentin Clement loadFromOffset(mlir::Location loc, mlir::Value a, mlir::LLVM::ConstantOp c0, 94df3b9810SValentin Clement mlir::LLVM::ConstantOp cDims, mlir::Value dim, int off, 95df3b9810SValentin Clement mlir::Type ty, 96df3b9810SValentin Clement mlir::ConversionPatternRewriter &rewriter) const { 97df3b9810SValentin Clement auto pty = mlir::LLVM::LLVMPointerType::get(ty); 98df3b9810SValentin Clement mlir::LLVM::ConstantOp c = genConstantOffset(loc, rewriter, off); 99df3b9810SValentin Clement mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, a, c0, cDims, dim, c); 100df3b9810SValentin Clement return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p); 101df3b9810SValentin Clement } 102df3b9810SValentin Clement 103df3b9810SValentin Clement /// Read base address from a fir.box. Returned address has type ty. 104df3b9810SValentin Clement mlir::Value 105df3b9810SValentin Clement loadBaseAddrFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box, 106df3b9810SValentin Clement mlir::ConversionPatternRewriter &rewriter) const { 107df3b9810SValentin Clement mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 108df3b9810SValentin Clement mlir::LLVM::ConstantOp cAddr = 109df3b9810SValentin Clement genConstantOffset(loc, rewriter, kAddrPosInBox); 110df3b9810SValentin Clement auto pty = mlir::LLVM::LLVMPointerType::get(ty); 111df3b9810SValentin Clement mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cAddr); 112df3b9810SValentin Clement return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p); 113df3b9810SValentin Clement } 114df3b9810SValentin Clement 115df3b9810SValentin Clement mlir::Value 116df3b9810SValentin Clement loadElementSizeFromBox(mlir::Location loc, mlir::Type ty, mlir::Value box, 117df3b9810SValentin Clement mlir::ConversionPatternRewriter &rewriter) const { 118df3b9810SValentin Clement mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 119df3b9810SValentin Clement mlir::LLVM::ConstantOp cElemLen = 120df3b9810SValentin Clement genConstantOffset(loc, rewriter, kElemLenPosInBox); 121df3b9810SValentin Clement auto pty = mlir::LLVM::LLVMPointerType::get(ty); 122df3b9810SValentin Clement mlir::LLVM::GEPOp p = genGEP(loc, pty, rewriter, box, c0, cElemLen); 123df3b9810SValentin Clement return rewriter.create<mlir::LLVM::LoadOp>(loc, ty, p); 124df3b9810SValentin Clement } 125df3b9810SValentin Clement 126*b6e44ecdSValentin Clement // Load the attribute from the \p box and perform a check against \p maskValue 127*b6e44ecdSValentin Clement // The final comparison is implemented as `(attribute & maskValue) != 0`. 128*b6e44ecdSValentin Clement mlir::Value genBoxAttributeCheck(mlir::Location loc, mlir::Value box, 129*b6e44ecdSValentin Clement mlir::ConversionPatternRewriter &rewriter, 130*b6e44ecdSValentin Clement unsigned maskValue) const { 131*b6e44ecdSValentin Clement mlir::Type attrTy = rewriter.getI32Type(); 132*b6e44ecdSValentin Clement mlir::Value attribute = 133*b6e44ecdSValentin Clement getValueFromBox(loc, box, attrTy, rewriter, kAttributePosInBox); 134*b6e44ecdSValentin Clement mlir::LLVM::ConstantOp attrMask = 135*b6e44ecdSValentin Clement genConstantOffset(loc, rewriter, maskValue); 136*b6e44ecdSValentin Clement auto maskRes = 137*b6e44ecdSValentin Clement rewriter.create<mlir::LLVM::AndOp>(loc, attrTy, attribute, attrMask); 138*b6e44ecdSValentin Clement mlir::LLVM::ConstantOp c0 = genConstantOffset(loc, rewriter, 0); 139*b6e44ecdSValentin Clement return rewriter.create<mlir::LLVM::ICmpOp>( 140*b6e44ecdSValentin Clement loc, mlir::LLVM::ICmpPredicate::ne, maskRes, c0); 141*b6e44ecdSValentin Clement } 142*b6e44ecdSValentin Clement 143df3b9810SValentin Clement template <typename... ARGS> 144df3b9810SValentin Clement mlir::LLVM::GEPOp genGEP(mlir::Location loc, mlir::Type ty, 145df3b9810SValentin Clement mlir::ConversionPatternRewriter &rewriter, 146df3b9810SValentin Clement mlir::Value base, ARGS... args) const { 147df3b9810SValentin Clement SmallVector<mlir::Value> cv{args...}; 148df3b9810SValentin Clement return rewriter.create<mlir::LLVM::GEPOp>(loc, ty, base, cv); 149df3b9810SValentin Clement } 150df3b9810SValentin Clement 151044d5b5dSValentin Clement fir::LLVMTypeConverter &lowerTy() const { 152044d5b5dSValentin Clement return *static_cast<fir::LLVMTypeConverter *>(this->getTypeConverter()); 153044d5b5dSValentin Clement } 154044d5b5dSValentin Clement }; 155044d5b5dSValentin Clement 1563ae8e442SValentin Clement /// FIR conversion pattern template 1573ae8e442SValentin Clement template <typename FromOp> 1583ae8e442SValentin Clement class FIROpAndTypeConversion : public FIROpConversion<FromOp> { 1593ae8e442SValentin Clement public: 1603ae8e442SValentin Clement using FIROpConversion<FromOp>::FIROpConversion; 1613ae8e442SValentin Clement using OpAdaptor = typename FromOp::Adaptor; 1623ae8e442SValentin Clement 1633ae8e442SValentin Clement mlir::LogicalResult 1643ae8e442SValentin Clement matchAndRewrite(FromOp op, OpAdaptor adaptor, 1653ae8e442SValentin Clement mlir::ConversionPatternRewriter &rewriter) const final { 1663ae8e442SValentin Clement mlir::Type ty = this->convertType(op.getType()); 1673ae8e442SValentin Clement return doRewrite(op, ty, adaptor, rewriter); 1683ae8e442SValentin Clement } 1693ae8e442SValentin Clement 1703ae8e442SValentin Clement virtual mlir::LogicalResult 1713ae8e442SValentin Clement doRewrite(FromOp addr, mlir::Type ty, OpAdaptor adaptor, 1723ae8e442SValentin Clement mlir::ConversionPatternRewriter &rewriter) const = 0; 1733ae8e442SValentin Clement }; 1743ae8e442SValentin Clement 1750c4a7a52SValentin Clement // Lower `fir.address_of` operation to `llvm.address_of` operation. 176044d5b5dSValentin Clement struct AddrOfOpConversion : public FIROpConversion<fir::AddrOfOp> { 177044d5b5dSValentin Clement using FIROpConversion::FIROpConversion; 178044d5b5dSValentin Clement 179044d5b5dSValentin Clement mlir::LogicalResult 180044d5b5dSValentin Clement matchAndRewrite(fir::AddrOfOp addr, OpAdaptor adaptor, 181044d5b5dSValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 182044d5b5dSValentin Clement auto ty = convertType(addr.getType()); 183044d5b5dSValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>( 184044d5b5dSValentin Clement addr, ty, addr.symbol().getRootReference().getValue()); 185044d5b5dSValentin Clement return success(); 186044d5b5dSValentin Clement } 187044d5b5dSValentin Clement }; 188044d5b5dSValentin Clement 189df3b9810SValentin Clement /// Lower `fir.box_addr` to the sequence of operations to extract the first 190df3b9810SValentin Clement /// element of the box. 191df3b9810SValentin Clement struct BoxAddrOpConversion : public FIROpConversion<fir::BoxAddrOp> { 192df3b9810SValentin Clement using FIROpConversion::FIROpConversion; 193df3b9810SValentin Clement 194df3b9810SValentin Clement mlir::LogicalResult 195df3b9810SValentin Clement matchAndRewrite(fir::BoxAddrOp boxaddr, OpAdaptor adaptor, 196df3b9810SValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 197df3b9810SValentin Clement mlir::Value a = adaptor.getOperands()[0]; 198df3b9810SValentin Clement auto loc = boxaddr.getLoc(); 199df3b9810SValentin Clement mlir::Type ty = convertType(boxaddr.getType()); 200df3b9810SValentin Clement if (auto argty = boxaddr.val().getType().dyn_cast<fir::BoxType>()) { 201df3b9810SValentin Clement rewriter.replaceOp(boxaddr, loadBaseAddrFromBox(loc, ty, a, rewriter)); 202df3b9810SValentin Clement } else { 203df3b9810SValentin Clement auto c0attr = rewriter.getI32IntegerAttr(0); 204df3b9810SValentin Clement auto c0 = mlir::ArrayAttr::get(boxaddr.getContext(), c0attr); 205df3b9810SValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(boxaddr, ty, a, 206df3b9810SValentin Clement c0); 207df3b9810SValentin Clement } 208df3b9810SValentin Clement return success(); 209df3b9810SValentin Clement } 210df3b9810SValentin Clement }; 211df3b9810SValentin Clement 212df3b9810SValentin Clement /// Lower `fir.box_dims` to a sequence of operations to extract the requested 213df3b9810SValentin Clement /// dimension infomartion from the boxed value. 214df3b9810SValentin Clement /// Result in a triple set of GEPs and loads. 215df3b9810SValentin Clement struct BoxDimsOpConversion : public FIROpConversion<fir::BoxDimsOp> { 216df3b9810SValentin Clement using FIROpConversion::FIROpConversion; 217df3b9810SValentin Clement 218df3b9810SValentin Clement mlir::LogicalResult 219df3b9810SValentin Clement matchAndRewrite(fir::BoxDimsOp boxdims, OpAdaptor adaptor, 220df3b9810SValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 221df3b9810SValentin Clement SmallVector<mlir::Type, 3> resultTypes = { 222df3b9810SValentin Clement convertType(boxdims.getResult(0).getType()), 223df3b9810SValentin Clement convertType(boxdims.getResult(1).getType()), 224df3b9810SValentin Clement convertType(boxdims.getResult(2).getType()), 225df3b9810SValentin Clement }; 226df3b9810SValentin Clement auto results = 227df3b9810SValentin Clement getDimsFromBox(boxdims.getLoc(), resultTypes, adaptor.getOperands()[0], 228df3b9810SValentin Clement adaptor.getOperands()[1], rewriter); 229df3b9810SValentin Clement rewriter.replaceOp(boxdims, results); 230df3b9810SValentin Clement return success(); 231df3b9810SValentin Clement } 232df3b9810SValentin Clement }; 233df3b9810SValentin Clement 234df3b9810SValentin Clement /// Lower `fir.box_elesize` to a sequence of operations ro extract the size of 235df3b9810SValentin Clement /// an element in the boxed value. 236df3b9810SValentin Clement struct BoxEleSizeOpConversion : public FIROpConversion<fir::BoxEleSizeOp> { 237df3b9810SValentin Clement using FIROpConversion::FIROpConversion; 238df3b9810SValentin Clement 239df3b9810SValentin Clement mlir::LogicalResult 240df3b9810SValentin Clement matchAndRewrite(fir::BoxEleSizeOp boxelesz, OpAdaptor adaptor, 241df3b9810SValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 242df3b9810SValentin Clement mlir::Value a = adaptor.getOperands()[0]; 243df3b9810SValentin Clement auto loc = boxelesz.getLoc(); 244df3b9810SValentin Clement auto ty = convertType(boxelesz.getType()); 245*b6e44ecdSValentin Clement auto elemSize = getValueFromBox(loc, a, ty, rewriter, kElemLenPosInBox); 246*b6e44ecdSValentin Clement rewriter.replaceOp(boxelesz, elemSize); 247*b6e44ecdSValentin Clement return success(); 248*b6e44ecdSValentin Clement } 249*b6e44ecdSValentin Clement }; 250*b6e44ecdSValentin Clement 251*b6e44ecdSValentin Clement /// Lower `fir.box_isalloc` to a sequence of operations to determine if the 252*b6e44ecdSValentin Clement /// boxed value was from an ALLOCATABLE entity. 253*b6e44ecdSValentin Clement struct BoxIsAllocOpConversion : public FIROpConversion<fir::BoxIsAllocOp> { 254*b6e44ecdSValentin Clement using FIROpConversion::FIROpConversion; 255*b6e44ecdSValentin Clement 256*b6e44ecdSValentin Clement mlir::LogicalResult 257*b6e44ecdSValentin Clement matchAndRewrite(fir::BoxIsAllocOp boxisalloc, OpAdaptor adaptor, 258*b6e44ecdSValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 259*b6e44ecdSValentin Clement mlir::Value box = adaptor.getOperands()[0]; 260*b6e44ecdSValentin Clement auto loc = boxisalloc.getLoc(); 261*b6e44ecdSValentin Clement mlir::Value check = 262*b6e44ecdSValentin Clement genBoxAttributeCheck(loc, box, rewriter, kAttrAllocatable); 263*b6e44ecdSValentin Clement rewriter.replaceOp(boxisalloc, check); 264*b6e44ecdSValentin Clement return success(); 265*b6e44ecdSValentin Clement } 266*b6e44ecdSValentin Clement }; 267*b6e44ecdSValentin Clement 268*b6e44ecdSValentin Clement /// Lower `fir.box_isarray` to a sequence of operations to determine if the 269*b6e44ecdSValentin Clement /// boxed is an array. 270*b6e44ecdSValentin Clement struct BoxIsArrayOpConversion : public FIROpConversion<fir::BoxIsArrayOp> { 271*b6e44ecdSValentin Clement using FIROpConversion::FIROpConversion; 272*b6e44ecdSValentin Clement 273*b6e44ecdSValentin Clement mlir::LogicalResult 274*b6e44ecdSValentin Clement matchAndRewrite(fir::BoxIsArrayOp boxisarray, OpAdaptor adaptor, 275*b6e44ecdSValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 276*b6e44ecdSValentin Clement mlir::Value a = adaptor.getOperands()[0]; 277*b6e44ecdSValentin Clement auto loc = boxisarray.getLoc(); 278*b6e44ecdSValentin Clement auto rank = 279*b6e44ecdSValentin Clement getValueFromBox(loc, a, rewriter.getI32Type(), rewriter, kRankPosInBox); 280*b6e44ecdSValentin Clement auto c0 = genConstantOffset(loc, rewriter, 0); 281*b6e44ecdSValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::ICmpOp>( 282*b6e44ecdSValentin Clement boxisarray, mlir::LLVM::ICmpPredicate::ne, rank, c0); 283*b6e44ecdSValentin Clement return success(); 284*b6e44ecdSValentin Clement } 285*b6e44ecdSValentin Clement }; 286*b6e44ecdSValentin Clement 287*b6e44ecdSValentin Clement /// Lower `fir.box_isptr` to a sequence of operations to determined if the 288*b6e44ecdSValentin Clement /// boxed value was from a POINTER entity. 289*b6e44ecdSValentin Clement struct BoxIsPtrOpConversion : public FIROpConversion<fir::BoxIsPtrOp> { 290*b6e44ecdSValentin Clement using FIROpConversion::FIROpConversion; 291*b6e44ecdSValentin Clement 292*b6e44ecdSValentin Clement mlir::LogicalResult 293*b6e44ecdSValentin Clement matchAndRewrite(fir::BoxIsPtrOp boxisptr, OpAdaptor adaptor, 294*b6e44ecdSValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 295*b6e44ecdSValentin Clement mlir::Value box = adaptor.getOperands()[0]; 296*b6e44ecdSValentin Clement auto loc = boxisptr.getLoc(); 297*b6e44ecdSValentin Clement mlir::Value check = genBoxAttributeCheck(loc, box, rewriter, kAttrPointer); 298*b6e44ecdSValentin Clement rewriter.replaceOp(boxisptr, check); 299df3b9810SValentin Clement return success(); 300df3b9810SValentin Clement } 301df3b9810SValentin Clement }; 302df3b9810SValentin Clement 303df3b9810SValentin Clement /// Lower `fir.box_rank` to the sequence of operation to extract the rank from 304df3b9810SValentin Clement /// the box. 305df3b9810SValentin Clement struct BoxRankOpConversion : public FIROpConversion<fir::BoxRankOp> { 306df3b9810SValentin Clement using FIROpConversion::FIROpConversion; 307df3b9810SValentin Clement 308df3b9810SValentin Clement mlir::LogicalResult 309df3b9810SValentin Clement matchAndRewrite(fir::BoxRankOp boxrank, OpAdaptor adaptor, 310df3b9810SValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 311df3b9810SValentin Clement mlir::Value a = adaptor.getOperands()[0]; 312df3b9810SValentin Clement auto loc = boxrank.getLoc(); 313df3b9810SValentin Clement mlir::Type ty = convertType(boxrank.getType()); 314*b6e44ecdSValentin Clement auto result = getValueFromBox(loc, a, ty, rewriter, kRankPosInBox); 315df3b9810SValentin Clement rewriter.replaceOp(boxrank, result); 316df3b9810SValentin Clement return success(); 317df3b9810SValentin Clement } 318df3b9810SValentin Clement }; 319df3b9810SValentin Clement 320ddd11b9aSAndrzej Warzynski // `fir.call` -> `llvm.call` 321ddd11b9aSAndrzej Warzynski struct CallOpConversion : public FIROpConversion<fir::CallOp> { 322ddd11b9aSAndrzej Warzynski using FIROpConversion::FIROpConversion; 323ddd11b9aSAndrzej Warzynski 324ddd11b9aSAndrzej Warzynski mlir::LogicalResult 325ddd11b9aSAndrzej Warzynski matchAndRewrite(fir::CallOp call, OpAdaptor adaptor, 326ddd11b9aSAndrzej Warzynski mlir::ConversionPatternRewriter &rewriter) const override { 327ddd11b9aSAndrzej Warzynski SmallVector<mlir::Type> resultTys; 328ddd11b9aSAndrzej Warzynski for (auto r : call.getResults()) 329ddd11b9aSAndrzej Warzynski resultTys.push_back(convertType(r.getType())); 330ddd11b9aSAndrzej Warzynski rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>( 331ddd11b9aSAndrzej Warzynski call, resultTys, adaptor.getOperands(), call->getAttrs()); 332ddd11b9aSAndrzej Warzynski return success(); 333ddd11b9aSAndrzej Warzynski } 334ddd11b9aSAndrzej Warzynski }; 335ddd11b9aSAndrzej Warzynski 336092cee5fSValentin Clement static mlir::Type getComplexEleTy(mlir::Type complex) { 337092cee5fSValentin Clement if (auto cc = complex.dyn_cast<mlir::ComplexType>()) 338092cee5fSValentin Clement return cc.getElementType(); 339092cee5fSValentin Clement return complex.cast<fir::ComplexType>().getElementType(); 340092cee5fSValentin Clement } 341092cee5fSValentin Clement 342092cee5fSValentin Clement /// convert value of from-type to value of to-type 343092cee5fSValentin Clement struct ConvertOpConversion : public FIROpConversion<fir::ConvertOp> { 344092cee5fSValentin Clement using FIROpConversion::FIROpConversion; 345092cee5fSValentin Clement 346092cee5fSValentin Clement static bool isFloatingPointTy(mlir::Type ty) { 347092cee5fSValentin Clement return ty.isa<mlir::FloatType>(); 348092cee5fSValentin Clement } 349092cee5fSValentin Clement 350092cee5fSValentin Clement mlir::LogicalResult 351092cee5fSValentin Clement matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor, 352092cee5fSValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 353092cee5fSValentin Clement auto fromTy = convertType(convert.value().getType()); 354092cee5fSValentin Clement auto toTy = convertType(convert.res().getType()); 355092cee5fSValentin Clement mlir::Value op0 = adaptor.getOperands()[0]; 356092cee5fSValentin Clement if (fromTy == toTy) { 357092cee5fSValentin Clement rewriter.replaceOp(convert, op0); 358092cee5fSValentin Clement return success(); 359092cee5fSValentin Clement } 360092cee5fSValentin Clement auto loc = convert.getLoc(); 361092cee5fSValentin Clement auto convertFpToFp = [&](mlir::Value val, unsigned fromBits, 362092cee5fSValentin Clement unsigned toBits, mlir::Type toTy) -> mlir::Value { 363092cee5fSValentin Clement if (fromBits == toBits) { 364092cee5fSValentin Clement // TODO: Converting between two floating-point representations with the 365092cee5fSValentin Clement // same bitwidth is not allowed for now. 366092cee5fSValentin Clement mlir::emitError(loc, 367092cee5fSValentin Clement "cannot implicitly convert between two floating-point " 368092cee5fSValentin Clement "representations of the same bitwidth"); 369092cee5fSValentin Clement return {}; 370092cee5fSValentin Clement } 371092cee5fSValentin Clement if (fromBits > toBits) 372092cee5fSValentin Clement return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val); 373092cee5fSValentin Clement return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val); 374092cee5fSValentin Clement }; 375092cee5fSValentin Clement // Complex to complex conversion. 376092cee5fSValentin Clement if (fir::isa_complex(convert.value().getType()) && 377092cee5fSValentin Clement fir::isa_complex(convert.res().getType())) { 378092cee5fSValentin Clement // Special case: handle the conversion of a complex such that both the 379092cee5fSValentin Clement // real and imaginary parts are converted together. 380092cee5fSValentin Clement auto zero = mlir::ArrayAttr::get(convert.getContext(), 381092cee5fSValentin Clement rewriter.getI32IntegerAttr(0)); 382092cee5fSValentin Clement auto one = mlir::ArrayAttr::get(convert.getContext(), 383092cee5fSValentin Clement rewriter.getI32IntegerAttr(1)); 384092cee5fSValentin Clement auto ty = convertType(getComplexEleTy(convert.value().getType())); 385092cee5fSValentin Clement auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, zero); 386092cee5fSValentin Clement auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, one); 387092cee5fSValentin Clement auto nt = convertType(getComplexEleTy(convert.res().getType())); 388092cee5fSValentin Clement auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty); 389092cee5fSValentin Clement auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt); 390092cee5fSValentin Clement auto rc = convertFpToFp(rp, fromBits, toBits, nt); 391092cee5fSValentin Clement auto ic = convertFpToFp(ip, fromBits, toBits, nt); 392092cee5fSValentin Clement auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy); 393092cee5fSValentin Clement auto i1 = 394092cee5fSValentin Clement rewriter.create<mlir::LLVM::InsertValueOp>(loc, toTy, un, rc, zero); 395092cee5fSValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, toTy, i1, 396092cee5fSValentin Clement ic, one); 397092cee5fSValentin Clement return mlir::success(); 398092cee5fSValentin Clement } 399092cee5fSValentin Clement // Floating point to floating point conversion. 400092cee5fSValentin Clement if (isFloatingPointTy(fromTy)) { 401092cee5fSValentin Clement if (isFloatingPointTy(toTy)) { 402092cee5fSValentin Clement auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy); 403092cee5fSValentin Clement auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy); 404092cee5fSValentin Clement auto v = convertFpToFp(op0, fromBits, toBits, toTy); 405092cee5fSValentin Clement rewriter.replaceOp(convert, v); 406092cee5fSValentin Clement return mlir::success(); 407092cee5fSValentin Clement } 408092cee5fSValentin Clement if (toTy.isa<mlir::IntegerType>()) { 409092cee5fSValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0); 410092cee5fSValentin Clement return mlir::success(); 411092cee5fSValentin Clement } 412092cee5fSValentin Clement } else if (fromTy.isa<mlir::IntegerType>()) { 413092cee5fSValentin Clement // Integer to integer conversion. 414092cee5fSValentin Clement if (toTy.isa<mlir::IntegerType>()) { 415092cee5fSValentin Clement auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy); 416092cee5fSValentin Clement auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy); 417092cee5fSValentin Clement assert(fromBits != toBits); 418092cee5fSValentin Clement if (fromBits > toBits) { 419092cee5fSValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0); 420092cee5fSValentin Clement return mlir::success(); 421092cee5fSValentin Clement } 422092cee5fSValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0); 423092cee5fSValentin Clement return mlir::success(); 424092cee5fSValentin Clement } 425092cee5fSValentin Clement // Integer to floating point conversion. 426092cee5fSValentin Clement if (isFloatingPointTy(toTy)) { 427092cee5fSValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0); 428092cee5fSValentin Clement return mlir::success(); 429092cee5fSValentin Clement } 430092cee5fSValentin Clement // Integer to pointer conversion. 431092cee5fSValentin Clement if (toTy.isa<mlir::LLVM::LLVMPointerType>()) { 432092cee5fSValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0); 433092cee5fSValentin Clement return mlir::success(); 434092cee5fSValentin Clement } 435092cee5fSValentin Clement } else if (fromTy.isa<mlir::LLVM::LLVMPointerType>()) { 436092cee5fSValentin Clement // Pointer to integer conversion. 437092cee5fSValentin Clement if (toTy.isa<mlir::IntegerType>()) { 438092cee5fSValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0); 439092cee5fSValentin Clement return mlir::success(); 440092cee5fSValentin Clement } 441092cee5fSValentin Clement // Pointer to pointer conversion. 442092cee5fSValentin Clement if (toTy.isa<mlir::LLVM::LLVMPointerType>()) { 443092cee5fSValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0); 444092cee5fSValentin Clement return mlir::success(); 445092cee5fSValentin Clement } 446092cee5fSValentin Clement } 447092cee5fSValentin Clement return emitError(loc) << "cannot convert " << fromTy << " to " << toTy; 448092cee5fSValentin Clement } 449092cee5fSValentin Clement }; 450092cee5fSValentin Clement 4510c4a7a52SValentin Clement /// Lower `fir.has_value` operation to `llvm.return` operation. 452044d5b5dSValentin Clement struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> { 453044d5b5dSValentin Clement using FIROpConversion::FIROpConversion; 454044d5b5dSValentin Clement 455044d5b5dSValentin Clement mlir::LogicalResult 456044d5b5dSValentin Clement matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor, 457044d5b5dSValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 458044d5b5dSValentin Clement rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands()); 459044d5b5dSValentin Clement return success(); 460044d5b5dSValentin Clement } 461044d5b5dSValentin Clement }; 462044d5b5dSValentin Clement 4630c4a7a52SValentin Clement /// Lower `fir.global` operation to `llvm.global` operation. 4640c4a7a52SValentin Clement /// `fir.insert_on_range` operations are replaced with constant dense attribute 4650c4a7a52SValentin Clement /// if they are applied on the full range. 466044d5b5dSValentin Clement struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> { 467044d5b5dSValentin Clement using FIROpConversion::FIROpConversion; 468044d5b5dSValentin Clement 469044d5b5dSValentin Clement mlir::LogicalResult 470044d5b5dSValentin Clement matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor, 471044d5b5dSValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 472044d5b5dSValentin Clement auto tyAttr = convertType(global.getType()); 473044d5b5dSValentin Clement if (global.getType().isa<fir::BoxType>()) 474044d5b5dSValentin Clement tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType(); 475044d5b5dSValentin Clement auto loc = global.getLoc(); 476044d5b5dSValentin Clement mlir::Attribute initAttr{}; 477044d5b5dSValentin Clement if (global.initVal()) 478044d5b5dSValentin Clement initAttr = global.initVal().getValue(); 479044d5b5dSValentin Clement auto linkage = convertLinkage(global.linkName()); 480044d5b5dSValentin Clement auto isConst = global.constant().hasValue(); 481044d5b5dSValentin Clement auto g = rewriter.create<mlir::LLVM::GlobalOp>( 482044d5b5dSValentin Clement loc, tyAttr, isConst, linkage, global.sym_name(), initAttr); 483044d5b5dSValentin Clement auto &gr = g.getInitializerRegion(); 484044d5b5dSValentin Clement rewriter.inlineRegionBefore(global.region(), gr, gr.end()); 485044d5b5dSValentin Clement if (!gr.empty()) { 486044d5b5dSValentin Clement // Replace insert_on_range with a constant dense attribute if the 487044d5b5dSValentin Clement // initialization is on the full range. 488044d5b5dSValentin Clement auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>(); 489044d5b5dSValentin Clement for (auto insertOp : insertOnRangeOps) { 490044d5b5dSValentin Clement if (isFullRange(insertOp.coor(), insertOp.getType())) { 491044d5b5dSValentin Clement auto seqTyAttr = convertType(insertOp.getType()); 492044d5b5dSValentin Clement auto *op = insertOp.val().getDefiningOp(); 493044d5b5dSValentin Clement auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op); 494044d5b5dSValentin Clement if (!constant) { 495044d5b5dSValentin Clement auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op); 496044d5b5dSValentin Clement if (!convertOp) 497044d5b5dSValentin Clement continue; 498044d5b5dSValentin Clement constant = cast<mlir::arith::ConstantOp>( 499044d5b5dSValentin Clement convertOp.value().getDefiningOp()); 500044d5b5dSValentin Clement } 501044d5b5dSValentin Clement mlir::Type vecType = mlir::VectorType::get( 502044d5b5dSValentin Clement insertOp.getType().getShape(), constant.getType()); 503044d5b5dSValentin Clement auto denseAttr = mlir::DenseElementsAttr::get( 504044d5b5dSValentin Clement vecType.cast<ShapedType>(), constant.value()); 505044d5b5dSValentin Clement rewriter.setInsertionPointAfter(insertOp); 506044d5b5dSValentin Clement rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>( 507044d5b5dSValentin Clement insertOp, seqTyAttr, denseAttr); 508044d5b5dSValentin Clement } 509044d5b5dSValentin Clement } 510044d5b5dSValentin Clement } 511044d5b5dSValentin Clement rewriter.eraseOp(global); 512044d5b5dSValentin Clement return success(); 513044d5b5dSValentin Clement } 514044d5b5dSValentin Clement 515044d5b5dSValentin Clement bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const { 516044d5b5dSValentin Clement auto extents = seqTy.getShape(); 517044d5b5dSValentin Clement if (indexes.size() / 2 != extents.size()) 518044d5b5dSValentin Clement return false; 519044d5b5dSValentin Clement for (unsigned i = 0; i < indexes.size(); i += 2) { 520044d5b5dSValentin Clement if (indexes[i].cast<IntegerAttr>().getInt() != 0) 521044d5b5dSValentin Clement return false; 522044d5b5dSValentin Clement if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1) 523044d5b5dSValentin Clement return false; 524044d5b5dSValentin Clement } 525044d5b5dSValentin Clement return true; 526044d5b5dSValentin Clement } 527044d5b5dSValentin Clement 5280c4a7a52SValentin Clement // TODO: String comparaison should be avoided. Replace linkName with an 5290c4a7a52SValentin Clement // enumeration. 530044d5b5dSValentin Clement mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const { 531044d5b5dSValentin Clement if (optLinkage.hasValue()) { 532044d5b5dSValentin Clement auto name = optLinkage.getValue(); 533044d5b5dSValentin Clement if (name == "internal") 534044d5b5dSValentin Clement return mlir::LLVM::Linkage::Internal; 535044d5b5dSValentin Clement if (name == "linkonce") 536044d5b5dSValentin Clement return mlir::LLVM::Linkage::Linkonce; 537044d5b5dSValentin Clement if (name == "common") 538044d5b5dSValentin Clement return mlir::LLVM::Linkage::Common; 539044d5b5dSValentin Clement if (name == "weak") 540044d5b5dSValentin Clement return mlir::LLVM::Linkage::Weak; 541044d5b5dSValentin Clement } 542044d5b5dSValentin Clement return mlir::LLVM::Linkage::External; 543044d5b5dSValentin Clement } 544044d5b5dSValentin Clement }; 545044d5b5dSValentin Clement 5468c239909SValentin Clement template <typename OP> 5478c239909SValentin Clement void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select, 5488c239909SValentin Clement typename OP::Adaptor adaptor, 5498c239909SValentin Clement mlir::ConversionPatternRewriter &rewriter) { 5508c239909SValentin Clement unsigned conds = select.getNumConditions(); 5518c239909SValentin Clement auto cases = select.getCases().getValue(); 5528c239909SValentin Clement mlir::Value selector = adaptor.selector(); 5538c239909SValentin Clement auto loc = select.getLoc(); 5548c239909SValentin Clement assert(conds > 0 && "select must have cases"); 5558c239909SValentin Clement 5568c239909SValentin Clement llvm::SmallVector<mlir::Block *> destinations; 5578c239909SValentin Clement llvm::SmallVector<mlir::ValueRange> destinationsOperands; 5588c239909SValentin Clement mlir::Block *defaultDestination; 5598c239909SValentin Clement mlir::ValueRange defaultOperands; 5608c239909SValentin Clement llvm::SmallVector<int32_t> caseValues; 5618c239909SValentin Clement 5628c239909SValentin Clement for (unsigned t = 0; t != conds; ++t) { 5638c239909SValentin Clement mlir::Block *dest = select.getSuccessor(t); 5648c239909SValentin Clement auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t); 5658c239909SValentin Clement const mlir::Attribute &attr = cases[t]; 5668c239909SValentin Clement if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) { 5678c239909SValentin Clement destinations.push_back(dest); 5688c239909SValentin Clement destinationsOperands.push_back(destOps.hasValue() ? *destOps 5698c239909SValentin Clement : ValueRange()); 5708c239909SValentin Clement caseValues.push_back(intAttr.getInt()); 5718c239909SValentin Clement continue; 5728c239909SValentin Clement } 5738c239909SValentin Clement assert(attr.template dyn_cast_or_null<mlir::UnitAttr>()); 5748c239909SValentin Clement assert((t + 1 == conds) && "unit must be last"); 5758c239909SValentin Clement defaultDestination = dest; 5768c239909SValentin Clement defaultOperands = destOps.hasValue() ? *destOps : ValueRange(); 5778c239909SValentin Clement } 5788c239909SValentin Clement 5798c239909SValentin Clement // LLVM::SwitchOp takes a i32 type for the selector. 5808c239909SValentin Clement if (select.getSelector().getType() != rewriter.getI32Type()) 5818c239909SValentin Clement selector = 5828c239909SValentin Clement rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector); 5838c239909SValentin Clement 5848c239909SValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>( 5858c239909SValentin Clement select, selector, 5868c239909SValentin Clement /*defaultDestination=*/defaultDestination, 5878c239909SValentin Clement /*defaultOperands=*/defaultOperands, 5888c239909SValentin Clement /*caseValues=*/caseValues, 5898c239909SValentin Clement /*caseDestinations=*/destinations, 5908c239909SValentin Clement /*caseOperands=*/destinationsOperands, 5918c239909SValentin Clement /*branchWeights=*/ArrayRef<int32_t>()); 5928c239909SValentin Clement } 5938c239909SValentin Clement 5948c239909SValentin Clement /// conversion of fir::SelectOp to an if-then-else ladder 5958c239909SValentin Clement struct SelectOpConversion : public FIROpConversion<fir::SelectOp> { 5968c239909SValentin Clement using FIROpConversion::FIROpConversion; 5978c239909SValentin Clement 5988c239909SValentin Clement mlir::LogicalResult 5998c239909SValentin Clement matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor, 6008c239909SValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 6018c239909SValentin Clement selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter); 6028c239909SValentin Clement return success(); 6038c239909SValentin Clement } 6048c239909SValentin Clement }; 6058c239909SValentin Clement 606e3349fa1SAndrzej Warzynski /// `fir.load` --> `llvm.load` 607e3349fa1SAndrzej Warzynski struct LoadOpConversion : public FIROpConversion<fir::LoadOp> { 608e3349fa1SAndrzej Warzynski using FIROpConversion::FIROpConversion; 609e3349fa1SAndrzej Warzynski 610e3349fa1SAndrzej Warzynski mlir::LogicalResult 611e3349fa1SAndrzej Warzynski matchAndRewrite(fir::LoadOp load, OpAdaptor adaptor, 612e3349fa1SAndrzej Warzynski mlir::ConversionPatternRewriter &rewriter) const override { 613e3349fa1SAndrzej Warzynski // fir.box is a special case because it is considered as an ssa values in 614e3349fa1SAndrzej Warzynski // fir, but it is lowered as a pointer to a descriptor. So fir.ref<fir.box> 615e3349fa1SAndrzej Warzynski // and fir.box end up being the same llvm types and loading a 616e3349fa1SAndrzej Warzynski // fir.ref<fir.box> is actually a no op in LLVM. 617e3349fa1SAndrzej Warzynski if (load.getType().isa<fir::BoxType>()) { 618e3349fa1SAndrzej Warzynski rewriter.replaceOp(load, adaptor.getOperands()[0]); 619e3349fa1SAndrzej Warzynski } else { 620e3349fa1SAndrzej Warzynski mlir::Type ty = convertType(load.getType()); 621e3349fa1SAndrzej Warzynski ArrayRef<NamedAttribute> at = load->getAttrs(); 622e3349fa1SAndrzej Warzynski rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>( 623e3349fa1SAndrzej Warzynski load, ty, adaptor.getOperands(), at); 624e3349fa1SAndrzej Warzynski } 625e3349fa1SAndrzej Warzynski return success(); 626e3349fa1SAndrzej Warzynski } 627e3349fa1SAndrzej Warzynski }; 628e3349fa1SAndrzej Warzynski 6298c239909SValentin Clement /// conversion of fir::SelectRankOp to an if-then-else ladder 6308c239909SValentin Clement struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> { 6318c239909SValentin Clement using FIROpConversion::FIROpConversion; 6328c239909SValentin Clement 6338c239909SValentin Clement mlir::LogicalResult 6348c239909SValentin Clement matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor, 6358c239909SValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 6368c239909SValentin Clement selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter); 6378c239909SValentin Clement return success(); 6388c239909SValentin Clement } 6398c239909SValentin Clement }; 6408c239909SValentin Clement 641e3349fa1SAndrzej Warzynski /// `fir.store` --> `llvm.store` 642e3349fa1SAndrzej Warzynski struct StoreOpConversion : public FIROpConversion<fir::StoreOp> { 643e3349fa1SAndrzej Warzynski using FIROpConversion::FIROpConversion; 644e3349fa1SAndrzej Warzynski 645e3349fa1SAndrzej Warzynski mlir::LogicalResult 646e3349fa1SAndrzej Warzynski matchAndRewrite(fir::StoreOp store, OpAdaptor adaptor, 647e3349fa1SAndrzej Warzynski mlir::ConversionPatternRewriter &rewriter) const override { 648e3349fa1SAndrzej Warzynski if (store.value().getType().isa<fir::BoxType>()) { 649e3349fa1SAndrzej Warzynski // fir.box value is actually in memory, load it first before storing it. 650e3349fa1SAndrzej Warzynski mlir::Location loc = store.getLoc(); 651e3349fa1SAndrzej Warzynski mlir::Type boxPtrTy = adaptor.getOperands()[0].getType(); 652e3349fa1SAndrzej Warzynski auto val = rewriter.create<mlir::LLVM::LoadOp>( 653e3349fa1SAndrzej Warzynski loc, boxPtrTy.cast<mlir::LLVM::LLVMPointerType>().getElementType(), 654e3349fa1SAndrzej Warzynski adaptor.getOperands()[0]); 655e3349fa1SAndrzej Warzynski rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 656e3349fa1SAndrzej Warzynski store, val, adaptor.getOperands()[1]); 657e3349fa1SAndrzej Warzynski } else { 658e3349fa1SAndrzej Warzynski rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>( 659e3349fa1SAndrzej Warzynski store, adaptor.getOperands()[0], adaptor.getOperands()[1]); 660e3349fa1SAndrzej Warzynski } 661e3349fa1SAndrzej Warzynski return success(); 662e3349fa1SAndrzej Warzynski } 663e3349fa1SAndrzej Warzynski }; 664e3349fa1SAndrzej Warzynski 665e3349fa1SAndrzej Warzynski /// convert to LLVM IR dialect `undef` 666044d5b5dSValentin Clement struct UndefOpConversion : public FIROpConversion<fir::UndefOp> { 667044d5b5dSValentin Clement using FIROpConversion::FIROpConversion; 668044d5b5dSValentin Clement 669044d5b5dSValentin Clement mlir::LogicalResult 670044d5b5dSValentin Clement matchAndRewrite(fir::UndefOp undef, OpAdaptor, 671044d5b5dSValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 672044d5b5dSValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>( 673044d5b5dSValentin Clement undef, convertType(undef.getType())); 674044d5b5dSValentin Clement return success(); 675044d5b5dSValentin Clement } 676044d5b5dSValentin Clement }; 677a7a61359SValentin Clement 678e3349fa1SAndrzej Warzynski /// `fir.unreachable` --> `llvm.unreachable` 67932e08248SAndrzej Warzynski struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> { 68032e08248SAndrzej Warzynski using FIROpConversion::FIROpConversion; 68132e08248SAndrzej Warzynski 68232e08248SAndrzej Warzynski mlir::LogicalResult 68332e08248SAndrzej Warzynski matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor, 68432e08248SAndrzej Warzynski mlir::ConversionPatternRewriter &rewriter) const override { 68532e08248SAndrzej Warzynski rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach); 68632e08248SAndrzej Warzynski return success(); 68732e08248SAndrzej Warzynski } 68832e08248SAndrzej Warzynski }; 68932e08248SAndrzej Warzynski 690a7a61359SValentin Clement struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> { 691a7a61359SValentin Clement using FIROpConversion::FIROpConversion; 692a7a61359SValentin Clement 693a7a61359SValentin Clement mlir::LogicalResult 694a7a61359SValentin Clement matchAndRewrite(fir::ZeroOp zero, OpAdaptor, 695a7a61359SValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 696a7a61359SValentin Clement auto ty = convertType(zero.getType()); 697a7a61359SValentin Clement if (ty.isa<mlir::LLVM::LLVMPointerType>()) { 698a7a61359SValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty); 699a7a61359SValentin Clement } else if (ty.isa<mlir::IntegerType>()) { 700a7a61359SValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 701a7a61359SValentin Clement zero, ty, mlir::IntegerAttr::get(zero.getType(), 0)); 702a7a61359SValentin Clement } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) { 703a7a61359SValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>( 704a7a61359SValentin Clement zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0)); 705a7a61359SValentin Clement } else { 706a7a61359SValentin Clement // TODO: create ConstantAggregateZero for FIR aggregate/array types. 70752d813edSValentin Clement return rewriter.notifyMatchFailure( 70852d813edSValentin Clement zero, 709a7a61359SValentin Clement "conversion of fir.zero with aggregate type not implemented yet"); 710a7a61359SValentin Clement } 711a7a61359SValentin Clement return success(); 712a7a61359SValentin Clement } 713a7a61359SValentin Clement }; 71432e08248SAndrzej Warzynski 71554c56347SValentin Clement // Code shared between insert_value and extract_value Ops. 71654c56347SValentin Clement struct ValueOpCommon { 71754c56347SValentin Clement // Translate the arguments pertaining to any multidimensional array to 71854c56347SValentin Clement // row-major order for LLVM-IR. 71954c56347SValentin Clement static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs, 72054c56347SValentin Clement mlir::Type ty) { 72154c56347SValentin Clement assert(ty && "type is null"); 72254c56347SValentin Clement const auto end = attrs.size(); 72354c56347SValentin Clement for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) { 72454c56347SValentin Clement if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 72554c56347SValentin Clement const auto dim = getDimension(seq); 72654c56347SValentin Clement if (dim > 1) { 72754c56347SValentin Clement auto ub = std::min(i + dim, end); 72854c56347SValentin Clement std::reverse(attrs.begin() + i, attrs.begin() + ub); 72954c56347SValentin Clement i += dim - 1; 73054c56347SValentin Clement } 73154c56347SValentin Clement ty = getArrayElementType(seq); 73254c56347SValentin Clement } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) { 73354c56347SValentin Clement ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()]; 73454c56347SValentin Clement } else { 73554c56347SValentin Clement llvm_unreachable("index into invalid type"); 73654c56347SValentin Clement } 73754c56347SValentin Clement } 73854c56347SValentin Clement } 73954c56347SValentin Clement 74054c56347SValentin Clement static llvm::SmallVector<mlir::Attribute> 74154c56347SValentin Clement collectIndices(mlir::ConversionPatternRewriter &rewriter, 74254c56347SValentin Clement mlir::ArrayAttr arrAttr) { 74354c56347SValentin Clement llvm::SmallVector<mlir::Attribute> attrs; 74454c56347SValentin Clement for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) { 74554c56347SValentin Clement if (i->isa<mlir::IntegerAttr>()) { 74654c56347SValentin Clement attrs.push_back(*i); 74754c56347SValentin Clement } else { 74854c56347SValentin Clement auto fieldName = i->cast<mlir::StringAttr>().getValue(); 74954c56347SValentin Clement ++i; 75054c56347SValentin Clement auto ty = i->cast<mlir::TypeAttr>().getValue(); 75154c56347SValentin Clement auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName); 75254c56347SValentin Clement attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index)); 75354c56347SValentin Clement } 75454c56347SValentin Clement } 75554c56347SValentin Clement return attrs; 75654c56347SValentin Clement } 75754c56347SValentin Clement 75854c56347SValentin Clement private: 75954c56347SValentin Clement static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) { 76054c56347SValentin Clement unsigned result = 1; 76154c56347SValentin Clement for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>(); 76254c56347SValentin Clement eleTy; 76354c56347SValentin Clement eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>()) 76454c56347SValentin Clement ++result; 76554c56347SValentin Clement return result; 76654c56347SValentin Clement } 76754c56347SValentin Clement 76854c56347SValentin Clement static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) { 76954c56347SValentin Clement auto eleTy = ty.getElementType(); 77054c56347SValentin Clement while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>()) 77154c56347SValentin Clement eleTy = arrTy.getElementType(); 77254c56347SValentin Clement return eleTy; 77354c56347SValentin Clement } 77454c56347SValentin Clement }; 77554c56347SValentin Clement 77654c56347SValentin Clement /// Extract a subobject value from an ssa-value of aggregate type 77754c56347SValentin Clement struct ExtractValueOpConversion 77854c56347SValentin Clement : public FIROpAndTypeConversion<fir::ExtractValueOp>, 77954c56347SValentin Clement public ValueOpCommon { 78054c56347SValentin Clement using FIROpAndTypeConversion::FIROpAndTypeConversion; 78154c56347SValentin Clement 78254c56347SValentin Clement mlir::LogicalResult 78354c56347SValentin Clement doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor, 78454c56347SValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 78554c56347SValentin Clement auto attrs = collectIndices(rewriter, extractVal.coor()); 78654c56347SValentin Clement toRowMajor(attrs, adaptor.getOperands()[0].getType()); 78754c56347SValentin Clement auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs); 78854c56347SValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>( 78954c56347SValentin Clement extractVal, ty, adaptor.getOperands()[0], position); 79054c56347SValentin Clement return success(); 79154c56347SValentin Clement } 79254c56347SValentin Clement }; 79354c56347SValentin Clement 79454c56347SValentin Clement /// InsertValue is the generalized instruction for the composition of new 79554c56347SValentin Clement /// aggregate type values. 79654c56347SValentin Clement struct InsertValueOpConversion 79754c56347SValentin Clement : public FIROpAndTypeConversion<fir::InsertValueOp>, 79854c56347SValentin Clement public ValueOpCommon { 79954c56347SValentin Clement using FIROpAndTypeConversion::FIROpAndTypeConversion; 80054c56347SValentin Clement 80154c56347SValentin Clement mlir::LogicalResult 80254c56347SValentin Clement doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor, 80354c56347SValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 80454c56347SValentin Clement auto attrs = collectIndices(rewriter, insertVal.coor()); 80554c56347SValentin Clement toRowMajor(attrs, adaptor.getOperands()[0].getType()); 80654c56347SValentin Clement auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs); 80754c56347SValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 80854c56347SValentin Clement insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1], 80954c56347SValentin Clement position); 81054c56347SValentin Clement return success(); 81154c56347SValentin Clement } 81254c56347SValentin Clement }; 81354c56347SValentin Clement 8143ae8e442SValentin Clement /// InsertOnRange inserts a value into a sequence over a range of offsets. 8153ae8e442SValentin Clement struct InsertOnRangeOpConversion 8163ae8e442SValentin Clement : public FIROpAndTypeConversion<fir::InsertOnRangeOp> { 8173ae8e442SValentin Clement using FIROpAndTypeConversion::FIROpAndTypeConversion; 8183ae8e442SValentin Clement 8193ae8e442SValentin Clement // Increments an array of subscripts in a row major fasion. 8203ae8e442SValentin Clement void incrementSubscripts(const SmallVector<uint64_t> &dims, 8213ae8e442SValentin Clement SmallVector<uint64_t> &subscripts) const { 8223ae8e442SValentin Clement for (size_t i = dims.size(); i > 0; --i) { 8233ae8e442SValentin Clement if (++subscripts[i - 1] < dims[i - 1]) { 8243ae8e442SValentin Clement return; 8253ae8e442SValentin Clement } 8263ae8e442SValentin Clement subscripts[i - 1] = 0; 8273ae8e442SValentin Clement } 8283ae8e442SValentin Clement } 8293ae8e442SValentin Clement 8303ae8e442SValentin Clement mlir::LogicalResult 8313ae8e442SValentin Clement doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor, 8323ae8e442SValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 8333ae8e442SValentin Clement 8343ae8e442SValentin Clement llvm::SmallVector<uint64_t> dims; 8353ae8e442SValentin Clement auto type = adaptor.getOperands()[0].getType(); 8363ae8e442SValentin Clement 8373ae8e442SValentin Clement // Iteratively extract the array dimensions from the type. 8383ae8e442SValentin Clement while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) { 8393ae8e442SValentin Clement dims.push_back(t.getNumElements()); 8403ae8e442SValentin Clement type = t.getElementType(); 8413ae8e442SValentin Clement } 8423ae8e442SValentin Clement 8433ae8e442SValentin Clement SmallVector<uint64_t> lBounds; 8443ae8e442SValentin Clement SmallVector<uint64_t> uBounds; 8453ae8e442SValentin Clement 8463ae8e442SValentin Clement // Extract integer value from the attribute 8473ae8e442SValentin Clement SmallVector<int64_t> coordinates = llvm::to_vector<4>( 8483ae8e442SValentin Clement llvm::map_range(range.coor(), [](Attribute a) -> int64_t { 8493ae8e442SValentin Clement return a.cast<IntegerAttr>().getInt(); 8503ae8e442SValentin Clement })); 8513ae8e442SValentin Clement 8523ae8e442SValentin Clement // Unzip the upper and lower bound and convert to a row major format. 8533ae8e442SValentin Clement for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) { 8543ae8e442SValentin Clement uBounds.push_back(*i++); 8553ae8e442SValentin Clement lBounds.push_back(*i); 8563ae8e442SValentin Clement } 8573ae8e442SValentin Clement 8583ae8e442SValentin Clement auto &subscripts = lBounds; 8593ae8e442SValentin Clement auto loc = range.getLoc(); 8603ae8e442SValentin Clement mlir::Value lastOp = adaptor.getOperands()[0]; 8613ae8e442SValentin Clement mlir::Value insertVal = adaptor.getOperands()[1]; 8623ae8e442SValentin Clement 8633ae8e442SValentin Clement auto i64Ty = rewriter.getI64Type(); 8643ae8e442SValentin Clement while (subscripts != uBounds) { 8653ae8e442SValentin Clement // Convert uint64_t's to Attribute's. 8663ae8e442SValentin Clement SmallVector<mlir::Attribute> subscriptAttrs; 8673ae8e442SValentin Clement for (const auto &subscript : subscripts) 8683ae8e442SValentin Clement subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript)); 8693ae8e442SValentin Clement lastOp = rewriter.create<mlir::LLVM::InsertValueOp>( 8703ae8e442SValentin Clement loc, ty, lastOp, insertVal, 8713ae8e442SValentin Clement ArrayAttr::get(range.getContext(), subscriptAttrs)); 8723ae8e442SValentin Clement 8733ae8e442SValentin Clement incrementSubscripts(dims, subscripts); 8743ae8e442SValentin Clement } 8753ae8e442SValentin Clement 8763ae8e442SValentin Clement // Convert uint64_t's to Attribute's. 8773ae8e442SValentin Clement SmallVector<mlir::Attribute> subscriptAttrs; 8783ae8e442SValentin Clement for (const auto &subscript : subscripts) 8793ae8e442SValentin Clement subscriptAttrs.push_back( 8803ae8e442SValentin Clement IntegerAttr::get(rewriter.getI64Type(), subscript)); 8813ae8e442SValentin Clement mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs); 8823ae8e442SValentin Clement 8833ae8e442SValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>( 8843ae8e442SValentin Clement range, ty, lastOp, insertVal, 8853ae8e442SValentin Clement ArrayAttr::get(range.getContext(), arrayRef)); 8863ae8e442SValentin Clement 8873ae8e442SValentin Clement return success(); 8883ae8e442SValentin Clement } 8893ae8e442SValentin Clement }; 8907b5132daSValentin Clement 8917b5132daSValentin Clement // 8927b5132daSValentin Clement // Primitive operations on Complex types 8937b5132daSValentin Clement // 8947b5132daSValentin Clement 8957b5132daSValentin Clement /// Generate inline code for complex addition/subtraction 8967b5132daSValentin Clement template <typename LLVMOP, typename OPTY> 8977b5132daSValentin Clement mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds, 8987b5132daSValentin Clement mlir::ConversionPatternRewriter &rewriter, 8997b5132daSValentin Clement fir::LLVMTypeConverter &lowering) { 9007b5132daSValentin Clement mlir::Value a = opnds[0]; 9017b5132daSValentin Clement mlir::Value b = opnds[1]; 9027b5132daSValentin Clement auto loc = sumop.getLoc(); 9037b5132daSValentin Clement auto ctx = sumop.getContext(); 9047b5132daSValentin Clement auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 9057b5132daSValentin Clement auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 9067b5132daSValentin Clement mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType())); 9077b5132daSValentin Clement mlir::Type ty = lowering.convertType(sumop.getType()); 9087b5132daSValentin Clement auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 9097b5132daSValentin Clement auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 9107b5132daSValentin Clement auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 9117b5132daSValentin Clement auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 9127b5132daSValentin Clement auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1); 9137b5132daSValentin Clement auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1); 9147b5132daSValentin Clement auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 9157b5132daSValentin Clement auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0); 9167b5132daSValentin Clement return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1); 9177b5132daSValentin Clement } 9187b5132daSValentin Clement 9197b5132daSValentin Clement struct AddcOpConversion : public FIROpConversion<fir::AddcOp> { 9207b5132daSValentin Clement using FIROpConversion::FIROpConversion; 9217b5132daSValentin Clement 9227b5132daSValentin Clement mlir::LogicalResult 9237b5132daSValentin Clement matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor, 9247b5132daSValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 9257b5132daSValentin Clement // given: (x + iy) + (x' + iy') 9267b5132daSValentin Clement // result: (x + x') + i(y + y') 9277b5132daSValentin Clement auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(), 9287b5132daSValentin Clement rewriter, lowerTy()); 9297b5132daSValentin Clement rewriter.replaceOp(addc, r.getResult()); 9307b5132daSValentin Clement return success(); 9317b5132daSValentin Clement } 9327b5132daSValentin Clement }; 9337b5132daSValentin Clement 9347b5132daSValentin Clement struct SubcOpConversion : public FIROpConversion<fir::SubcOp> { 9357b5132daSValentin Clement using FIROpConversion::FIROpConversion; 9367b5132daSValentin Clement 9377b5132daSValentin Clement mlir::LogicalResult 9387b5132daSValentin Clement matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor, 9397b5132daSValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 9407b5132daSValentin Clement // given: (x + iy) - (x' + iy') 9417b5132daSValentin Clement // result: (x - x') + i(y - y') 9427b5132daSValentin Clement auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(), 9437b5132daSValentin Clement rewriter, lowerTy()); 9447b5132daSValentin Clement rewriter.replaceOp(subc, r.getResult()); 9457b5132daSValentin Clement return success(); 9467b5132daSValentin Clement } 9477b5132daSValentin Clement }; 9487b5132daSValentin Clement 9497b5132daSValentin Clement /// Inlined complex multiply 9507b5132daSValentin Clement struct MulcOpConversion : public FIROpConversion<fir::MulcOp> { 9517b5132daSValentin Clement using FIROpConversion::FIROpConversion; 9527b5132daSValentin Clement 9537b5132daSValentin Clement mlir::LogicalResult 9547b5132daSValentin Clement matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor, 9557b5132daSValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 9567b5132daSValentin Clement // TODO: Can we use a call to __muldc3 ? 9577b5132daSValentin Clement // given: (x + iy) * (x' + iy') 9587b5132daSValentin Clement // result: (xx'-yy')+i(xy'+yx') 9597b5132daSValentin Clement mlir::Value a = adaptor.getOperands()[0]; 9607b5132daSValentin Clement mlir::Value b = adaptor.getOperands()[1]; 9617b5132daSValentin Clement auto loc = mulc.getLoc(); 9627b5132daSValentin Clement auto *ctx = mulc.getContext(); 9637b5132daSValentin Clement auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 9647b5132daSValentin Clement auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 9657b5132daSValentin Clement mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType())); 9667b5132daSValentin Clement mlir::Type ty = convertType(mulc.getType()); 9677b5132daSValentin Clement auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 9687b5132daSValentin Clement auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 9697b5132daSValentin Clement auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 9707b5132daSValentin Clement auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 9717b5132daSValentin Clement auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 9727b5132daSValentin Clement auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 9737b5132daSValentin Clement auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 9747b5132daSValentin Clement auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx); 9757b5132daSValentin Clement auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 9767b5132daSValentin Clement auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy); 9777b5132daSValentin Clement auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 9787b5132daSValentin Clement auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 9797b5132daSValentin Clement auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 9807b5132daSValentin Clement rewriter.replaceOp(mulc, r0.getResult()); 9817b5132daSValentin Clement return success(); 9827b5132daSValentin Clement } 9837b5132daSValentin Clement }; 9847b5132daSValentin Clement 9857b5132daSValentin Clement /// Inlined complex division 9867b5132daSValentin Clement struct DivcOpConversion : public FIROpConversion<fir::DivcOp> { 9877b5132daSValentin Clement using FIROpConversion::FIROpConversion; 9887b5132daSValentin Clement 9897b5132daSValentin Clement mlir::LogicalResult 9907b5132daSValentin Clement matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor, 9917b5132daSValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 9927b5132daSValentin Clement // TODO: Can we use a call to __divdc3 instead? 9937b5132daSValentin Clement // Just generate inline code for now. 9947b5132daSValentin Clement // given: (x + iy) / (x' + iy') 9957b5132daSValentin Clement // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y' 9967b5132daSValentin Clement mlir::Value a = adaptor.getOperands()[0]; 9977b5132daSValentin Clement mlir::Value b = adaptor.getOperands()[1]; 9987b5132daSValentin Clement auto loc = divc.getLoc(); 9997b5132daSValentin Clement auto *ctx = divc.getContext(); 10007b5132daSValentin Clement auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0)); 10017b5132daSValentin Clement auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1)); 10027b5132daSValentin Clement mlir::Type eleTy = convertType(getComplexEleTy(divc.getType())); 10037b5132daSValentin Clement mlir::Type ty = convertType(divc.getType()); 10047b5132daSValentin Clement auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0); 10057b5132daSValentin Clement auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1); 10067b5132daSValentin Clement auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0); 10077b5132daSValentin Clement auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1); 10087b5132daSValentin Clement auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1); 10097b5132daSValentin Clement auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1); 10107b5132daSValentin Clement auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1); 10117b5132daSValentin Clement auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1); 10127b5132daSValentin Clement auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1); 10137b5132daSValentin Clement auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1); 10147b5132daSValentin Clement auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1); 10157b5132daSValentin Clement auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy); 10167b5132daSValentin Clement auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy); 10177b5132daSValentin Clement auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d); 10187b5132daSValentin Clement auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d); 10197b5132daSValentin Clement auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty); 10207b5132daSValentin Clement auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0); 10217b5132daSValentin Clement auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1); 10227b5132daSValentin Clement rewriter.replaceOp(divc, r0.getResult()); 10237b5132daSValentin Clement return success(); 10247b5132daSValentin Clement } 10257b5132daSValentin Clement }; 10267b5132daSValentin Clement 10277b5132daSValentin Clement /// Inlined complex negation 10287b5132daSValentin Clement struct NegcOpConversion : public FIROpConversion<fir::NegcOp> { 10297b5132daSValentin Clement using FIROpConversion::FIROpConversion; 10307b5132daSValentin Clement 10317b5132daSValentin Clement mlir::LogicalResult 10327b5132daSValentin Clement matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor, 10337b5132daSValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 10347b5132daSValentin Clement // given: -(x + iy) 10357b5132daSValentin Clement // result: -x - iy 10367b5132daSValentin Clement auto *ctxt = neg.getContext(); 10377b5132daSValentin Clement auto eleTy = convertType(getComplexEleTy(neg.getType())); 10387b5132daSValentin Clement auto ty = convertType(neg.getType()); 10397b5132daSValentin Clement auto loc = neg.getLoc(); 10407b5132daSValentin Clement mlir::Value o0 = adaptor.getOperands()[0]; 10417b5132daSValentin Clement auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0)); 10427b5132daSValentin Clement auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1)); 10437b5132daSValentin Clement auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0); 10447b5132daSValentin Clement auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1); 10457b5132daSValentin Clement auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp); 10467b5132daSValentin Clement auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip); 10477b5132daSValentin Clement auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0); 10487b5132daSValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1); 10497b5132daSValentin Clement return success(); 10507b5132daSValentin Clement } 10517b5132daSValentin Clement }; 10527b5132daSValentin Clement 1053044d5b5dSValentin Clement } // namespace 1054044d5b5dSValentin Clement 1055044d5b5dSValentin Clement namespace { 1056044d5b5dSValentin Clement /// Convert FIR dialect to LLVM dialect 1057044d5b5dSValentin Clement /// 1058044d5b5dSValentin Clement /// This pass lowers all FIR dialect operations to LLVM IR dialect. An 1059044d5b5dSValentin Clement /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect. 1060044d5b5dSValentin Clement /// 1061044d5b5dSValentin Clement /// This pass is not complete yet. We are upstreaming it in small patches. 1062044d5b5dSValentin Clement class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> { 1063044d5b5dSValentin Clement public: 1064044d5b5dSValentin Clement mlir::ModuleOp getModule() { return getOperation(); } 1065044d5b5dSValentin Clement 1066044d5b5dSValentin Clement void runOnOperation() override final { 10677b5132daSValentin Clement auto mod = getModule(); 10687b5132daSValentin Clement if (!forcedTargetTriple.empty()) { 10697b5132daSValentin Clement fir::setTargetTriple(mod, forcedTargetTriple); 10707b5132daSValentin Clement } 10717b5132daSValentin Clement 1072044d5b5dSValentin Clement auto *context = getModule().getContext(); 1073044d5b5dSValentin Clement fir::LLVMTypeConverter typeConverter{getModule()}; 1074044d5b5dSValentin Clement mlir::OwningRewritePatternList pattern(context); 1075df3b9810SValentin Clement pattern.insert< 1076df3b9810SValentin Clement AddcOpConversion, AddrOfOpConversion, BoxAddrOpConversion, 1077*b6e44ecdSValentin Clement BoxDimsOpConversion, BoxEleSizeOpConversion, BoxIsAllocOpConversion, 1078*b6e44ecdSValentin Clement BoxIsArrayOpConversion, BoxIsPtrOpConversion, BoxRankOpConversion, 1079df3b9810SValentin Clement CallOpConversion, ConvertOpConversion, DivcOpConversion, 1080df3b9810SValentin Clement ExtractValueOpConversion, HasValueOpConversion, GlobalOpConversion, 1081df3b9810SValentin Clement InsertOnRangeOpConversion, InsertValueOpConversion, LoadOpConversion, 1082df3b9810SValentin Clement NegcOpConversion, MulcOpConversion, SelectOpConversion, 1083df3b9810SValentin Clement SelectRankOpConversion, StoreOpConversion, SubcOpConversion, 1084df3b9810SValentin Clement UndefOpConversion, UnreachableOpConversion, ZeroOpConversion>( 1085df3b9810SValentin Clement typeConverter); 1086044d5b5dSValentin Clement mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern); 1087044d5b5dSValentin Clement mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter, 1088044d5b5dSValentin Clement pattern); 1089044d5b5dSValentin Clement mlir::ConversionTarget target{*context}; 1090044d5b5dSValentin Clement target.addLegalDialect<mlir::LLVM::LLVMDialect>(); 1091044d5b5dSValentin Clement 1092044d5b5dSValentin Clement // required NOPs for applying a full conversion 1093044d5b5dSValentin Clement target.addLegalOp<mlir::ModuleOp>(); 1094044d5b5dSValentin Clement 1095044d5b5dSValentin Clement // apply the patterns 1096044d5b5dSValentin Clement if (mlir::failed(mlir::applyFullConversion(getModule(), target, 1097044d5b5dSValentin Clement std::move(pattern)))) { 1098044d5b5dSValentin Clement signalPassFailure(); 1099044d5b5dSValentin Clement } 1100044d5b5dSValentin Clement } 1101044d5b5dSValentin Clement }; 1102044d5b5dSValentin Clement } // namespace 1103044d5b5dSValentin Clement 1104044d5b5dSValentin Clement std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() { 1105044d5b5dSValentin Clement return std::make_unique<FIRToLLVMLowering>(); 1106044d5b5dSValentin Clement } 1107