xref: /llvm-project/flang/include/flang/Evaluate/common.h (revision 0cda970ecc8a885acf7298a61370a1368b0ea39b)
1 //===-- include/flang/Evaluate/common.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_COMMON_H_
10 #define FORTRAN_EVALUATE_COMMON_H_
11 
12 #include "flang/Common/Fortran-features.h"
13 #include "flang/Common/Fortran.h"
14 #include "flang/Common/default-kinds.h"
15 #include "flang/Common/enum-set.h"
16 #include "flang/Common/idioms.h"
17 #include "flang/Common/indirection.h"
18 #include "flang/Common/restorer.h"
19 #include "flang/Common/target-rounding.h"
20 #include "flang/Parser/char-block.h"
21 #include "flang/Parser/message.h"
22 #include <cinttypes>
23 #include <map>
24 #include <set>
25 #include <string>
26 
27 namespace Fortran::semantics {
28 class DerivedTypeSpec;
29 }
30 
31 namespace Fortran::evaluate {
32 class IntrinsicProcTable;
33 class TargetCharacteristics;
34 
35 using common::ConstantSubscript;
36 using common::RealFlag;
37 using common::RealFlags;
38 using common::RelationalOperator;
39 
40 // Integers are always ordered; reals may not be.
41 ENUM_CLASS(Ordering, Less, Equal, Greater)
42 ENUM_CLASS(Relation, Less, Equal, Greater, Unordered)
43 
44 template <typename A>
45 static constexpr Ordering Compare(const A &x, const A &y) {
46   if (x < y) {
47     return Ordering::Less;
48   } else if (x > y) {
49     return Ordering::Greater;
50   } else {
51     return Ordering::Equal;
52   }
53 }
54 
55 template <typename CH>
56 static constexpr Ordering Compare(
57     const std::basic_string<CH> &x, const std::basic_string<CH> &y) {
58   std::size_t xLen{x.size()}, yLen{y.size()};
59   using String = std::basic_string<CH>;
60   // Fortran CHARACTER comparison is defined with blank padding
61   // to extend a shorter operand.
62   if (xLen < yLen) {
63     return Compare(String{x}.append(yLen - xLen, CH{' '}), y);
64   } else if (xLen > yLen) {
65     return Compare(x, String{y}.append(xLen - yLen, CH{' '}));
66   } else if (x < y) {
67     return Ordering::Less;
68   } else if (x > y) {
69     return Ordering::Greater;
70   } else {
71     return Ordering::Equal;
72   }
73 }
74 
75 static constexpr Ordering Reverse(Ordering ordering) {
76   if (ordering == Ordering::Less) {
77     return Ordering::Greater;
78   } else if (ordering == Ordering::Greater) {
79     return Ordering::Less;
80   } else {
81     return Ordering::Equal;
82   }
83 }
84 
85 static constexpr Relation RelationFromOrdering(Ordering ordering) {
86   if (ordering == Ordering::Less) {
87     return Relation::Less;
88   } else if (ordering == Ordering::Greater) {
89     return Relation::Greater;
90   } else {
91     return Relation::Equal;
92   }
93 }
94 
95 static constexpr Relation Reverse(Relation relation) {
96   if (relation == Relation::Less) {
97     return Relation::Greater;
98   } else if (relation == Relation::Greater) {
99     return Relation::Less;
100   } else {
101     return relation;
102   }
103 }
104 
105 static constexpr bool Satisfies(RelationalOperator op, Ordering order) {
106   switch (order) {
107   case Ordering::Less:
108     return op == RelationalOperator::LT || op == RelationalOperator::LE ||
109         op == RelationalOperator::NE;
110   case Ordering::Equal:
111     return op == RelationalOperator::LE || op == RelationalOperator::EQ ||
112         op == RelationalOperator::GE;
113   case Ordering::Greater:
114     return op == RelationalOperator::NE || op == RelationalOperator::GE ||
115         op == RelationalOperator::GT;
116   }
117   return false; // silence g++ warning
118 }
119 
120 static constexpr bool Satisfies(RelationalOperator op, Relation relation) {
121   switch (relation) {
122   case Relation::Less:
123     return Satisfies(op, Ordering::Less);
124   case Relation::Equal:
125     return Satisfies(op, Ordering::Equal);
126   case Relation::Greater:
127     return Satisfies(op, Ordering::Greater);
128   case Relation::Unordered:
129     return op == RelationalOperator::NE;
130   }
131   return false; // silence g++ warning
132 }
133 
134 template <typename A> struct ValueWithRealFlags {
135   A AccumulateFlags(RealFlags &f) {
136     f |= flags;
137     return value;
138   }
139   A value;
140   RealFlags flags{};
141 };
142 
143 #if FLANG_BIG_ENDIAN
144 constexpr bool isHostLittleEndian{false};
145 #elif FLANG_LITTLE_ENDIAN
146 constexpr bool isHostLittleEndian{true};
147 #else
148 #error host endianness is not known
149 #endif
150 
151 // HostUnsignedInt<BITS> finds the smallest native unsigned integer type
152 // whose size is >= BITS.
153 template <bool LE8, bool LE16, bool LE32, bool LE64> struct SmallestUInt {};
154 template <> struct SmallestUInt<true, true, true, true> {
155   using type = std::uint8_t;
156 };
157 template <> struct SmallestUInt<false, true, true, true> {
158   using type = std::uint16_t;
159 };
160 template <> struct SmallestUInt<false, false, true, true> {
161   using type = std::uint32_t;
162 };
163 template <> struct SmallestUInt<false, false, false, true> {
164   using type = std::uint64_t;
165 };
166 template <int BITS>
167 using HostUnsignedInt =
168     typename SmallestUInt<BITS <= 8, BITS <= 16, BITS <= 32, BITS <= 64>::type;
169 
170 // Many classes in this library follow a common paradigm.
171 // - There is no default constructor (Class() {}), usually to prevent the
172 //   need for std::monostate as a default constituent in a std::variant<>.
173 // - There are full copy and move semantics for construction and assignment.
174 // - Discriminated unions have a std::variant<> member "u" and support
175 //   explicit copy and move constructors as well as comparison for equality.
176 #define DECLARE_CONSTRUCTORS_AND_ASSIGNMENTS(t) \
177   t(const t &); \
178   t(t &&); \
179   t &operator=(const t &); \
180   t &operator=(t &&);
181 #define DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(t) \
182   t(const t &) = default; \
183   t(t &&) = default; \
184   t &operator=(const t &) = default; \
185   t &operator=(t &&) = default;
186 #define DEFINE_DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(t) \
187   t::t(const t &) = default; \
188   t::t(t &&) = default; \
189   t &t::operator=(const t &) = default; \
190   t &t::operator=(t &&) = default;
191 #define CONSTEXPR_CONSTRUCTORS_AND_ASSIGNMENTS(t) \
192   constexpr t(const t &) = default; \
193   constexpr t(t &&) = default; \
194   constexpr t &operator=(const t &) = default; \
195   constexpr t &operator=(t &&) = default;
196 
197 #define CLASS_BOILERPLATE(t) \
198   t() = delete; \
199   DEFAULT_CONSTRUCTORS_AND_ASSIGNMENTS(t)
200 
201 #define UNION_CONSTRUCTORS(t) \
202   template <typename _A> explicit t(const _A &x) : u{x} {} \
203   template <typename _A, typename = common::NoLvalue<_A>> \
204   explicit t(_A &&x) : u(std::move(x)) {}
205 
206 #define EVALUATE_UNION_CLASS_BOILERPLATE(t) \
207   CLASS_BOILERPLATE(t) \
208   UNION_CONSTRUCTORS(t) \
209   bool operator==(const t &) const;
210 
211 // Forward definition of Expr<> so that it can be indirectly used in its own
212 // definition
213 template <typename A> class Expr;
214 
215 class FoldingContext {
216 public:
217   FoldingContext(const common::IntrinsicTypeDefaultKinds &d,
218       const IntrinsicProcTable &t, const TargetCharacteristics &c,
219       const common::LanguageFeatureControl &lfc,
220       std::set<std::string> &tempNames)
221       : defaults_{d}, intrinsics_{t}, targetCharacteristics_{c},
222         languageFeatures_{lfc}, tempNames_{tempNames} {}
223   FoldingContext(const parser::ContextualMessages &m,
224       const common::IntrinsicTypeDefaultKinds &d, const IntrinsicProcTable &t,
225       const TargetCharacteristics &c, const common::LanguageFeatureControl &lfc,
226       std::set<std::string> &tempNames)
227       : messages_{m}, defaults_{d}, intrinsics_{t}, targetCharacteristics_{c},
228         languageFeatures_{lfc}, tempNames_{tempNames} {}
229   FoldingContext(const FoldingContext &that)
230       : messages_{that.messages_}, defaults_{that.defaults_},
231         intrinsics_{that.intrinsics_},
232         targetCharacteristics_{that.targetCharacteristics_},
233         pdtInstance_{that.pdtInstance_}, impliedDos_{that.impliedDos_},
234         languageFeatures_{that.languageFeatures_}, tempNames_{that.tempNames_} {
235   }
236   FoldingContext(
237       const FoldingContext &that, const parser::ContextualMessages &m)
238       : messages_{m}, defaults_{that.defaults_}, intrinsics_{that.intrinsics_},
239         targetCharacteristics_{that.targetCharacteristics_},
240         pdtInstance_{that.pdtInstance_}, impliedDos_{that.impliedDos_},
241         languageFeatures_{that.languageFeatures_}, tempNames_{that.tempNames_} {
242   }
243 
244   parser::ContextualMessages &messages() { return messages_; }
245   const parser::ContextualMessages &messages() const { return messages_; }
246   const common::IntrinsicTypeDefaultKinds &defaults() const {
247     return defaults_;
248   }
249   const semantics::DerivedTypeSpec *pdtInstance() const { return pdtInstance_; }
250   const IntrinsicProcTable &intrinsics() const { return intrinsics_; }
251   const TargetCharacteristics &targetCharacteristics() const {
252     return targetCharacteristics_;
253   }
254   const common::LanguageFeatureControl &languageFeatures() const {
255     return languageFeatures_;
256   }
257   std::optional<parser::CharBlock> moduleFileName() const {
258     return moduleFileName_;
259   }
260   FoldingContext &set_moduleFileName(std::optional<parser::CharBlock> n) {
261     moduleFileName_ = n;
262     return *this;
263   }
264 
265   ConstantSubscript &StartImpliedDo(parser::CharBlock, ConstantSubscript = 1);
266   std::optional<ConstantSubscript> GetImpliedDo(parser::CharBlock) const;
267   void EndImpliedDo(parser::CharBlock);
268 
269   std::map<parser::CharBlock, ConstantSubscript> &impliedDos() {
270     return impliedDos_;
271   }
272 
273   common::Restorer<const semantics::DerivedTypeSpec *> WithPDTInstance(
274       const semantics::DerivedTypeSpec &spec) {
275     return common::ScopedSet(pdtInstance_, &spec);
276   }
277   common::Restorer<const semantics::DerivedTypeSpec *> WithoutPDTInstance() {
278     return common::ScopedSet(pdtInstance_, nullptr);
279   }
280 
281   parser::CharBlock SaveTempName(std::string &&name) {
282     return {*tempNames_.emplace(std::move(name)).first};
283   }
284 
285 private:
286   parser::ContextualMessages messages_;
287   const common::IntrinsicTypeDefaultKinds &defaults_;
288   const IntrinsicProcTable &intrinsics_;
289   const TargetCharacteristics &targetCharacteristics_;
290   const semantics::DerivedTypeSpec *pdtInstance_{nullptr};
291   std::optional<parser::CharBlock> moduleFileName_;
292   std::map<parser::CharBlock, ConstantSubscript> impliedDos_;
293   const common::LanguageFeatureControl &languageFeatures_;
294   std::set<std::string> &tempNames_;
295 };
296 
297 void RealFlagWarnings(FoldingContext &, const RealFlags &, const char *op);
298 } // namespace Fortran::evaluate
299 #endif // FORTRAN_EVALUATE_COMMON_H_
300