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 | |
32 | /// If an IntegralAP is constructed from Memory, it DOES NOT OWN THAT MEMORY. |
33 | /// It will NOT copy the memory (unless, of course, copy() is called) and it |
34 | /// won't alllocate anything. The allocation should happen via InterpState or |
35 | /// Program. |
36 | template <bool Signed> class IntegralAP final { |
37 | public: |
38 | union { |
39 | uint64_t *Memory = nullptr; |
40 | uint64_t Val; |
41 | }; |
42 | uint32_t BitWidth = 0; |
43 | friend IntegralAP<!Signed>; |
44 | |
45 | template <typename T, bool InputSigned> |
46 | static T truncateCast(const APInt &V) { |
47 | constexpr unsigned BitSize = sizeof(T) * 8; |
48 | if (BitSize >= V.getBitWidth()) { |
49 | APInt Extended; |
50 | if constexpr (InputSigned) |
51 | Extended = V.sext(width: BitSize); |
52 | else |
53 | Extended = V.zext(width: BitSize); |
54 | return std::is_signed_v<T> ? Extended.getSExtValue() |
55 | : Extended.getZExtValue(); |
56 | } |
57 | |
58 | return std::is_signed_v<T> ? V.trunc(width: BitSize).getSExtValue() |
59 | : V.trunc(width: BitSize).getZExtValue(); |
60 | } |
61 | |
62 | APInt getValue() const { |
63 | if (singleWord()) |
64 | return APInt(BitWidth, Val, Signed); |
65 | unsigned NumWords = llvm::APInt::getNumWords(BitWidth); |
66 | return llvm::APInt(BitWidth, NumWords, Memory); |
67 | } |
68 | |
69 | public: |
70 | using AsUnsigned = IntegralAP<false>; |
71 | |
72 | void take(uint64_t *NewMemory) { |
73 | assert(!singleWord()); |
74 | std::memcpy(dest: NewMemory, src: Memory, n: numWords() * sizeof(uint64_t)); |
75 | Memory = NewMemory; |
76 | } |
77 | |
78 | void copy(const APInt &V) { |
79 | assert(BitWidth == V.getBitWidth()); |
80 | assert(numWords() == V.getNumWords()); |
81 | |
82 | if (V.isSingleWord()) { |
83 | if constexpr (Signed) |
84 | Val = V.getSExtValue(); |
85 | else |
86 | Val = V.getZExtValue(); |
87 | return; |
88 | } |
89 | assert(Memory); |
90 | std::memcpy(dest: Memory, src: V.getRawData(), n: V.getNumWords() * sizeof(uint64_t)); |
91 | } |
92 | |
93 | IntegralAP() = default; |
94 | /// Zeroed, single-word IntegralAP of the given bitwidth. |
95 | IntegralAP(unsigned BitWidth) : Val(0), BitWidth(BitWidth) { |
96 | assert(singleWord()); |
97 | } |
98 | IntegralAP(uint64_t *Memory, unsigned BitWidth) |
99 | : Memory(Memory), BitWidth(BitWidth) {} |
100 | IntegralAP(const APInt &V) : BitWidth(V.getBitWidth()) { |
101 | if (V.isSingleWord()) { |
102 | Val = Signed ? V.getSExtValue() : V.getZExtValue(); |
103 | } else { |
104 | Memory = const_cast<uint64_t *>(V.getRawData()); |
105 | } |
106 | } |
107 | |
108 | IntegralAP operator-() const { return IntegralAP(-getValue()); } |
109 | bool operator>(const IntegralAP &RHS) const { |
110 | if constexpr (Signed) |
111 | return getValue().sgt(RHS.getValue()); |
112 | return getValue().ugt(RHS.getValue()); |
113 | } |
114 | bool operator>=(unsigned RHS) const { |
115 | if constexpr (Signed) |
116 | return getValue().sge(RHS); |
117 | return getValue().uge(RHS); |
118 | } |
119 | bool operator<(IntegralAP RHS) const { |
120 | if constexpr (Signed) |
121 | return getValue().slt(RHS.getValue()); |
122 | return getValue().ult(RHS.getValue()); |
123 | } |
124 | |
125 | template <typename Ty, typename = std::enable_if_t<std::is_integral_v<Ty>>> |
126 | explicit operator Ty() const { |
127 | return truncateCast<Ty, Signed>(getValue()); |
128 | } |
129 | |
130 | template <typename T> static IntegralAP from(T Value, unsigned NumBits = 0) { |
131 | if (NumBits == 0) |
132 | NumBits = sizeof(T) * 8; |
133 | assert(NumBits > 0); |
134 | assert(APInt::getNumWords(NumBits) == 1); |
135 | APInt Copy = APInt(NumBits, static_cast<uint64_t>(Value), Signed); |
136 | return IntegralAP<Signed>(Copy); |
137 | } |
138 | |
139 | constexpr uint32_t bitWidth() const { return BitWidth; } |
140 | constexpr unsigned numWords() const { return APInt::getNumWords(BitWidth); } |
141 | constexpr bool singleWord() const { return numWords() == 1; } |
142 | |
143 | APSInt toAPSInt(unsigned Bits = 0) const { |
144 | if (Bits == 0) |
145 | Bits = bitWidth(); |
146 | |
147 | APInt V = getValue(); |
148 | if constexpr (Signed) |
149 | return APSInt(getValue().sext(Bits), !Signed); |
150 | else |
151 | return APSInt(getValue().zext(Bits), !Signed); |
152 | } |
153 | APValue toAPValue(const ASTContext &) const { return APValue(toAPSInt()); } |
154 | |
155 | bool isZero() const { return getValue().isZero(); } |
156 | bool isPositive() const { |
157 | if constexpr (Signed) |
158 | return getValue().isNonNegative(); |
159 | return true; |
160 | } |
161 | bool isNegative() const { |
162 | if constexpr (Signed) |
163 | return !getValue().isNonNegative(); |
164 | return false; |
165 | } |
166 | bool isMin() const { |
167 | if constexpr (Signed) |
168 | return getValue().isMinSignedValue(); |
169 | return getValue().isMinValue(); |
170 | } |
171 | bool isMax() const { |
172 | if constexpr (Signed) |
173 | return getValue().isMaxSignedValue(); |
174 | return getValue().isMaxValue(); |
175 | } |
176 | static constexpr bool isSigned() { return Signed; } |
177 | bool isMinusOne() const { return Signed && getValue().isAllOnes(); } |
178 | |
179 | unsigned countLeadingZeros() const { return getValue().countl_zero(); } |
180 | |
181 | void print(llvm::raw_ostream &OS) const { getValue().print(OS, Signed); } |
182 | std::string toDiagnosticString(const ASTContext &Ctx) const { |
183 | std::string NameStr; |
184 | llvm::raw_string_ostream OS(NameStr); |
185 | print(OS); |
186 | return NameStr; |
187 | } |
188 | |
189 | IntegralAP truncate(unsigned BitWidth) const { |
190 | if constexpr (Signed) |
191 | return IntegralAP( |
192 | getValue().trunc(BitWidth).sextOrTrunc(this->bitWidth())); |
193 | else |
194 | return IntegralAP( |
195 | getValue().trunc(BitWidth).zextOrTrunc(this->bitWidth())); |
196 | } |
197 | |
198 | IntegralAP<false> toUnsigned() const { |
199 | return IntegralAP<false>(Memory, BitWidth); |
200 | } |
201 | |
202 | void bitcastToMemory(std::byte *Dest) const { |
203 | llvm::StoreIntToMemory(IntVal: getValue(), Dst: (uint8_t *)Dest, StoreBytes: bitWidth() / 8); |
204 | } |
205 | |
206 | static void bitcastFromMemory(const std::byte *Src, unsigned BitWidth, |
207 | IntegralAP *Result) { |
208 | APInt V(BitWidth, static_cast<uint64_t>(0), Signed); |
209 | llvm::LoadIntFromMemory(IntVal&: V, Src: (const uint8_t *)Src, LoadBytes: BitWidth / 8); |
210 | Result->copy(V); |
211 | } |
212 | |
213 | ComparisonCategoryResult compare(const IntegralAP &RHS) const { |
214 | assert(Signed == RHS.isSigned()); |
215 | assert(bitWidth() == RHS.bitWidth()); |
216 | APInt V1 = getValue(); |
217 | APInt V2 = RHS.getValue(); |
218 | if constexpr (Signed) { |
219 | if (V1.slt(RHS: V2)) |
220 | return ComparisonCategoryResult::Less; |
221 | if (V1.sgt(RHS: V2)) |
222 | return ComparisonCategoryResult::Greater; |
223 | return ComparisonCategoryResult::Equal; |
224 | } |
225 | |
226 | assert(!Signed); |
227 | if (V1.ult(RHS: V2)) |
228 | return ComparisonCategoryResult::Less; |
229 | if (V1.ugt(RHS: V2)) |
230 | return ComparisonCategoryResult::Greater; |
231 | return ComparisonCategoryResult::Equal; |
232 | } |
233 | |
234 | static bool increment(IntegralAP A, IntegralAP *R) { |
235 | APSInt One(APInt(A.bitWidth(), 1ull, Signed), !Signed); |
236 | return add(A, B: IntegralAP<Signed>(One), OpBits: A.bitWidth() + 1, R); |
237 | } |
238 | |
239 | static bool decrement(IntegralAP A, IntegralAP *R) { |
240 | APSInt One(APInt(A.bitWidth(), 1ull, Signed), !Signed); |
241 | return sub(A, B: IntegralAP<Signed>(One), OpBits: A.bitWidth() + 1, R); |
242 | } |
243 | |
244 | static bool add(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { |
245 | return CheckAddSubMulUB<std::plus>(A, B, OpBits, R); |
246 | } |
247 | |
248 | static bool sub(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { |
249 | return CheckAddSubMulUB<std::minus>(A, B, OpBits, R); |
250 | } |
251 | |
252 | static bool mul(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { |
253 | return CheckAddSubMulUB<std::multiplies>(A, B, OpBits, R); |
254 | } |
255 | |
256 | static bool rem(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { |
257 | if constexpr (Signed) |
258 | R->copy(A.getValue().srem(B.getValue())); |
259 | else |
260 | R->copy(A.getValue().urem(B.getValue())); |
261 | return false; |
262 | } |
263 | |
264 | static bool div(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) { |
265 | if constexpr (Signed) |
266 | R->copy(A.getValue().sdiv(B.getValue())); |
267 | else |
268 | R->copy(A.getValue().udiv(B.getValue())); |
269 | return false; |
270 | } |
271 | |
272 | static bool bitAnd(IntegralAP A, IntegralAP B, unsigned OpBits, |
273 | IntegralAP *R) { |
274 | R->copy(A.getValue() & B.getValue()); |
275 | return false; |
276 | } |
277 | |
278 | static bool bitOr(IntegralAP A, IntegralAP B, unsigned OpBits, |
279 | IntegralAP *R) { |
280 | R->copy(A.getValue() | B.getValue()); |
281 | return false; |
282 | } |
283 | |
284 | static bool bitXor(IntegralAP A, IntegralAP B, unsigned OpBits, |
285 | IntegralAP *R) { |
286 | R->copy(A.getValue() ^ B.getValue()); |
287 | return false; |
288 | } |
289 | |
290 | static bool neg(const IntegralAP &A, IntegralAP *R) { |
291 | APInt AI = A.getValue(); |
292 | AI.negate(); |
293 | R->copy(AI); |
294 | return false; |
295 | } |
296 | |
297 | static bool comp(IntegralAP A, IntegralAP *R) { |
298 | R->copy(~A.getValue()); |
299 | return false; |
300 | } |
301 | |
302 | static void shiftLeft(const IntegralAP A, const IntegralAP B, unsigned OpBits, |
303 | IntegralAP *R) { |
304 | *R = IntegralAP(A.getValue().shl(B.getValue().getZExtValue())); |
305 | } |
306 | |
307 | static void shiftRight(const IntegralAP A, const IntegralAP B, |
308 | unsigned OpBits, IntegralAP *R) { |
309 | unsigned ShiftAmount = B.getValue().getZExtValue(); |
310 | if constexpr (Signed) |
311 | R->copy(A.getValue().ashr(ShiftAmount)); |
312 | else |
313 | R->copy(A.getValue().lshr(ShiftAmount)); |
314 | } |
315 | |
316 | // === Serialization support === |
317 | size_t bytesToSerialize() const { |
318 | assert(BitWidth != 0); |
319 | return sizeof(uint32_t) + (numWords() * sizeof(uint64_t)); |
320 | } |
321 | |
322 | void serialize(std::byte *Buff) const { |
323 | std::memcpy(dest: Buff, src: &BitWidth, n: sizeof(uint32_t)); |
324 | if (singleWord()) |
325 | std::memcpy(dest: Buff + sizeof(uint32_t), src: &Val, n: sizeof(uint64_t)); |
326 | else { |
327 | std::memcpy(dest: Buff + sizeof(uint32_t), src: Memory, |
328 | n: numWords() * sizeof(uint64_t)); |
329 | } |
330 | } |
331 | |
332 | static uint32_t deserializeSize(const std::byte *Buff) { |
333 | return *reinterpret_cast<const uint32_t *>(Buff); |
334 | } |
335 | |
336 | static void deserialize(const std::byte *Buff, IntegralAP<Signed> *Result) { |
337 | uint32_t BitWidth = Result->BitWidth; |
338 | assert(BitWidth != 0); |
339 | unsigned NumWords = llvm::APInt::getNumWords(BitWidth); |
340 | |
341 | if (NumWords == 1) |
342 | std::memcpy(dest: &Result->Val, src: Buff + sizeof(uint32_t), n: sizeof(uint64_t)); |
343 | else { |
344 | assert(Result->Memory); |
345 | std::memcpy(dest: Result->Memory, src: Buff + sizeof(uint32_t), |
346 | n: NumWords * sizeof(uint64_t)); |
347 | } |
348 | } |
349 | |
350 | private: |
351 | template <template <typename T> class Op> |
352 | static bool CheckAddSubMulUB(const IntegralAP &A, const IntegralAP &B, |
353 | unsigned BitWidth, IntegralAP *R) { |
354 | if constexpr (!Signed) { |
355 | R->copy(Op<APInt>{}(A.getValue(), B.getValue())); |
356 | return false; |
357 | } |
358 | |
359 | const APSInt &LHS = A.toAPSInt(); |
360 | const APSInt &RHS = B.toAPSInt(); |
361 | APSInt Value = Op<APSInt>{}(LHS.extend(width: BitWidth), RHS.extend(width: BitWidth)); |
362 | APSInt Result = Value.trunc(width: LHS.getBitWidth()); |
363 | R->copy(Result); |
364 | |
365 | return Result.extend(width: BitWidth) != Value; |
366 | } |
367 | }; |
368 | |
369 | template <bool Signed> |
370 | inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, |
371 | IntegralAP<Signed> I) { |
372 | I.print(OS); |
373 | return OS; |
374 | } |
375 | |
376 | template <bool Signed> |
377 | IntegralAP<Signed> getSwappedBytes(IntegralAP<Signed> F) { |
378 | return F; |
379 | } |
380 | |
381 | } // namespace interp |
382 | } // namespace clang |
383 | |
384 | #endif |
385 | |