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_AP_H |
14 | #define LLVM_CLANG_AST_INTERP_INTEGRAL_AP_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 | |
26 | namespace clang { |
27 | namespace interp { |
28 | |
29 | using APInt = llvm::APInt; |
30 | using APSInt = llvm::APSInt; |
31 | template <unsigned Bits, bool Signed> class Integral; |
32 | |
33 | template <bool Signed> class IntegralAP final { |
34 | private: |
35 | friend IntegralAP<!Signed>; |
36 | APInt V; |
37 | |
38 | template <typename T, bool InputSigned> |
39 | static T truncateCast(const APInt &V) { |
40 | constexpr unsigned BitSize = sizeof(T) * 8; |
41 | if (BitSize >= V.getBitWidth()) { |
42 | APInt Extended; |
43 | if constexpr (InputSigned) |
44 | Extended = V.sext(width: BitSize); |
45 | else |
46 | Extended = V.zext(width: BitSize); |
47 | return std::is_signed_v<T> ? Extended.getSExtValue() |
48 | : Extended.getZExtValue(); |
49 | } |
50 | |
51 | return std::is_signed_v<T> ? V.trunc(width: BitSize).getSExtValue() |
52 | : V.trunc(width: BitSize).getZExtValue(); |
53 | } |
54 | |
55 | public: |
56 | using AsUnsigned = IntegralAP<false>; |
57 | |
58 | template <typename T> |
59 | IntegralAP(T Value, unsigned BitWidth) |
60 | : V(APInt(BitWidth, static_cast<uint64_t>(Value), Signed)) {} |
61 | |
62 | IntegralAP(APInt V) : V(V) {} |
63 | /// Arbitrary value for uninitialized variables. |
64 | IntegralAP() : IntegralAP(-1, 3) {} |
65 | |
66 | IntegralAP operator-() const { return IntegralAP(-V); } |
67 | IntegralAP operator-(const IntegralAP &Other) const { |
68 | return IntegralAP(V - Other.V); |
69 | } |
70 | bool operator>(const IntegralAP &RHS) const { |
71 | if constexpr (Signed) |
72 | return V.ugt(RHS.V); |
73 | return V.sgt(RHS.V); |
74 | } |
75 | bool operator>=(IntegralAP RHS) const { |
76 | if constexpr (Signed) |
77 | return V.uge(RHS.V); |
78 | return V.sge(RHS.V); |
79 | } |
80 | bool operator<(IntegralAP RHS) const { |
81 | if constexpr (Signed) |
82 | return V.slt(RHS.V); |
83 | return V.slt(RHS.V); |
84 | } |
85 | bool operator<=(IntegralAP RHS) const { |
86 | if constexpr (Signed) |
87 | return V.ult(RHS.V); |
88 | return V.ult(RHS.V); |
89 | } |
90 | |
91 | template <typename Ty, typename = std::enable_if_t<std::is_integral_v<Ty>>> |
92 | explicit operator Ty() const { |
93 | return truncateCast<Ty, Signed>(V); |
94 | } |
95 | |
96 | template <typename T> static IntegralAP from(T Value, unsigned NumBits = 0) { |
97 | assert(NumBits > 0); |
98 | APInt Copy = APInt(NumBits, static_cast<uint64_t>(Value), Signed); |
99 | |
100 | return IntegralAP<Signed>(Copy); |
101 | } |
102 | |
103 | template <bool InputSigned> |
104 | static IntegralAP from(IntegralAP<InputSigned> V, unsigned NumBits = 0) { |
105 | if (NumBits == 0) |
106 | NumBits = V.bitWidth(); |
107 | |
108 | if constexpr (InputSigned) |
109 | return IntegralAP<Signed>(V.V.sextOrTrunc(NumBits)); |
110 | return IntegralAP<Signed>(V.V.zextOrTrunc(NumBits)); |
111 | } |
112 | |
113 | template <unsigned Bits, bool InputSigned> |
114 | static IntegralAP from(Integral<Bits, InputSigned> I, unsigned BitWidth) { |
115 | APInt Copy = APInt(BitWidth, static_cast<uint64_t>(I), InputSigned); |
116 | |
117 | return IntegralAP<Signed>(Copy); |
118 | } |
119 | |
120 | static IntegralAP zero(int32_t BitWidth) { |
121 | APInt V = APInt(BitWidth, 0LL, Signed); |
122 | return IntegralAP(V); |
123 | } |
124 | |
125 | constexpr unsigned bitWidth() const { return V.getBitWidth(); } |
126 | |
127 | APSInt toAPSInt(unsigned Bits = 0) const { |
128 | if (Bits == 0) |
129 | Bits = bitWidth(); |
130 | |
131 | if constexpr (Signed) |
132 | return APSInt(V.sext(width: Bits), !Signed); |
133 | else |
134 | return APSInt(V.zext(width: Bits), !Signed); |
135 | } |
136 | APValue toAPValue(const ASTContext &) const { return APValue(toAPSInt()); } |
137 | |
138 | bool isZero() const { return V.isZero(); } |
139 | bool isPositive() const { return V.isNonNegative(); } |
140 | bool isNegative() const { return !V.isNonNegative(); } |
141 | bool isMin() const { return V.isMinValue(); } |
142 | bool isMax() const { return V.isMaxValue(); } |
143 | static constexpr bool isSigned() { return Signed; } |
144 | bool isMinusOne() const { return Signed && V == -1; } |
145 | |
146 | unsigned countLeadingZeros() const { return V.countl_zero(); } |
147 | |
148 | void print(llvm::raw_ostream &OS) const { OS << V; } |
149 | std::string toDiagnosticString(const ASTContext &Ctx) const { |
150 | std::string NameStr; |
151 | llvm::raw_string_ostream OS(NameStr); |
152 | print(OS); |
153 | return NameStr; |
154 | } |
155 | |
156 | IntegralAP truncate(unsigned BitWidth) const { |
157 | if constexpr (Signed) |
158 | return IntegralAP(V.trunc(width: BitWidth).sextOrTrunc(width: this->bitWidth())); |
159 | else |
160 | return IntegralAP(V.trunc(width: BitWidth).zextOrTrunc(width: this->bitWidth())); |
161 | } |
162 | |
163 | IntegralAP<false> toUnsigned() const { |
164 | APInt Copy = V; |
165 | return IntegralAP<false>(Copy); |
166 | } |
167 | |
168 | ComparisonCategoryResult compare(const IntegralAP &RHS) const { |
169 | assert(Signed == RHS.isSigned()); |
170 | assert(bitWidth() == RHS.bitWidth()); |
171 | if constexpr (Signed) { |
172 | if (V.slt(RHS.V)) |
173 | return ComparisonCategoryResult::Less; |
174 | if (V.sgt(RHS.V)) |
175 | return ComparisonCategoryResult::Greater; |
176 | return ComparisonCategoryResult::Equal; |
177 | } |
178 | |
179 | assert(!Signed); |
180 | if (V.ult(RHS.V)) |
181 | return ComparisonCategoryResult::Less; |
182 | if (V.ugt(RHS.V)) |
183 | return ComparisonCategoryResult::Greater; |
184 | return ComparisonCategoryResult::Equal; |
185 | } |
186 | |
187 | static bool increment(IntegralAP A, IntegralAP *R) { |
188 | IntegralAP<Signed> One(1, A.bitWidth()); |
189 | return add(A, B: One, OpBits: A.bitWidth() + 1, R); |
190 | } |
191 | |
192 | static bool decrement(IntegralAP A, IntegralAP *R) { |
193 | IntegralAP<Signed> One(1, A.bitWidth()); |
194 | return sub(A, B: One, OpBits: A.bitWidth() + 1, R); |
195 | } |
196 | |
197 | static bool add(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { |
198 | return CheckAddSubMulUB<std::plus>(A, B, OpBits, R); |
199 | } |
200 | |
201 | static bool sub(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { |
202 | return CheckAddSubMulUB<std::minus>(A, B, OpBits, R); |
203 | } |
204 | |
205 | static bool mul(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { |
206 | return CheckAddSubMulUB<std::multiplies>(A, B, OpBits, R); |
207 | } |
208 | |
209 | static bool rem(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { |
210 | if constexpr (Signed) |
211 | *R = IntegralAP(A.V.srem(B.V)); |
212 | else |
213 | *R = IntegralAP(A.V.urem(B.V)); |
214 | return false; |
215 | } |
216 | |
217 | static bool div(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { |
218 | if constexpr (Signed) |
219 | *R = IntegralAP(A.V.sdiv(B.V)); |
220 | else |
221 | *R = IntegralAP(A.V.udiv(B.V)); |
222 | return false; |
223 | } |
224 | |
225 | static bool bitAnd(IntegralAP A, IntegralAP B, unsigned OpBits, |
226 | IntegralAP *R) { |
227 | *R = IntegralAP(A.V & B.V); |
228 | return false; |
229 | } |
230 | |
231 | static bool bitOr(IntegralAP A, IntegralAP B, unsigned OpBits, |
232 | IntegralAP *R) { |
233 | *R = IntegralAP(A.V | B.V); |
234 | return false; |
235 | } |
236 | |
237 | static bool bitXor(IntegralAP A, IntegralAP B, unsigned OpBits, |
238 | IntegralAP *R) { |
239 | *R = IntegralAP(A.V ^ B.V); |
240 | return false; |
241 | } |
242 | |
243 | static bool neg(const IntegralAP &A, IntegralAP *R) { |
244 | APInt AI = A.V; |
245 | AI.negate(); |
246 | *R = IntegralAP(AI); |
247 | return false; |
248 | } |
249 | |
250 | static bool comp(IntegralAP A, IntegralAP *R) { |
251 | *R = IntegralAP(~A.V); |
252 | return false; |
253 | } |
254 | |
255 | static void shiftLeft(const IntegralAP A, const IntegralAP B, unsigned OpBits, |
256 | IntegralAP *R) { |
257 | *R = IntegralAP(A.V.shl(B.V.getZExtValue())); |
258 | } |
259 | |
260 | static void shiftRight(const IntegralAP A, const IntegralAP B, |
261 | unsigned OpBits, IntegralAP *R) { |
262 | unsigned ShiftAmount = B.V.getZExtValue(); |
263 | if constexpr (Signed) |
264 | *R = IntegralAP(A.V.ashr(ShiftAmount)); |
265 | else |
266 | *R = IntegralAP(A.V.lshr(ShiftAmount)); |
267 | } |
268 | |
269 | // === Serialization support === |
270 | size_t bytesToSerialize() const { |
271 | // 4 bytes for the BitWidth followed by N bytes for the actual APInt. |
272 | return sizeof(uint32_t) + (V.getBitWidth() / CHAR_BIT); |
273 | } |
274 | |
275 | void serialize(std::byte *Buff) const { |
276 | assert(V.getBitWidth() < std::numeric_limits<uint8_t>::max()); |
277 | uint32_t BitWidth = V.getBitWidth(); |
278 | |
279 | std::memcpy(dest: Buff, src: &BitWidth, n: sizeof(uint32_t)); |
280 | llvm::StoreIntToMemory(IntVal: V, Dst: (uint8_t *)(Buff + sizeof(uint32_t)), |
281 | StoreBytes: BitWidth / CHAR_BIT); |
282 | } |
283 | |
284 | static IntegralAP<Signed> deserialize(const std::byte *Buff) { |
285 | uint32_t BitWidth; |
286 | std::memcpy(dest: &BitWidth, src: Buff, n: sizeof(uint32_t)); |
287 | IntegralAP<Signed> Val(APInt(BitWidth, 0ull, !Signed)); |
288 | |
289 | llvm::LoadIntFromMemory(IntVal&: Val.V, Src: (const uint8_t *)Buff + sizeof(uint32_t), |
290 | LoadBytes: BitWidth / CHAR_BIT); |
291 | return Val; |
292 | } |
293 | |
294 | private: |
295 | template <template <typename T> class Op> |
296 | static bool CheckAddSubMulUB(const IntegralAP &A, const IntegralAP &B, |
297 | unsigned BitWidth, IntegralAP *R) { |
298 | if constexpr (!Signed) { |
299 | R->V = Op<APInt>{}(A.V, B.V); |
300 | return false; |
301 | } |
302 | |
303 | const APSInt &LHS = A.toAPSInt(); |
304 | const APSInt &RHS = B.toAPSInt(); |
305 | APSInt Value = Op<APSInt>{}(LHS.extend(width: BitWidth), RHS.extend(width: BitWidth)); |
306 | APSInt Result = Value.trunc(width: LHS.getBitWidth()); |
307 | R->V = Result; |
308 | |
309 | return Result.extend(width: BitWidth) != Value; |
310 | } |
311 | }; |
312 | |
313 | template <bool Signed> |
314 | inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, |
315 | IntegralAP<Signed> I) { |
316 | I.print(OS); |
317 | return OS; |
318 | } |
319 | |
320 | template <bool Signed> |
321 | IntegralAP<Signed> getSwappedBytes(IntegralAP<Signed> F) { |
322 | return F; |
323 | } |
324 | |
325 | } // namespace interp |
326 | } // namespace clang |
327 | |
328 | #endif |
329 | |