1e5dd7070Spatrick //===--- Integral.h - Wrapper for numeric types for the VM ------*- C++ -*-===//
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick // Defines the VM types and helpers operating on types.
10e5dd7070Spatrick //
11e5dd7070Spatrick //===----------------------------------------------------------------------===//
12e5dd7070Spatrick
13e5dd7070Spatrick #ifndef LLVM_CLANG_AST_INTERP_INTEGRAL_H
14e5dd7070Spatrick #define LLVM_CLANG_AST_INTERP_INTEGRAL_H
15e5dd7070Spatrick
16e5dd7070Spatrick #include "clang/AST/ComparisonCategories.h"
17e5dd7070Spatrick #include "clang/AST/APValue.h"
18e5dd7070Spatrick #include "llvm/ADT/APSInt.h"
19e5dd7070Spatrick #include "llvm/Support/MathExtras.h"
20e5dd7070Spatrick #include "llvm/Support/raw_ostream.h"
21e5dd7070Spatrick #include <cstddef>
22e5dd7070Spatrick #include <cstdint>
23e5dd7070Spatrick
24e5dd7070Spatrick namespace clang {
25e5dd7070Spatrick namespace interp {
26e5dd7070Spatrick
27e5dd7070Spatrick using APInt = llvm::APInt;
28e5dd7070Spatrick using APSInt = llvm::APSInt;
29e5dd7070Spatrick
30e5dd7070Spatrick /// Helper to compare two comparable types.
31e5dd7070Spatrick template <typename T>
Compare(const T & X,const T & Y)32e5dd7070Spatrick ComparisonCategoryResult Compare(const T &X, const T &Y) {
33e5dd7070Spatrick if (X < Y)
34e5dd7070Spatrick return ComparisonCategoryResult::Less;
35e5dd7070Spatrick if (X > Y)
36e5dd7070Spatrick return ComparisonCategoryResult::Greater;
37e5dd7070Spatrick return ComparisonCategoryResult::Equal;
38e5dd7070Spatrick }
39e5dd7070Spatrick
40e5dd7070Spatrick // Helper structure to select the representation.
41e5dd7070Spatrick template <unsigned Bits, bool Signed> struct Repr;
42e5dd7070Spatrick template <> struct Repr<8, false> { using Type = uint8_t; };
43e5dd7070Spatrick template <> struct Repr<16, false> { using Type = uint16_t; };
44e5dd7070Spatrick template <> struct Repr<32, false> { using Type = uint32_t; };
45e5dd7070Spatrick template <> struct Repr<64, false> { using Type = uint64_t; };
46e5dd7070Spatrick template <> struct Repr<8, true> { using Type = int8_t; };
47e5dd7070Spatrick template <> struct Repr<16, true> { using Type = int16_t; };
48e5dd7070Spatrick template <> struct Repr<32, true> { using Type = int32_t; };
49e5dd7070Spatrick template <> struct Repr<64, true> { using Type = int64_t; };
50e5dd7070Spatrick
51e5dd7070Spatrick /// Wrapper around numeric types.
52e5dd7070Spatrick ///
53e5dd7070Spatrick /// These wrappers are required to shared an interface between APSint and
54e5dd7070Spatrick /// builtin primitive numeral types, while optimising for storage and
55e5dd7070Spatrick /// allowing methods operating on primitive type to compile to fast code.
56*12c85518Srobert template <unsigned Bits, bool Signed> class Integral final {
57e5dd7070Spatrick private:
58e5dd7070Spatrick template <unsigned OtherBits, bool OtherSigned> friend class Integral;
59e5dd7070Spatrick
60e5dd7070Spatrick // The primitive representing the integral.
61*12c85518Srobert using ReprT = typename Repr<Bits, Signed>::Type;
62*12c85518Srobert ReprT V;
63e5dd7070Spatrick
64e5dd7070Spatrick /// Primitive representing limits.
65*12c85518Srobert static const auto Min = std::numeric_limits<ReprT>::min();
66*12c85518Srobert static const auto Max = std::numeric_limits<ReprT>::max();
67e5dd7070Spatrick
68e5dd7070Spatrick /// Construct an integral from anything that is convertible to storage.
69e5dd7070Spatrick template <typename T> explicit Integral(T V) : V(V) {}
70e5dd7070Spatrick
71e5dd7070Spatrick public:
72e5dd7070Spatrick /// Zero-initializes an integral.
73e5dd7070Spatrick Integral() : V(0) {}
74e5dd7070Spatrick
75e5dd7070Spatrick /// Constructs an integral from another integral.
76e5dd7070Spatrick template <unsigned SrcBits, bool SrcSign>
77e5dd7070Spatrick explicit Integral(Integral<SrcBits, SrcSign> V) : V(V.V) {}
78e5dd7070Spatrick
79e5dd7070Spatrick /// Construct an integral from a value based on signedness.
80e5dd7070Spatrick explicit Integral(const APSInt &V)
81e5dd7070Spatrick : V(V.isSigned() ? V.getSExtValue() : V.getZExtValue()) {}
82e5dd7070Spatrick
83e5dd7070Spatrick bool operator<(Integral RHS) const { return V < RHS.V; }
84e5dd7070Spatrick bool operator>(Integral RHS) const { return V > RHS.V; }
85e5dd7070Spatrick bool operator<=(Integral RHS) const { return V <= RHS.V; }
86e5dd7070Spatrick bool operator>=(Integral RHS) const { return V >= RHS.V; }
87e5dd7070Spatrick bool operator==(Integral RHS) const { return V == RHS.V; }
88e5dd7070Spatrick bool operator!=(Integral RHS) const { return V != RHS.V; }
89e5dd7070Spatrick
90e5dd7070Spatrick bool operator>(unsigned RHS) const {
91e5dd7070Spatrick return V >= 0 && static_cast<unsigned>(V) > RHS;
92e5dd7070Spatrick }
93e5dd7070Spatrick
94e5dd7070Spatrick Integral operator-() const { return Integral(-V); }
95e5dd7070Spatrick Integral operator~() const { return Integral(~V); }
96e5dd7070Spatrick
97e5dd7070Spatrick template <unsigned DstBits, bool DstSign>
98e5dd7070Spatrick explicit operator Integral<DstBits, DstSign>() const {
99e5dd7070Spatrick return Integral<DstBits, DstSign>(V);
100e5dd7070Spatrick }
101e5dd7070Spatrick
102e5dd7070Spatrick explicit operator unsigned() const { return V; }
103e5dd7070Spatrick explicit operator int64_t() const { return V; }
104e5dd7070Spatrick explicit operator uint64_t() const { return V; }
105e5dd7070Spatrick
106e5dd7070Spatrick APSInt toAPSInt() const {
107e5dd7070Spatrick return APSInt(APInt(Bits, static_cast<uint64_t>(V), Signed), !Signed);
108e5dd7070Spatrick }
109e5dd7070Spatrick APSInt toAPSInt(unsigned NumBits) const {
110*12c85518Srobert if constexpr (Signed)
111e5dd7070Spatrick return APSInt(toAPSInt().sextOrTrunc(NumBits), !Signed);
112e5dd7070Spatrick else
113e5dd7070Spatrick return APSInt(toAPSInt().zextOrTrunc(NumBits), !Signed);
114e5dd7070Spatrick }
115e5dd7070Spatrick APValue toAPValue() const { return APValue(toAPSInt()); }
116e5dd7070Spatrick
117e5dd7070Spatrick Integral<Bits, false> toUnsigned() const {
118e5dd7070Spatrick return Integral<Bits, false>(*this);
119e5dd7070Spatrick }
120e5dd7070Spatrick
121e5dd7070Spatrick constexpr static unsigned bitWidth() { return Bits; }
122e5dd7070Spatrick
123e5dd7070Spatrick bool isZero() const { return !V; }
124e5dd7070Spatrick
125e5dd7070Spatrick bool isMin() const { return *this == min(bitWidth()); }
126e5dd7070Spatrick
127*12c85518Srobert bool isMinusOne() const { return Signed && V == ReprT(-1); }
128e5dd7070Spatrick
129e5dd7070Spatrick constexpr static bool isSigned() { return Signed; }
130e5dd7070Spatrick
131*12c85518Srobert bool isNegative() const { return V < ReprT(0); }
132e5dd7070Spatrick bool isPositive() const { return !isNegative(); }
133e5dd7070Spatrick
134e5dd7070Spatrick ComparisonCategoryResult compare(const Integral &RHS) const {
135e5dd7070Spatrick return Compare(V, RHS.V);
136e5dd7070Spatrick }
137e5dd7070Spatrick
138*12c85518Srobert unsigned countLeadingZeros() const {
139*12c85518Srobert return llvm::countLeadingZeros<ReprT>(V);
140*12c85518Srobert }
141e5dd7070Spatrick
142e5dd7070Spatrick Integral truncate(unsigned TruncBits) const {
143e5dd7070Spatrick if (TruncBits >= Bits)
144e5dd7070Spatrick return *this;
145*12c85518Srobert const ReprT BitMask = (ReprT(1) << ReprT(TruncBits)) - 1;
146*12c85518Srobert const ReprT SignBit = ReprT(1) << (TruncBits - 1);
147*12c85518Srobert const ReprT ExtMask = ~BitMask;
148e5dd7070Spatrick return Integral((V & BitMask) | (Signed && (V & SignBit) ? ExtMask : 0));
149e5dd7070Spatrick }
150e5dd7070Spatrick
151e5dd7070Spatrick void print(llvm::raw_ostream &OS) const { OS << V; }
152e5dd7070Spatrick
153e5dd7070Spatrick static Integral min(unsigned NumBits) {
154e5dd7070Spatrick return Integral(Min);
155e5dd7070Spatrick }
156e5dd7070Spatrick static Integral max(unsigned NumBits) {
157e5dd7070Spatrick return Integral(Max);
158e5dd7070Spatrick }
159e5dd7070Spatrick
160*12c85518Srobert template <typename ValT> static Integral from(ValT Value) {
161*12c85518Srobert if constexpr (std::is_integral<ValT>::value)
162e5dd7070Spatrick return Integral(Value);
163*12c85518Srobert else
164*12c85518Srobert return Integral::from(static_cast<Integral::ReprT>(Value));
165e5dd7070Spatrick }
166e5dd7070Spatrick
167e5dd7070Spatrick template <unsigned SrcBits, bool SrcSign>
168ec727ea7Spatrick static std::enable_if_t<SrcBits != 0, Integral>
169e5dd7070Spatrick from(Integral<SrcBits, SrcSign> Value) {
170e5dd7070Spatrick return Integral(Value.V);
171e5dd7070Spatrick }
172e5dd7070Spatrick
173e5dd7070Spatrick template <bool SrcSign> static Integral from(Integral<0, SrcSign> Value) {
174*12c85518Srobert if constexpr (SrcSign)
175e5dd7070Spatrick return Integral(Value.V.getSExtValue());
176e5dd7070Spatrick else
177e5dd7070Spatrick return Integral(Value.V.getZExtValue());
178e5dd7070Spatrick }
179e5dd7070Spatrick
180e5dd7070Spatrick static Integral zero() { return from(0); }
181e5dd7070Spatrick
182e5dd7070Spatrick template <typename T> static Integral from(T Value, unsigned NumBits) {
183e5dd7070Spatrick return Integral(Value);
184e5dd7070Spatrick }
185e5dd7070Spatrick
186e5dd7070Spatrick static bool inRange(int64_t Value, unsigned NumBits) {
187*12c85518Srobert return CheckRange<ReprT, Min, Max>(Value);
188e5dd7070Spatrick }
189e5dd7070Spatrick
190e5dd7070Spatrick static bool increment(Integral A, Integral *R) {
191*12c85518Srobert return add(A, Integral(ReprT(1)), A.bitWidth(), R);
192e5dd7070Spatrick }
193e5dd7070Spatrick
194e5dd7070Spatrick static bool decrement(Integral A, Integral *R) {
195*12c85518Srobert return sub(A, Integral(ReprT(1)), A.bitWidth(), R);
196e5dd7070Spatrick }
197e5dd7070Spatrick
198e5dd7070Spatrick static bool add(Integral A, Integral B, unsigned OpBits, Integral *R) {
199e5dd7070Spatrick return CheckAddUB(A.V, B.V, R->V);
200e5dd7070Spatrick }
201e5dd7070Spatrick
202e5dd7070Spatrick static bool sub(Integral A, Integral B, unsigned OpBits, Integral *R) {
203e5dd7070Spatrick return CheckSubUB(A.V, B.V, R->V);
204e5dd7070Spatrick }
205e5dd7070Spatrick
206e5dd7070Spatrick static bool mul(Integral A, Integral B, unsigned OpBits, Integral *R) {
207e5dd7070Spatrick return CheckMulUB(A.V, B.V, R->V);
208e5dd7070Spatrick }
209e5dd7070Spatrick
210*12c85518Srobert static bool rem(Integral A, Integral B, unsigned OpBits, Integral *R) {
211*12c85518Srobert *R = Integral(A.V % B.V);
212*12c85518Srobert return false;
213e5dd7070Spatrick }
214e5dd7070Spatrick
215*12c85518Srobert static bool div(Integral A, Integral B, unsigned OpBits, Integral *R) {
216*12c85518Srobert *R = Integral(A.V / B.V);
217*12c85518Srobert return false;
218*12c85518Srobert }
219*12c85518Srobert
220*12c85518Srobert static bool bitAnd(Integral A, Integral B, unsigned OpBits, Integral *R) {
221*12c85518Srobert *R = Integral(A.V & B.V);
222*12c85518Srobert return false;
223*12c85518Srobert }
224*12c85518Srobert
225*12c85518Srobert static bool bitOr(Integral A, Integral B, unsigned OpBits, Integral *R) {
226*12c85518Srobert *R = Integral(A.V | B.V);
227*12c85518Srobert return false;
228*12c85518Srobert }
229*12c85518Srobert
230*12c85518Srobert static bool bitXor(Integral A, Integral B, unsigned OpBits, Integral *R) {
231*12c85518Srobert *R = Integral(A.V ^ B.V);
232*12c85518Srobert return false;
233*12c85518Srobert }
234*12c85518Srobert
235*12c85518Srobert static bool neg(Integral A, Integral *R) {
236*12c85518Srobert *R = -A;
237*12c85518Srobert return false;
238*12c85518Srobert }
239*12c85518Srobert
240*12c85518Srobert static bool comp(Integral A, Integral *R) {
241*12c85518Srobert *R = Integral(~A.V);
242*12c85518Srobert return false;
243*12c85518Srobert }
244*12c85518Srobert
245*12c85518Srobert private:
246*12c85518Srobert template <typename T> static bool CheckAddUB(T A, T B, T &R) {
247*12c85518Srobert if constexpr (std::is_signed_v<T>) {
248*12c85518Srobert return llvm::AddOverflow<T>(A, B, R);
249*12c85518Srobert } else {
250e5dd7070Spatrick R = A + B;
251e5dd7070Spatrick return false;
252e5dd7070Spatrick }
253e5dd7070Spatrick }
254e5dd7070Spatrick
255*12c85518Srobert template <typename T> static bool CheckSubUB(T A, T B, T &R) {
256*12c85518Srobert if constexpr (std::is_signed_v<T>) {
257*12c85518Srobert return llvm::SubOverflow<T>(A, B, R);
258*12c85518Srobert } else {
259e5dd7070Spatrick R = A - B;
260e5dd7070Spatrick return false;
261e5dd7070Spatrick }
262e5dd7070Spatrick }
263e5dd7070Spatrick
264*12c85518Srobert template <typename T> static bool CheckMulUB(T A, T B, T &R) {
265*12c85518Srobert if constexpr (std::is_signed_v<T>) {
266*12c85518Srobert return llvm::MulOverflow<T>(A, B, R);
267*12c85518Srobert } else {
268e5dd7070Spatrick R = A * B;
269e5dd7070Spatrick return false;
270e5dd7070Spatrick }
271e5dd7070Spatrick }
272*12c85518Srobert template <typename T, T Min, T Max> static bool CheckRange(int64_t V) {
273*12c85518Srobert if constexpr (std::is_signed_v<T>) {
274*12c85518Srobert return Min <= V && V <= Max;
275*12c85518Srobert } else {
276e5dd7070Spatrick return V >= 0 && static_cast<uint64_t>(V) <= Max;
277e5dd7070Spatrick }
278*12c85518Srobert }
279e5dd7070Spatrick };
280e5dd7070Spatrick
281e5dd7070Spatrick template <unsigned Bits, bool Signed>
282e5dd7070Spatrick llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Integral<Bits, Signed> I) {
283e5dd7070Spatrick I.print(OS);
284e5dd7070Spatrick return OS;
285e5dd7070Spatrick }
286e5dd7070Spatrick
287e5dd7070Spatrick } // namespace interp
288e5dd7070Spatrick } // namespace clang
289e5dd7070Spatrick
290e5dd7070Spatrick #endif
291