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, llvm::ArrayRef(Memory, NumWords));
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 constexpr static bool isNumber() { return true; }
143
144 APSInt toAPSInt(unsigned Bits = 0) const {
145 if (Bits == 0)
146 Bits = bitWidth();
147
148 APInt V = getValue();
149 if constexpr (Signed)
150 return APSInt(getValue().sext(Bits), !Signed);
151 else
152 return APSInt(getValue().zext(Bits), !Signed);
153 }
154 APValue toAPValue(const ASTContext &) const { return APValue(toAPSInt()); }
155
156 bool isZero() const { return getValue().isZero(); }
157 bool isPositive() const {
158 if constexpr (Signed)
159 return getValue().isNonNegative();
160 return true;
161 }
162 bool isNegative() const {
163 if constexpr (Signed)
164 return !getValue().isNonNegative();
165 return false;
166 }
167 bool isMin() const {
168 if constexpr (Signed)
169 return getValue().isMinSignedValue();
170 return getValue().isMinValue();
171 }
172 bool isMax() const {
173 if constexpr (Signed)
174 return getValue().isMaxSignedValue();
175 return getValue().isMaxValue();
176 }
177 static constexpr bool isSigned() { return Signed; }
178 bool isMinusOne() const { return Signed && getValue().isAllOnes(); }
179
180 unsigned countLeadingZeros() const { return getValue().countl_zero(); }
181
182 void print(llvm::raw_ostream &OS) const { getValue().print(OS, Signed); }
183 std::string toDiagnosticString(const ASTContext &Ctx) const {
184 std::string NameStr;
185 llvm::raw_string_ostream OS(NameStr);
186 print(OS);
187 return NameStr;
188 }
189
190 IntegralAP truncate(unsigned BitWidth) const {
191 if constexpr (Signed)
192 return IntegralAP(
193 getValue().trunc(BitWidth).sextOrTrunc(this->bitWidth()));
194 else
195 return IntegralAP(
196 getValue().trunc(BitWidth).zextOrTrunc(this->bitWidth()));
197 }
198
199 IntegralAP<false> toUnsigned() const {
200 return IntegralAP<false>(Memory, BitWidth);
201 }
202
203 void bitcastToMemory(std::byte *Dest) const {
204 llvm::StoreIntToMemory(IntVal: getValue(), Dst: (uint8_t *)Dest, StoreBytes: bitWidth() / 8);
205 }
206
207 static void bitcastFromMemory(const std::byte *Src, unsigned BitWidth,
208 IntegralAP *Result) {
209 APInt V(BitWidth, static_cast<uint64_t>(0), Signed);
210 llvm::LoadIntFromMemory(IntVal&: V, Src: (const uint8_t *)Src, LoadBytes: BitWidth / 8);
211 Result->copy(V);
212 }
213
214 ComparisonCategoryResult compare(const IntegralAP &RHS) const {
215 assert(Signed == RHS.isSigned());
216 assert(bitWidth() == RHS.bitWidth());
217 APInt V1 = getValue();
218 APInt V2 = RHS.getValue();
219 if constexpr (Signed) {
220 if (V1.slt(RHS: V2))
221 return ComparisonCategoryResult::Less;
222 if (V1.sgt(RHS: V2))
223 return ComparisonCategoryResult::Greater;
224 return ComparisonCategoryResult::Equal;
225 }
226
227 assert(!Signed);
228 if (V1.ult(RHS: V2))
229 return ComparisonCategoryResult::Less;
230 if (V1.ugt(RHS: V2))
231 return ComparisonCategoryResult::Greater;
232 return ComparisonCategoryResult::Equal;
233 }
234
235 static bool increment(IntegralAP A, IntegralAP *R) {
236 APSInt One(APInt(A.bitWidth(), 1ull, Signed), !Signed);
237 return add(A, B: IntegralAP<Signed>(One), OpBits: A.bitWidth() + 1, R);
238 }
239
240 static bool decrement(IntegralAP A, IntegralAP *R) {
241 APSInt One(APInt(A.bitWidth(), 1ull, Signed), !Signed);
242 return sub(A, B: IntegralAP<Signed>(One), OpBits: A.bitWidth() + 1, R);
243 }
244
245 static bool add(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) {
246 return CheckAddSubMulUB<std::plus>(A, B, OpBits, R);
247 }
248
249 static bool sub(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) {
250 return CheckAddSubMulUB<std::minus>(A, B, OpBits, R);
251 }
252
253 static bool mul(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) {
254 return CheckAddSubMulUB<std::multiplies>(A, B, OpBits, R);
255 }
256
257 static bool rem(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) {
258 if constexpr (Signed)
259 R->copy(V: A.getValue().srem(B.getValue()));
260 else
261 R->copy(V: A.getValue().urem(B.getValue()));
262 return false;
263 }
264
265 static bool div(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) {
266 if constexpr (Signed)
267 R->copy(V: A.getValue().sdiv(B.getValue()));
268 else
269 R->copy(V: A.getValue().udiv(B.getValue()));
270 return false;
271 }
272
273 static bool bitAnd(IntegralAP A, IntegralAP B, unsigned OpBits,
274 IntegralAP *R) {
275 R->copy(V: A.getValue() & B.getValue());
276 return false;
277 }
278
279 static bool bitOr(IntegralAP A, IntegralAP B, unsigned OpBits,
280 IntegralAP *R) {
281 R->copy(V: A.getValue() | B.getValue());
282 return false;
283 }
284
285 static bool bitXor(IntegralAP A, IntegralAP B, unsigned OpBits,
286 IntegralAP *R) {
287 R->copy(V: A.getValue() ^ B.getValue());
288 return false;
289 }
290
291 static bool neg(const IntegralAP &A, IntegralAP *R) {
292 APInt AI = A.getValue();
293 AI.negate();
294 R->copy(V: AI);
295 return false;
296 }
297
298 static bool comp(IntegralAP A, IntegralAP *R) {
299 R->copy(V: ~A.getValue());
300 return false;
301 }
302
303 static void shiftLeft(const IntegralAP A, const IntegralAP B, unsigned OpBits,
304 IntegralAP *R) {
305 *R = IntegralAP(A.getValue().shl(B.getValue().getZExtValue()));
306 }
307
308 static void shiftRight(const IntegralAP A, const IntegralAP B,
309 unsigned OpBits, IntegralAP *R) {
310 unsigned ShiftAmount = B.getValue().getZExtValue();
311 if constexpr (Signed)
312 R->copy(V: A.getValue().ashr(ShiftAmount));
313 else
314 R->copy(V: A.getValue().lshr(ShiftAmount));
315 }
316
317 // === Serialization support ===
318 size_t bytesToSerialize() const {
319 assert(BitWidth != 0);
320 return sizeof(uint32_t) + (numWords() * sizeof(uint64_t));
321 }
322
323 void serialize(std::byte *Buff) const {
324 std::memcpy(dest: Buff, src: &BitWidth, n: sizeof(uint32_t));
325 if (singleWord())
326 std::memcpy(dest: Buff + sizeof(uint32_t), src: &Val, n: sizeof(uint64_t));
327 else {
328 std::memcpy(dest: Buff + sizeof(uint32_t), src: Memory,
329 n: numWords() * sizeof(uint64_t));
330 }
331 }
332
333 static uint32_t deserializeSize(const std::byte *Buff) {
334 return *reinterpret_cast<const uint32_t *>(Buff);
335 }
336
337 static void deserialize(const std::byte *Buff, IntegralAP<Signed> *Result) {
338 uint32_t BitWidth = Result->BitWidth;
339 assert(BitWidth != 0);
340 unsigned NumWords = llvm::APInt::getNumWords(BitWidth);
341
342 if (NumWords == 1)
343 std::memcpy(dest: &Result->Val, src: Buff + sizeof(uint32_t), n: sizeof(uint64_t));
344 else {
345 assert(Result->Memory);
346 std::memcpy(dest: Result->Memory, src: Buff + sizeof(uint32_t),
347 n: NumWords * sizeof(uint64_t));
348 }
349 }
350
351private:
352 template <template <typename T> class Op>
353 static bool CheckAddSubMulUB(const IntegralAP &A, const IntegralAP &B,
354 unsigned BitWidth, IntegralAP *R) {
355 if constexpr (!Signed) {
356 R->copy(V: Op<APInt>{}(A.getValue(), B.getValue()));
357 return false;
358 }
359
360 const APSInt &LHS = A.toAPSInt();
361 const APSInt &RHS = B.toAPSInt();
362 APSInt Value = Op<APSInt>{}(LHS.extend(width: BitWidth), RHS.extend(width: BitWidth));
363 APSInt Result = Value.trunc(width: LHS.getBitWidth());
364 R->copy(V: Result);
365
366 return Result.extend(width: BitWidth) != Value;
367 }
368};
369
370template <bool Signed>
371inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
372 IntegralAP<Signed> I) {
373 I.print(OS);
374 return OS;
375}
376
377template <bool Signed>
378IntegralAP<Signed> getSwappedBytes(IntegralAP<Signed> F) {
379 return F;
380}
381
382} // namespace interp
383} // namespace clang
384
385#endif
386