1 //===-- SymbolMap.h -- lowering internal symbol map -------------*- C++ -*-===//
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 #ifndef FORTRAN_LOWER_SYMBOLMAP_H
14 #define FORTRAN_LOWER_SYMBOLMAP_H
15 
16 #include "flang/Common/reference.h"
17 #include "flang/Optimizer/Builder/BoxValue.h"
18 #include "flang/Optimizer/Dialect/FIRType.h"
19 #include "flang/Optimizer/Support/Matcher.h"
20 #include "flang/Semantics/symbol.h"
21 #include "mlir/IR/Value.h"
22 #include "llvm/ADT/ArrayRef.h"
23 #include "llvm/ADT/DenseMap.h"
24 #include "llvm/ADT/Optional.h"
25 #include "llvm/ADT/SmallVector.h"
26 #include "llvm/Support/Compiler.h"
27 
28 namespace Fortran::lower {
29 
30 struct SymbolBox;
31 class SymMap;
32 llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const SymbolBox &symMap);
33 llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const SymMap &symMap);
34 
35 //===----------------------------------------------------------------------===//
36 // Symbol information
37 //===----------------------------------------------------------------------===//
38 
39 /// A dictionary entry of ssa-values that together compose a variable referenced
40 /// by a Symbol. For example, the declaration
41 ///
42 ///   CHARACTER(LEN=i) :: c(j1,j2)
43 ///
44 /// is a single variable `c`. This variable is a two-dimensional array of
45 /// CHARACTER. It has a starting address and three dynamic properties: the LEN
46 /// parameter `i` a runtime value describing the length of the CHARACTER, and
47 /// the `j1` and `j2` runtime values, which describe the shape of the array.
48 ///
49 /// The lowering bridge needs to be able to record all four of these ssa-values
50 /// in the lookup table to be able to correctly lower Fortran to FIR.
51 struct SymbolBox : public fir::details::matcher<SymbolBox> {
52   // For lookups that fail, have a monostate
53   using None = std::monostate;
54 
55   // Trivial intrinsic type
56   using Intrinsic = fir::AbstractBox;
57 
58   // Array variable that uses bounds notation
59   using FullDim = fir::ArrayBoxValue;
60 
61   // CHARACTER type variable with its dependent type LEN parameter
62   using Char = fir::CharBoxValue;
63 
64   // CHARACTER array variable using bounds notation
65   using CharFullDim = fir::CharArrayBoxValue;
66 
67   // Pointer or allocatable variable
68   using PointerOrAllocatable = fir::MutableBoxValue;
69 
70   // Non pointer/allocatable variable that must be tracked with
71   // a fir.box (either because it is not contiguous, or assumed rank, or assumed
72   // type, or polymorphic, or because the fir.box is describing an optional
73   // value and cannot be read into one of the other category when lowering the
74   // symbol).
75   using Box = fir::BoxValue;
76 
77   using VT = std::variant<Intrinsic, FullDim, Char, CharFullDim,
78                           PointerOrAllocatable, Box, None>;
79 
80   //===--------------------------------------------------------------------===//
81   // Constructors
82   //===--------------------------------------------------------------------===//
83 
SymbolBoxSymbolBox84   SymbolBox() : box{None{}} {}
85   template <typename A>
SymbolBoxSymbolBox86   SymbolBox(const A &x) : box{x} {}
87 
88   explicit operator bool() const { return !std::holds_alternative<None>(box); }
89 
toExtendedValueSymbolBox90   fir::ExtendedValue toExtendedValue() const {
91     return match(
92         [](const Fortran::lower::SymbolBox::Intrinsic &box)
93             -> fir::ExtendedValue { return box.getAddr(); },
94         [](const Fortran::lower::SymbolBox::None &) -> fir::ExtendedValue {
95           llvm::report_fatal_error("symbol not mapped");
96         },
97         [](const auto &box) -> fir::ExtendedValue { return box; });
98   }
99 
100   //===--------------------------------------------------------------------===//
101   // Accessors
102   //===--------------------------------------------------------------------===//
103 
104   /// Get address of the boxed value. For a scalar, this is the address of the
105   /// scalar. For an array, this is the address of the first element in the
106   /// array, etc.
getAddrSymbolBox107   mlir::Value getAddr() const {
108     return match([](const None &) { return mlir::Value{}; },
109                  [](const auto &x) { return x.getAddr(); });
110   }
111 
112   /// Does the boxed value have an intrinsic type?
isIntrinsicSymbolBox113   bool isIntrinsic() const {
114     return match([](const Intrinsic &) { return true; },
115                  [](const Char &) { return true; },
116                  [](const PointerOrAllocatable &x) {
117                    return !x.isDerived() && !x.isUnlimitedPolymorphic();
118                  },
119                  [](const Box &x) {
120                    return !x.isDerived() && !x.isUnlimitedPolymorphic();
121                  },
122                  [](const auto &x) { return false; });
123   }
124 
125   /// Does the boxed value have a rank greater than zero?
hasRankSymbolBox126   bool hasRank() const {
127     return match([](const Intrinsic &) { return false; },
128                  [](const Char &) { return false; },
129                  [](const None &) { return false; },
130                  [](const PointerOrAllocatable &x) { return x.hasRank(); },
131                  [](const Box &x) { return x.hasRank(); },
132                  [](const auto &x) { return x.getExtents().size() > 0; });
133   }
134 
135   /// Does the boxed value have trivial lower bounds (== 1)?
hasSimpleLBoundsSymbolBox136   bool hasSimpleLBounds() const {
137     return match(
138         [](const FullDim &arr) { return arr.getLBounds().empty(); },
139         [](const CharFullDim &arr) { return arr.getLBounds().empty(); },
140         [](const Box &arr) { return arr.getLBounds().empty(); },
141         [](const auto &) { return false; });
142   }
143 
144   /// Does the boxed value have a constant shape?
hasConstantShapeSymbolBox145   bool hasConstantShape() const {
146     if (auto eleTy = fir::dyn_cast_ptrEleTy(getAddr().getType()))
147       if (auto arrTy = eleTy.dyn_cast<fir::SequenceType>())
148         return arrTy.hasConstantShape();
149     return false;
150   }
151 
152   /// Get the lbound if the box explicitly contains it.
getLBoundSymbolBox153   mlir::Value getLBound(unsigned dim) const {
154     return match([&](const FullDim &box) { return box.getLBounds()[dim]; },
155                  [&](const CharFullDim &box) { return box.getLBounds()[dim]; },
156                  [&](const Box &box) { return box.getLBounds()[dim]; },
157                  [](const auto &) { return mlir::Value{}; });
158   }
159 
160   /// Apply the lambda `func` to this box value.
161   template <typename ON, typename RT>
162   constexpr RT apply(RT(&&func)(const ON &)) const {
163     if (auto *x = std::get_if<ON>(&box))
164       return func(*x);
165     return RT{};
166   }
167 
matcheeSymbolBox168   const VT &matchee() const { return box; }
169 
170   friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
171                                        const SymbolBox &symBox);
172 
173   /// Dump the map. For debugging.
dumpSymbolBox174   LLVM_DUMP_METHOD void dump() const { llvm::errs() << *this << '\n'; }
175 
176 private:
177   VT box;
178 };
179 
180 //===----------------------------------------------------------------------===//
181 // Map of symbol information
182 //===----------------------------------------------------------------------===//
183 
184 /// Helper class to map front-end symbols to their MLIR representation. This
185 /// provides a way to lookup the ssa-values that comprise a Fortran symbol's
186 /// runtime attributes. These attributes include its address, its dynamic size,
187 /// dynamic bounds information for non-scalar entities, dynamic type parameters,
188 /// etc.
189 class SymMap {
190 public:
191   using AcDoVar = llvm::StringRef;
192 
SymMap()193   SymMap() { pushScope(); }
194   SymMap(const SymMap &) = delete;
195 
pushScope()196   void pushScope() { symbolMapStack.emplace_back(); }
popScope()197   void popScope() {
198     symbolMapStack.pop_back();
199     assert(symbolMapStack.size() >= 1);
200   }
201 
202   /// Add an extended value to the symbol table.
203   void addSymbol(semantics::SymbolRef sym, const fir::ExtendedValue &ext,
204                  bool force = false);
205 
206   /// Add a trivial symbol mapping to an address.
207   void addSymbol(semantics::SymbolRef sym, mlir::Value value,
208                  bool force = false) {
209     makeSym(sym, SymbolBox::Intrinsic(value), force);
210   }
211 
212   /// Add a scalar CHARACTER mapping to an (address, len).
213   void addCharSymbol(semantics::SymbolRef sym, mlir::Value value,
214                      mlir::Value len, bool force = false) {
215     makeSym(sym, SymbolBox::Char(value, len), force);
216   }
217   void addCharSymbol(semantics::SymbolRef sym, const SymbolBox::Char &value,
218                      bool force = false) {
219     makeSym(sym, value, force);
220   }
221 
222   /// Add an array mapping with (address, shape).
223   void addSymbolWithShape(semantics::SymbolRef sym, mlir::Value value,
224                           llvm::ArrayRef<mlir::Value> shape,
225                           bool force = false) {
226     makeSym(sym, SymbolBox::FullDim(value, shape), force);
227   }
228   void addSymbolWithShape(semantics::SymbolRef sym,
229                           const SymbolBox::FullDim &value, bool force = false) {
230     makeSym(sym, value, force);
231   }
232 
233   /// Add an array of CHARACTER mapping.
234   void addCharSymbolWithShape(semantics::SymbolRef sym, mlir::Value value,
235                               mlir::Value len,
236                               llvm::ArrayRef<mlir::Value> shape,
237                               bool force = false) {
238     makeSym(sym, SymbolBox::CharFullDim(value, len, shape), force);
239   }
240   void addCharSymbolWithShape(semantics::SymbolRef sym,
241                               const SymbolBox::CharFullDim &value,
242                               bool force = false) {
243     makeSym(sym, value, force);
244   }
245 
246   /// Add an array mapping with bounds notation.
247   void addSymbolWithBounds(semantics::SymbolRef sym, mlir::Value value,
248                            llvm::ArrayRef<mlir::Value> extents,
249                            llvm::ArrayRef<mlir::Value> lbounds,
250                            bool force = false) {
251     makeSym(sym, SymbolBox::FullDim(value, extents, lbounds), force);
252   }
253   void addSymbolWithBounds(semantics::SymbolRef sym,
254                            const SymbolBox::FullDim &value,
255                            bool force = false) {
256     makeSym(sym, value, force);
257   }
258 
259   /// Add an array of CHARACTER with bounds notation.
260   void addCharSymbolWithBounds(semantics::SymbolRef sym, mlir::Value value,
261                                mlir::Value len,
262                                llvm::ArrayRef<mlir::Value> extents,
263                                llvm::ArrayRef<mlir::Value> lbounds,
264                                bool force = false) {
265     makeSym(sym, SymbolBox::CharFullDim(value, len, extents, lbounds), force);
266   }
267   void addCharSymbolWithBounds(semantics::SymbolRef sym,
268                                const SymbolBox::CharFullDim &value,
269                                bool force = false) {
270     makeSym(sym, value, force);
271   }
272 
273   void addAllocatableOrPointer(semantics::SymbolRef sym,
274                                fir::MutableBoxValue box, bool force = false) {
275     makeSym(sym, box, force);
276   }
277 
278   void addBoxSymbol(semantics::SymbolRef sym, mlir::Value irBox,
279                     llvm::ArrayRef<mlir::Value> lbounds,
280                     llvm::ArrayRef<mlir::Value> explicitParams,
281                     llvm::ArrayRef<mlir::Value> explicitExtents,
282                     bool force = false) {
283     makeSym(sym,
284             SymbolBox::Box(irBox, lbounds, explicitParams, explicitExtents),
285             force);
286   }
287   void addBoxSymbol(semantics::SymbolRef sym, const SymbolBox::Box &value,
288                     bool force = false) {
289     makeSym(sym, value, force);
290   }
291 
292   /// Find `symbol` and return its value if it appears in the current mappings.
293   SymbolBox lookupSymbol(semantics::SymbolRef sym);
lookupSymbol(const semantics::Symbol * sym)294   SymbolBox lookupSymbol(const semantics::Symbol *sym) {
295     return lookupSymbol(*sym);
296   }
297 
298   /// Find `symbol` and return its value if it appears in the inner-most level
299   /// map.
300   SymbolBox shallowLookupSymbol(semantics::SymbolRef sym);
shallowLookupSymbol(const semantics::Symbol * sym)301   SymbolBox shallowLookupSymbol(const semantics::Symbol *sym) {
302     return shallowLookupSymbol(*sym);
303   }
304 
305   /// Find `symbol` and return its value if it appears in the one level up map
306   /// such as for the host variable in host-association in OpenMP code.
307   SymbolBox lookupOneLevelUpSymbol(semantics::SymbolRef sym);
lookupOneLevelUpSymbol(const semantics::Symbol * sym)308   SymbolBox lookupOneLevelUpSymbol(const semantics::Symbol *sym) {
309     return lookupOneLevelUpSymbol(*sym);
310   }
311 
312   /// Add a new binding from the ac-do-variable `var` to `value`.
pushImpliedDoBinding(AcDoVar var,mlir::Value value)313   void pushImpliedDoBinding(AcDoVar var, mlir::Value value) {
314     impliedDoStack.emplace_back(var, value);
315   }
316 
317   /// Pop the most recent implied do binding off the stack.
popImpliedDoBinding()318   void popImpliedDoBinding() {
319     assert(!impliedDoStack.empty());
320     impliedDoStack.pop_back();
321   }
322 
323   /// Lookup the ac-do-variable and return the Value it is bound to.
324   /// If the variable is not found, returns a null Value.
325   mlir::Value lookupImpliedDo(AcDoVar var);
326 
327   /// Remove all symbols from the map.
clear()328   void clear() {
329     symbolMapStack.clear();
330     symbolMapStack.emplace_back();
331     assert(symbolMapStack.size() == 1);
332     impliedDoStack.clear();
333   }
334 
335   friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os,
336                                        const SymMap &symMap);
337 
338   /// Dump the map. For debugging.
dump()339   LLVM_DUMP_METHOD void dump() const { llvm::errs() << *this << '\n'; }
340 
341 private:
342   /// Add `symbol` to the current map and bind a `box`.
343   void makeSym(semantics::SymbolRef symRef, const SymbolBox &box,
344                bool force = false) {
345     const auto *sym = &symRef.get().GetUltimate();
346     if (force)
347       symbolMapStack.back().erase(sym);
348     assert(box && "cannot add an undefined symbol box");
349     symbolMapStack.back().try_emplace(sym, box);
350   }
351 
352   llvm::SmallVector<llvm::DenseMap<const semantics::Symbol *, SymbolBox>>
353       symbolMapStack;
354 
355   // Implied DO induction variables are not represented as Se::Symbol in
356   // Ev::Expr. Keep the variable markers in their own stack.
357   llvm::SmallVector<std::pair<AcDoVar, mlir::Value>> impliedDoStack;
358 };
359 
360 } // namespace Fortran::lower
361 
362 #endif // FORTRAN_LOWER_SYMBOLMAP_H
363