1 //===-- CodeGen.cpp -- bridge to lower to LLVM ----------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "flang/Optimizer/CodeGen/CodeGen.h"
14 #include "PassDetail.h"
15 #include "flang/Optimizer/Dialect/FIROps.h"
16 #include "flang/Optimizer/Dialect/FIRType.h"
17 #include "flang/Optimizer/Support/FIRContext.h"
18 #include "mlir/Conversion/ArithmeticToLLVM/ArithmeticToLLVM.h"
19 #include "mlir/Conversion/LLVMCommon/Pattern.h"
20 #include "mlir/Conversion/LLVMCommon/TypeConverter.h"
21 #include "mlir/Conversion/StandardToLLVM/ConvertStandardToLLVM.h"
22 #include "mlir/IR/BuiltinTypes.h"
23 #include "mlir/IR/Matchers.h"
24 #include "mlir/Pass/Pass.h"
25 #include "llvm/ADT/ArrayRef.h"
26 
27 #define DEBUG_TYPE "flang-codegen"
28 
29 // fir::LLVMTypeConverter for converting to LLVM IR dialect types.
30 #include "TypeConverter.h"
31 
32 namespace {
33 /// FIR conversion pattern template
34 template <typename FromOp>
35 class FIROpConversion : public mlir::ConvertOpToLLVMPattern<FromOp> {
36 public:
37   explicit FIROpConversion(fir::LLVMTypeConverter &lowering)
38       : mlir::ConvertOpToLLVMPattern<FromOp>(lowering) {}
39 
40 protected:
41   mlir::Type convertType(mlir::Type ty) const {
42     return lowerTy().convertType(ty);
43   }
44 
45   fir::LLVMTypeConverter &lowerTy() const {
46     return *static_cast<fir::LLVMTypeConverter *>(this->getTypeConverter());
47   }
48 };
49 
50 /// FIR conversion pattern template
51 template <typename FromOp>
52 class FIROpAndTypeConversion : public FIROpConversion<FromOp> {
53 public:
54   using FIROpConversion<FromOp>::FIROpConversion;
55   using OpAdaptor = typename FromOp::Adaptor;
56 
57   mlir::LogicalResult
58   matchAndRewrite(FromOp op, OpAdaptor adaptor,
59                   mlir::ConversionPatternRewriter &rewriter) const final {
60     mlir::Type ty = this->convertType(op.getType());
61     return doRewrite(op, ty, adaptor, rewriter);
62   }
63 
64   virtual mlir::LogicalResult
65   doRewrite(FromOp addr, mlir::Type ty, OpAdaptor adaptor,
66             mlir::ConversionPatternRewriter &rewriter) const = 0;
67 };
68 
69 // Lower `fir.address_of` operation to `llvm.address_of` operation.
70 struct AddrOfOpConversion : public FIROpConversion<fir::AddrOfOp> {
71   using FIROpConversion::FIROpConversion;
72 
73   mlir::LogicalResult
74   matchAndRewrite(fir::AddrOfOp addr, OpAdaptor adaptor,
75                   mlir::ConversionPatternRewriter &rewriter) const override {
76     auto ty = convertType(addr.getType());
77     rewriter.replaceOpWithNewOp<mlir::LLVM::AddressOfOp>(
78         addr, ty, addr.symbol().getRootReference().getValue());
79     return success();
80   }
81 };
82 
83 // `fir.call` -> `llvm.call`
84 struct CallOpConversion : public FIROpConversion<fir::CallOp> {
85   using FIROpConversion::FIROpConversion;
86 
87   mlir::LogicalResult
88   matchAndRewrite(fir::CallOp call, OpAdaptor adaptor,
89                   mlir::ConversionPatternRewriter &rewriter) const override {
90     SmallVector<mlir::Type> resultTys;
91     for (auto r : call.getResults())
92       resultTys.push_back(convertType(r.getType()));
93     rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
94         call, resultTys, adaptor.getOperands(), call->getAttrs());
95     return success();
96   }
97 };
98 
99 static mlir::Type getComplexEleTy(mlir::Type complex) {
100   if (auto cc = complex.dyn_cast<mlir::ComplexType>())
101     return cc.getElementType();
102   return complex.cast<fir::ComplexType>().getElementType();
103 }
104 
105 /// convert value of from-type to value of to-type
106 struct ConvertOpConversion : public FIROpConversion<fir::ConvertOp> {
107   using FIROpConversion::FIROpConversion;
108 
109   static bool isFloatingPointTy(mlir::Type ty) {
110     return ty.isa<mlir::FloatType>();
111   }
112 
113   mlir::LogicalResult
114   matchAndRewrite(fir::ConvertOp convert, OpAdaptor adaptor,
115                   mlir::ConversionPatternRewriter &rewriter) const override {
116     auto fromTy = convertType(convert.value().getType());
117     auto toTy = convertType(convert.res().getType());
118     mlir::Value op0 = adaptor.getOperands()[0];
119     if (fromTy == toTy) {
120       rewriter.replaceOp(convert, op0);
121       return success();
122     }
123     auto loc = convert.getLoc();
124     auto convertFpToFp = [&](mlir::Value val, unsigned fromBits,
125                              unsigned toBits, mlir::Type toTy) -> mlir::Value {
126       if (fromBits == toBits) {
127         // TODO: Converting between two floating-point representations with the
128         // same bitwidth is not allowed for now.
129         mlir::emitError(loc,
130                         "cannot implicitly convert between two floating-point "
131                         "representations of the same bitwidth");
132         return {};
133       }
134       if (fromBits > toBits)
135         return rewriter.create<mlir::LLVM::FPTruncOp>(loc, toTy, val);
136       return rewriter.create<mlir::LLVM::FPExtOp>(loc, toTy, val);
137     };
138     // Complex to complex conversion.
139     if (fir::isa_complex(convert.value().getType()) &&
140         fir::isa_complex(convert.res().getType())) {
141       // Special case: handle the conversion of a complex such that both the
142       // real and imaginary parts are converted together.
143       auto zero = mlir::ArrayAttr::get(convert.getContext(),
144                                        rewriter.getI32IntegerAttr(0));
145       auto one = mlir::ArrayAttr::get(convert.getContext(),
146                                       rewriter.getI32IntegerAttr(1));
147       auto ty = convertType(getComplexEleTy(convert.value().getType()));
148       auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, zero);
149       auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, ty, op0, one);
150       auto nt = convertType(getComplexEleTy(convert.res().getType()));
151       auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(ty);
152       auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(nt);
153       auto rc = convertFpToFp(rp, fromBits, toBits, nt);
154       auto ic = convertFpToFp(ip, fromBits, toBits, nt);
155       auto un = rewriter.create<mlir::LLVM::UndefOp>(loc, toTy);
156       auto i1 =
157           rewriter.create<mlir::LLVM::InsertValueOp>(loc, toTy, un, rc, zero);
158       rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(convert, toTy, i1,
159                                                              ic, one);
160       return mlir::success();
161     }
162     // Floating point to floating point conversion.
163     if (isFloatingPointTy(fromTy)) {
164       if (isFloatingPointTy(toTy)) {
165         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
166         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
167         auto v = convertFpToFp(op0, fromBits, toBits, toTy);
168         rewriter.replaceOp(convert, v);
169         return mlir::success();
170       }
171       if (toTy.isa<mlir::IntegerType>()) {
172         rewriter.replaceOpWithNewOp<mlir::LLVM::FPToSIOp>(convert, toTy, op0);
173         return mlir::success();
174       }
175     } else if (fromTy.isa<mlir::IntegerType>()) {
176       // Integer to integer conversion.
177       if (toTy.isa<mlir::IntegerType>()) {
178         auto fromBits = mlir::LLVM::getPrimitiveTypeSizeInBits(fromTy);
179         auto toBits = mlir::LLVM::getPrimitiveTypeSizeInBits(toTy);
180         assert(fromBits != toBits);
181         if (fromBits > toBits) {
182           rewriter.replaceOpWithNewOp<mlir::LLVM::TruncOp>(convert, toTy, op0);
183           return mlir::success();
184         }
185         rewriter.replaceOpWithNewOp<mlir::LLVM::SExtOp>(convert, toTy, op0);
186         return mlir::success();
187       }
188       // Integer to floating point conversion.
189       if (isFloatingPointTy(toTy)) {
190         rewriter.replaceOpWithNewOp<mlir::LLVM::SIToFPOp>(convert, toTy, op0);
191         return mlir::success();
192       }
193       // Integer to pointer conversion.
194       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
195         rewriter.replaceOpWithNewOp<mlir::LLVM::IntToPtrOp>(convert, toTy, op0);
196         return mlir::success();
197       }
198     } else if (fromTy.isa<mlir::LLVM::LLVMPointerType>()) {
199       // Pointer to integer conversion.
200       if (toTy.isa<mlir::IntegerType>()) {
201         rewriter.replaceOpWithNewOp<mlir::LLVM::PtrToIntOp>(convert, toTy, op0);
202         return mlir::success();
203       }
204       // Pointer to pointer conversion.
205       if (toTy.isa<mlir::LLVM::LLVMPointerType>()) {
206         rewriter.replaceOpWithNewOp<mlir::LLVM::BitcastOp>(convert, toTy, op0);
207         return mlir::success();
208       }
209     }
210     return emitError(loc) << "cannot convert " << fromTy << " to " << toTy;
211   }
212 };
213 
214 /// Lower `fir.has_value` operation to `llvm.return` operation.
215 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> {
216   using FIROpConversion::FIROpConversion;
217 
218   mlir::LogicalResult
219   matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor,
220                   mlir::ConversionPatternRewriter &rewriter) const override {
221     rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands());
222     return success();
223   }
224 };
225 
226 /// Lower `fir.global` operation to `llvm.global` operation.
227 /// `fir.insert_on_range` operations are replaced with constant dense attribute
228 /// if they are applied on the full range.
229 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> {
230   using FIROpConversion::FIROpConversion;
231 
232   mlir::LogicalResult
233   matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor,
234                   mlir::ConversionPatternRewriter &rewriter) const override {
235     auto tyAttr = convertType(global.getType());
236     if (global.getType().isa<fir::BoxType>())
237       tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType();
238     auto loc = global.getLoc();
239     mlir::Attribute initAttr{};
240     if (global.initVal())
241       initAttr = global.initVal().getValue();
242     auto linkage = convertLinkage(global.linkName());
243     auto isConst = global.constant().hasValue();
244     auto g = rewriter.create<mlir::LLVM::GlobalOp>(
245         loc, tyAttr, isConst, linkage, global.sym_name(), initAttr);
246     auto &gr = g.getInitializerRegion();
247     rewriter.inlineRegionBefore(global.region(), gr, gr.end());
248     if (!gr.empty()) {
249       // Replace insert_on_range with a constant dense attribute if the
250       // initialization is on the full range.
251       auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>();
252       for (auto insertOp : insertOnRangeOps) {
253         if (isFullRange(insertOp.coor(), insertOp.getType())) {
254           auto seqTyAttr = convertType(insertOp.getType());
255           auto *op = insertOp.val().getDefiningOp();
256           auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op);
257           if (!constant) {
258             auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op);
259             if (!convertOp)
260               continue;
261             constant = cast<mlir::arith::ConstantOp>(
262                 convertOp.value().getDefiningOp());
263           }
264           mlir::Type vecType = mlir::VectorType::get(
265               insertOp.getType().getShape(), constant.getType());
266           auto denseAttr = mlir::DenseElementsAttr::get(
267               vecType.cast<ShapedType>(), constant.value());
268           rewriter.setInsertionPointAfter(insertOp);
269           rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
270               insertOp, seqTyAttr, denseAttr);
271         }
272       }
273     }
274     rewriter.eraseOp(global);
275     return success();
276   }
277 
278   bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const {
279     auto extents = seqTy.getShape();
280     if (indexes.size() / 2 != extents.size())
281       return false;
282     for (unsigned i = 0; i < indexes.size(); i += 2) {
283       if (indexes[i].cast<IntegerAttr>().getInt() != 0)
284         return false;
285       if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1)
286         return false;
287     }
288     return true;
289   }
290 
291   // TODO: String comparaison should be avoided. Replace linkName with an
292   // enumeration.
293   mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const {
294     if (optLinkage.hasValue()) {
295       auto name = optLinkage.getValue();
296       if (name == "internal")
297         return mlir::LLVM::Linkage::Internal;
298       if (name == "linkonce")
299         return mlir::LLVM::Linkage::Linkonce;
300       if (name == "common")
301         return mlir::LLVM::Linkage::Common;
302       if (name == "weak")
303         return mlir::LLVM::Linkage::Weak;
304     }
305     return mlir::LLVM::Linkage::External;
306   }
307 };
308 
309 template <typename OP>
310 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select,
311                            typename OP::Adaptor adaptor,
312                            mlir::ConversionPatternRewriter &rewriter) {
313   unsigned conds = select.getNumConditions();
314   auto cases = select.getCases().getValue();
315   mlir::Value selector = adaptor.selector();
316   auto loc = select.getLoc();
317   assert(conds > 0 && "select must have cases");
318 
319   llvm::SmallVector<mlir::Block *> destinations;
320   llvm::SmallVector<mlir::ValueRange> destinationsOperands;
321   mlir::Block *defaultDestination;
322   mlir::ValueRange defaultOperands;
323   llvm::SmallVector<int32_t> caseValues;
324 
325   for (unsigned t = 0; t != conds; ++t) {
326     mlir::Block *dest = select.getSuccessor(t);
327     auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t);
328     const mlir::Attribute &attr = cases[t];
329     if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) {
330       destinations.push_back(dest);
331       destinationsOperands.push_back(destOps.hasValue() ? *destOps
332                                                         : ValueRange());
333       caseValues.push_back(intAttr.getInt());
334       continue;
335     }
336     assert(attr.template dyn_cast_or_null<mlir::UnitAttr>());
337     assert((t + 1 == conds) && "unit must be last");
338     defaultDestination = dest;
339     defaultOperands = destOps.hasValue() ? *destOps : ValueRange();
340   }
341 
342   // LLVM::SwitchOp takes a i32 type for the selector.
343   if (select.getSelector().getType() != rewriter.getI32Type())
344     selector =
345         rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector);
346 
347   rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
348       select, selector,
349       /*defaultDestination=*/defaultDestination,
350       /*defaultOperands=*/defaultOperands,
351       /*caseValues=*/caseValues,
352       /*caseDestinations=*/destinations,
353       /*caseOperands=*/destinationsOperands,
354       /*branchWeights=*/ArrayRef<int32_t>());
355 }
356 
357 /// conversion of fir::SelectOp to an if-then-else ladder
358 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> {
359   using FIROpConversion::FIROpConversion;
360 
361   mlir::LogicalResult
362   matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor,
363                   mlir::ConversionPatternRewriter &rewriter) const override {
364     selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter);
365     return success();
366   }
367 };
368 
369 /// conversion of fir::SelectRankOp to an if-then-else ladder
370 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> {
371   using FIROpConversion::FIROpConversion;
372 
373   mlir::LogicalResult
374   matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor,
375                   mlir::ConversionPatternRewriter &rewriter) const override {
376     selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter);
377     return success();
378   }
379 };
380 
381 // convert to LLVM IR dialect `undef`
382 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> {
383   using FIROpConversion::FIROpConversion;
384 
385   mlir::LogicalResult
386   matchAndRewrite(fir::UndefOp undef, OpAdaptor,
387                   mlir::ConversionPatternRewriter &rewriter) const override {
388     rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
389         undef, convertType(undef.getType()));
390     return success();
391   }
392 };
393 
394 // convert to LLVM IR dialect `unreachable`
395 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> {
396   using FIROpConversion::FIROpConversion;
397 
398   mlir::LogicalResult
399   matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor,
400                   mlir::ConversionPatternRewriter &rewriter) const override {
401     rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach);
402     return success();
403   }
404 };
405 
406 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> {
407   using FIROpConversion::FIROpConversion;
408 
409   mlir::LogicalResult
410   matchAndRewrite(fir::ZeroOp zero, OpAdaptor,
411                   mlir::ConversionPatternRewriter &rewriter) const override {
412     auto ty = convertType(zero.getType());
413     if (ty.isa<mlir::LLVM::LLVMPointerType>()) {
414       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty);
415     } else if (ty.isa<mlir::IntegerType>()) {
416       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
417           zero, ty, mlir::IntegerAttr::get(zero.getType(), 0));
418     } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) {
419       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
420           zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0));
421     } else {
422       // TODO: create ConstantAggregateZero for FIR aggregate/array types.
423       return rewriter.notifyMatchFailure(
424           zero,
425           "conversion of fir.zero with aggregate type not implemented yet");
426     }
427     return success();
428   }
429 };
430 
431 // Code shared between insert_value and extract_value Ops.
432 struct ValueOpCommon {
433   // Translate the arguments pertaining to any multidimensional array to
434   // row-major order for LLVM-IR.
435   static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs,
436                          mlir::Type ty) {
437     assert(ty && "type is null");
438     const auto end = attrs.size();
439     for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) {
440       if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
441         const auto dim = getDimension(seq);
442         if (dim > 1) {
443           auto ub = std::min(i + dim, end);
444           std::reverse(attrs.begin() + i, attrs.begin() + ub);
445           i += dim - 1;
446         }
447         ty = getArrayElementType(seq);
448       } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) {
449         ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()];
450       } else {
451         llvm_unreachable("index into invalid type");
452       }
453     }
454   }
455 
456   static llvm::SmallVector<mlir::Attribute>
457   collectIndices(mlir::ConversionPatternRewriter &rewriter,
458                  mlir::ArrayAttr arrAttr) {
459     llvm::SmallVector<mlir::Attribute> attrs;
460     for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) {
461       if (i->isa<mlir::IntegerAttr>()) {
462         attrs.push_back(*i);
463       } else {
464         auto fieldName = i->cast<mlir::StringAttr>().getValue();
465         ++i;
466         auto ty = i->cast<mlir::TypeAttr>().getValue();
467         auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName);
468         attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index));
469       }
470     }
471     return attrs;
472   }
473 
474 private:
475   static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) {
476     unsigned result = 1;
477     for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>();
478          eleTy;
479          eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>())
480       ++result;
481     return result;
482   }
483 
484   static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) {
485     auto eleTy = ty.getElementType();
486     while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>())
487       eleTy = arrTy.getElementType();
488     return eleTy;
489   }
490 };
491 
492 /// Extract a subobject value from an ssa-value of aggregate type
493 struct ExtractValueOpConversion
494     : public FIROpAndTypeConversion<fir::ExtractValueOp>,
495       public ValueOpCommon {
496   using FIROpAndTypeConversion::FIROpAndTypeConversion;
497 
498   mlir::LogicalResult
499   doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor,
500             mlir::ConversionPatternRewriter &rewriter) const override {
501     auto attrs = collectIndices(rewriter, extractVal.coor());
502     toRowMajor(attrs, adaptor.getOperands()[0].getType());
503     auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs);
504     rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
505         extractVal, ty, adaptor.getOperands()[0], position);
506     return success();
507   }
508 };
509 
510 /// InsertValue is the generalized instruction for the composition of new
511 /// aggregate type values.
512 struct InsertValueOpConversion
513     : public FIROpAndTypeConversion<fir::InsertValueOp>,
514       public ValueOpCommon {
515   using FIROpAndTypeConversion::FIROpAndTypeConversion;
516 
517   mlir::LogicalResult
518   doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor,
519             mlir::ConversionPatternRewriter &rewriter) const override {
520     auto attrs = collectIndices(rewriter, insertVal.coor());
521     toRowMajor(attrs, adaptor.getOperands()[0].getType());
522     auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs);
523     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
524         insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1],
525         position);
526     return success();
527   }
528 };
529 
530 /// InsertOnRange inserts a value into a sequence over a range of offsets.
531 struct InsertOnRangeOpConversion
532     : public FIROpAndTypeConversion<fir::InsertOnRangeOp> {
533   using FIROpAndTypeConversion::FIROpAndTypeConversion;
534 
535   // Increments an array of subscripts in a row major fasion.
536   void incrementSubscripts(const SmallVector<uint64_t> &dims,
537                            SmallVector<uint64_t> &subscripts) const {
538     for (size_t i = dims.size(); i > 0; --i) {
539       if (++subscripts[i - 1] < dims[i - 1]) {
540         return;
541       }
542       subscripts[i - 1] = 0;
543     }
544   }
545 
546   mlir::LogicalResult
547   doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor,
548             mlir::ConversionPatternRewriter &rewriter) const override {
549 
550     llvm::SmallVector<uint64_t> dims;
551     auto type = adaptor.getOperands()[0].getType();
552 
553     // Iteratively extract the array dimensions from the type.
554     while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
555       dims.push_back(t.getNumElements());
556       type = t.getElementType();
557     }
558 
559     SmallVector<uint64_t> lBounds;
560     SmallVector<uint64_t> uBounds;
561 
562     // Extract integer value from the attribute
563     SmallVector<int64_t> coordinates = llvm::to_vector<4>(
564         llvm::map_range(range.coor(), [](Attribute a) -> int64_t {
565           return a.cast<IntegerAttr>().getInt();
566         }));
567 
568     // Unzip the upper and lower bound and convert to a row major format.
569     for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) {
570       uBounds.push_back(*i++);
571       lBounds.push_back(*i);
572     }
573 
574     auto &subscripts = lBounds;
575     auto loc = range.getLoc();
576     mlir::Value lastOp = adaptor.getOperands()[0];
577     mlir::Value insertVal = adaptor.getOperands()[1];
578 
579     auto i64Ty = rewriter.getI64Type();
580     while (subscripts != uBounds) {
581       // Convert uint64_t's to Attribute's.
582       SmallVector<mlir::Attribute> subscriptAttrs;
583       for (const auto &subscript : subscripts)
584         subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript));
585       lastOp = rewriter.create<mlir::LLVM::InsertValueOp>(
586           loc, ty, lastOp, insertVal,
587           ArrayAttr::get(range.getContext(), subscriptAttrs));
588 
589       incrementSubscripts(dims, subscripts);
590     }
591 
592     // Convert uint64_t's to Attribute's.
593     SmallVector<mlir::Attribute> subscriptAttrs;
594     for (const auto &subscript : subscripts)
595       subscriptAttrs.push_back(
596           IntegerAttr::get(rewriter.getI64Type(), subscript));
597     mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs);
598 
599     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
600         range, ty, lastOp, insertVal,
601         ArrayAttr::get(range.getContext(), arrayRef));
602 
603     return success();
604   }
605 };
606 
607 //
608 // Primitive operations on Complex types
609 //
610 
611 /// Generate inline code for complex addition/subtraction
612 template <typename LLVMOP, typename OPTY>
613 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds,
614                                      mlir::ConversionPatternRewriter &rewriter,
615                                      fir::LLVMTypeConverter &lowering) {
616   mlir::Value a = opnds[0];
617   mlir::Value b = opnds[1];
618   auto loc = sumop.getLoc();
619   auto ctx = sumop.getContext();
620   auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
621   auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
622   mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType()));
623   mlir::Type ty = lowering.convertType(sumop.getType());
624   auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
625   auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
626   auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
627   auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
628   auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1);
629   auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1);
630   auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
631   auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0);
632   return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1);
633 }
634 
635 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> {
636   using FIROpConversion::FIROpConversion;
637 
638   mlir::LogicalResult
639   matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor,
640                   mlir::ConversionPatternRewriter &rewriter) const override {
641     // given: (x + iy) + (x' + iy')
642     // result: (x + x') + i(y + y')
643     auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(),
644                                             rewriter, lowerTy());
645     rewriter.replaceOp(addc, r.getResult());
646     return success();
647   }
648 };
649 
650 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> {
651   using FIROpConversion::FIROpConversion;
652 
653   mlir::LogicalResult
654   matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor,
655                   mlir::ConversionPatternRewriter &rewriter) const override {
656     // given: (x + iy) - (x' + iy')
657     // result: (x - x') + i(y - y')
658     auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(),
659                                             rewriter, lowerTy());
660     rewriter.replaceOp(subc, r.getResult());
661     return success();
662   }
663 };
664 
665 /// Inlined complex multiply
666 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> {
667   using FIROpConversion::FIROpConversion;
668 
669   mlir::LogicalResult
670   matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor,
671                   mlir::ConversionPatternRewriter &rewriter) const override {
672     // TODO: Can we use a call to __muldc3 ?
673     // given: (x + iy) * (x' + iy')
674     // result: (xx'-yy')+i(xy'+yx')
675     mlir::Value a = adaptor.getOperands()[0];
676     mlir::Value b = adaptor.getOperands()[1];
677     auto loc = mulc.getLoc();
678     auto *ctx = mulc.getContext();
679     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
680     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
681     mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType()));
682     mlir::Type ty = convertType(mulc.getType());
683     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
684     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
685     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
686     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
687     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
688     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
689     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
690     auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx);
691     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
692     auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy);
693     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
694     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
695     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
696     rewriter.replaceOp(mulc, r0.getResult());
697     return success();
698   }
699 };
700 
701 /// Inlined complex division
702 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> {
703   using FIROpConversion::FIROpConversion;
704 
705   mlir::LogicalResult
706   matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor,
707                   mlir::ConversionPatternRewriter &rewriter) const override {
708     // TODO: Can we use a call to __divdc3 instead?
709     // Just generate inline code for now.
710     // given: (x + iy) / (x' + iy')
711     // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y'
712     mlir::Value a = adaptor.getOperands()[0];
713     mlir::Value b = adaptor.getOperands()[1];
714     auto loc = divc.getLoc();
715     auto *ctx = divc.getContext();
716     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
717     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
718     mlir::Type eleTy = convertType(getComplexEleTy(divc.getType()));
719     mlir::Type ty = convertType(divc.getType());
720     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
721     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
722     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
723     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
724     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
725     auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1);
726     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
727     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
728     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
729     auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1);
730     auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1);
731     auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy);
732     auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy);
733     auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d);
734     auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d);
735     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
736     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
737     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
738     rewriter.replaceOp(divc, r0.getResult());
739     return success();
740   }
741 };
742 
743 /// Inlined complex negation
744 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> {
745   using FIROpConversion::FIROpConversion;
746 
747   mlir::LogicalResult
748   matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor,
749                   mlir::ConversionPatternRewriter &rewriter) const override {
750     // given: -(x + iy)
751     // result: -x - iy
752     auto *ctxt = neg.getContext();
753     auto eleTy = convertType(getComplexEleTy(neg.getType()));
754     auto ty = convertType(neg.getType());
755     auto loc = neg.getLoc();
756     mlir::Value o0 = adaptor.getOperands()[0];
757     auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
758     auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
759     auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0);
760     auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1);
761     auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp);
762     auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip);
763     auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0);
764     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1);
765     return success();
766   }
767 };
768 
769 } // namespace
770 
771 namespace {
772 /// Convert FIR dialect to LLVM dialect
773 ///
774 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An
775 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect.
776 ///
777 /// This pass is not complete yet. We are upstreaming it in small patches.
778 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
779 public:
780   mlir::ModuleOp getModule() { return getOperation(); }
781 
782   void runOnOperation() override final {
783     auto mod = getModule();
784     if (!forcedTargetTriple.empty()) {
785       fir::setTargetTriple(mod, forcedTargetTriple);
786     }
787 
788     auto *context = getModule().getContext();
789     fir::LLVMTypeConverter typeConverter{getModule()};
790     mlir::OwningRewritePatternList pattern(context);
791     pattern
792         .insert<AddcOpConversion, AddrOfOpConversion, CallOpConversion,
793                 ConvertOpConversion, DivcOpConversion, ExtractValueOpConversion,
794                 HasValueOpConversion, GlobalOpConversion,
795                 InsertOnRangeOpConversion, InsertValueOpConversion,
796                 NegcOpConversion, MulcOpConversion, SelectOpConversion,
797                 SelectRankOpConversion, SubcOpConversion, UndefOpConversion,
798                 UnreachableOpConversion, ZeroOpConversion>(typeConverter);
799     mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern);
800     mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
801                                                             pattern);
802     mlir::ConversionTarget target{*context};
803     target.addLegalDialect<mlir::LLVM::LLVMDialect>();
804 
805     // required NOPs for applying a full conversion
806     target.addLegalOp<mlir::ModuleOp>();
807 
808     // apply the patterns
809     if (mlir::failed(mlir::applyFullConversion(getModule(), target,
810                                                std::move(pattern)))) {
811       signalPassFailure();
812     }
813   }
814 };
815 } // namespace
816 
817 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() {
818   return std::make_unique<FIRToLLVMLowering>();
819 }
820