1 //===-- include/flang/Evaluate/fold-designator.h ----------------*- 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_EVALUATE_FOLD_DESIGNATOR_H_
10 #define FORTRAN_EVALUATE_FOLD_DESIGNATOR_H_
11 
12 // Resolves a designator at compilation time to a base symbol, a byte offset
13 // from that symbol, and a byte size.  Also resolves in the reverse direction,
14 // reconstructing a designator from a symbol, byte offset, and size.
15 // Used for resolving variables in DATA statements to ranges in their
16 // initial images.
17 // Some designators can also be folded into constant pointer descriptors,
18 // which also have per-dimension extent and stride information suitable
19 // for initializing a descriptor.
20 // (The designators that cannot be folded are those with vector-valued
21 // subscripts; they are allowed as DATA statement objects, but are not valid
22 // initial pointer targets.)
23 
24 #include "common.h"
25 #include "expression.h"
26 #include "fold.h"
27 #include "shape.h"
28 #include "type.h"
29 #include "variable.h"
30 #include <optional>
31 #include <variant>
32 
33 namespace Fortran::evaluate {
34 
35 using common::ConstantSubscript;
36 
37 // Identifies a single contiguous interval of bytes at a fixed offset
38 // from a known symbol.
39 class OffsetSymbol {
40 public:
OffsetSymbol(const Symbol & symbol,std::size_t bytes)41   OffsetSymbol(const Symbol &symbol, std::size_t bytes)
42       : symbol_{symbol}, size_{bytes} {}
DECLARE_CONSTRUCTORS_AND_ASSIGNMENTS(OffsetSymbol)43   DECLARE_CONSTRUCTORS_AND_ASSIGNMENTS(OffsetSymbol)
44 
45   const Symbol &symbol() const { return *symbol_; }
set_symbol(const Symbol & symbol)46   void set_symbol(const Symbol &symbol) { symbol_ = symbol; };
offset()47   ConstantSubscript offset() const { return offset_; }
Augment(ConstantSubscript n)48   void Augment(ConstantSubscript n) { offset_ += n; }
size()49   std::size_t size() const { return size_; }
set_size(std::size_t bytes)50   void set_size(std::size_t bytes) { size_ = bytes; }
51 
52 private:
53   SymbolRef symbol_;
54   ConstantSubscript offset_{0};
55   std::size_t size_;
56 };
57 
58 // Folds a Designator<T> into a sequence of OffsetSymbols, if it can
59 // be so folded.  Array sections yield multiple results, each
60 // corresponding to an element in array element order.
61 class DesignatorFolder {
62 public:
DesignatorFolder(FoldingContext & c)63   explicit DesignatorFolder(FoldingContext &c) : context_{c} {}
64 
isEmpty()65   bool isEmpty() const { return isEmpty_; }
isOutOfRange()66   bool isOutOfRange() const { return isOutOfRange_; }
67 
68   template <typename T>
FoldDesignator(const Expr<T> & expr)69   std::optional<OffsetSymbol> FoldDesignator(const Expr<T> &expr) {
70     return std::visit(
71         [&](const auto &x) { return FoldDesignator(x, elementNumber_++); },
72         expr.u);
73   }
74 
75 private:
76   std::optional<OffsetSymbol> FoldDesignator(const Symbol &, ConstantSubscript);
FoldDesignator(const SymbolRef & x,ConstantSubscript which)77   std::optional<OffsetSymbol> FoldDesignator(
78       const SymbolRef &x, ConstantSubscript which) {
79     return FoldDesignator(*x, which);
80   }
81   std::optional<OffsetSymbol> FoldDesignator(
82       const ArrayRef &, ConstantSubscript);
83   std::optional<OffsetSymbol> FoldDesignator(
84       const Component &, ConstantSubscript);
85   std::optional<OffsetSymbol> FoldDesignator(
86       const ComplexPart &, ConstantSubscript);
87   std::optional<OffsetSymbol> FoldDesignator(
88       const Substring &, ConstantSubscript);
89   std::optional<OffsetSymbol> FoldDesignator(
90       const DataRef &, ConstantSubscript);
91   std::optional<OffsetSymbol> FoldDesignator(
92       const NamedEntity &, ConstantSubscript);
93   std::optional<OffsetSymbol> FoldDesignator(
94       const CoarrayRef &, ConstantSubscript);
95   std::optional<OffsetSymbol> FoldDesignator(
96       const ProcedureDesignator &, ConstantSubscript);
97 
98   template <typename T>
FoldDesignator(const Expr<T> & expr,ConstantSubscript which)99   std::optional<OffsetSymbol> FoldDesignator(
100       const Expr<T> &expr, ConstantSubscript which) {
101     return std::visit(
102         [&](const auto &x) { return FoldDesignator(x, which); }, expr.u);
103   }
104 
105   template <typename A>
FoldDesignator(const A & x,ConstantSubscript)106   std::optional<OffsetSymbol> FoldDesignator(const A &x, ConstantSubscript) {
107     return std::nullopt;
108   }
109 
110   template <typename T>
FoldDesignator(const Designator<T> & designator,ConstantSubscript which)111   std::optional<OffsetSymbol> FoldDesignator(
112       const Designator<T> &designator, ConstantSubscript which) {
113     return std::visit(
114         [&](const auto &x) { return FoldDesignator(x, which); }, designator.u);
115   }
116   template <int KIND>
FoldDesignator(const Designator<Type<TypeCategory::Character,KIND>> & designator,ConstantSubscript which)117   std::optional<OffsetSymbol> FoldDesignator(
118       const Designator<Type<TypeCategory::Character, KIND>> &designator,
119       ConstantSubscript which) {
120     return std::visit(
121         common::visitors{
122             [&](const Substring &ss) {
123               if (const auto *dataRef{ss.GetParentIf<DataRef>()}) {
124                 if (auto result{FoldDesignator(*dataRef, which)}) {
125                   if (auto start{ToInt64(ss.lower())}) {
126                     std::optional<ConstantSubscript> end;
127                     auto len{dataRef->LEN()};
128                     if (ss.upper()) {
129                       end = ToInt64(*ss.upper());
130                     } else if (len) {
131                       end = ToInt64(*len);
132                     }
133                     if (end) {
134                       if (*start < 1) {
135                         isOutOfRange_ = true;
136                       }
137                       result->Augment(KIND * (*start - 1));
138                       result->set_size(
139                           *end >= *start ? KIND * (*end - *start + 1) : 0);
140                       if (len) {
141                         if (auto lenVal{ToInt64(*len)}) {
142                           if (*end > *lenVal) {
143                             isOutOfRange_ = true;
144                           }
145                         }
146                       }
147                       return result;
148                     }
149                   }
150                 }
151               }
152               return std::optional<OffsetSymbol>{};
153             },
154             [&](const auto &x) { return FoldDesignator(x, which); },
155         },
156         designator.u);
157   }
158 
159   FoldingContext &context_;
160   ConstantSubscript elementNumber_{0}; // zero-based
161   bool isEmpty_{false};
162   bool isOutOfRange_{false};
163 };
164 
165 // Reconstructs a Designator<> from a symbol and an offset.
166 std::optional<Expr<SomeType>> OffsetToDesignator(
167     FoldingContext &, const Symbol &, ConstantSubscript offset, std::size_t);
168 std::optional<Expr<SomeType>> OffsetToDesignator(
169     FoldingContext &, const OffsetSymbol &);
170 
171 // Represents a compile-time constant Descriptor suitable for use
172 // as a pointer initializer.  Lower bounds are always 1.
173 struct ConstantObjectPointer : public OffsetSymbol {
174   struct Dimension {
175     ConstantSubscript byteStride;
176     ConstantSubscript extent;
177   };
178   using Dimensions = std::vector<Dimension>;
179 
ConstantObjectPointerConstantObjectPointer180   ConstantObjectPointer(
181       const Symbol &symbol, std::size_t size, Dimensions &&dims)
182       : OffsetSymbol{symbol, size}, dimensions{std::move(dims)} {}
183 
184   // Folds a designator to a constant pointer.  Crashes on failure.
185   // Use IsInitialDataTarget() to validate the expression beforehand.
186   static ConstantObjectPointer From(FoldingContext &, const Expr<SomeType> &);
187 
188   Dimensions dimensions;
189 };
190 
191 } // namespace Fortran::evaluate
192 #endif // FORTRAN_EVALUATE_FOLD_DESIGNATOR_H_
193