1 //===-- ConvertExpr.cpp ---------------------------------------------------===//
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/Lower/ConvertExpr.h"
14 #include "flang/Evaluate/fold.h"
15 #include "flang/Evaluate/real.h"
16 #include "flang/Evaluate/traverse.h"
17 #include "flang/Lower/AbstractConverter.h"
18 #include "flang/Lower/SymbolMap.h"
19 #include "flang/Lower/Todo.h"
20 #include "flang/Semantics/expression.h"
21 #include "flang/Semantics/symbol.h"
22 #include "flang/Semantics/tools.h"
23 #include "flang/Semantics/type.h"
24 #include "mlir/Dialect/StandardOps/IR/Ops.h"
25 #include "llvm/Support/Debug.h"
26 
27 #define DEBUG_TYPE "flang-lower-expr"
28 
29 //===----------------------------------------------------------------------===//
30 // The composition and structure of Fortran::evaluate::Expr is defined in
31 // the various header files in include/flang/Evaluate. You are referred
32 // there for more information on these data structures. Generally speaking,
33 // these data structures are a strongly typed family of abstract data types
34 // that, composed as trees, describe the syntax of Fortran expressions.
35 //
36 // This part of the bridge can traverse these tree structures and lower them
37 // to the correct FIR representation in SSA form.
38 //===----------------------------------------------------------------------===//
39 
40 /// Place \p exv in memory if it is not already a memory reference. If
41 /// \p forceValueType is provided, the value is first casted to the provided
42 /// type before being stored (this is mainly intended for logicals whose value
43 /// may be `i1` but needed to be stored as Fortran logicals).
44 static fir::ExtendedValue
45 placeScalarValueInMemory(fir::FirOpBuilder &builder, mlir::Location loc,
46                          const fir::ExtendedValue &exv,
47                          mlir::Type storageType) {
48   mlir::Value valBase = fir::getBase(exv);
49   if (fir::conformsWithPassByRef(valBase.getType()))
50     return exv;
51 
52   assert(!fir::hasDynamicSize(storageType) &&
53          "only expect statically sized scalars to be by value");
54 
55   // Since `a` is not itself a valid referent, determine its value and
56   // create a temporary location at the beginning of the function for
57   // referencing.
58   mlir::Value val = builder.createConvert(loc, storageType, valBase);
59   mlir::Value temp = builder.createTemporary(
60       loc, storageType,
61       llvm::ArrayRef<mlir::NamedAttribute>{
62           Fortran::lower::getAdaptToByRefAttr(builder)});
63   builder.create<fir::StoreOp>(loc, val, temp);
64   return fir::substBase(exv, temp);
65 }
66 
67 /// Generate a load of a value from an address. Beware that this will lose
68 /// any dynamic type information for polymorphic entities (note that unlimited
69 /// polymorphic cannot be loaded and must not be provided here).
70 static fir::ExtendedValue genLoad(fir::FirOpBuilder &builder,
71                                   mlir::Location loc,
72                                   const fir::ExtendedValue &addr) {
73   return addr.match(
74       [](const fir::CharBoxValue &box) -> fir::ExtendedValue { return box; },
75       [&](const fir::UnboxedValue &v) -> fir::ExtendedValue {
76         if (fir::unwrapRefType(fir::getBase(v).getType())
77                 .isa<fir::RecordType>())
78           return v;
79         return builder.create<fir::LoadOp>(loc, fir::getBase(v));
80       },
81       [&](const fir::MutableBoxValue &box) -> fir::ExtendedValue {
82         TODO(loc, "genLoad for MutableBoxValue");
83       },
84       [&](const fir::BoxValue &box) -> fir::ExtendedValue {
85         TODO(loc, "genLoad for BoxValue");
86       },
87       [&](const auto &) -> fir::ExtendedValue {
88         fir::emitFatalError(
89             loc, "attempting to load whole array or procedure address");
90       });
91 }
92 
93 namespace {
94 
95 /// Lowering of Fortran::evaluate::Expr<T> expressions
96 class ScalarExprLowering {
97 public:
98   using ExtValue = fir::ExtendedValue;
99 
100   explicit ScalarExprLowering(mlir::Location loc,
101                               Fortran::lower::AbstractConverter &converter,
102                               Fortran::lower::SymMap &symMap)
103       : location{loc}, converter{converter},
104         builder{converter.getFirOpBuilder()}, symMap{symMap} {}
105 
106   mlir::Location getLoc() { return location; }
107 
108   template <typename A>
109   mlir::Value genunbox(const A &expr) {
110     ExtValue e = genval(expr);
111     if (const fir::UnboxedValue *r = e.getUnboxed())
112       return *r;
113     fir::emitFatalError(getLoc(), "unboxed expression expected");
114   }
115 
116   /// Generate an integral constant of `value`
117   template <int KIND>
118   mlir::Value genIntegerConstant(mlir::MLIRContext *context,
119                                  std::int64_t value) {
120     mlir::Type type =
121         converter.genType(Fortran::common::TypeCategory::Integer, KIND);
122     return builder.createIntegerConstant(getLoc(), type, value);
123   }
124 
125   /// Generate a logical/boolean constant of `value`
126   mlir::Value genBoolConstant(bool value) {
127     return builder.createBool(getLoc(), value);
128   }
129 
130   /// Returns a reference to a symbol or its box/boxChar descriptor if it has
131   /// one.
132   ExtValue gen(Fortran::semantics::SymbolRef sym) {
133     if (Fortran::lower::SymbolBox val = symMap.lookupSymbol(sym))
134       return val.match([&val](auto &) { return val.toExtendedValue(); });
135     LLVM_DEBUG(llvm::dbgs()
136                << "unknown symbol: " << sym << "\nmap: " << symMap << '\n');
137     fir::emitFatalError(getLoc(), "symbol is not mapped to any IR value");
138   }
139 
140   ExtValue genLoad(const ExtValue &exv) {
141     return ::genLoad(builder, getLoc(), exv);
142   }
143 
144   ExtValue genval(Fortran::semantics::SymbolRef sym) {
145     ExtValue var = gen(sym);
146     if (const fir::UnboxedValue *s = var.getUnboxed())
147       if (fir::isReferenceLike(s->getType()))
148         return genLoad(*s);
149     return var;
150   }
151 
152   ExtValue genval(const Fortran::evaluate::BOZLiteralConstant &) {
153     TODO(getLoc(), "genval BOZ");
154   }
155 
156   /// Return indirection to function designated in ProcedureDesignator.
157   /// The type of the function indirection is not guaranteed to match the one
158   /// of the ProcedureDesignator due to Fortran implicit typing rules.
159   ExtValue genval(const Fortran::evaluate::ProcedureDesignator &proc) {
160     TODO(getLoc(), "genval ProcedureDesignator");
161   }
162 
163   ExtValue genval(const Fortran::evaluate::NullPointer &) {
164     TODO(getLoc(), "genval NullPointer");
165   }
166 
167   ExtValue genval(const Fortran::evaluate::StructureConstructor &ctor) {
168     TODO(getLoc(), "genval StructureConstructor");
169   }
170 
171   /// Lowering of an <i>ac-do-variable</i>, which is not a Symbol.
172   ExtValue genval(const Fortran::evaluate::ImpliedDoIndex &var) {
173     TODO(getLoc(), "genval ImpliedDoIndex");
174   }
175 
176   ExtValue genval(const Fortran::evaluate::DescriptorInquiry &desc) {
177     TODO(getLoc(), "genval DescriptorInquiry");
178   }
179 
180   ExtValue genval(const Fortran::evaluate::TypeParamInquiry &) {
181     TODO(getLoc(), "genval TypeParamInquiry");
182   }
183 
184   template <int KIND>
185   ExtValue genval(const Fortran::evaluate::ComplexComponent<KIND> &part) {
186     TODO(getLoc(), "genval ComplexComponent");
187   }
188 
189   template <int KIND>
190   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
191                       Fortran::common::TypeCategory::Integer, KIND>> &op) {
192     mlir::Value input = genunbox(op.left());
193     // Like LLVM, integer negation is the binary op "0 - value"
194     mlir::Value zero = genIntegerConstant<KIND>(builder.getContext(), 0);
195     return builder.create<mlir::arith::SubIOp>(getLoc(), zero, input);
196   }
197 
198   template <int KIND>
199   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
200                       Fortran::common::TypeCategory::Real, KIND>> &op) {
201     return builder.create<mlir::arith::NegFOp>(getLoc(), genunbox(op.left()));
202   }
203   template <int KIND>
204   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
205                       Fortran::common::TypeCategory::Complex, KIND>> &op) {
206     return builder.create<fir::NegcOp>(getLoc(), genunbox(op.left()));
207   }
208 
209   template <typename OpTy>
210   mlir::Value createBinaryOp(const ExtValue &left, const ExtValue &right) {
211     assert(fir::isUnboxedValue(left) && fir::isUnboxedValue(right));
212     mlir::Value lhs = fir::getBase(left);
213     mlir::Value rhs = fir::getBase(right);
214     assert(lhs.getType() == rhs.getType() && "types must be the same");
215     return builder.create<OpTy>(getLoc(), lhs, rhs);
216   }
217 
218   template <typename OpTy, typename A>
219   mlir::Value createBinaryOp(const A &ex) {
220     ExtValue left = genval(ex.left());
221     return createBinaryOp<OpTy>(left, genval(ex.right()));
222   }
223 
224 #undef GENBIN
225 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp)                           \
226   template <int KIND>                                                          \
227   ExtValue genval(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \
228                       Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) { \
229     return createBinaryOp<GenBinFirOp>(x);                                     \
230   }
231 
232   GENBIN(Add, Integer, mlir::arith::AddIOp)
233   GENBIN(Add, Real, mlir::arith::AddFOp)
234   GENBIN(Add, Complex, fir::AddcOp)
235   GENBIN(Subtract, Integer, mlir::arith::SubIOp)
236   GENBIN(Subtract, Real, mlir::arith::SubFOp)
237   GENBIN(Subtract, Complex, fir::SubcOp)
238   GENBIN(Multiply, Integer, mlir::arith::MulIOp)
239   GENBIN(Multiply, Real, mlir::arith::MulFOp)
240   GENBIN(Multiply, Complex, fir::MulcOp)
241   GENBIN(Divide, Integer, mlir::arith::DivSIOp)
242   GENBIN(Divide, Real, mlir::arith::DivFOp)
243   GENBIN(Divide, Complex, fir::DivcOp)
244 
245   template <Fortran::common::TypeCategory TC, int KIND>
246   ExtValue genval(
247       const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &op) {
248     TODO(getLoc(), "genval Power");
249   }
250 
251   template <Fortran::common::TypeCategory TC, int KIND>
252   ExtValue genval(
253       const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>
254           &op) {
255     TODO(getLoc(), "genval RealToInt");
256   }
257 
258   template <int KIND>
259   ExtValue genval(const Fortran::evaluate::ComplexConstructor<KIND> &op) {
260     TODO(getLoc(), "genval ComplexConstructor");
261   }
262 
263   template <int KIND>
264   ExtValue genval(const Fortran::evaluate::Concat<KIND> &op) {
265     TODO(getLoc(), "genval Concat<KIND>");
266   }
267 
268   /// MIN and MAX operations
269   template <Fortran::common::TypeCategory TC, int KIND>
270   ExtValue
271   genval(const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>>
272              &op) {
273     TODO(getLoc(), "genval Extremum<TC, KIND>");
274   }
275 
276   template <int KIND>
277   ExtValue genval(const Fortran::evaluate::SetLength<KIND> &x) {
278     TODO(getLoc(), "genval SetLength<KIND>");
279   }
280 
281   template <int KIND>
282   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
283                       Fortran::common::TypeCategory::Integer, KIND>> &op) {
284     TODO(getLoc(), "genval integer comparison");
285   }
286   template <int KIND>
287   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
288                       Fortran::common::TypeCategory::Real, KIND>> &op) {
289     TODO(getLoc(), "genval real comparison");
290   }
291   template <int KIND>
292   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
293                       Fortran::common::TypeCategory::Complex, KIND>> &op) {
294     TODO(getLoc(), "genval complex comparison");
295   }
296   template <int KIND>
297   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
298                       Fortran::common::TypeCategory::Character, KIND>> &op) {
299     TODO(getLoc(), "genval char comparison");
300   }
301 
302   ExtValue
303   genval(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &op) {
304     TODO(getLoc(), "genval comparison");
305   }
306 
307   template <Fortran::common::TypeCategory TC1, int KIND,
308             Fortran::common::TypeCategory TC2>
309   ExtValue
310   genval(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>,
311                                           TC2> &convert) {
312     mlir::Type ty = converter.genType(TC1, KIND);
313     mlir::Value operand = genunbox(convert.left());
314     return builder.convertWithSemantics(getLoc(), ty, operand);
315   }
316 
317   template <typename A>
318   ExtValue genval(const Fortran::evaluate::Parentheses<A> &op) {
319     TODO(getLoc(), "genval parentheses<A>");
320   }
321 
322   template <int KIND>
323   ExtValue genval(const Fortran::evaluate::Not<KIND> &op) {
324     TODO(getLoc(), "genval Not<KIND>");
325   }
326 
327   template <int KIND>
328   ExtValue genval(const Fortran::evaluate::LogicalOperation<KIND> &op) {
329     TODO(getLoc(), "genval LogicalOperation<KIND>");
330   }
331 
332   /// Convert a scalar literal constant to IR.
333   template <Fortran::common::TypeCategory TC, int KIND>
334   ExtValue genScalarLit(
335       const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>
336           &value) {
337     if constexpr (TC == Fortran::common::TypeCategory::Integer) {
338       return genIntegerConstant<KIND>(builder.getContext(), value.ToInt64());
339     } else if constexpr (TC == Fortran::common::TypeCategory::Logical) {
340       return genBoolConstant(value.IsTrue());
341     } else if constexpr (TC == Fortran::common::TypeCategory::Real) {
342       TODO(getLoc(), "genval real constant");
343     } else if constexpr (TC == Fortran::common::TypeCategory::Complex) {
344       TODO(getLoc(), "genval complex constant");
345     } else /*constexpr*/ {
346       llvm_unreachable("unhandled constant");
347     }
348   }
349 
350   /// Convert a ascii scalar literal CHARACTER to IR. (specialization)
351   ExtValue
352   genAsciiScalarLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type<
353                         Fortran::common::TypeCategory::Character, 1>> &value,
354                     int64_t len) {
355     assert(value.size() == static_cast<std::uint64_t>(len) &&
356            "value.size() doesn't match with len");
357     return fir::factory::createStringLiteral(builder, getLoc(), value);
358   }
359 
360   template <Fortran::common::TypeCategory TC, int KIND>
361   ExtValue
362   genval(const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>>
363              &con) {
364     if (con.Rank() > 0)
365       TODO(getLoc(), "genval array constant");
366     std::optional<Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>>
367         opt = con.GetScalarValue();
368     assert(opt.has_value() && "constant has no value");
369     if constexpr (TC == Fortran::common::TypeCategory::Character) {
370       if constexpr (KIND == 1)
371         return genAsciiScalarLit(opt.value(), con.LEN());
372       TODO(getLoc(), "genval for Character with KIND != 1");
373     } else {
374       return genScalarLit<TC, KIND>(opt.value());
375     }
376   }
377 
378   fir::ExtendedValue genval(
379       const Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived> &con) {
380     TODO(getLoc(), "genval constant derived");
381   }
382 
383   template <typename A>
384   ExtValue genval(const Fortran::evaluate::ArrayConstructor<A> &) {
385     TODO(getLoc(), "genval ArrayConstructor<A>");
386   }
387 
388   ExtValue gen(const Fortran::evaluate::ComplexPart &x) {
389     TODO(getLoc(), "gen ComplexPart");
390   }
391   ExtValue genval(const Fortran::evaluate::ComplexPart &x) {
392     TODO(getLoc(), "genval ComplexPart");
393   }
394 
395   ExtValue gen(const Fortran::evaluate::Substring &s) {
396     TODO(getLoc(), "gen Substring");
397   }
398   ExtValue genval(const Fortran::evaluate::Substring &ss) {
399     TODO(getLoc(), "genval Substring");
400   }
401 
402   ExtValue genval(const Fortran::evaluate::Subscript &subs) {
403     TODO(getLoc(), "genval Subscript");
404   }
405 
406   ExtValue gen(const Fortran::evaluate::DataRef &dref) {
407     TODO(getLoc(), "gen DataRef");
408   }
409   ExtValue genval(const Fortran::evaluate::DataRef &dref) {
410     TODO(getLoc(), "genval DataRef");
411   }
412 
413   ExtValue gen(const Fortran::evaluate::Component &cmpt) {
414     TODO(getLoc(), "gen Component");
415   }
416   ExtValue genval(const Fortran::evaluate::Component &cmpt) {
417     TODO(getLoc(), "genval Component");
418   }
419 
420   ExtValue genval(const Fortran::semantics::Bound &bound) {
421     TODO(getLoc(), "genval Bound");
422   }
423 
424   ExtValue gen(const Fortran::evaluate::ArrayRef &aref) {
425     TODO(getLoc(), "gen ArrayRef");
426   }
427   ExtValue genval(const Fortran::evaluate::ArrayRef &aref) {
428     TODO(getLoc(), "genval ArrayRef");
429   }
430 
431   ExtValue gen(const Fortran::evaluate::CoarrayRef &coref) {
432     TODO(getLoc(), "gen CoarrayRef");
433   }
434   ExtValue genval(const Fortran::evaluate::CoarrayRef &coref) {
435     TODO(getLoc(), "genval CoarrayRef");
436   }
437 
438   template <typename A>
439   ExtValue gen(const Fortran::evaluate::Designator<A> &des) {
440     return std::visit([&](const auto &x) { return gen(x); }, des.u);
441   }
442   template <typename A>
443   ExtValue genval(const Fortran::evaluate::Designator<A> &des) {
444     return std::visit([&](const auto &x) { return genval(x); }, des.u);
445   }
446 
447   template <typename A>
448   ExtValue gen(const Fortran::evaluate::FunctionRef<A> &funcRef) {
449     TODO(getLoc(), "gen FunctionRef<A>");
450   }
451 
452   template <typename A>
453   ExtValue genval(const Fortran::evaluate::FunctionRef<A> &funcRef) {
454     TODO(getLoc(), "genval FunctionRef<A>");
455   }
456 
457   ExtValue genval(const Fortran::evaluate::ProcedureRef &procRef) {
458     TODO(getLoc(), "genval ProcedureRef");
459   }
460 
461   template <typename A>
462   ExtValue genval(const Fortran::evaluate::Expr<A> &x) {
463     if (isScalar(x))
464       return std::visit([&](const auto &e) { return genval(e); }, x.u);
465     TODO(getLoc(), "genval Expr<A> arrays");
466   }
467 
468   /// Helper to detect Transformational function reference.
469   template <typename T>
470   bool isTransformationalRef(const T &) {
471     return false;
472   }
473   template <typename T>
474   bool isTransformationalRef(const Fortran::evaluate::FunctionRef<T> &funcRef) {
475     return !funcRef.IsElemental() && funcRef.Rank();
476   }
477   template <typename T>
478   bool isTransformationalRef(Fortran::evaluate::Expr<T> expr) {
479     return std::visit([&](const auto &e) { return isTransformationalRef(e); },
480                       expr.u);
481   }
482 
483   template <typename A>
484   ExtValue gen(const Fortran::evaluate::Expr<A> &x) {
485     // Whole array symbols or components, and results of transformational
486     // functions already have a storage and the scalar expression lowering path
487     // is used to not create a new temporary storage.
488     if (isScalar(x) ||
489         Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(x) ||
490         isTransformationalRef(x))
491       return std::visit([&](const auto &e) { return genref(e); }, x.u);
492     TODO(getLoc(), "gen Expr non-scalar");
493   }
494 
495   template <typename A>
496   bool isScalar(const A &x) {
497     return x.Rank() == 0;
498   }
499 
500   template <int KIND>
501   ExtValue genval(const Fortran::evaluate::Expr<Fortran::evaluate::Type<
502                       Fortran::common::TypeCategory::Logical, KIND>> &exp) {
503     return std::visit([&](const auto &e) { return genval(e); }, exp.u);
504   }
505 
506   using RefSet =
507       std::tuple<Fortran::evaluate::ComplexPart, Fortran::evaluate::Substring,
508                  Fortran::evaluate::DataRef, Fortran::evaluate::Component,
509                  Fortran::evaluate::ArrayRef, Fortran::evaluate::CoarrayRef,
510                  Fortran::semantics::SymbolRef>;
511   template <typename A>
512   static constexpr bool inRefSet = Fortran::common::HasMember<A, RefSet>;
513 
514   template <typename A, typename = std::enable_if_t<inRefSet<A>>>
515   ExtValue genref(const A &a) {
516     return gen(a);
517   }
518   template <typename A>
519   ExtValue genref(const A &a) {
520     mlir::Type storageType = converter.genType(toEvExpr(a));
521     return placeScalarValueInMemory(builder, getLoc(), genval(a), storageType);
522   }
523 
524   template <typename A, template <typename> typename T,
525             typename B = std::decay_t<T<A>>,
526             std::enable_if_t<
527                 std::is_same_v<B, Fortran::evaluate::Expr<A>> ||
528                     std::is_same_v<B, Fortran::evaluate::Designator<A>> ||
529                     std::is_same_v<B, Fortran::evaluate::FunctionRef<A>>,
530                 bool> = true>
531   ExtValue genref(const T<A> &x) {
532     return gen(x);
533   }
534 
535 private:
536   mlir::Location location;
537   Fortran::lower::AbstractConverter &converter;
538   fir::FirOpBuilder &builder;
539   Fortran::lower::SymMap &symMap;
540 };
541 } // namespace
542 
543 fir::ExtendedValue Fortran::lower::createSomeExtendedExpression(
544     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
545     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap) {
546   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n');
547   return ScalarExprLowering{loc, converter, symMap}.genval(expr);
548 }
549 
550 fir::ExtendedValue Fortran::lower::createSomeExtendedAddress(
551     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
552     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap) {
553   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n');
554   return ScalarExprLowering{loc, converter, symMap}.gen(expr);
555 }
556