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     TODO(getLoc(), "genval Negate integer");
193   }
194 
195   template <int KIND>
196   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
197                       Fortran::common::TypeCategory::Real, KIND>> &op) {
198     TODO(getLoc(), "genval Negate real");
199   }
200   template <int KIND>
201   ExtValue genval(const Fortran::evaluate::Negate<Fortran::evaluate::Type<
202                       Fortran::common::TypeCategory::Complex, KIND>> &op) {
203     TODO(getLoc(), "genval Negate complex");
204   }
205 
206 #undef GENBIN
207 #define GENBIN(GenBinEvOp, GenBinTyCat, GenBinFirOp)                           \
208   template <int KIND>                                                          \
209   ExtValue genval(const Fortran::evaluate::GenBinEvOp<Fortran::evaluate::Type< \
210                       Fortran::common::TypeCategory::GenBinTyCat, KIND>> &x) { \
211     TODO(getLoc(), "genval GenBinEvOp");                                       \
212   }
213 
214   GENBIN(Add, Integer, mlir::arith::AddIOp)
215   GENBIN(Add, Real, mlir::arith::AddFOp)
216   GENBIN(Add, Complex, fir::AddcOp)
217   GENBIN(Subtract, Integer, mlir::arith::SubIOp)
218   GENBIN(Subtract, Real, mlir::arith::SubFOp)
219   GENBIN(Subtract, Complex, fir::SubcOp)
220   GENBIN(Multiply, Integer, mlir::arith::MulIOp)
221   GENBIN(Multiply, Real, mlir::arith::MulFOp)
222   GENBIN(Multiply, Complex, fir::MulcOp)
223   GENBIN(Divide, Integer, mlir::arith::DivSIOp)
224   GENBIN(Divide, Real, mlir::arith::DivFOp)
225   GENBIN(Divide, Complex, fir::DivcOp)
226 
227   template <Fortran::common::TypeCategory TC, int KIND>
228   ExtValue genval(
229       const Fortran::evaluate::Power<Fortran::evaluate::Type<TC, KIND>> &op) {
230     TODO(getLoc(), "genval Power");
231   }
232 
233   template <Fortran::common::TypeCategory TC, int KIND>
234   ExtValue genval(
235       const Fortran::evaluate::RealToIntPower<Fortran::evaluate::Type<TC, KIND>>
236           &op) {
237     TODO(getLoc(), "genval RealToInt");
238   }
239 
240   template <int KIND>
241   ExtValue genval(const Fortran::evaluate::ComplexConstructor<KIND> &op) {
242     TODO(getLoc(), "genval ComplexConstructor");
243   }
244 
245   template <int KIND>
246   ExtValue genval(const Fortran::evaluate::Concat<KIND> &op) {
247     TODO(getLoc(), "genval Concat<KIND>");
248   }
249 
250   /// MIN and MAX operations
251   template <Fortran::common::TypeCategory TC, int KIND>
252   ExtValue
253   genval(const Fortran::evaluate::Extremum<Fortran::evaluate::Type<TC, KIND>>
254              &op) {
255     TODO(getLoc(), "genval Extremum<TC, KIND>");
256   }
257 
258   template <int KIND>
259   ExtValue genval(const Fortran::evaluate::SetLength<KIND> &x) {
260     TODO(getLoc(), "genval SetLength<KIND>");
261   }
262 
263   template <int KIND>
264   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
265                       Fortran::common::TypeCategory::Integer, KIND>> &op) {
266     TODO(getLoc(), "genval integer comparison");
267   }
268   template <int KIND>
269   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
270                       Fortran::common::TypeCategory::Real, KIND>> &op) {
271     TODO(getLoc(), "genval real comparison");
272   }
273   template <int KIND>
274   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
275                       Fortran::common::TypeCategory::Complex, KIND>> &op) {
276     TODO(getLoc(), "genval complex comparison");
277   }
278   template <int KIND>
279   ExtValue genval(const Fortran::evaluate::Relational<Fortran::evaluate::Type<
280                       Fortran::common::TypeCategory::Character, KIND>> &op) {
281     TODO(getLoc(), "genval char comparison");
282   }
283 
284   ExtValue
285   genval(const Fortran::evaluate::Relational<Fortran::evaluate::SomeType> &op) {
286     TODO(getLoc(), "genval comparison");
287   }
288 
289   template <Fortran::common::TypeCategory TC1, int KIND,
290             Fortran::common::TypeCategory TC2>
291   ExtValue
292   genval(const Fortran::evaluate::Convert<Fortran::evaluate::Type<TC1, KIND>,
293                                           TC2> &convert) {
294     mlir::Type ty = converter.genType(TC1, KIND);
295     mlir::Value operand = genunbox(convert.left());
296     return builder.convertWithSemantics(getLoc(), ty, operand);
297   }
298 
299   template <typename A>
300   ExtValue genval(const Fortran::evaluate::Parentheses<A> &op) {
301     TODO(getLoc(), "genval parentheses<A>");
302   }
303 
304   template <int KIND>
305   ExtValue genval(const Fortran::evaluate::Not<KIND> &op) {
306     TODO(getLoc(), "genval Not<KIND>");
307   }
308 
309   template <int KIND>
310   ExtValue genval(const Fortran::evaluate::LogicalOperation<KIND> &op) {
311     TODO(getLoc(), "genval LogicalOperation<KIND>");
312   }
313 
314   /// Convert a scalar literal constant to IR.
315   template <Fortran::common::TypeCategory TC, int KIND>
316   ExtValue genScalarLit(
317       const Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>
318           &value) {
319     if constexpr (TC == Fortran::common::TypeCategory::Integer) {
320       return genIntegerConstant<KIND>(builder.getContext(), value.ToInt64());
321     } else if constexpr (TC == Fortran::common::TypeCategory::Logical) {
322       return genBoolConstant(value.IsTrue());
323     } else if constexpr (TC == Fortran::common::TypeCategory::Real) {
324       TODO(getLoc(), "genval real constant");
325     } else if constexpr (TC == Fortran::common::TypeCategory::Complex) {
326       TODO(getLoc(), "genval complex constant");
327     } else /*constexpr*/ {
328       llvm_unreachable("unhandled constant");
329     }
330   }
331 
332   /// Convert a ascii scalar literal CHARACTER to IR. (specialization)
333   ExtValue
334   genAsciiScalarLit(const Fortran::evaluate::Scalar<Fortran::evaluate::Type<
335                         Fortran::common::TypeCategory::Character, 1>> &value,
336                     int64_t len) {
337     assert(value.size() == static_cast<std::uint64_t>(len) &&
338            "value.size() doesn't match with len");
339     return fir::factory::createStringLiteral(builder, getLoc(), value);
340   }
341 
342   template <Fortran::common::TypeCategory TC, int KIND>
343   ExtValue
344   genval(const Fortran::evaluate::Constant<Fortran::evaluate::Type<TC, KIND>>
345              &con) {
346     if (con.Rank() > 0)
347       TODO(getLoc(), "genval array constant");
348     std::optional<Fortran::evaluate::Scalar<Fortran::evaluate::Type<TC, KIND>>>
349         opt = con.GetScalarValue();
350     assert(opt.has_value() && "constant has no value");
351     if constexpr (TC == Fortran::common::TypeCategory::Character) {
352       if constexpr (KIND == 1)
353         return genAsciiScalarLit(opt.value(), con.LEN());
354       TODO(getLoc(), "genval for Character with KIND != 1");
355     } else {
356       return genScalarLit<TC, KIND>(opt.value());
357     }
358   }
359 
360   fir::ExtendedValue genval(
361       const Fortran::evaluate::Constant<Fortran::evaluate::SomeDerived> &con) {
362     TODO(getLoc(), "genval constant derived");
363   }
364 
365   template <typename A>
366   ExtValue genval(const Fortran::evaluate::ArrayConstructor<A> &) {
367     TODO(getLoc(), "genval ArrayConstructor<A>");
368   }
369 
370   ExtValue gen(const Fortran::evaluate::ComplexPart &x) {
371     TODO(getLoc(), "gen ComplexPart");
372   }
373   ExtValue genval(const Fortran::evaluate::ComplexPart &x) {
374     TODO(getLoc(), "genval ComplexPart");
375   }
376 
377   ExtValue gen(const Fortran::evaluate::Substring &s) {
378     TODO(getLoc(), "gen Substring");
379   }
380   ExtValue genval(const Fortran::evaluate::Substring &ss) {
381     TODO(getLoc(), "genval Substring");
382   }
383 
384   ExtValue genval(const Fortran::evaluate::Subscript &subs) {
385     TODO(getLoc(), "genval Subscript");
386   }
387 
388   ExtValue gen(const Fortran::evaluate::DataRef &dref) {
389     TODO(getLoc(), "gen DataRef");
390   }
391   ExtValue genval(const Fortran::evaluate::DataRef &dref) {
392     TODO(getLoc(), "genval DataRef");
393   }
394 
395   ExtValue gen(const Fortran::evaluate::Component &cmpt) {
396     TODO(getLoc(), "gen Component");
397   }
398   ExtValue genval(const Fortran::evaluate::Component &cmpt) {
399     TODO(getLoc(), "genval Component");
400   }
401 
402   ExtValue genval(const Fortran::semantics::Bound &bound) {
403     TODO(getLoc(), "genval Bound");
404   }
405 
406   ExtValue gen(const Fortran::evaluate::ArrayRef &aref) {
407     TODO(getLoc(), "gen ArrayRef");
408   }
409   ExtValue genval(const Fortran::evaluate::ArrayRef &aref) {
410     TODO(getLoc(), "genval ArrayRef");
411   }
412 
413   ExtValue gen(const Fortran::evaluate::CoarrayRef &coref) {
414     TODO(getLoc(), "gen CoarrayRef");
415   }
416   ExtValue genval(const Fortran::evaluate::CoarrayRef &coref) {
417     TODO(getLoc(), "genval CoarrayRef");
418   }
419 
420   template <typename A>
421   ExtValue gen(const Fortran::evaluate::Designator<A> &des) {
422     return std::visit([&](const auto &x) { return gen(x); }, des.u);
423   }
424   template <typename A>
425   ExtValue genval(const Fortran::evaluate::Designator<A> &des) {
426     return std::visit([&](const auto &x) { return genval(x); }, des.u);
427   }
428 
429   template <typename A>
430   ExtValue gen(const Fortran::evaluate::FunctionRef<A> &funcRef) {
431     TODO(getLoc(), "gen FunctionRef<A>");
432   }
433 
434   template <typename A>
435   ExtValue genval(const Fortran::evaluate::FunctionRef<A> &funcRef) {
436     TODO(getLoc(), "genval FunctionRef<A>");
437   }
438 
439   ExtValue genval(const Fortran::evaluate::ProcedureRef &procRef) {
440     TODO(getLoc(), "genval ProcedureRef");
441   }
442 
443   template <typename A>
444   ExtValue genval(const Fortran::evaluate::Expr<A> &x) {
445     if (isScalar(x))
446       return std::visit([&](const auto &e) { return genval(e); }, x.u);
447     TODO(getLoc(), "genval Expr<A> arrays");
448   }
449 
450   /// Helper to detect Transformational function reference.
451   template <typename T>
452   bool isTransformationalRef(const T &) {
453     return false;
454   }
455   template <typename T>
456   bool isTransformationalRef(const Fortran::evaluate::FunctionRef<T> &funcRef) {
457     return !funcRef.IsElemental() && funcRef.Rank();
458   }
459   template <typename T>
460   bool isTransformationalRef(Fortran::evaluate::Expr<T> expr) {
461     return std::visit([&](const auto &e) { return isTransformationalRef(e); },
462                       expr.u);
463   }
464 
465   template <typename A>
466   ExtValue gen(const Fortran::evaluate::Expr<A> &x) {
467     // Whole array symbols or components, and results of transformational
468     // functions already have a storage and the scalar expression lowering path
469     // is used to not create a new temporary storage.
470     if (isScalar(x) ||
471         Fortran::evaluate::UnwrapWholeSymbolOrComponentDataRef(x) ||
472         isTransformationalRef(x))
473       return std::visit([&](const auto &e) { return genref(e); }, x.u);
474     TODO(getLoc(), "gen Expr non-scalar");
475   }
476 
477   template <typename A>
478   bool isScalar(const A &x) {
479     return x.Rank() == 0;
480   }
481 
482   template <int KIND>
483   ExtValue genval(const Fortran::evaluate::Expr<Fortran::evaluate::Type<
484                       Fortran::common::TypeCategory::Logical, KIND>> &exp) {
485     return std::visit([&](const auto &e) { return genval(e); }, exp.u);
486   }
487 
488   using RefSet =
489       std::tuple<Fortran::evaluate::ComplexPart, Fortran::evaluate::Substring,
490                  Fortran::evaluate::DataRef, Fortran::evaluate::Component,
491                  Fortran::evaluate::ArrayRef, Fortran::evaluate::CoarrayRef,
492                  Fortran::semantics::SymbolRef>;
493   template <typename A>
494   static constexpr bool inRefSet = Fortran::common::HasMember<A, RefSet>;
495 
496   template <typename A, typename = std::enable_if_t<inRefSet<A>>>
497   ExtValue genref(const A &a) {
498     return gen(a);
499   }
500   template <typename A>
501   ExtValue genref(const A &a) {
502     mlir::Type storageType = converter.genType(toEvExpr(a));
503     return placeScalarValueInMemory(builder, getLoc(), genval(a), storageType);
504   }
505 
506   template <typename A, template <typename> typename T,
507             typename B = std::decay_t<T<A>>,
508             std::enable_if_t<
509                 std::is_same_v<B, Fortran::evaluate::Expr<A>> ||
510                     std::is_same_v<B, Fortran::evaluate::Designator<A>> ||
511                     std::is_same_v<B, Fortran::evaluate::FunctionRef<A>>,
512                 bool> = true>
513   ExtValue genref(const T<A> &x) {
514     return gen(x);
515   }
516 
517 private:
518   mlir::Location location;
519   Fortran::lower::AbstractConverter &converter;
520   fir::FirOpBuilder &builder;
521   Fortran::lower::SymMap &symMap;
522 };
523 } // namespace
524 
525 fir::ExtendedValue Fortran::lower::createSomeExtendedExpression(
526     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
527     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap) {
528   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "expr: ") << '\n');
529   return ScalarExprLowering{loc, converter, symMap}.genval(expr);
530 }
531 
532 fir::ExtendedValue Fortran::lower::createSomeExtendedAddress(
533     mlir::Location loc, Fortran::lower::AbstractConverter &converter,
534     const Fortran::lower::SomeExpr &expr, Fortran::lower::SymMap &symMap) {
535   LLVM_DEBUG(expr.AsFortran(llvm::dbgs() << "address: ") << '\n');
536   return ScalarExprLowering{loc, converter, symMap}.gen(expr);
537 }
538