diff --git a/flang/lib/Lower/SymbolMap.h b/flang/lib/Lower/SymbolMap.h new file mode 100644 --- /dev/null +++ b/flang/lib/Lower/SymbolMap.h @@ -0,0 +1,257 @@ +//===-- 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 +// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_LOWER_SYMBOLMAP_H +#define FORTRAN_LOWER_SYMBOLMAP_H + +#include "flang/Common/idioms.h" +#include "flang/Common/reference.h" +#include "flang/Lower/Support/BoxValue.h" +#include "flang/Optimizer/Dialect/FIRType.h" +#include "flang/Semantics/symbol.h" +#include "mlir/IR/Value.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" + +namespace Fortran::lower { + +//===----------------------------------------------------------------------===// +// 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 { + // 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; + + // Generalized derived type variable + using Derived = fir::BoxValue; + + //===--------------------------------------------------------------------===// + // Constructors + //===--------------------------------------------------------------------===// + + SymbolBox() : box{None{}} {} + template + SymbolBox(const A &x) : box{x} {} + + operator bool() const { return !std::holds_alternative(box); } + + // This operator returns the address of the boxed value. TODO: consider + // eliminating this in favor of explicit conversion. + operator mlir::Value() const { return getAddr(); } + + //===--------------------------------------------------------------------===// + // 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 std::visit(common::visitors{ + [](const None &) { return mlir::Value{}; }, + [](const auto &x) { return x.addr; }, + }, + box); + } + + /// Get the LEN type parameter of a CHARACTER boxed value. + llvm::Optional getCharLen() const { + using T = llvm::Optional; + return std::visit(common::visitors{ + [](const Char &x) { return T{x.len}; }, + [](const CharFullDim &x) { return T{x.len}; }, + [](const auto &) { return T{}; }, + }, + box); + } + + /// Does the boxed value have an intrinsic type? + bool isIntrinsic() const { + return std::visit(common::visitors{ + [](const Intrinsic &) { return true; }, + [](const Char &) { return true; }, + [](const auto &x) { return false; }, + }, + box); + } + + /// Does the boxed value have a rank greater than zero? + bool hasRank() const { + return std::visit(common::visitors{ + [](const Intrinsic &) { return false; }, + [](const Char &) { return false; }, + [](const None &) { return false; }, + [](const auto &x) { return x.extents.size() > 0; }, + }, + box); + } + + /// Does the boxed value have trivial lower bounds (== 1)? + bool hasSimpleLBounds() const { + if (auto *arr = std::get_if(&box)) + return arr->lbounds.empty(); + if (auto *arr = std::get_if(&box)) + return arr->lbounds.empty(); + if (auto *arr = std::get_if(&box)) + return (arr->extents.size() > 0) && arr->lbounds.empty(); + return false; + } + + /// Does the boxed value have a constant shape? + bool hasConstantShape() const { + if (auto eleTy = fir::dyn_cast_ptrEleTy(getAddr().getType())) + if (auto arrTy = eleTy.dyn_cast()) + return arrTy.hasConstantShape(); + return false; + } + + /// Get the lbound if the box explicitly contains it. + mlir::Value getLBound(unsigned dim) const { + return std::visit( + common::visitors{ + [&](const FullDim &box) { return box.lbounds[dim]; }, + [&](const CharFullDim &box) { return box.lbounds[dim]; }, + [&](const Derived &box) { return box.lbounds[dim]; }, + [](const auto &) { return mlir::Value{}; }}, + box); + } + + /// Apply the lambda `func` to this box value. + template + constexpr RT apply(RT(&&func)(const ON &)) const { + if (auto *x = std::get_if(&box)) + return func(*x); + return RT{}; + } + + std::variant 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: + /// 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); + } + + /// Add an array mapping with (address, shape). + void addSymbolWithShape(semantics::SymbolRef sym, mlir::Value value, + llvm::ArrayRef shape, + bool force = false) { + makeSym(sym, SymbolBox::FullDim(value, shape), force); + } + + /// Add an array of CHARACTER mapping. + void addCharSymbolWithShape(semantics::SymbolRef sym, mlir::Value value, + mlir::Value len, + llvm::ArrayRef shape, + bool force = false) { + makeSym(sym, SymbolBox::CharFullDim(value, len, shape), force); + } + + /// Add an array mapping with bounds notation. + void addSymbolWithBounds(semantics::SymbolRef sym, mlir::Value value, + llvm::ArrayRef extents, + llvm::ArrayRef lbounds, + bool force = false) { + makeSym(sym, SymbolBox::FullDim(value, extents, lbounds), force); + } + + /// Add an array of CHARACTER with bounds notation. + void addCharSymbolWithBounds(semantics::SymbolRef sym, mlir::Value value, + mlir::Value len, + llvm::ArrayRef extents, + llvm::ArrayRef lbounds, + bool force = false) { + makeSym(sym, SymbolBox::CharFullDim(value, len, extents, lbounds), force); + } + + /// Generalized derived type mapping. + void addDerivedSymbol(semantics::SymbolRef sym, mlir::Value value, + mlir::Value size, llvm::ArrayRef extents, + llvm::ArrayRef lbounds, + llvm::ArrayRef params, + bool force = false) { + makeSym(sym, SymbolBox::Derived(value, size, params, extents, lbounds), + force); + } + + /// Find `symbol` and return its value if it appears in the current mappings. + SymbolBox lookupSymbol(semantics::SymbolRef sym) { + auto iter = symbolMap.find(&*sym); + return (iter == symbolMap.end()) ? SymbolBox() : iter->second; + } + + /// Remove `sym` from the map. + void erase(semantics::SymbolRef sym) { symbolMap.erase(&*sym); } + + /// Remove all symbols from the map. + void clear() { symbolMap.clear(); } + + /// Dump the map. For debugging. + void dump() const; + +private: + /// Add `symbol` to the current map and bind a `box`. + void makeSym(semantics::SymbolRef sym, const SymbolBox &box, + bool force = false) { + if (force) + erase(sym); + assert(box && "cannot add an undefined symbol box"); + symbolMap.try_emplace(&*sym, box); + } + + llvm::DenseMap symbolMap; +}; + +} // namespace Fortran::lower + +#endif // FORTRAN_LOWER_SYMBOLMAP_H