Skip to content

Commit 64a849a

Browse files
authored
[clang][Interp] Support arbitrary precision constants (#79747)
Add (de)serialization support for them, like we do for Floating values.
1 parent 96c907d commit 64a849a

File tree

7 files changed

+110
-7
lines changed

7 files changed

+110
-7
lines changed

clang/lib/AST/Interp/ByteCodeEmitter.cpp

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "ByteCodeGenError.h"
1111
#include "Context.h"
1212
#include "Floating.h"
13+
#include "IntegralAP.h"
1314
#include "Opcode.h"
1415
#include "Program.h"
1516
#include "clang/AST/ASTLambda.h"
@@ -209,9 +210,11 @@ static void emit(Program &P, std::vector<std::byte> &Code, const T &Val,
209210
}
210211
}
211212

212-
template <>
213-
void emit(Program &P, std::vector<std::byte> &Code, const Floating &Val,
214-
bool &Success) {
213+
/// Emits a serializable value. These usually (potentially) contain
214+
/// heap-allocated memory and aren't trivially copyable.
215+
template <typename T>
216+
static void emitSerialized(std::vector<std::byte> &Code, const T &Val,
217+
bool &Success) {
215218
size_t Size = Val.bytesToSerialize();
216219

217220
if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {
@@ -228,6 +231,24 @@ void emit(Program &P, std::vector<std::byte> &Code, const Floating &Val,
228231
Val.serialize(Code.data() + ValPos);
229232
}
230233

234+
template <>
235+
void emit(Program &P, std::vector<std::byte> &Code, const Floating &Val,
236+
bool &Success) {
237+
emitSerialized(Code, Val, Success);
238+
}
239+
240+
template <>
241+
void emit(Program &P, std::vector<std::byte> &Code,
242+
const IntegralAP<false> &Val, bool &Success) {
243+
emitSerialized(Code, Val, Success);
244+
}
245+
246+
template <>
247+
void emit(Program &P, std::vector<std::byte> &Code, const IntegralAP<true> &Val,
248+
bool &Success) {
249+
emitSerialized(Code, Val, Success);
250+
}
251+
231252
template <typename... Tys>
232253
bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &... Args, const SourceInfo &SI) {
233254
bool Success = true;

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2204,15 +2204,13 @@ bool ByteCodeExprGen<Emitter>::emitConst(T Value, PrimType Ty, const Expr *E) {
22042204
return this->emitConstSint64(Value, E);
22052205
case PT_Uint64:
22062206
return this->emitConstUint64(Value, E);
2207-
case PT_IntAP:
2208-
case PT_IntAPS:
2209-
assert(false);
2210-
return false;
22112207
case PT_Bool:
22122208
return this->emitConstBool(Value, E);
22132209
case PT_Ptr:
22142210
case PT_FnPtr:
22152211
case PT_Float:
2212+
case PT_IntAP:
2213+
case PT_IntAPS:
22162214
llvm_unreachable("Invalid integral type");
22172215
break;
22182216
}
@@ -2228,6 +2226,11 @@ bool ByteCodeExprGen<Emitter>::emitConst(T Value, const Expr *E) {
22282226
template <class Emitter>
22292227
bool ByteCodeExprGen<Emitter>::emitConst(const APSInt &Value, PrimType Ty,
22302228
const Expr *E) {
2229+
if (Ty == PT_IntAPS)
2230+
return this->emitConstIntAPS(Value, E);
2231+
if (Ty == PT_IntAP)
2232+
return this->emitConstIntAP(Value, E);
2233+
22312234
if (Value.isSigned())
22322235
return this->emitConst(Value.getSExtValue(), Ty, E);
22332236
return this->emitConst(Value.getZExtValue(), Ty, E);

clang/lib/AST/Interp/Disasm.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#include "Floating.h"
1414
#include "Function.h"
15+
#include "IntegralAP.h"
1516
#include "Opcode.h"
1617
#include "PrimType.h"
1718
#include "Program.h"
@@ -37,6 +38,20 @@ template <> inline Floating ReadArg<Floating>(Program &P, CodePtr &OpPC) {
3738
return F;
3839
}
3940

41+
template <>
42+
inline IntegralAP<false> ReadArg<IntegralAP<false>>(Program &P, CodePtr &OpPC) {
43+
IntegralAP<false> I = IntegralAP<false>::deserialize(*OpPC);
44+
OpPC += align(I.bytesToSerialize());
45+
return I;
46+
}
47+
48+
template <>
49+
inline IntegralAP<true> ReadArg<IntegralAP<true>>(Program &P, CodePtr &OpPC) {
50+
IntegralAP<true> I = IntegralAP<true>::deserialize(*OpPC);
51+
OpPC += align(I.bytesToSerialize());
52+
return I;
53+
}
54+
4055
LLVM_DUMP_METHOD void Function::dump() const { dump(llvm::errs()); }
4156

4257
LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const {

clang/lib/AST/Interp/IntegralAP.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,31 @@ template <bool Signed> class IntegralAP final {
263263
*R = IntegralAP(A.V.lshr(ShiftAmount));
264264
}
265265

266+
// === Serialization support ===
267+
size_t bytesToSerialize() const {
268+
// 4 bytes for the BitWidth followed by N bytes for the actual APInt.
269+
return sizeof(uint32_t) + (V.getBitWidth() / CHAR_BIT);
270+
}
271+
272+
void serialize(std::byte *Buff) const {
273+
assert(V.getBitWidth() < std::numeric_limits<uint8_t>::max());
274+
uint32_t BitWidth = V.getBitWidth();
275+
276+
std::memcpy(Buff, &BitWidth, sizeof(uint32_t));
277+
llvm::StoreIntToMemory(V, (uint8_t *)(Buff + sizeof(uint32_t)),
278+
BitWidth / CHAR_BIT);
279+
}
280+
281+
static IntegralAP<Signed> deserialize(const std::byte *Buff) {
282+
uint32_t BitWidth;
283+
std::memcpy(&BitWidth, Buff, sizeof(uint32_t));
284+
IntegralAP<Signed> Val(APInt(BitWidth, 0ull, !Signed));
285+
286+
llvm::LoadIntFromMemory(Val.V, (const uint8_t *)Buff + sizeof(uint32_t),
287+
BitWidth / CHAR_BIT);
288+
return Val;
289+
}
290+
266291
private:
267292
template <template <typename T> class Op>
268293
static bool CheckAddSubMulUB(const IntegralAP &A, const IntegralAP &B,
@@ -289,6 +314,11 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
289314
return OS;
290315
}
291316

317+
template <bool Signed>
318+
IntegralAP<Signed> getSwappedBytes(IntegralAP<Signed> F) {
319+
return F;
320+
}
321+
292322
} // namespace interp
293323
} // namespace clang
294324

clang/lib/AST/Interp/Interp.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2059,6 +2059,22 @@ template <> inline Floating ReadArg<Floating>(InterpState &S, CodePtr &OpPC) {
20592059
return F;
20602060
}
20612061

2062+
template <>
2063+
inline IntegralAP<false> ReadArg<IntegralAP<false>>(InterpState &S,
2064+
CodePtr &OpPC) {
2065+
IntegralAP<false> I = IntegralAP<false>::deserialize(*OpPC);
2066+
OpPC += align(I.bytesToSerialize());
2067+
return I;
2068+
}
2069+
2070+
template <>
2071+
inline IntegralAP<true> ReadArg<IntegralAP<true>>(InterpState &S,
2072+
CodePtr &OpPC) {
2073+
IntegralAP<true> I = IntegralAP<true>::deserialize(*OpPC);
2074+
OpPC += align(I.bytesToSerialize());
2075+
return I;
2076+
}
2077+
20622078
} // namespace interp
20632079
} // namespace clang
20642080

clang/lib/AST/Interp/Opcodes.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ def ArgUint32 : ArgType { let Name = "uint32_t"; }
4545
def ArgSint64 : ArgType { let Name = "int64_t"; }
4646
def ArgUint64 : ArgType { let Name = "uint64_t"; }
4747
def ArgFloat : ArgType { let Name = "Floating"; }
48+
def ArgIntAP : ArgType { let Name = "IntegralAP<false>"; }
49+
def ArgIntAPS : ArgType { let Name = "IntegralAP<true>"; }
4850
def ArgBool : ArgType { let Name = "bool"; }
4951

5052
def ArgFunction : ArgType { let Name = "const Function *"; }
@@ -244,6 +246,8 @@ def ConstUint32 : ConstOpcode<Uint32, ArgUint32>;
244246
def ConstSint64 : ConstOpcode<Sint64, ArgSint64>;
245247
def ConstUint64 : ConstOpcode<Uint64, ArgUint64>;
246248
def ConstFloat : ConstOpcode<Float, ArgFloat>;
249+
def constIntAP : ConstOpcode<IntAP, ArgIntAP>;
250+
def constIntAPS : ConstOpcode<IntAPS, ArgIntAPS>;
247251
def ConstBool : ConstOpcode<Bool, ArgBool>;
248252

249253
// [] -> [Integer]

clang/test/AST/Interp/intap.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,20 @@ namespace i128 {
154154
constexpr uint128_t ui128Zero{};
155155
static_assert(ui128Zero == 0, "");
156156

157+
158+
enum LargeEnum : signed __int128 {
159+
LV = (signed __int128)1 << 127,
160+
};
161+
162+
constexpr LargeEnum F = LV;
163+
static_assert(F == (signed __int128)1 << 127, "");
164+
constexpr LargeEnum getLargeEnum() {
165+
return LV;
166+
}
167+
static_assert(getLargeEnum() == (signed __int128)1 << 127, "");
168+
169+
170+
157171
#if __cplusplus >= 201402L
158172
template <typename T>
159173
constexpr T CastFrom(__int128_t A) {

0 commit comments

Comments
 (0)