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