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 /// Lower `fir.has_value` operation to `llvm.return` operation.
100 struct HasValueOpConversion : public FIROpConversion<fir::HasValueOp> {
101   using FIROpConversion::FIROpConversion;
102 
103   mlir::LogicalResult
104   matchAndRewrite(fir::HasValueOp op, OpAdaptor adaptor,
105                   mlir::ConversionPatternRewriter &rewriter) const override {
106     rewriter.replaceOpWithNewOp<LLVM::ReturnOp>(op, adaptor.getOperands());
107     return success();
108   }
109 };
110 
111 /// Lower `fir.global` operation to `llvm.global` operation.
112 /// `fir.insert_on_range` operations are replaced with constant dense attribute
113 /// if they are applied on the full range.
114 struct GlobalOpConversion : public FIROpConversion<fir::GlobalOp> {
115   using FIROpConversion::FIROpConversion;
116 
117   mlir::LogicalResult
118   matchAndRewrite(fir::GlobalOp global, OpAdaptor adaptor,
119                   mlir::ConversionPatternRewriter &rewriter) const override {
120     auto tyAttr = convertType(global.getType());
121     if (global.getType().isa<fir::BoxType>())
122       tyAttr = tyAttr.cast<mlir::LLVM::LLVMPointerType>().getElementType();
123     auto loc = global.getLoc();
124     mlir::Attribute initAttr{};
125     if (global.initVal())
126       initAttr = global.initVal().getValue();
127     auto linkage = convertLinkage(global.linkName());
128     auto isConst = global.constant().hasValue();
129     auto g = rewriter.create<mlir::LLVM::GlobalOp>(
130         loc, tyAttr, isConst, linkage, global.sym_name(), initAttr);
131     auto &gr = g.getInitializerRegion();
132     rewriter.inlineRegionBefore(global.region(), gr, gr.end());
133     if (!gr.empty()) {
134       // Replace insert_on_range with a constant dense attribute if the
135       // initialization is on the full range.
136       auto insertOnRangeOps = gr.front().getOps<fir::InsertOnRangeOp>();
137       for (auto insertOp : insertOnRangeOps) {
138         if (isFullRange(insertOp.coor(), insertOp.getType())) {
139           auto seqTyAttr = convertType(insertOp.getType());
140           auto *op = insertOp.val().getDefiningOp();
141           auto constant = mlir::dyn_cast<mlir::arith::ConstantOp>(op);
142           if (!constant) {
143             auto convertOp = mlir::dyn_cast<fir::ConvertOp>(op);
144             if (!convertOp)
145               continue;
146             constant = cast<mlir::arith::ConstantOp>(
147                 convertOp.value().getDefiningOp());
148           }
149           mlir::Type vecType = mlir::VectorType::get(
150               insertOp.getType().getShape(), constant.getType());
151           auto denseAttr = mlir::DenseElementsAttr::get(
152               vecType.cast<ShapedType>(), constant.value());
153           rewriter.setInsertionPointAfter(insertOp);
154           rewriter.replaceOpWithNewOp<mlir::arith::ConstantOp>(
155               insertOp, seqTyAttr, denseAttr);
156         }
157       }
158     }
159     rewriter.eraseOp(global);
160     return success();
161   }
162 
163   bool isFullRange(mlir::ArrayAttr indexes, fir::SequenceType seqTy) const {
164     auto extents = seqTy.getShape();
165     if (indexes.size() / 2 != extents.size())
166       return false;
167     for (unsigned i = 0; i < indexes.size(); i += 2) {
168       if (indexes[i].cast<IntegerAttr>().getInt() != 0)
169         return false;
170       if (indexes[i + 1].cast<IntegerAttr>().getInt() != extents[i / 2] - 1)
171         return false;
172     }
173     return true;
174   }
175 
176   // TODO: String comparaison should be avoided. Replace linkName with an
177   // enumeration.
178   mlir::LLVM::Linkage convertLinkage(Optional<StringRef> optLinkage) const {
179     if (optLinkage.hasValue()) {
180       auto name = optLinkage.getValue();
181       if (name == "internal")
182         return mlir::LLVM::Linkage::Internal;
183       if (name == "linkonce")
184         return mlir::LLVM::Linkage::Linkonce;
185       if (name == "common")
186         return mlir::LLVM::Linkage::Common;
187       if (name == "weak")
188         return mlir::LLVM::Linkage::Weak;
189     }
190     return mlir::LLVM::Linkage::External;
191   }
192 };
193 
194 template <typename OP>
195 void selectMatchAndRewrite(fir::LLVMTypeConverter &lowering, OP select,
196                            typename OP::Adaptor adaptor,
197                            mlir::ConversionPatternRewriter &rewriter) {
198   unsigned conds = select.getNumConditions();
199   auto cases = select.getCases().getValue();
200   mlir::Value selector = adaptor.selector();
201   auto loc = select.getLoc();
202   assert(conds > 0 && "select must have cases");
203 
204   llvm::SmallVector<mlir::Block *> destinations;
205   llvm::SmallVector<mlir::ValueRange> destinationsOperands;
206   mlir::Block *defaultDestination;
207   mlir::ValueRange defaultOperands;
208   llvm::SmallVector<int32_t> caseValues;
209 
210   for (unsigned t = 0; t != conds; ++t) {
211     mlir::Block *dest = select.getSuccessor(t);
212     auto destOps = select.getSuccessorOperands(adaptor.getOperands(), t);
213     const mlir::Attribute &attr = cases[t];
214     if (auto intAttr = attr.template dyn_cast<mlir::IntegerAttr>()) {
215       destinations.push_back(dest);
216       destinationsOperands.push_back(destOps.hasValue() ? *destOps
217                                                         : ValueRange());
218       caseValues.push_back(intAttr.getInt());
219       continue;
220     }
221     assert(attr.template dyn_cast_or_null<mlir::UnitAttr>());
222     assert((t + 1 == conds) && "unit must be last");
223     defaultDestination = dest;
224     defaultOperands = destOps.hasValue() ? *destOps : ValueRange();
225   }
226 
227   // LLVM::SwitchOp takes a i32 type for the selector.
228   if (select.getSelector().getType() != rewriter.getI32Type())
229     selector =
230         rewriter.create<LLVM::TruncOp>(loc, rewriter.getI32Type(), selector);
231 
232   rewriter.replaceOpWithNewOp<mlir::LLVM::SwitchOp>(
233       select, selector,
234       /*defaultDestination=*/defaultDestination,
235       /*defaultOperands=*/defaultOperands,
236       /*caseValues=*/caseValues,
237       /*caseDestinations=*/destinations,
238       /*caseOperands=*/destinationsOperands,
239       /*branchWeights=*/ArrayRef<int32_t>());
240 }
241 
242 /// conversion of fir::SelectOp to an if-then-else ladder
243 struct SelectOpConversion : public FIROpConversion<fir::SelectOp> {
244   using FIROpConversion::FIROpConversion;
245 
246   mlir::LogicalResult
247   matchAndRewrite(fir::SelectOp op, OpAdaptor adaptor,
248                   mlir::ConversionPatternRewriter &rewriter) const override {
249     selectMatchAndRewrite<fir::SelectOp>(lowerTy(), op, adaptor, rewriter);
250     return success();
251   }
252 };
253 
254 /// conversion of fir::SelectRankOp to an if-then-else ladder
255 struct SelectRankOpConversion : public FIROpConversion<fir::SelectRankOp> {
256   using FIROpConversion::FIROpConversion;
257 
258   mlir::LogicalResult
259   matchAndRewrite(fir::SelectRankOp op, OpAdaptor adaptor,
260                   mlir::ConversionPatternRewriter &rewriter) const override {
261     selectMatchAndRewrite<fir::SelectRankOp>(lowerTy(), op, adaptor, rewriter);
262     return success();
263   }
264 };
265 
266 // convert to LLVM IR dialect `undef`
267 struct UndefOpConversion : public FIROpConversion<fir::UndefOp> {
268   using FIROpConversion::FIROpConversion;
269 
270   mlir::LogicalResult
271   matchAndRewrite(fir::UndefOp undef, OpAdaptor,
272                   mlir::ConversionPatternRewriter &rewriter) const override {
273     rewriter.replaceOpWithNewOp<mlir::LLVM::UndefOp>(
274         undef, convertType(undef.getType()));
275     return success();
276   }
277 };
278 
279 // convert to LLVM IR dialect `unreachable`
280 struct UnreachableOpConversion : public FIROpConversion<fir::UnreachableOp> {
281   using FIROpConversion::FIROpConversion;
282 
283   mlir::LogicalResult
284   matchAndRewrite(fir::UnreachableOp unreach, OpAdaptor adaptor,
285                   mlir::ConversionPatternRewriter &rewriter) const override {
286     rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(unreach);
287     return success();
288   }
289 };
290 
291 struct ZeroOpConversion : public FIROpConversion<fir::ZeroOp> {
292   using FIROpConversion::FIROpConversion;
293 
294   mlir::LogicalResult
295   matchAndRewrite(fir::ZeroOp zero, OpAdaptor,
296                   mlir::ConversionPatternRewriter &rewriter) const override {
297     auto ty = convertType(zero.getType());
298     if (ty.isa<mlir::LLVM::LLVMPointerType>()) {
299       rewriter.replaceOpWithNewOp<mlir::LLVM::NullOp>(zero, ty);
300     } else if (ty.isa<mlir::IntegerType>()) {
301       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
302           zero, ty, mlir::IntegerAttr::get(zero.getType(), 0));
303     } else if (mlir::LLVM::isCompatibleFloatingPointType(ty)) {
304       rewriter.replaceOpWithNewOp<mlir::LLVM::ConstantOp>(
305           zero, ty, mlir::FloatAttr::get(zero.getType(), 0.0));
306     } else {
307       // TODO: create ConstantAggregateZero for FIR aggregate/array types.
308       return rewriter.notifyMatchFailure(
309           zero,
310           "conversion of fir.zero with aggregate type not implemented yet");
311     }
312     return success();
313   }
314 };
315 
316 // Code shared between insert_value and extract_value Ops.
317 struct ValueOpCommon {
318   // Translate the arguments pertaining to any multidimensional array to
319   // row-major order for LLVM-IR.
320   static void toRowMajor(SmallVectorImpl<mlir::Attribute> &attrs,
321                          mlir::Type ty) {
322     assert(ty && "type is null");
323     const auto end = attrs.size();
324     for (std::remove_const_t<decltype(end)> i = 0; i < end; ++i) {
325       if (auto seq = ty.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
326         const auto dim = getDimension(seq);
327         if (dim > 1) {
328           auto ub = std::min(i + dim, end);
329           std::reverse(attrs.begin() + i, attrs.begin() + ub);
330           i += dim - 1;
331         }
332         ty = getArrayElementType(seq);
333       } else if (auto st = ty.dyn_cast<mlir::LLVM::LLVMStructType>()) {
334         ty = st.getBody()[attrs[i].cast<mlir::IntegerAttr>().getInt()];
335       } else {
336         llvm_unreachable("index into invalid type");
337       }
338     }
339   }
340 
341   static llvm::SmallVector<mlir::Attribute>
342   collectIndices(mlir::ConversionPatternRewriter &rewriter,
343                  mlir::ArrayAttr arrAttr) {
344     llvm::SmallVector<mlir::Attribute> attrs;
345     for (auto i = arrAttr.begin(), e = arrAttr.end(); i != e; ++i) {
346       if (i->isa<mlir::IntegerAttr>()) {
347         attrs.push_back(*i);
348       } else {
349         auto fieldName = i->cast<mlir::StringAttr>().getValue();
350         ++i;
351         auto ty = i->cast<mlir::TypeAttr>().getValue();
352         auto index = ty.cast<fir::RecordType>().getFieldIndex(fieldName);
353         attrs.push_back(mlir::IntegerAttr::get(rewriter.getI32Type(), index));
354       }
355     }
356     return attrs;
357   }
358 
359 private:
360   static unsigned getDimension(mlir::LLVM::LLVMArrayType ty) {
361     unsigned result = 1;
362     for (auto eleTy = ty.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>();
363          eleTy;
364          eleTy = eleTy.getElementType().dyn_cast<mlir::LLVM::LLVMArrayType>())
365       ++result;
366     return result;
367   }
368 
369   static mlir::Type getArrayElementType(mlir::LLVM::LLVMArrayType ty) {
370     auto eleTy = ty.getElementType();
371     while (auto arrTy = eleTy.dyn_cast<mlir::LLVM::LLVMArrayType>())
372       eleTy = arrTy.getElementType();
373     return eleTy;
374   }
375 };
376 
377 /// Extract a subobject value from an ssa-value of aggregate type
378 struct ExtractValueOpConversion
379     : public FIROpAndTypeConversion<fir::ExtractValueOp>,
380       public ValueOpCommon {
381   using FIROpAndTypeConversion::FIROpAndTypeConversion;
382 
383   mlir::LogicalResult
384   doRewrite(fir::ExtractValueOp extractVal, mlir::Type ty, OpAdaptor adaptor,
385             mlir::ConversionPatternRewriter &rewriter) const override {
386     auto attrs = collectIndices(rewriter, extractVal.coor());
387     toRowMajor(attrs, adaptor.getOperands()[0].getType());
388     auto position = mlir::ArrayAttr::get(extractVal.getContext(), attrs);
389     rewriter.replaceOpWithNewOp<mlir::LLVM::ExtractValueOp>(
390         extractVal, ty, adaptor.getOperands()[0], position);
391     return success();
392   }
393 };
394 
395 /// InsertValue is the generalized instruction for the composition of new
396 /// aggregate type values.
397 struct InsertValueOpConversion
398     : public FIROpAndTypeConversion<fir::InsertValueOp>,
399       public ValueOpCommon {
400   using FIROpAndTypeConversion::FIROpAndTypeConversion;
401 
402   mlir::LogicalResult
403   doRewrite(fir::InsertValueOp insertVal, mlir::Type ty, OpAdaptor adaptor,
404             mlir::ConversionPatternRewriter &rewriter) const override {
405     auto attrs = collectIndices(rewriter, insertVal.coor());
406     toRowMajor(attrs, adaptor.getOperands()[0].getType());
407     auto position = mlir::ArrayAttr::get(insertVal.getContext(), attrs);
408     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
409         insertVal, ty, adaptor.getOperands()[0], adaptor.getOperands()[1],
410         position);
411     return success();
412   }
413 };
414 
415 /// InsertOnRange inserts a value into a sequence over a range of offsets.
416 struct InsertOnRangeOpConversion
417     : public FIROpAndTypeConversion<fir::InsertOnRangeOp> {
418   using FIROpAndTypeConversion::FIROpAndTypeConversion;
419 
420   // Increments an array of subscripts in a row major fasion.
421   void incrementSubscripts(const SmallVector<uint64_t> &dims,
422                            SmallVector<uint64_t> &subscripts) const {
423     for (size_t i = dims.size(); i > 0; --i) {
424       if (++subscripts[i - 1] < dims[i - 1]) {
425         return;
426       }
427       subscripts[i - 1] = 0;
428     }
429   }
430 
431   mlir::LogicalResult
432   doRewrite(fir::InsertOnRangeOp range, mlir::Type ty, OpAdaptor adaptor,
433             mlir::ConversionPatternRewriter &rewriter) const override {
434 
435     llvm::SmallVector<uint64_t> dims;
436     auto type = adaptor.getOperands()[0].getType();
437 
438     // Iteratively extract the array dimensions from the type.
439     while (auto t = type.dyn_cast<mlir::LLVM::LLVMArrayType>()) {
440       dims.push_back(t.getNumElements());
441       type = t.getElementType();
442     }
443 
444     SmallVector<uint64_t> lBounds;
445     SmallVector<uint64_t> uBounds;
446 
447     // Extract integer value from the attribute
448     SmallVector<int64_t> coordinates = llvm::to_vector<4>(
449         llvm::map_range(range.coor(), [](Attribute a) -> int64_t {
450           return a.cast<IntegerAttr>().getInt();
451         }));
452 
453     // Unzip the upper and lower bound and convert to a row major format.
454     for (auto i = coordinates.rbegin(), e = coordinates.rend(); i != e; ++i) {
455       uBounds.push_back(*i++);
456       lBounds.push_back(*i);
457     }
458 
459     auto &subscripts = lBounds;
460     auto loc = range.getLoc();
461     mlir::Value lastOp = adaptor.getOperands()[0];
462     mlir::Value insertVal = adaptor.getOperands()[1];
463 
464     auto i64Ty = rewriter.getI64Type();
465     while (subscripts != uBounds) {
466       // Convert uint64_t's to Attribute's.
467       SmallVector<mlir::Attribute> subscriptAttrs;
468       for (const auto &subscript : subscripts)
469         subscriptAttrs.push_back(IntegerAttr::get(i64Ty, subscript));
470       lastOp = rewriter.create<mlir::LLVM::InsertValueOp>(
471           loc, ty, lastOp, insertVal,
472           ArrayAttr::get(range.getContext(), subscriptAttrs));
473 
474       incrementSubscripts(dims, subscripts);
475     }
476 
477     // Convert uint64_t's to Attribute's.
478     SmallVector<mlir::Attribute> subscriptAttrs;
479     for (const auto &subscript : subscripts)
480       subscriptAttrs.push_back(
481           IntegerAttr::get(rewriter.getI64Type(), subscript));
482     mlir::ArrayRef<mlir::Attribute> arrayRef(subscriptAttrs);
483 
484     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(
485         range, ty, lastOp, insertVal,
486         ArrayAttr::get(range.getContext(), arrayRef));
487 
488     return success();
489   }
490 };
491 
492 static mlir::Type getComplexEleTy(mlir::Type complex) {
493   if (auto cc = complex.dyn_cast<mlir::ComplexType>())
494     return cc.getElementType();
495   return complex.cast<fir::ComplexType>().getElementType();
496 }
497 
498 //
499 // Primitive operations on Complex types
500 //
501 
502 /// Generate inline code for complex addition/subtraction
503 template <typename LLVMOP, typename OPTY>
504 mlir::LLVM::InsertValueOp complexSum(OPTY sumop, mlir::ValueRange opnds,
505                                      mlir::ConversionPatternRewriter &rewriter,
506                                      fir::LLVMTypeConverter &lowering) {
507   mlir::Value a = opnds[0];
508   mlir::Value b = opnds[1];
509   auto loc = sumop.getLoc();
510   auto ctx = sumop.getContext();
511   auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
512   auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
513   mlir::Type eleTy = lowering.convertType(getComplexEleTy(sumop.getType()));
514   mlir::Type ty = lowering.convertType(sumop.getType());
515   auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
516   auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
517   auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
518   auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
519   auto rx = rewriter.create<LLVMOP>(loc, eleTy, x0, x1);
520   auto ry = rewriter.create<LLVMOP>(loc, eleTy, y0, y1);
521   auto r0 = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
522   auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r0, rx, c0);
523   return rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ry, c1);
524 }
525 
526 struct AddcOpConversion : public FIROpConversion<fir::AddcOp> {
527   using FIROpConversion::FIROpConversion;
528 
529   mlir::LogicalResult
530   matchAndRewrite(fir::AddcOp addc, OpAdaptor adaptor,
531                   mlir::ConversionPatternRewriter &rewriter) const override {
532     // given: (x + iy) + (x' + iy')
533     // result: (x + x') + i(y + y')
534     auto r = complexSum<mlir::LLVM::FAddOp>(addc, adaptor.getOperands(),
535                                             rewriter, lowerTy());
536     rewriter.replaceOp(addc, r.getResult());
537     return success();
538   }
539 };
540 
541 struct SubcOpConversion : public FIROpConversion<fir::SubcOp> {
542   using FIROpConversion::FIROpConversion;
543 
544   mlir::LogicalResult
545   matchAndRewrite(fir::SubcOp subc, OpAdaptor adaptor,
546                   mlir::ConversionPatternRewriter &rewriter) const override {
547     // given: (x + iy) - (x' + iy')
548     // result: (x - x') + i(y - y')
549     auto r = complexSum<mlir::LLVM::FSubOp>(subc, adaptor.getOperands(),
550                                             rewriter, lowerTy());
551     rewriter.replaceOp(subc, r.getResult());
552     return success();
553   }
554 };
555 
556 /// Inlined complex multiply
557 struct MulcOpConversion : public FIROpConversion<fir::MulcOp> {
558   using FIROpConversion::FIROpConversion;
559 
560   mlir::LogicalResult
561   matchAndRewrite(fir::MulcOp mulc, OpAdaptor adaptor,
562                   mlir::ConversionPatternRewriter &rewriter) const override {
563     // TODO: Can we use a call to __muldc3 ?
564     // given: (x + iy) * (x' + iy')
565     // result: (xx'-yy')+i(xy'+yx')
566     mlir::Value a = adaptor.getOperands()[0];
567     mlir::Value b = adaptor.getOperands()[1];
568     auto loc = mulc.getLoc();
569     auto *ctx = mulc.getContext();
570     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
571     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
572     mlir::Type eleTy = convertType(getComplexEleTy(mulc.getType()));
573     mlir::Type ty = convertType(mulc.getType());
574     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
575     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
576     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
577     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
578     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
579     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
580     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
581     auto ri = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xy, yx);
582     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
583     auto rr = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, xx, yy);
584     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
585     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
586     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
587     rewriter.replaceOp(mulc, r0.getResult());
588     return success();
589   }
590 };
591 
592 /// Inlined complex division
593 struct DivcOpConversion : public FIROpConversion<fir::DivcOp> {
594   using FIROpConversion::FIROpConversion;
595 
596   mlir::LogicalResult
597   matchAndRewrite(fir::DivcOp divc, OpAdaptor adaptor,
598                   mlir::ConversionPatternRewriter &rewriter) const override {
599     // TODO: Can we use a call to __divdc3 instead?
600     // Just generate inline code for now.
601     // given: (x + iy) / (x' + iy')
602     // result: ((xx'+yy')/d) + i((yx'-xy')/d) where d = x'x' + y'y'
603     mlir::Value a = adaptor.getOperands()[0];
604     mlir::Value b = adaptor.getOperands()[1];
605     auto loc = divc.getLoc();
606     auto *ctx = divc.getContext();
607     auto c0 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(0));
608     auto c1 = mlir::ArrayAttr::get(ctx, rewriter.getI32IntegerAttr(1));
609     mlir::Type eleTy = convertType(getComplexEleTy(divc.getType()));
610     mlir::Type ty = convertType(divc.getType());
611     auto x0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c0);
612     auto y0 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, a, c1);
613     auto x1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c0);
614     auto y1 = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, b, c1);
615     auto xx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, x1);
616     auto x1x1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x1, x1);
617     auto yx = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, x1);
618     auto xy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, x0, y1);
619     auto yy = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y0, y1);
620     auto y1y1 = rewriter.create<mlir::LLVM::FMulOp>(loc, eleTy, y1, y1);
621     auto d = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, x1x1, y1y1);
622     auto rrn = rewriter.create<mlir::LLVM::FAddOp>(loc, eleTy, xx, yy);
623     auto rin = rewriter.create<mlir::LLVM::FSubOp>(loc, eleTy, yx, xy);
624     auto rr = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rrn, d);
625     auto ri = rewriter.create<mlir::LLVM::FDivOp>(loc, eleTy, rin, d);
626     auto ra = rewriter.create<mlir::LLVM::UndefOp>(loc, ty);
627     auto r1 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, ra, rr, c0);
628     auto r0 = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, r1, ri, c1);
629     rewriter.replaceOp(divc, r0.getResult());
630     return success();
631   }
632 };
633 
634 /// Inlined complex negation
635 struct NegcOpConversion : public FIROpConversion<fir::NegcOp> {
636   using FIROpConversion::FIROpConversion;
637 
638   mlir::LogicalResult
639   matchAndRewrite(fir::NegcOp neg, OpAdaptor adaptor,
640                   mlir::ConversionPatternRewriter &rewriter) const override {
641     // given: -(x + iy)
642     // result: -x - iy
643     auto *ctxt = neg.getContext();
644     auto eleTy = convertType(getComplexEleTy(neg.getType()));
645     auto ty = convertType(neg.getType());
646     auto loc = neg.getLoc();
647     mlir::Value o0 = adaptor.getOperands()[0];
648     auto c0 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(0));
649     auto c1 = mlir::ArrayAttr::get(ctxt, rewriter.getI32IntegerAttr(1));
650     auto rp = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c0);
651     auto ip = rewriter.create<mlir::LLVM::ExtractValueOp>(loc, eleTy, o0, c1);
652     auto nrp = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, rp);
653     auto nip = rewriter.create<mlir::LLVM::FNegOp>(loc, eleTy, ip);
654     auto r = rewriter.create<mlir::LLVM::InsertValueOp>(loc, ty, o0, nrp, c0);
655     rewriter.replaceOpWithNewOp<mlir::LLVM::InsertValueOp>(neg, ty, r, nip, c1);
656     return success();
657   }
658 };
659 
660 } // namespace
661 
662 namespace {
663 /// Convert FIR dialect to LLVM dialect
664 ///
665 /// This pass lowers all FIR dialect operations to LLVM IR dialect. An
666 /// MLIR pass is used to lower residual Std dialect to LLVM IR dialect.
667 ///
668 /// This pass is not complete yet. We are upstreaming it in small patches.
669 class FIRToLLVMLowering : public fir::FIRToLLVMLoweringBase<FIRToLLVMLowering> {
670 public:
671   mlir::ModuleOp getModule() { return getOperation(); }
672 
673   void runOnOperation() override final {
674     auto *context = getModule().getContext();
675     fir::LLVMTypeConverter typeConverter{getModule()};
676     mlir::OwningRewritePatternList pattern(context);
677     pattern.insert<AddcOpConversion, AddrOfOpConversion, CallOpConversion,
678                    DivcOpConversion, ExtractValueOpConversion,
679                    HasValueOpConversion, GlobalOpConversion,
680                    InsertOnRangeOpConversion, InsertValueOpConversion,
681                    NegcOpConversion, MulcOpConversion, SelectOpConversion,
682                    SelectRankOpConversion, SubcOpConversion, UndefOpConversion,
683                    UnreachableOpConversion, ZeroOpConversion>(typeConverter);
684     mlir::populateStdToLLVMConversionPatterns(typeConverter, pattern);
685     mlir::arith::populateArithmeticToLLVMConversionPatterns(typeConverter,
686                                                             pattern);
687     mlir::ConversionTarget target{*context};
688     target.addLegalDialect<mlir::LLVM::LLVMDialect>();
689 
690     // required NOPs for applying a full conversion
691     target.addLegalOp<mlir::ModuleOp>();
692 
693     // apply the patterns
694     if (mlir::failed(mlir::applyFullConversion(getModule(), target,
695                                                std::move(pattern)))) {
696       signalPassFailure();
697     }
698   }
699 };
700 } // namespace
701 
702 std::unique_ptr<mlir::Pass> fir::createFIRToLLVMPass() {
703   return std::make_unique<FIRToLLVMLowering>();
704 }
705