1*044d5b5dSValentin Clement //===-- CodeGen.cpp -- bridge to lower to LLVM ----------------------------===// 2*044d5b5dSValentin Clement // 3*044d5b5dSValentin Clement // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*044d5b5dSValentin Clement // See https://llvm.org/LICENSE.txt for license information. 5*044d5b5dSValentin Clement // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*044d5b5dSValentin Clement // 7*044d5b5dSValentin Clement //===----------------------------------------------------------------------===// 8*044d5b5dSValentin Clement // 9*044d5b5dSValentin Clement // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ 10*044d5b5dSValentin Clement // 11*044d5b5dSValentin Clement //===----------------------------------------------------------------------===// 12*044d5b5dSValentin Clement 13*044d5b5dSValentin Clement #include "flang/Optimizer/CodeGen/CodeGen.h" 14*044d5b5dSValentin Clement #include "PassDetail.h" 15*044d5b5dSValentin Clement #include "flang/Optimizer/Dialect/FIROps.h" 16*044d5b5dSValentin Clement #include "flang/Optimizer/Dialect/FIRType.h" 17*044d5b5dSValentin Clement #include "mlir/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.h" 18*044d5b5dSValentin Clement #include "mlir/Conversion/LLVMCommon/Pattern.h" 19*044d5b5dSValentin Clement #include "mlir/Conversion/LLVMCommon/TypeConverter.h" 20*044d5b5dSValentin Clement #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h" 21*044d5b5dSValentin Clement #include "mlir/IR/BuiltinTypes.h" 22*044d5b5dSValentin Clement #include "mlir/Pass/Pass.h" 23*044d5b5dSValentin Clement #include "llvm/ADT/ArrayRef.h" 24*044d5b5dSValentin Clement 25*044d5b5dSValentin Clement #define DEBUG_TYPE "flang-codegen" 26*044d5b5dSValentin Clement 27*044d5b5dSValentin Clement // fir::LLVMTypeConverter for converting to LLVM IR dialect types. 28*044d5b5dSValentin Clement #include "TypeConverter.h" 29*044d5b5dSValentin Clement 30*044d5b5dSValentin Clement namespace { 31*044d5b5dSValentin Clement /// FIR conversion pattern template 32*044d5b5dSValentin Clement template <typename FromOp> 33*044d5b5dSValentin Clement class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> { 34*044d5b5dSValentin Clement public: 35*044d5b5dSValentin Clement explicit FIROpConversion(fir::LLVMTypeConverter &lowering) 36*044d5b5dSValentin Clement : mlir::ConvertOpToLLVMPattern<FromOp>(lowering) {} 37*044d5b5dSValentin Clement 38*044d5b5dSValentin Clement protected: 39*044d5b5dSValentin Clement mlir::Type convertType(mlir::Type ty) const { 40*044d5b5dSValentin Clement return lowerTy().convertType(ty); 41*044d5b5dSValentin Clement } 42*044d5b5dSValentin Clement 43*044d5b5dSValentin Clement fir::LLVMTypeConverter &lowerTy() const { 44*044d5b5dSValentin Clement return *static_cast<fir::LLVMTypeConverter *>(this->getTypeConverter()); 45*044d5b5dSValentin Clement } 46*044d5b5dSValentin Clement }; 47*044d5b5dSValentin Clement } // namespace 48*044d5b5dSValentin Clement 49*044d5b5dSValentin Clement namespace { 50*044d5b5dSValentin Clement struct AddrOfOpConversion : public FIROpConversion<fir::AddrOfOp> { 51*044d5b5dSValentin Clement using FIROpConversion::FIROpConversion; 52*044d5b5dSValentin Clement 53*044d5b5dSValentin Clement mlir::LogicalResult 54*044d5b5dSValentin Clement matchAndRewrite(fir::AddrOfOp addr, OpAdaptor adaptor, 55*044d5b5dSValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 56*044d5b5dSValentin Clement auto ty = convertType(addr.getType()); 57*044d5b5dSValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>( 58*044d5b5dSValentin Clement addr, ty, addr.symbol().getRootReference().getValue()); 59*044d5b5dSValentin Clement return success(); 60*044d5b5dSValentin Clement } 61*044d5b5dSValentin Clement }; 62*044d5b5dSValentin Clement 63*044d5b5dSValentin Clement struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> { 64*044d5b5dSValentin Clement using FIROpConversion::FIROpConversion; 65*044d5b5dSValentin Clement 66*044d5b5dSValentin Clement mlir::LogicalResult 67*044d5b5dSValentin Clement matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor, 68*044d5b5dSValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 69*044d5b5dSValentin Clement rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands()); 70*044d5b5dSValentin Clement return success(); 71*044d5b5dSValentin Clement } 72*044d5b5dSValentin Clement }; 73*044d5b5dSValentin Clement 74*044d5b5dSValentin Clement struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> { 75*044d5b5dSValentin Clement using FIROpConversion::FIROpConversion; 76*044d5b5dSValentin Clement 77*044d5b5dSValentin Clement mlir::LogicalResult 78*044d5b5dSValentin Clement matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor, 79*044d5b5dSValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 80*044d5b5dSValentin Clement auto tyAttr = convertType(global.getType()); 81*044d5b5dSValentin Clement if (global.getType().isa<fir::BoxType>()) 82*044d5b5dSValentin Clement tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType(); 83*044d5b5dSValentin Clement auto loc = global.getLoc(); 84*044d5b5dSValentin Clement mlir::Attribute initAttr{}; 85*044d5b5dSValentin Clement if (global.initVal()) 86*044d5b5dSValentin Clement initAttr = global.initVal().getValue(); 87*044d5b5dSValentin Clement auto linkage = convertLinkage(global.linkName()); 88*044d5b5dSValentin Clement auto isConst = global.constant().hasValue(); 89*044d5b5dSValentin Clement auto g = rewriter.create<mlir::LLVM::GlobalOp>( 90*044d5b5dSValentin Clement loc, tyAttr, isConst, linkage, global.sym_name(), initAttr); 91*044d5b5dSValentin Clement auto &gr = g.getInitializerRegion(); 92*044d5b5dSValentin Clement rewriter.inlineRegionBefore(global.region(), gr, gr.end()); 93*044d5b5dSValentin Clement if (!gr.empty()) { 94*044d5b5dSValentin Clement // Replace insert_on_range with a constant dense attribute if the 95*044d5b5dSValentin Clement // initialization is on the full range. 96*044d5b5dSValentin Clement auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>(); 97*044d5b5dSValentin Clement for (auto insertOp : insertOnRangeOps) { 98*044d5b5dSValentin Clement if (isFullRange(insertOp.coor(), insertOp.getType())) { 99*044d5b5dSValentin Clement auto seqTyAttr = convertType(insertOp.getType()); 100*044d5b5dSValentin Clement auto *op = insertOp.val().getDefiningOp(); 101*044d5b5dSValentin Clement auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op); 102*044d5b5dSValentin Clement if (!constant) { 103*044d5b5dSValentin Clement auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op); 104*044d5b5dSValentin Clement if (!convertOp) 105*044d5b5dSValentin Clement continue; 106*044d5b5dSValentin Clement constant = cast<mlir::arith::ConstantOp>( 107*044d5b5dSValentin Clement convertOp.value().getDefiningOp()); 108*044d5b5dSValentin Clement } 109*044d5b5dSValentin Clement mlir::Type vecType = mlir::VectorType::get( 110*044d5b5dSValentin Clement insertOp.getType().getShape(), constant.getType()); 111*044d5b5dSValentin Clement auto denseAttr = mlir::DenseElementsAttr::get( 112*044d5b5dSValentin Clement vecType.cast<ShapedType>(), constant.value()); 113*044d5b5dSValentin Clement rewriter.setInsertionPointAfter(insertOp); 114*044d5b5dSValentin Clement rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>( 115*044d5b5dSValentin Clement insertOp, seqTyAttr, denseAttr); 116*044d5b5dSValentin Clement } 117*044d5b5dSValentin Clement } 118*044d5b5dSValentin Clement } 119*044d5b5dSValentin Clement rewriter.eraseOp(global); 120*044d5b5dSValentin Clement return success(); 121*044d5b5dSValentin Clement } 122*044d5b5dSValentin Clement 123*044d5b5dSValentin Clement bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const { 124*044d5b5dSValentin Clement auto extents = seqTy.getShape(); 125*044d5b5dSValentin Clement if (indexes.size() / 2 != extents.size()) 126*044d5b5dSValentin Clement return false; 127*044d5b5dSValentin Clement for (unsigned i = 0; i < indexes.size(); i += 2) { 128*044d5b5dSValentin Clement if (indexes[i].cast<IntegerAttr>().getInt() != 0) 129*044d5b5dSValentin Clement return false; 130*044d5b5dSValentin Clement if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1) 131*044d5b5dSValentin Clement return false; 132*044d5b5dSValentin Clement } 133*044d5b5dSValentin Clement return true; 134*044d5b5dSValentin Clement } 135*044d5b5dSValentin Clement 136*044d5b5dSValentin Clement mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const { 137*044d5b5dSValentin Clement if (optLinkage.hasValue()) { 138*044d5b5dSValentin Clement auto name = optLinkage.getValue(); 139*044d5b5dSValentin Clement if (name == "internal") 140*044d5b5dSValentin Clement return mlir::LLVM::Linkage::Internal; 141*044d5b5dSValentin Clement if (name == "linkonce") 142*044d5b5dSValentin Clement return mlir::LLVM::Linkage::Linkonce; 143*044d5b5dSValentin Clement if (name == "common") 144*044d5b5dSValentin Clement return mlir::LLVM::Linkage::Common; 145*044d5b5dSValentin Clement if (name == "weak") 146*044d5b5dSValentin Clement return mlir::LLVM::Linkage::Weak; 147*044d5b5dSValentin Clement } 148*044d5b5dSValentin Clement return mlir::LLVM::Linkage::External; 149*044d5b5dSValentin Clement } 150*044d5b5dSValentin Clement }; 151*044d5b5dSValentin Clement 152*044d5b5dSValentin Clement // convert to LLVM IR dialect `undef` 153*044d5b5dSValentin Clement struct UndefOpConversion : public FIROpConversion<fir::UndefOp> { 154*044d5b5dSValentin Clement using FIROpConversion::FIROpConversion; 155*044d5b5dSValentin Clement 156*044d5b5dSValentin Clement mlir::LogicalResult 157*044d5b5dSValentin Clement matchAndRewrite(fir::UndefOp undef, OpAdaptor, 158*044d5b5dSValentin Clement mlir::ConversionPatternRewriter &rewriter) const override { 159*044d5b5dSValentin Clement rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>( 160*044d5b5dSValentin Clement undef, convertType(undef.getType())); 161*044d5b5dSValentin Clement return success(); 162*044d5b5dSValentin Clement } 163*044d5b5dSValentin Clement }; 164*044d5b5dSValentin Clement } // namespace 165*044d5b5dSValentin Clement 166*044d5b5dSValentin Clement namespace { 167*044d5b5dSValentin Clement /// Convert FIR dialect to LLVM dialect 168*044d5b5dSValentin Clement /// 169*044d5b5dSValentin Clement /// This pass lowers all FIR dialect operations to LLVM IR dialect. An 170*044d5b5dSValentin Clement /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect. 171*044d5b5dSValentin Clement /// 172*044d5b5dSValentin Clement /// This pass is not complete yet. We are upstreaming it in small patches. 173*044d5b5dSValentin Clement class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> { 174*044d5b5dSValentin Clement public: 175*044d5b5dSValentin Clement mlir::ModuleOp getModule() { return getOperation(); } 176*044d5b5dSValentin Clement 177*044d5b5dSValentin Clement void runOnOperation() override final { 178*044d5b5dSValentin Clement auto *context = getModule().getContext(); 179*044d5b5dSValentin Clement fir::LLVMTypeConverter typeConverter{getModule()}; 180*044d5b5dSValentin Clement auto loc = mlir::UnknownLoc::get(context); 181*044d5b5dSValentin Clement mlir::OwningRewritePatternList pattern(context); 182*044d5b5dSValentin Clement pattern.insert<AddrOfOpConversion, HasValueOpConversion, GlobalOpConversion, 183*044d5b5dSValentin Clement UndefOpConversion>(typeConverter); 184*044d5b5dSValentin Clement mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern); 185*044d5b5dSValentin Clement mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter, 186*044d5b5dSValentin Clement pattern); 187*044d5b5dSValentin Clement mlir::ConversionTarget target{*context}; 188*044d5b5dSValentin Clement target.addLegalDialect<mlir::LLVM::LLVMDialect>(); 189*044d5b5dSValentin Clement 190*044d5b5dSValentin Clement // required NOPs for applying a full conversion 191*044d5b5dSValentin Clement target.addLegalOp<mlir::ModuleOp>(); 192*044d5b5dSValentin Clement 193*044d5b5dSValentin Clement // apply the patterns 194*044d5b5dSValentin Clement if (mlir::failed(mlir::applyFullConversion(getModule(), target, 195*044d5b5dSValentin Clement std::move(pattern)))) { 196*044d5b5dSValentin Clement mlir::emitError(loc, "error in converting to LLVM-IR dialect\n"); 197*044d5b5dSValentin Clement signalPassFailure(); 198*044d5b5dSValentin Clement } 199*044d5b5dSValentin Clement } 200*044d5b5dSValentin Clement }; 201*044d5b5dSValentin Clement } // namespace 202*044d5b5dSValentin Clement 203*044d5b5dSValentin Clement std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() { 204*044d5b5dSValentin Clement return std::make_unique<FIRToLLVMLowering>(); 205*044d5b5dSValentin Clement } 206