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.h"
13 #include "flang/Common/default-kinds.h"
14 #include "flang/Common/enum-set.h"
15 #include "flang/Common/idioms.h"
16 #include "flang/Common/indirection.h"
17 #include "flang/Common/restorer.h"
18 #include "flang/Parser/char-block.h"
19 #include "flang/Parser/message.h"
20 #include <cinttypes>
21 #include <map>
22 #include <string>
23
24 namespace Fortran::semantics {
25 class DerivedTypeSpec;
26 }
27
28 namespace Fortran::evaluate {
29 class IntrinsicProcTable;
30 class TargetCharacteristics;
31
32 using common::ConstantSubscript;
33 using common::RelationalOperator;
34
35 // Integers are always ordered; reals may not be.
ENUM_CLASS(Ordering,Less,Equal,Greater)36 ENUM_CLASS(Ordering, Less, Equal, Greater)
37 ENUM_CLASS(Relation, Less, Equal, Greater, Unordered)
38
39 template <typename A>
40 static constexpr Ordering Compare(const A &x, const A &y) {
41 if (x < y) {
42 return Ordering::Less;
43 } else if (x > y) {
44 return Ordering::Greater;
45 } else {
46 return Ordering::Equal;
47 }
48 }
49
50 template <typename CH>
Compare(const std::basic_string<CH> & x,const std::basic_string<CH> & y)51 static constexpr Ordering Compare(
52 const std::basic_string<CH> &x, const std::basic_string<CH> &y) {
53 std::size_t xLen{x.size()}, yLen{y.size()};
54 using String = std::basic_string<CH>;
55 // Fortran CHARACTER comparison is defined with blank padding
56 // to extend a shorter operand.
57 if (xLen < yLen) {
58 return Compare(String{x}.append(yLen - xLen, CH{' '}), y);
59 } else if (xLen > yLen) {
60 return Compare(x, String{y}.append(xLen - yLen, CH{' '}));
61 } else if (x < y) {
62 return Ordering::Less;
63 } else if (x > y) {
64 return Ordering::Greater;
65 } else {
66 return Ordering::Equal;
67 }
68 }
69
Reverse(Ordering ordering)70 static constexpr Ordering Reverse(Ordering ordering) {
71 if (ordering == Ordering::Less) {
72 return Ordering::Greater;
73 } else if (ordering == Ordering::Greater) {
74 return Ordering::Less;
75 } else {
76 return Ordering::Equal;
77 }
78 }
79
RelationFromOrdering(Ordering ordering)80 static constexpr Relation RelationFromOrdering(Ordering ordering) {
81 if (ordering == Ordering::Less) {
82 return Relation::Less;
83 } else if (ordering == Ordering::Greater) {
84 return Relation::Greater;
85 } else {
86 return Relation::Equal;
87 }
88 }
89
Reverse(Relation relation)90 static constexpr Relation Reverse(Relation relation) {
91 if (relation == Relation::Less) {
92 return Relation::Greater;
93 } else if (relation == Relation::Greater) {
94 return Relation::Less;
95 } else {
96 return relation;
97 }
98 }
99
Satisfies(RelationalOperator op,Ordering order)100 static constexpr bool Satisfies(RelationalOperator op, Ordering order) {
101 switch (order) {
102 case Ordering::Less:
103 return op == RelationalOperator::LT || op == RelationalOperator::LE ||
104 op == RelationalOperator::NE;
105 case Ordering::Equal:
106 return op == RelationalOperator::LE || op == RelationalOperator::EQ ||
107 op == RelationalOperator::GE;
108 case Ordering::Greater:
109 return op == RelationalOperator::NE || op == RelationalOperator::GE ||
110 op == RelationalOperator::GT;
111 }
112 return false; // silence g++ warning
113 }
114
Satisfies(RelationalOperator op,Relation relation)115 static constexpr bool Satisfies(RelationalOperator op, Relation relation) {
116 switch (relation) {
117 case Relation::Less:
118 return Satisfies(op, Ordering::Less);
119 case Relation::Equal:
120 return Satisfies(op, Ordering::Equal);
121 case Relation::Greater:
122 return Satisfies(op, Ordering::Greater);
123 case Relation::Unordered:
124 return op == RelationalOperator::NE;
125 }
126 return false; // silence g++ warning
127 }
128
129 ENUM_CLASS(
130 RealFlag, Overflow, DivideByZero, InvalidArgument, Underflow, Inexact)
131
132 using RealFlags = common::EnumSet<RealFlag, RealFlag_enumSize>;
133
134 template <typename A> struct ValueWithRealFlags {
AccumulateFlagsValueWithRealFlags135 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 : defaults_{d}, intrinsics_{t}, targetCharacteristics_{c} {}
220 FoldingContext(const parser::ContextualMessages &m,
221 const common::IntrinsicTypeDefaultKinds &d, const IntrinsicProcTable &t,
222 const TargetCharacteristics &c)
223 : messages_{m}, defaults_{d}, intrinsics_{t}, targetCharacteristics_{c} {}
224 FoldingContext(const FoldingContext &that)
225 : messages_{that.messages_}, defaults_{that.defaults_},
226 intrinsics_{that.intrinsics_},
227 targetCharacteristics_{that.targetCharacteristics_},
228 pdtInstance_{that.pdtInstance_}, impliedDos_{that.impliedDos_} {}
229 FoldingContext(
230 const FoldingContext &that, const parser::ContextualMessages &m)
231 : messages_{m}, defaults_{that.defaults_}, intrinsics_{that.intrinsics_},
232 targetCharacteristics_{that.targetCharacteristics_},
233 pdtInstance_{that.pdtInstance_}, impliedDos_{that.impliedDos_} {}
234
235 parser::ContextualMessages &messages() { return messages_; }
236 const parser::ContextualMessages &messages() const { return messages_; }
237 const common::IntrinsicTypeDefaultKinds &defaults() const {
238 return defaults_;
239 }
240 const semantics::DerivedTypeSpec *pdtInstance() const { return pdtInstance_; }
241 const IntrinsicProcTable &intrinsics() const { return intrinsics_; }
242 const TargetCharacteristics &targetCharacteristics() const {
243 return targetCharacteristics_;
244 }
245 bool inModuleFile() const { return inModuleFile_; }
246 FoldingContext &set_inModuleFile(bool yes = true) {
247 inModuleFile_ = yes;
248 return *this;
249 }
250
251 ConstantSubscript &StartImpliedDo(parser::CharBlock, ConstantSubscript = 1);
252 std::optional<ConstantSubscript> GetImpliedDo(parser::CharBlock) const;
253 void EndImpliedDo(parser::CharBlock);
254
255 std::map<parser::CharBlock, ConstantSubscript> &impliedDos() {
256 return impliedDos_;
257 }
258
259 common::Restorer<const semantics::DerivedTypeSpec *> WithPDTInstance(
260 const semantics::DerivedTypeSpec &spec) {
261 return common::ScopedSet(pdtInstance_, &spec);
262 }
263
264 private:
265 parser::ContextualMessages messages_;
266 const common::IntrinsicTypeDefaultKinds &defaults_;
267 const IntrinsicProcTable &intrinsics_;
268 const TargetCharacteristics &targetCharacteristics_;
269 const semantics::DerivedTypeSpec *pdtInstance_{nullptr};
270 bool inModuleFile_{false};
271 std::map<parser::CharBlock, ConstantSubscript> impliedDos_;
272 };
273
274 void RealFlagWarnings(FoldingContext &, const RealFlags &, const char *op);
275 } // namespace Fortran::evaluate
276 #endif // FORTRAN_EVALUATE_COMMON_H_
277