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