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