FLANG
integer-value-impl.h
1//===-- lib/Evaluate/integer-value-impl.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_INTEGER_VALUE_IMPL_H_
10#define FORTRAN_EVALUATE_INTEGER_VALUE_IMPL_H_
11
12#include "flang/Evaluate/integer.h"
13#include "llvm/Support/ErrorHandling.h"
14#include <cstdint>
15#include <cstring>
16#include <string>
17#include <type_traits>
18#include <variant>
19
20// Some environments, viz. glibc 2.17 and *BSD, allow the macro HUGE
21// to leak out of <math.h>.
22#undef HUGE
23
24namespace Fortran::evaluate::value {
25
26class IntegerValueImpl {
27public:
28 // Per-KIND fixed-width backing formats. I80 (X87IntegerContainer) is not
29 // itself a Fortran INTEGER kind, but used as REAL(10) storage. While
30 // RealValue has its own RealValueImpl, IntegerValue still needs to able to
31 // hold it with conversions such as RealValue::IntegerValue().
32 using I8 = Integer<8>;
33 using I16 = Integer<16>;
34 using I32 = Integer<32>;
35 using I64 = Integer<64>;
36 using I80 = X87IntegerContainer;
37 using I128 = Integer<128>;
38 using Storage = std::variant<std::monostate, I8, I16, I32, I64, I80, I128>;
39
40 struct ValueWithOverflow;
41 struct ValueWithCarry;
42 struct Product;
44 struct PowerWithErrors;
45
46 // rule-of-five
47 ~IntegerValueImpl() = default;
48 IntegerValueImpl(const IntegerValueImpl &) = default;
49 IntegerValueImpl(IntegerValueImpl &&) = default;
50 IntegerValueImpl &operator=(const IntegerValueImpl &) = default;
51 IntegerValueImpl &operator=(IntegerValueImpl &&) = default;
52
53 IntegerValueImpl() = default;
54 IntegerValueImpl(int kind, const IntegerValueImpl &x) : IntegerValueImpl(x) {
55 CHECK(x.kind() == kind);
56 }
57
58 static IntegerValueImpl Zero(int kind);
59
60 IntegerValueImpl(int kind, uint64_t v, bool isSigned) {
61 WithWordProto(kind, [=](auto wordProto) {
62 using T = decltype(wordProto);
63 storage_ = isSigned ? T{static_cast<int64_t>(v)} : T{v};
64 });
65 }
66
67 IntegerValueImpl(int kind, Fortran::common::uint128_t v) {
68 WithWordProto(kind, [=](auto wordProto) {
69 using T = decltype(wordProto);
70 std::uint64_t lo{static_cast<std::uint64_t>(v)};
71 std::uint64_t hi{static_cast<std::uint64_t>(v >> 64)};
72 storage_ = T{lo}.IOR(T{hi}.SHIFTL(64));
73 });
74 }
75
76 template <typename T> static IntegerValueImpl FromWord(const T &n) {
77 IntegerValueImpl v;
78 v.storage_ = n;
79 return v;
80 }
81
82 static IntegerValueImpl FromRawBytes(
83 int kind, const void *raw, std::size_t expectedSize);
84
85 void print(llvm::raw_ostream &os) const;
86
87#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
88 LLVM_DUMP_METHOD void dump() const;
89#endif
90
91 bool IsNull() const { return storage_.index() == 0; }
92 int kind() const;
93
94 int bits() const;
95
96 std::size_t bytesStored() const { return bytesStored(kind()); }
97 static constexpr std::size_t bytesStored(int kind) {
98 switch (kind) {
99 case 3:
100 return 2;
101 case 10:
102 return 16;
103 default:
104 return kind;
105 }
106 }
107
108 bool IsZero() const;
109
111 static IntegerValueImpl MASKL(int kind, int places);
113 static IntegerValueImpl MASKR(int kind, int places);
114 static IntegerValueImpl HUGE(int kind);
115 static IntegerValueImpl Least(int kind);
116
117 bool IsNegative() const;
118
119 std::uint64_t ToUInt64() const;
120 std::int64_t ToInt64() const;
121
122 Fortran::common::uint128_t ToUInt128() const;
123 Fortran::common::int128_t ToInt128() const;
124
125 // Signed/unsigned comparisons
126 Ordering CompareSigned(const IntegerValueImpl &y) const;
127 Ordering CompareUnsigned(const IntegerValueImpl &y) const;
128 Ordering CompareToZeroSigned() const;
129
130 // Arithmetic
131 ValueWithOverflow Negate() const;
132 ValueWithOverflow ABS() const;
133
134 ValueWithCarry AddUnsigned(
135 const IntegerValueImpl &y, bool carryIn = false) const;
136 ValueWithOverflow AddSigned(const IntegerValueImpl &y) const;
137 ValueWithOverflow SubtractSigned(const IntegerValueImpl &y) const;
138 ValueWithOverflow DIM(const IntegerValueImpl &y) const;
139 ValueWithOverflow SIGN(const IntegerValueImpl &sign) const;
140
141 Product MultiplySigned(const IntegerValueImpl &y) const;
142 Product MultiplyUnsigned(const IntegerValueImpl &y) const;
143 QuotientWithRemainder DivideSigned(const IntegerValueImpl &y) const;
144 QuotientWithRemainder DivideUnsigned(const IntegerValueImpl &y) const;
145 ValueWithOverflow MODULO(const IntegerValueImpl &y) const;
146 PowerWithErrors Power(const IntegerValueImpl &e) const;
147
148 // Bitwise operations
149 IntegerValueImpl NOT() const;
150 IntegerValueImpl IAND(const IntegerValueImpl &y) const;
151 IntegerValueImpl IOR(const IntegerValueImpl &y) const;
152 IntegerValueImpl IEOR(const IntegerValueImpl &y) const;
153 IntegerValueImpl MERGE_BITS(
154 const IntegerValueImpl &y, const IntegerValueImpl &mask) const;
155 IntegerValueImpl MAX(const IntegerValueImpl &y) const {
156 return CompareSigned(y) == Ordering::Less ? y : *this;
157 }
158 IntegerValueImpl MIN(const IntegerValueImpl &y) const {
159 return CompareSigned(y) == Ordering::Less ? *this : y;
160 }
161
162 // Shift operations
163 IntegerValueImpl ISHFT(int count) const {
164 return count < 0 ? SHIFTR(-count) : SHIFTL(count);
165 }
166 IntegerValueImpl SHIFTL(int count) const;
167 IntegerValueImpl SHIFTR(int count) const;
168 IntegerValueImpl SHIFTA(int count) const;
169 IntegerValueImpl ISHFTC(int count, int size) const;
170 IntegerValueImpl ISHFTC(int count) const { return ISHFTC(count, bits()); }
171 IntegerValueImpl IBITS(int pos, int size) const;
172 IntegerValueImpl IBSET(int pos) const;
173 IntegerValueImpl IBCLR(int pos) const;
174 IntegerValueImpl DSHIFTL(const IntegerValueImpl &fill, int count) const;
175 IntegerValueImpl DSHIFTR(const IntegerValueImpl &v2, int count) const;
176 bool BTEST(int pos) const;
177 int LEADZ() const;
178 int TRAILZ() const;
179 int POPCNT() const;
180 bool POPPAR() const;
181
182 static ValueWithOverflow ConvertSigned(
183 int toKind, const IntegerValueImpl &from);
184 static ValueWithOverflow ConvertUnsigned(
185 int toKind, const IntegerValueImpl &from);
186
187 static ValueWithOverflow Read(
188 int kind, const char *&pp, int base, bool isSigned);
189
190 // Formatting
191 std::string SignedDecimal() const;
192 std::string UnsignedDecimal() const;
193 std::string Hexadecimal() const;
194
195 void StoreRawBytes(void *dst, size_t size, bool *changed) const;
196
197 // Compile-time dispatchers to current/specified kind
198
201 template <typename T> const T &GetWord() const {
202 // Null can represent any type
203 if (IsNull()) {
204 // Immutable null constant since we need to return a reference
205 static const T null;
206 return null;
207 }
208
209 // std::get throws std::bad_variant_access, but we do not want to rely soley
210 // on exceptions here.
211 if (!std::holds_alternative<T>(storage_)) {
212 DIE("value does not store the requested kind");
213 }
214
215 return std::get<T>(storage_);
216 }
217
218 template <typename F>
219 auto WithWordProto(F &&f) const
220 -> decltype(std::declval<F>()(std::declval<I64>())) {
221 return WithWordProto(kind(), std::forward<F>(f));
222 }
223
224 template <typename F>
225 static auto WithWordProto(int kind, F &&f)
226 -> decltype(std::declval<F>()(std::declval<I64>())) {
227 switch (kind) {
228 case 1:
229 return f(I8{});
230 case 2:
231 case 3:
232 return f(I16{});
233 case 4:
234 return f(I32{});
235 case 8:
236 return f(I64{});
237 case 10:
238 return f(I80{});
239 case 16:
240 return f(I128{});
241 default:
242 DIE("unsupported integer width");
243 }
244 }
245
246 template <typename F>
247 auto WithWord(F &&f) const -> decltype(f(std::declval<const I64 &>())) {
248 return common::visit(
249 common::visitors{
250 [](std::monostate) -> decltype(f(std::declval<const I64 &>())) {
251 DIE("operation on uninitialized IntegerValueImpl");
252 },
253 f,
254 },
255 storage_);
256 }
257
258private:
259 Storage storage_;
260};
261
263 IntegerValueImpl value;
264 bool overflow{false};
265};
266
268 IntegerValueImpl value;
269 bool carry{false};
270};
271
273 IntegerValueImpl upper, lower;
274 bool SignedMultiplicationOverflowed() const { return overflow; }
275 bool overflow{false};
276};
277
279 IntegerValueImpl quotient, remainder;
280 bool divisionByZero{false}, overflow{false};
281};
282
284 IntegerValueImpl power;
285 bool divisionByZero{false}, overflow{false}, zeroToZero{false};
286};
287
288} // namespace Fortran::evaluate::value
289
290namespace llvm {
292inline raw_ostream &operator<<(
293 raw_ostream &os, const Fortran::evaluate::value::IntegerValueImpl &v) {
294 v.print(os);
295 return os;
296}
297} // namespace llvm
298
299#endif // FORTRAN_EVALUATE_INTEGER_VALUE_IMPL_H_
Definition integer-value-impl.h:26
static IntegerValueImpl MASKL(int kind, int places)
Left-justified mask (e.g., MASKL(1) has only its sign bit set)
Definition integer-value-impl.cpp:70
static IntegerValueImpl MASKR(int kind, int places)
Right-justified mask (e.g., MASKR(1) == 1, MASKR(2) == 3, &c.)
Definition integer-value-impl.cpp:77
const T & GetWord() const
Definition integer-value-impl.h:201
Definition integer.h:65
Definition integer-value-impl.h:272