1//===--- Integral.h - Wrapper for numeric types for the VM ------*- 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// Defines the VM types and helpers operating on types.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef LLVM_CLANG_AST_INTERP_INTEGRAL_H
14#define LLVM_CLANG_AST_INTERP_INTEGRAL_H
15
16#include "clang/AST/APValue.h"
17#include "clang/AST/ComparisonCategories.h"
18#include "llvm/ADT/APSInt.h"
19#include "llvm/Support/MathExtras.h"
20#include "llvm/Support/raw_ostream.h"
21#include <cstddef>
22#include <cstdint>
23
24#include "Primitives.h"
25
26namespace clang {
27namespace interp {
28
29using APInt = llvm::APInt;
30using APSInt = llvm::APSInt;
31
32template <bool Signed> class IntegralAP;
33
34// Helper structure to select the representation.
35template <unsigned Bits, bool Signed> struct Repr;
36template <> struct Repr<8, false> {
37 using Type = uint8_t;
38};
39template <> struct Repr<16, false> {
40 using Type = uint16_t;
41};
42template <> struct Repr<32, false> {
43 using Type = uint32_t;
44};
45template <> struct Repr<64, false> {
46 using Type = uint64_t;
47};
48template <> struct Repr<8, true> {
49 using Type = int8_t;
50};
51template <> struct Repr<16, true> {
52 using Type = int16_t;
53};
54template <> struct Repr<32, true> {
55 using Type = int32_t;
56};
57template <> struct Repr<64, true> {
58 using Type = int64_t;
59};
60
61/// Wrapper around numeric types.
62///
63/// These wrappers are required to shared an interface between APSint and
64/// builtin primitive numeral types, while optimising for storage and
65/// allowing methods operating on primitive type to compile to fast code.
66template <unsigned Bits, bool Signed> class Integral final {
67private:
68 template <unsigned OtherBits, bool OtherSigned> friend class Integral;
69
70 // The primitive representing the integral.
71 using ReprT = typename Repr<Bits, Signed>::Type;
72 ReprT V;
73 static_assert(std::is_trivially_copyable_v<ReprT>);
74
75 /// Primitive representing limits.
76 static const auto Min = std::numeric_limits<ReprT>::min();
77 static const auto Max = std::numeric_limits<ReprT>::max();
78
79 /// Construct an integral from anything that is convertible to storage.
80 template <typename T> explicit Integral(T V) : V(V) {}
81
82public:
83 using AsUnsigned = Integral<Bits, false>;
84
85 /// Zero-initializes an integral.
86 Integral() : V(0) {}
87
88 /// Constructs an integral from another integral.
89 template <unsigned SrcBits, bool SrcSign>
90 explicit Integral(Integral<SrcBits, SrcSign> V) : V(V.V) {}
91
92 /// Construct an integral from a value based on signedness.
93 explicit Integral(const APSInt &V)
94 : V(V.isSigned() ? V.getSExtValue() : V.getZExtValue()) {}
95
96 bool operator<(Integral RHS) const { return V < RHS.V; }
97 bool operator>(Integral RHS) const { return V > RHS.V; }
98 bool operator<=(Integral RHS) const { return V <= RHS.V; }
99 bool operator>=(Integral RHS) const { return V >= RHS.V; }
100 bool operator==(Integral RHS) const { return V == RHS.V; }
101 bool operator!=(Integral RHS) const { return V != RHS.V; }
102 bool operator>=(unsigned RHS) const {
103 return static_cast<unsigned>(V) >= RHS;
104 }
105
106 bool operator>(unsigned RHS) const {
107 return V >= 0 && static_cast<unsigned>(V) > RHS;
108 }
109
110 Integral operator-() const { return Integral(-V); }
111 Integral operator-(const Integral &Other) const {
112 return Integral(V - Other.V);
113 }
114 Integral operator~() const { return Integral(~V); }
115
116 template <unsigned DstBits, bool DstSign>
117 explicit operator Integral<DstBits, DstSign>() const {
118 return Integral<DstBits, DstSign>(V);
119 }
120
121 template <typename Ty, typename = std::enable_if_t<std::is_integral_v<Ty>>>
122 explicit operator Ty() const {
123 return V;
124 }
125
126 APSInt toAPSInt() const {
127 return APSInt(APInt(Bits, static_cast<uint64_t>(V), Signed), !Signed);
128 }
129 APSInt toAPSInt(unsigned BitWidth) const {
130 return APSInt(toAPInt(BitWidth), !Signed);
131 }
132 APInt toAPInt(unsigned BitWidth) const {
133 if constexpr (Signed)
134 return APInt(Bits, static_cast<uint64_t>(V), Signed)
135 .sextOrTrunc(width: BitWidth);
136 else
137 return APInt(Bits, static_cast<uint64_t>(V), Signed)
138 .zextOrTrunc(width: BitWidth);
139 }
140 APValue toAPValue(const ASTContext &) const { return APValue(toAPSInt()); }
141
142 Integral<Bits, false> toUnsigned() const {
143 return Integral<Bits, false>(*this);
144 }
145
146 constexpr static unsigned bitWidth() { return Bits; }
147
148 bool isZero() const { return !V; }
149
150 bool isMin() const { return *this == min(NumBits: bitWidth()); }
151
152 bool isMinusOne() const { return Signed && V == ReprT(-1); }
153
154 constexpr static bool isSigned() { return Signed; }
155
156 bool isNegative() const { return V < ReprT(0); }
157 bool isPositive() const { return !isNegative(); }
158
159 ComparisonCategoryResult compare(const Integral &RHS) const {
160 return Compare(V, RHS.V);
161 }
162
163 void bitcastToMemory(std::byte *Dest) const {
164 std::memcpy(dest: Dest, src: &V, n: sizeof(V));
165 }
166
167 static Integral bitcastFromMemory(const std::byte *Src, unsigned BitWidth) {
168 assert(BitWidth == sizeof(ReprT) * 8);
169 ReprT V;
170
171 std::memcpy(dest: &V, src: Src, n: sizeof(ReprT));
172 return Integral(V);
173 }
174
175 std::string toDiagnosticString(const ASTContext &Ctx) const {
176 std::string NameStr;
177 llvm::raw_string_ostream OS(NameStr);
178 OS << V;
179 return NameStr;
180 }
181
182 unsigned countLeadingZeros() const {
183 if constexpr (!Signed)
184 return llvm::countl_zero<ReprT>(V);
185 if (isPositive())
186 return llvm::countl_zero<typename AsUnsigned::ReprT>(
187 static_cast<typename AsUnsigned::ReprT>(V));
188 llvm_unreachable("Don't call countLeadingZeros() on negative values.");
189 }
190
191 Integral truncate(unsigned TruncBits) const {
192 assert(TruncBits >= 1);
193 if (TruncBits >= Bits)
194 return *this;
195 const ReprT BitMask = (ReprT(1) << ReprT(TruncBits)) - 1;
196 const ReprT SignBit = ReprT(1) << (TruncBits - 1);
197 const ReprT ExtMask = ~BitMask;
198 return Integral((V & BitMask) | (Signed && (V & SignBit) ? ExtMask : 0));
199 }
200
201 void print(llvm::raw_ostream &OS) const { OS << V; }
202
203 static Integral min(unsigned NumBits) { return Integral(Min); }
204 static Integral max(unsigned NumBits) { return Integral(Max); }
205
206 template <typename ValT> static Integral from(ValT Value) {
207 if constexpr (std::is_integral<ValT>::value)
208 return Integral(Value);
209 else
210 return Integral::from(static_cast<Integral::ReprT>(Value));
211 }
212
213 template <unsigned SrcBits, bool SrcSign>
214 static std::enable_if_t<SrcBits != 0, Integral>
215 from(Integral<SrcBits, SrcSign> Value) {
216 return Integral(Value.V);
217 }
218
219 static Integral zero(unsigned BitWidth = 0) { return from(0); }
220
221 template <typename T> static Integral from(T Value, unsigned NumBits) {
222 return Integral(Value);
223 }
224
225 static bool inRange(int64_t Value, unsigned NumBits) {
226 return CheckRange<ReprT, Min, Max>(Value);
227 }
228
229 static bool increment(Integral A, Integral *R) {
230 return add(A, B: Integral(ReprT(1)), OpBits: A.bitWidth(), R);
231 }
232
233 static bool decrement(Integral A, Integral *R) {
234 return sub(A, B: Integral(ReprT(1)), OpBits: A.bitWidth(), R);
235 }
236
237 static bool add(Integral A, Integral B, unsigned OpBits, Integral *R) {
238 return CheckAddUB(A.V, B.V, R->V);
239 }
240
241 static bool sub(Integral A, Integral B, unsigned OpBits, Integral *R) {
242 return CheckSubUB(A.V, B.V, R->V);
243 }
244
245 static bool mul(Integral A, Integral B, unsigned OpBits, Integral *R) {
246 return CheckMulUB(A.V, B.V, R->V);
247 }
248
249 static bool rem(Integral A, Integral B, unsigned OpBits, Integral *R) {
250 *R = Integral(A.V % B.V);
251 return false;
252 }
253
254 static bool div(Integral A, Integral B, unsigned OpBits, Integral *R) {
255 *R = Integral(A.V / B.V);
256 return false;
257 }
258
259 static bool bitAnd(Integral A, Integral B, unsigned OpBits, Integral *R) {
260 *R = Integral(A.V & B.V);
261 return false;
262 }
263
264 static bool bitOr(Integral A, Integral B, unsigned OpBits, Integral *R) {
265 *R = Integral(A.V | B.V);
266 return false;
267 }
268
269 static bool bitXor(Integral A, Integral B, unsigned OpBits, Integral *R) {
270 *R = Integral(A.V ^ B.V);
271 return false;
272 }
273
274 static bool neg(Integral A, Integral *R) {
275 if (Signed && A.isMin())
276 return true;
277
278 *R = -A;
279 return false;
280 }
281
282 static bool comp(Integral A, Integral *R) {
283 *R = Integral(~A.V);
284 return false;
285 }
286
287 template <unsigned RHSBits, bool RHSSign>
288 static void shiftLeft(const Integral A, const Integral<RHSBits, RHSSign> B,
289 unsigned OpBits, Integral *R) {
290 *R = Integral::from(A.V << B.V, OpBits);
291 }
292
293 template <unsigned RHSBits, bool RHSSign>
294 static void shiftRight(const Integral A, const Integral<RHSBits, RHSSign> B,
295 unsigned OpBits, Integral *R) {
296 *R = Integral::from(A.V >> B.V, OpBits);
297 }
298
299private:
300 template <typename T> static bool CheckAddUB(T A, T B, T &R) {
301 if constexpr (std::is_signed_v<T>) {
302 return llvm::AddOverflow<T>(A, B, R);
303 } else {
304 R = A + B;
305 return false;
306 }
307 }
308
309 template <typename T> static bool CheckSubUB(T A, T B, T &R) {
310 if constexpr (std::is_signed_v<T>) {
311 return llvm::SubOverflow<T>(A, B, R);
312 } else {
313 R = A - B;
314 return false;
315 }
316 }
317
318 template <typename T> static bool CheckMulUB(T A, T B, T &R) {
319 if constexpr (std::is_signed_v<T>) {
320 return llvm::MulOverflow<T>(A, B, R);
321 } else {
322 R = A * B;
323 return false;
324 }
325 }
326 template <typename T, T Min, T Max> static bool CheckRange(int64_t V) {
327 if constexpr (std::is_signed_v<T>) {
328 return Min <= V && V <= Max;
329 } else {
330 return V >= 0 && static_cast<uint64_t>(V) <= Max;
331 }
332 }
333};
334
335template <unsigned Bits, bool Signed>
336llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, Integral<Bits, Signed> I) {
337 I.print(OS);
338 return OS;
339}
340
341} // namespace interp
342} // namespace clang
343
344#endif
345