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 #ifndef FORTRAN_LOWER_SYMBOLMAP_H
10 #define FORTRAN_LOWER_SYMBOLMAP_H
11 
12 #include "flang/Common/idioms.h"
13 #include "flang/Common/reference.h"
14 #include "flang/Lower/Support/BoxValue.h"
15 #include "flang/Optimizer/Dialect/FIRType.h"
16 #include "flang/Semantics/symbol.h"
17 #include "mlir/IR/Value.h"
18 #include "llvm/ADT/ArrayRef.h"
19 #include "llvm/ADT/DenseMap.h"
20 #include "llvm/ADT/Optional.h"
21 #include "llvm/ADT/SmallVector.h"
22 #include "llvm/Support/Compiler.h"
23 
24 namespace Fortran::lower {
25 
26 //===----------------------------------------------------------------------===//
27 // Symbol information
28 //===----------------------------------------------------------------------===//
29 
30 /// A dictionary entry of ssa-values that together compose a variable referenced
31 /// by a Symbol. For example, the declaration
32 ///
33 ///   CHARACTER(LEN=i) :: c(j1,j2)
34 ///
35 /// is a single variable `c`. This variable is a two-dimensional array of
36 /// CHARACTER. It has a starting address and three dynamic properties: the LEN
37 /// parameter `i` a runtime value describing the length of the CHARACTER, and
38 /// the `j1` and `j2` runtime values, which describe the shape of the array.
39 ///
40 /// The lowering bridge needs to be able to record all four of these ssa-values
41 /// in the lookup table to be able to correctly lower Fortran to FIR.
42 struct SymbolBox {
43   // For lookups that fail, have a monostate
44   using None = std::monostate;
45 
46   // Trivial intrinsic type
47   using Intrinsic = fir::AbstractBox;
48 
49   // Array variable that uses bounds notation
50   using FullDim = fir::ArrayBoxValue;
51 
52   // CHARACTER type variable with its dependent type LEN parameter
53   using Char = fir::CharBoxValue;
54 
55   // CHARACTER array variable using bounds notation
56   using CharFullDim = fir::CharArrayBoxValue;
57 
58   // Generalized derived type variable
59   using Derived = fir::BoxValue;
60 
61   //===--------------------------------------------------------------------===//
62   // Constructors
63   //===--------------------------------------------------------------------===//
64 
SymbolBoxSymbolBox65   SymbolBox() : box{None{}} {}
66   template <typename A>
SymbolBoxSymbolBox67   SymbolBox(const A &x) : box{x} {}
68 
69   operator bool() const { return !std::holds_alternative<None>(box); }
70 
71   // This operator returns the address of the boxed value. TODO: consider
72   // eliminating this in favor of explicit conversion.
ValueSymbolBox73   operator mlir::Value() const { return getAddr(); }
74 
75   //===--------------------------------------------------------------------===//
76   // Accessors
77   //===--------------------------------------------------------------------===//
78 
79   /// Get address of the boxed value. For a scalar, this is the address of the
80   /// scalar. For an array, this is the address of the first element in the
81   /// array, etc.
getAddrSymbolBox82   mlir::Value getAddr() const {
83     return std::visit(common::visitors{
84                           [](const None &) { return mlir::Value{}; },
85                           [](const auto &x) { return x.getAddr(); },
86                       },
87                       box);
88   }
89 
90   /// Get the LEN type parameter of a CHARACTER boxed value.
getCharLenSymbolBox91   llvm::Optional<mlir::Value> getCharLen() const {
92     using T = llvm::Optional<mlir::Value>;
93     return std::visit(common::visitors{
94                           [](const Char &x) { return T{x.getLen()}; },
95                           [](const CharFullDim &x) { return T{x.getLen()}; },
96                           [](const auto &) { return T{}; },
97                       },
98                       box);
99   }
100 
101   /// Does the boxed value have an intrinsic type?
isIntrinsicSymbolBox102   bool isIntrinsic() const {
103     return std::visit(common::visitors{
104                           [](const Intrinsic &) { return true; },
105                           [](const Char &) { return true; },
106                           [](const auto &x) { return false; },
107                       },
108                       box);
109   }
110 
111   /// Does the boxed value have a rank greater than zero?
hasRankSymbolBox112   bool hasRank() const {
113     return std::visit(
114         common::visitors{
115             [](const Intrinsic &) { return false; },
116             [](const Char &) { return false; },
117             [](const None &) { return false; },
118             [](const auto &x) { return x.getExtents().size() > 0; },
119         },
120         box);
121   }
122 
123   /// Does the boxed value have trivial lower bounds (== 1)?
hasSimpleLBoundsSymbolBox124   bool hasSimpleLBounds() const {
125     if (auto *arr = std::get_if<FullDim>(&box))
126       return arr->getLBounds().empty();
127     if (auto *arr = std::get_if<CharFullDim>(&box))
128       return arr->getLBounds().empty();
129     if (auto *arr = std::get_if<Derived>(&box))
130       return (arr->getExtents().size() > 0) && arr->getLBounds().empty();
131     return false;
132   }
133 
134   /// Does the boxed value have a constant shape?
hasConstantShapeSymbolBox135   bool hasConstantShape() const {
136     if (auto eleTy = fir::dyn_cast_ptrEleTy(getAddr().getType()))
137       if (auto arrTy = eleTy.dyn_cast<fir::SequenceType>())
138         return arrTy.hasConstantShape();
139     return false;
140   }
141 
142   /// Get the lbound if the box explicitly contains it.
getLBoundSymbolBox143   mlir::Value getLBound(unsigned dim) const {
144     return std::visit(
145         common::visitors{
146             [&](const FullDim &box) { return box.getLBounds()[dim]; },
147             [&](const CharFullDim &box) { return box.getLBounds()[dim]; },
148             [&](const Derived &box) { return box.getLBounds()[dim]; },
149             [](const auto &) { return mlir::Value{}; }},
150         box);
151   }
152 
153   /// Apply the lambda `func` to this box value.
154   template <typename ON, typename RT>
155   constexpr RT apply(RT(&&func)(const ON &)) const {
156     if (auto *x = std::get_if<ON>(&box))
157       return func(*x);
158     return RT{};
159   }
160 
161   std::variant<Intrinsic, FullDim, Char, CharFullDim, Derived, None> box;
162 };
163 
164 //===----------------------------------------------------------------------===//
165 // Map of symbol information
166 //===----------------------------------------------------------------------===//
167 
168 /// Helper class to map front-end symbols to their MLIR representation. This
169 /// provides a way to lookup the ssa-values that comprise a Fortran symbol's
170 /// runtime attributes. These attributes include its address, its dynamic size,
171 /// dynamic bounds information for non-scalar entities, dynamic type parameters,
172 /// etc.
173 class SymMap {
174 public:
175   /// Add a trivial symbol mapping to an address.
176   void addSymbol(semantics::SymbolRef sym, mlir::Value value,
177                  bool force = false) {
178     makeSym(sym, SymbolBox::Intrinsic(value), force);
179   }
180 
181   /// Add a scalar CHARACTER mapping to an (address, len).
182   void addCharSymbol(semantics::SymbolRef sym, mlir::Value value,
183                      mlir::Value len, bool force = false) {
184     makeSym(sym, SymbolBox::Char(value, len), force);
185   }
186 
187   /// Add an array mapping with (address, shape).
188   void addSymbolWithShape(semantics::SymbolRef sym, mlir::Value value,
189                           llvm::ArrayRef<mlir::Value> shape,
190                           bool force = false) {
191     makeSym(sym, SymbolBox::FullDim(value, shape), force);
192   }
193 
194   /// Add an array of CHARACTER mapping.
195   void addCharSymbolWithShape(semantics::SymbolRef sym, mlir::Value value,
196                               mlir::Value len,
197                               llvm::ArrayRef<mlir::Value> shape,
198                               bool force = false) {
199     makeSym(sym, SymbolBox::CharFullDim(value, len, shape), force);
200   }
201 
202   /// Add an array mapping with bounds notation.
203   void addSymbolWithBounds(semantics::SymbolRef sym, mlir::Value value,
204                            llvm::ArrayRef<mlir::Value> extents,
205                            llvm::ArrayRef<mlir::Value> lbounds,
206                            bool force = false) {
207     makeSym(sym, SymbolBox::FullDim(value, extents, lbounds), force);
208   }
209 
210   /// Add an array of CHARACTER with bounds notation.
211   void addCharSymbolWithBounds(semantics::SymbolRef sym, mlir::Value value,
212                                mlir::Value len,
213                                llvm::ArrayRef<mlir::Value> extents,
214                                llvm::ArrayRef<mlir::Value> lbounds,
215                                bool force = false) {
216     makeSym(sym, SymbolBox::CharFullDim(value, len, extents, lbounds), force);
217   }
218 
219   /// Generalized derived type mapping.
220   void addDerivedSymbol(semantics::SymbolRef sym, mlir::Value value,
221                         mlir::Value size, llvm::ArrayRef<mlir::Value> extents,
222                         llvm::ArrayRef<mlir::Value> lbounds,
223                         llvm::ArrayRef<mlir::Value> params,
224                         bool force = false) {
225     makeSym(sym, SymbolBox::Derived(value, size, params, extents, lbounds),
226             force);
227   }
228 
229   /// Find `symbol` and return its value if it appears in the current mappings.
lookupSymbol(semantics::SymbolRef sym)230   SymbolBox lookupSymbol(semantics::SymbolRef sym) {
231     auto iter = symbolMap.find(&*sym);
232     return (iter == symbolMap.end()) ? SymbolBox() : iter->second;
233   }
234 
235   /// Remove `sym` from the map.
erase(semantics::SymbolRef sym)236   void erase(semantics::SymbolRef sym) { symbolMap.erase(&*sym); }
237 
238   /// Remove all symbols from the map.
clear()239   void clear() { symbolMap.clear(); }
240 
241   /// Dump the map. For debugging.
242   LLVM_DUMP_METHOD void dump() const;
243 
244 private:
245   /// Add `symbol` to the current map and bind a `box`.
246   void makeSym(semantics::SymbolRef sym, const SymbolBox &box,
247                bool force = false) {
248     if (force)
249       erase(sym);
250     assert(box && "cannot add an undefined symbol box");
251     symbolMap.try_emplace(&*sym, box);
252   }
253 
254   llvm::DenseMap<const semantics::Symbol *, SymbolBox> symbolMap;
255 };
256 
257 } // namespace Fortran::lower
258 
259 #endif // FORTRAN_LOWER_SYMBOLMAP_H
260