Skip to content

Commit 16b9e6f

Browse files
authored
[clang][Interp] Add IntegralAP for arbitrary-precision integers (#65844)
This adds `IntegralAP` backing the two new primtypes `IntAP` (unsigned arbitrary-precision int) and `IntAPS` (same but signed). We use this for `int128` support (which isn't available on all host systems we support AFAIK) and I think we can also use this for `_BitInt` later.
1 parent 6403287 commit 16b9e6f

File tree

12 files changed

+383
-14
lines changed

12 files changed

+383
-14
lines changed

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,14 +171,17 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
171171
return this->discard(SubExpr);
172172
std::optional<PrimType> FromT = classify(SubExpr->getType());
173173
std::optional<PrimType> ToT = classify(CE->getType());
174+
174175
if (!FromT || !ToT)
175176
return false;
176177

177178
if (!this->visit(SubExpr))
178179
return false;
179180

180-
if (FromT == ToT)
181+
if (FromT == ToT) {
182+
assert(ToT != PT_IntAP && ToT != PT_IntAPS);
181183
return true;
184+
}
182185

183186
return this->emitCast(*FromT, *ToT, CE);
184187
}
@@ -1638,6 +1641,9 @@ bool ByteCodeExprGen<Emitter>::visitZeroInitializer(QualType QT,
16381641
return this->emitZeroSint64(E);
16391642
case PT_Uint64:
16401643
return this->emitZeroUint64(E);
1644+
case PT_IntAP:
1645+
case PT_IntAPS:
1646+
assert(false);
16411647
case PT_Ptr:
16421648
return this->emitNullPtr(E);
16431649
case PT_FnPtr:
@@ -1877,6 +1883,9 @@ bool ByteCodeExprGen<Emitter>::emitConst(T Value, PrimType Ty, const Expr *E) {
18771883
return this->emitConstSint64(Value, E);
18781884
case PT_Uint64:
18791885
return this->emitConstUint64(Value, E);
1886+
case PT_IntAP:
1887+
case PT_IntAPS:
1888+
assert(false);
18801889
case PT_Bool:
18811890
return this->emitConstBool(Value, E);
18821891
case PT_Ptr:

clang/lib/AST/Interp/Context.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ std::optional<PrimType> Context::classify(QualType T) const {
103103
case 8:
104104
return PT_Sint8;
105105
default:
106-
return std::nullopt;
106+
return PT_IntAPS;
107107
}
108108
}
109109

@@ -118,7 +118,7 @@ std::optional<PrimType> Context::classify(QualType T) const {
118118
case 8:
119119
return PT_Uint8;
120120
default:
121-
return std::nullopt;
121+
return PT_IntAP;
122122
}
123123
}
124124

clang/lib/AST/Interp/Descriptor.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "Boolean.h"
1111
#include "Floating.h"
1212
#include "FunctionPointer.h"
13+
#include "IntegralAP.h"
1314
#include "Pointer.h"
1415
#include "PrimType.h"
1516
#include "Record.h"
@@ -182,6 +183,10 @@ static BlockCtorFn getCtorPrim(PrimType Type) {
182183
// constructor called.
183184
if (Type == PT_Float)
184185
return ctorTy<PrimConv<PT_Float>::T>;
186+
if (Type == PT_IntAP)
187+
return ctorTy<PrimConv<PT_IntAP>::T>;
188+
if (Type == PT_IntAPS)
189+
return ctorTy<PrimConv<PT_IntAPS>::T>;
185190

186191
COMPOSITE_TYPE_SWITCH(Type, return ctorTy<T>, return nullptr);
187192
}
@@ -191,6 +196,10 @@ static BlockDtorFn getDtorPrim(PrimType Type) {
191196
// destructor called, since they might allocate memory.
192197
if (Type == PT_Float)
193198
return dtorTy<PrimConv<PT_Float>::T>;
199+
if (Type == PT_IntAP)
200+
return dtorTy<PrimConv<PT_IntAP>::T>;
201+
if (Type == PT_IntAPS)
202+
return dtorTy<PrimConv<PT_IntAPS>::T>;
194203

195204
COMPOSITE_TYPE_SWITCH(Type, return dtorTy<T>, return nullptr);
196205
}

clang/lib/AST/Interp/EvalEmitter.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "EvalEmitter.h"
1010
#include "ByteCodeGenError.h"
1111
#include "Context.h"
12+
#include "IntegralAP.h"
1213
#include "Interp.h"
1314
#include "Opcode.h"
1415
#include "clang/AST/DeclCXX.h"

clang/lib/AST/Interp/Integral.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ namespace interp {
2929
using APInt = llvm::APInt;
3030
using APSInt = llvm::APSInt;
3131

32+
template <bool Signed> class IntegralAP;
33+
3234
// Helper structure to select the representation.
3335
template <unsigned Bits, bool Signed> struct Repr;
3436
template <> struct Repr<8, false> { using Type = uint8_t; };
@@ -61,6 +63,8 @@ template <unsigned Bits, bool Signed> class Integral final {
6163
template <typename T> explicit Integral(T V) : V(V) {}
6264

6365
public:
66+
using AsUnsigned = Integral<Bits, false>;
67+
6468
/// Zero-initializes an integral.
6569
Integral() : V(0) {}
6670

clang/lib/AST/Interp/IntegralAP.h

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
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+
class Boolean;
33+
34+
template <bool Signed> class IntegralAP final {
35+
public:
36+
APSInt V;
37+
38+
public:
39+
using AsUnsigned = IntegralAP<false>;
40+
41+
template <typename T>
42+
IntegralAP(T Value) : V(APInt(sizeof(T) * 8, Value, std::is_signed_v<T>)) {}
43+
44+
IntegralAP(APInt V) : V(V) {}
45+
IntegralAP(APSInt V) : V(V) {}
46+
/// Arbitrary value for initialized variables.
47+
IntegralAP() : V(APSInt::getMaxValue(1024, Signed)) {}
48+
49+
IntegralAP operator-() const { return IntegralAP(-V); }
50+
bool operator>(IntegralAP RHS) const { return V > RHS.V; }
51+
bool operator>=(IntegralAP RHS) const { return V >= RHS.V; }
52+
bool operator<(IntegralAP RHS) const { return V < RHS.V; }
53+
bool operator<=(IntegralAP RHS) const { return V <= RHS.V; }
54+
55+
explicit operator bool() const { return !V.isZero(); }
56+
explicit operator int8_t() const { return V.getSExtValue(); }
57+
explicit operator uint8_t() const { return V.getZExtValue(); }
58+
explicit operator int16_t() const { return V.getSExtValue(); }
59+
explicit operator uint16_t() const { return V.getZExtValue(); }
60+
explicit operator int32_t() const { return V.getSExtValue(); }
61+
explicit operator uint32_t() const { return V.getZExtValue(); }
62+
explicit operator int64_t() const { return V.getSExtValue(); }
63+
explicit operator uint64_t() const { return V.getZExtValue(); }
64+
65+
template <typename T> static IntegralAP from(T Value, unsigned NumBits = 0) {
66+
assert(NumBits > 0);
67+
APSInt Copy = APSInt(APInt(NumBits, Value, Signed), !Signed);
68+
69+
return IntegralAP<Signed>(Copy);
70+
}
71+
72+
template <bool InputSigned>
73+
static IntegralAP from(IntegralAP<InputSigned> V, unsigned NumBits = 0) {
74+
if constexpr (Signed == InputSigned)
75+
return V;
76+
77+
APSInt Copy = V.V;
78+
Copy.setIsSigned(Signed);
79+
80+
return IntegralAP<Signed>(Copy);
81+
}
82+
83+
template <unsigned Bits, bool InputSigned>
84+
static IntegralAP from(Integral<Bits, InputSigned> I) {
85+
// FIXME: Take bits parameter.
86+
APSInt Copy =
87+
APSInt(APInt(128, static_cast<int64_t>(I), InputSigned), !Signed);
88+
Copy.setIsSigned(Signed);
89+
90+
assert(Copy.isSigned() == Signed);
91+
return IntegralAP<Signed>(Copy);
92+
}
93+
static IntegralAP from(const Boolean &B) {
94+
assert(false);
95+
return IntegralAP::zero();
96+
}
97+
98+
static IntegralAP zero() {
99+
assert(false);
100+
return IntegralAP(0);
101+
}
102+
103+
// FIXME: This can't be static if the bitwidth depends on V.
104+
static constexpr unsigned bitWidth() { return 128; }
105+
106+
APSInt toAPSInt(unsigned Bits = 0) const { return V; }
107+
APValue toAPValue() const { return APValue(V); }
108+
109+
bool isZero() const { return V.isZero(); }
110+
bool isPositive() const { return V.isNonNegative(); }
111+
bool isNegative() const { return !V.isNonNegative(); }
112+
bool isMin() const { return V.isMinValue(); }
113+
bool isMax() const { return V.isMaxValue(); }
114+
static bool isSigned() { return Signed; }
115+
bool isMinusOne() const { return Signed && V == -1; }
116+
117+
unsigned countLeadingZeros() const { return V.countl_zero(); }
118+
119+
void print(llvm::raw_ostream &OS) const { OS << V; }
120+
121+
IntegralAP truncate(unsigned bitWidth) const {
122+
assert(false);
123+
return V;
124+
}
125+
126+
IntegralAP<false> toUnsigned() const {
127+
APSInt Copy = V;
128+
Copy.setIsSigned(false);
129+
return IntegralAP<false>(Copy);
130+
}
131+
132+
ComparisonCategoryResult compare(const IntegralAP &RHS) const {
133+
return Compare(V, RHS.V);
134+
}
135+
136+
static bool increment(IntegralAP A, IntegralAP *R) {
137+
assert(false);
138+
*R = IntegralAP(A.V + 1);
139+
return false;
140+
}
141+
142+
static bool decrement(IntegralAP A, IntegralAP *R) {
143+
assert(false);
144+
*R = IntegralAP(A.V - 1);
145+
return false;
146+
}
147+
148+
static bool add(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) {
149+
return CheckAddUB(A, B, OpBits, R);
150+
}
151+
152+
static bool sub(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) {
153+
/// FIXME: Gotta check if the result fits into OpBits bits.
154+
return CheckSubUB(A, B, R);
155+
}
156+
157+
static bool mul(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) {
158+
assert(false);
159+
// return CheckMulUB(A.V, B.V, R->V);
160+
return false;
161+
}
162+
163+
static bool rem(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) {
164+
assert(false);
165+
*R = IntegralAP(A.V % B.V);
166+
return false;
167+
}
168+
169+
static bool div(IntegralAP A, IntegralAP B, unsigned OpBits, IntegralAP *R) {
170+
assert(false);
171+
*R = IntegralAP(A.V / B.V);
172+
return false;
173+
}
174+
175+
static bool bitAnd(IntegralAP A, IntegralAP B, unsigned OpBits,
176+
IntegralAP *R) {
177+
assert(false);
178+
*R = IntegralAP(A.V & B.V);
179+
return false;
180+
}
181+
182+
static bool bitOr(IntegralAP A, IntegralAP B, unsigned OpBits,
183+
IntegralAP *R) {
184+
assert(false);
185+
*R = IntegralAP(A.V | B.V);
186+
return false;
187+
}
188+
189+
static bool bitXor(IntegralAP A, IntegralAP B, unsigned OpBits,
190+
IntegralAP *R) {
191+
assert(false);
192+
*R = IntegralAP(A.V ^ B.V);
193+
return false;
194+
}
195+
196+
static bool neg(const IntegralAP &A, IntegralAP *R) {
197+
APSInt AI = A.V;
198+
199+
AI.setIsSigned(Signed);
200+
*R = IntegralAP(AI);
201+
return false;
202+
}
203+
204+
static bool comp(IntegralAP A, IntegralAP *R) {
205+
assert(false);
206+
*R = IntegralAP(~A.V);
207+
return false;
208+
}
209+
210+
static void shiftLeft(const IntegralAP A, const IntegralAP B, unsigned OpBits,
211+
IntegralAP *R) {
212+
*R = IntegralAP(A.V << B.V.getZExtValue());
213+
}
214+
215+
static void shiftRight(const IntegralAP A, const IntegralAP B,
216+
unsigned OpBits, IntegralAP *R) {
217+
*R = IntegralAP(A.V >> B.V.getZExtValue());
218+
}
219+
220+
private:
221+
static bool CheckAddUB(const IntegralAP &A, const IntegralAP &B,
222+
unsigned BitWidth, IntegralAP *R) {
223+
if (!A.isSigned()) {
224+
R->V = A.V + B.V;
225+
return false;
226+
}
227+
228+
const APSInt &LHS = A.V;
229+
const APSInt &RHS = B.V;
230+
231+
APSInt Value(LHS.extend(BitWidth) + RHS.extend(BitWidth), false);
232+
APSInt Result = Value.trunc(LHS.getBitWidth());
233+
if (Result.extend(BitWidth) != Value)
234+
return true;
235+
236+
R->V = Result;
237+
return false;
238+
}
239+
static bool CheckSubUB(const IntegralAP &A, const IntegralAP &B,
240+
IntegralAP *R) {
241+
R->V = A.V - B.V;
242+
return false; // Success!
243+
}
244+
};
245+
246+
template <bool Signed>
247+
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
248+
IntegralAP<Signed> I) {
249+
I.print(OS);
250+
return OS;
251+
}
252+
253+
} // namespace interp
254+
} // namespace clang
255+
256+
#endif

0 commit comments

Comments
 (0)