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
26namespace clang {
27namespace interp {
28
29using APInt = llvm::APInt;
30using 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.
36template <bool Signed> class IntegralAP final {
37public:
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
69public:
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
350private:
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
369template <bool Signed>
370inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
371 IntegralAP<Signed> I) {
372 I.print(OS);
373 return OS;
374}
375
376template <bool Signed>
377IntegralAP<Signed> getSwappedBytes(IntegralAP<Signed> F) {
378 return F;
379}
380
381} // namespace interp
382} // namespace clang
383
384#endif
385