| //===-- SymbolMap.h -- lowering internal symbol map -------------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/ |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef FORTRAN_LOWER_SYMBOLMAP_H |
| #define FORTRAN_LOWER_SYMBOLMAP_H |
| |
| #include "flang/Common/reference.h" |
| #include "flang/Optimizer/Builder/BoxValue.h" |
| #include "flang/Optimizer/Dialect/FIRType.h" |
| #include "flang/Optimizer/Dialect/FortranVariableInterface.h" |
| #include "flang/Optimizer/Support/Matcher.h" |
| #include "flang/Semantics/symbol.h" |
| #include "mlir/IR/Value.h" |
| #include "llvm/ADT/ArrayRef.h" |
| #include "llvm/ADT/DenseMap.h" |
| #include "llvm/ADT/SmallVector.h" |
| #include "llvm/Support/Compiler.h" |
| #include <optional> |
| |
| namespace Fortran::lower { |
| |
| struct SymbolBox; |
| class SymMap; |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const SymbolBox &symMap); |
| llvm::raw_ostream &operator<<(llvm::raw_ostream &os, const SymMap &symMap); |
| |
| //===----------------------------------------------------------------------===// |
| // Symbol information |
| //===----------------------------------------------------------------------===// |
| |
| /// A dictionary entry of ssa-values that together compose a variable referenced |
| /// by a Symbol. For example, the declaration |
| /// |
| /// CHARACTER(LEN=i) :: c(j1,j2) |
| /// |
| /// is a single variable `c`. This variable is a two-dimensional array of |
| /// CHARACTER. It has a starting address and three dynamic properties: the LEN |
| /// parameter `i` a runtime value describing the length of the CHARACTER, and |
| /// the `j1` and `j2` runtime values, which describe the shape of the array. |
| /// |
| /// The lowering bridge needs to be able to record all four of these ssa-values |
| /// in the lookup table to be able to correctly lower Fortran to FIR. |
| struct SymbolBox : public fir::details::matcher<SymbolBox> { |
| // For lookups that fail, have a monostate |
| using None = std::monostate; |
| |
| // Trivial intrinsic type |
| using Intrinsic = fir::AbstractBox; |
| |
| // Array variable that uses bounds notation |
| using FullDim = fir::ArrayBoxValue; |
| |
| // CHARACTER type variable with its dependent type LEN parameter |
| using Char = fir::CharBoxValue; |
| |
| // CHARACTER array variable using bounds notation |
| using CharFullDim = fir::CharArrayBoxValue; |
| |
| // Pointer or allocatable variable |
| using PointerOrAllocatable = fir::MutableBoxValue; |
| |
| // Non pointer/allocatable variable that must be tracked with |
| // a fir.box (either because it is not contiguous, or assumed rank, or assumed |
| // type, or polymorphic, or because the fir.box is describing an optional |
| // value and cannot be read into one of the other category when lowering the |
| // symbol). |
| using Box = fir::BoxValue; |
| |
| using VT = |
| std::variant<Intrinsic, FullDim, Char, CharFullDim, PointerOrAllocatable, |
| Box, fir::FortranVariableOpInterface, None>; |
| |
| //===--------------------------------------------------------------------===// |
| // Constructors |
| //===--------------------------------------------------------------------===// |
| |
| SymbolBox() : box{None{}} {} |
| template <typename A> |
| SymbolBox(const A &x) : box{x} {} |
| |
| explicit operator bool() const { return !std::holds_alternative<None>(box); } |
| |
| //===--------------------------------------------------------------------===// |
| // Accessors |
| //===--------------------------------------------------------------------===// |
| |
| /// Get address of the boxed value. For a scalar, this is the address of the |
| /// scalar. For an array, this is the address of the first element in the |
| /// array, etc. |
| mlir::Value getAddr() const { |
| return match([](const None &) { return mlir::Value{}; }, |
| [](const fir::FortranVariableOpInterface &x) { |
| return fir::FortranVariableOpInterface(x).getBase(); |
| }, |
| [](const auto &x) { return x.getAddr(); }); |
| } |
| |
| std::optional<fir::FortranVariableOpInterface> |
| getIfFortranVariableOpInterface() { |
| return match( |
| [](const fir::FortranVariableOpInterface &x) |
| -> std::optional<fir::FortranVariableOpInterface> { return x; }, |
| [](const auto &x) -> std::optional<fir::FortranVariableOpInterface> { |
| return std::nullopt; |
| }); |
| } |
| |
| /// Apply the lambda `func` to this box value. |
| template <typename ON, typename RT> |
| constexpr RT apply(RT (&&func)(const ON &)) const { |
| if (auto *x = std::get_if<ON>(&box)) |
| return func(*x); |
| return RT{}; |
| } |
| |
| const VT &matchee() const { return box; } |
| |
| friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os, |
| const SymbolBox &symBox); |
| |
| /// Dump the map. For debugging. |
| LLVM_DUMP_METHOD void dump() const { llvm::errs() << *this << '\n'; } |
| |
| private: |
| VT box; |
| }; |
| |
| //===----------------------------------------------------------------------===// |
| // Map of symbol information |
| //===----------------------------------------------------------------------===// |
| |
| /// Helper class to map front-end symbols to their MLIR representation. This |
| /// provides a way to lookup the ssa-values that comprise a Fortran symbol's |
| /// runtime attributes. These attributes include its address, its dynamic size, |
| /// dynamic bounds information for non-scalar entities, dynamic type parameters, |
| /// etc. |
| class SymMap { |
| public: |
| using AcDoVar = llvm::StringRef; |
| |
| SymMap() { pushScope(); } |
| SymMap(const SymMap &) = delete; |
| |
| void pushScope() { symbolMapStack.emplace_back(); } |
| void popScope() { |
| symbolMapStack.pop_back(); |
| assert(symbolMapStack.size() >= 1); |
| } |
| |
| /// Add an extended value to the symbol table. |
| void addSymbol(semantics::SymbolRef sym, const fir::ExtendedValue &ext, |
| bool force = false); |
| |
| /// Add a trivial symbol mapping to an address. |
| void addSymbol(semantics::SymbolRef sym, mlir::Value value, |
| bool force = false) { |
| makeSym(sym, SymbolBox::Intrinsic(value), force); |
| } |
| |
| /// Add a scalar CHARACTER mapping to an (address, len). |
| void addCharSymbol(semantics::SymbolRef sym, mlir::Value value, |
| mlir::Value len, bool force = false) { |
| makeSym(sym, SymbolBox::Char(value, len), force); |
| } |
| void addCharSymbol(semantics::SymbolRef sym, const SymbolBox::Char &value, |
| bool force = false) { |
| makeSym(sym, value, force); |
| } |
| |
| /// Add an array mapping with (address, shape). |
| void addSymbolWithShape(semantics::SymbolRef sym, mlir::Value value, |
| llvm::ArrayRef<mlir::Value> shape, |
| bool force = false) { |
| makeSym(sym, SymbolBox::FullDim(value, shape), force); |
| } |
| void addSymbolWithShape(semantics::SymbolRef sym, |
| const SymbolBox::FullDim &value, bool force = false) { |
| makeSym(sym, value, force); |
| } |
| |
| /// Add an array of CHARACTER mapping. |
| void addCharSymbolWithShape(semantics::SymbolRef sym, mlir::Value value, |
| mlir::Value len, |
| llvm::ArrayRef<mlir::Value> shape, |
| bool force = false) { |
| makeSym(sym, SymbolBox::CharFullDim(value, len, shape), force); |
| } |
| void addCharSymbolWithShape(semantics::SymbolRef sym, |
| const SymbolBox::CharFullDim &value, |
| bool force = false) { |
| makeSym(sym, value, force); |
| } |
| |
| /// Add an array mapping with bounds notation. |
| void addSymbolWithBounds(semantics::SymbolRef sym, mlir::Value value, |
| llvm::ArrayRef<mlir::Value> extents, |
| llvm::ArrayRef<mlir::Value> lbounds, |
| bool force = false) { |
| makeSym(sym, SymbolBox::FullDim(value, extents, lbounds), force); |
| } |
| void addSymbolWithBounds(semantics::SymbolRef sym, |
| const SymbolBox::FullDim &value, |
| bool force = false) { |
| makeSym(sym, value, force); |
| } |
| |
| /// Add an array of CHARACTER with bounds notation. |
| void addCharSymbolWithBounds(semantics::SymbolRef sym, mlir::Value value, |
| mlir::Value len, |
| llvm::ArrayRef<mlir::Value> extents, |
| llvm::ArrayRef<mlir::Value> lbounds, |
| bool force = false) { |
| makeSym(sym, SymbolBox::CharFullDim(value, len, extents, lbounds), force); |
| } |
| void addCharSymbolWithBounds(semantics::SymbolRef sym, |
| const SymbolBox::CharFullDim &value, |
| bool force = false) { |
| makeSym(sym, value, force); |
| } |
| |
| void addAllocatableOrPointer(semantics::SymbolRef sym, |
| fir::MutableBoxValue box, bool force = false) { |
| makeSym(sym, box, force); |
| } |
| |
| void addBoxSymbol(semantics::SymbolRef sym, mlir::Value irBox, |
| llvm::ArrayRef<mlir::Value> lbounds, |
| llvm::ArrayRef<mlir::Value> explicitParams, |
| llvm::ArrayRef<mlir::Value> explicitExtents, |
| bool force = false) { |
| makeSym(sym, |
| SymbolBox::Box(irBox, lbounds, explicitParams, explicitExtents), |
| force); |
| } |
| void addBoxSymbol(semantics::SymbolRef sym, const SymbolBox::Box &value, |
| bool force = false) { |
| makeSym(sym, value, force); |
| } |
| |
| /// Find `symbol` and return its value if it appears in the current mappings. |
| SymbolBox lookupSymbol(semantics::SymbolRef sym); |
| SymbolBox lookupSymbol(const semantics::Symbol *sym) { |
| return lookupSymbol(*sym); |
| } |
| |
| /// Find `symbol` and return its value if it appears in the inner-most level |
| /// map. |
| SymbolBox shallowLookupSymbol(semantics::SymbolRef sym); |
| SymbolBox shallowLookupSymbol(const semantics::Symbol *sym) { |
| return shallowLookupSymbol(*sym); |
| } |
| |
| /// Find `symbol` and return its value if it appears in the one level up map |
| /// such as for the host variable in host-association in OpenMP code. |
| SymbolBox lookupOneLevelUpSymbol(semantics::SymbolRef sym); |
| SymbolBox lookupOneLevelUpSymbol(const semantics::Symbol *sym) { |
| return lookupOneLevelUpSymbol(*sym); |
| } |
| |
| /// Add a new binding from the ac-do-variable `var` to `value`. |
| void pushImpliedDoBinding(AcDoVar var, mlir::Value value) { |
| impliedDoStack.emplace_back(var, value); |
| } |
| |
| /// Pop the most recent implied do binding off the stack. |
| void popImpliedDoBinding() { |
| assert(!impliedDoStack.empty()); |
| impliedDoStack.pop_back(); |
| } |
| |
| /// Lookup the ac-do-variable and return the Value it is bound to. |
| /// If the variable is not found, returns a null Value. |
| mlir::Value lookupImpliedDo(AcDoVar var); |
| |
| /// Remove all symbols from the map. |
| void clear() { |
| symbolMapStack.clear(); |
| symbolMapStack.emplace_back(); |
| assert(symbolMapStack.size() == 1); |
| impliedDoStack.clear(); |
| } |
| |
| friend llvm::raw_ostream &operator<<(llvm::raw_ostream &os, |
| const SymMap &symMap); |
| |
| /// Dump the map. For debugging. |
| LLVM_DUMP_METHOD void dump() const { llvm::errs() << *this << '\n'; } |
| |
| void addVariableDefinition(semantics::SymbolRef symRef, |
| fir::FortranVariableOpInterface definingOp, |
| bool force = false) { |
| makeSym(symRef, SymbolBox(definingOp), force); |
| } |
| |
| void copySymbolBinding(semantics::SymbolRef src, |
| semantics::SymbolRef target) { |
| auto symBox = lookupSymbol(src); |
| assert(symBox && "source binding does not exists"); |
| makeSym(target, symBox, /*force=*/false); |
| } |
| |
| std::optional<fir::FortranVariableOpInterface> |
| lookupVariableDefinition(semantics::SymbolRef sym) { |
| if (auto symBox = lookupSymbol(sym)) |
| return symBox.getIfFortranVariableOpInterface(); |
| return std::nullopt; |
| } |
| |
| private: |
| /// Bind `box` to `symRef` in the symbol map. |
| void makeSym(semantics::SymbolRef symRef, const SymbolBox &box, |
| bool force = false) { |
| auto *sym = symRef->HasLocalLocality() ? &*symRef : &symRef->GetUltimate(); |
| if (force) |
| symbolMapStack.back().erase(sym); |
| assert(box && "cannot add an undefined symbol box"); |
| symbolMapStack.back().try_emplace(sym, box); |
| } |
| |
| llvm::SmallVector<llvm::DenseMap<const semantics::Symbol *, SymbolBox>> |
| symbolMapStack; |
| |
| // Implied DO induction variables are not represented as Se::Symbol in |
| // Ev::Expr. Keep the variable markers in their own stack. |
| llvm::SmallVector<std::pair<AcDoVar, mlir::Value>> impliedDoStack; |
| }; |
| |
| } // namespace Fortran::lower |
| |
| #endif // FORTRAN_LOWER_SYMBOLMAP_H |