Skip to content

[clang][Interp] Support arbitrary precision constants #79747

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions clang/lib/AST/Interp/ByteCodeEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "ByteCodeGenError.h"
#include "Context.h"
#include "Floating.h"
#include "IntegralAP.h"
#include "Opcode.h"
#include "Program.h"
#include "clang/AST/ASTLambda.h"
Expand Down Expand Up @@ -209,9 +210,11 @@ static void emit(Program &P, std::vector<std::byte> &Code, const T &Val,
}
}

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

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

template <>
void emit(Program &P, std::vector<std::byte> &Code, const Floating &Val,
bool &Success) {
emitSerialized(Code, Val, Success);
}

template <>
void emit(Program &P, std::vector<std::byte> &Code,
const IntegralAP<false> &Val, bool &Success) {
emitSerialized(Code, Val, Success);
}

template <>
void emit(Program &P, std::vector<std::byte> &Code, const IntegralAP<true> &Val,
bool &Success) {
emitSerialized(Code, Val, Success);
}

template <typename... Tys>
bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &... Args, const SourceInfo &SI) {
bool Success = true;
Expand Down
11 changes: 7 additions & 4 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2191,15 +2191,13 @@ bool ByteCodeExprGen<Emitter>::emitConst(T Value, PrimType Ty, const Expr *E) {
return this->emitConstSint64(Value, E);
case PT_Uint64:
return this->emitConstUint64(Value, E);
case PT_IntAP:
case PT_IntAPS:
assert(false);
return false;
case PT_Bool:
return this->emitConstBool(Value, E);
case PT_Ptr:
case PT_FnPtr:
case PT_Float:
case PT_IntAP:
case PT_IntAPS:
llvm_unreachable("Invalid integral type");
break;
}
Expand All @@ -2215,6 +2213,11 @@ bool ByteCodeExprGen<Emitter>::emitConst(T Value, const Expr *E) {
template <class Emitter>
bool ByteCodeExprGen<Emitter>::emitConst(const APSInt &Value, PrimType Ty,
const Expr *E) {
if (Ty == PT_IntAPS)
return this->emitConstIntAPS(Value, E);
if (Ty == PT_IntAP)
return this->emitConstIntAP(Value, E);

if (Value.isSigned())
return this->emitConst(Value.getSExtValue(), Ty, E);
return this->emitConst(Value.getZExtValue(), Ty, E);
Expand Down
15 changes: 15 additions & 0 deletions clang/lib/AST/Interp/Disasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "Floating.h"
#include "Function.h"
#include "IntegralAP.h"
#include "Opcode.h"
#include "PrimType.h"
#include "Program.h"
Expand All @@ -37,6 +38,20 @@ template <> inline Floating ReadArg<Floating>(Program &P, CodePtr &OpPC) {
return F;
}

template <>
inline IntegralAP<false> ReadArg<IntegralAP<false>>(Program &P, CodePtr &OpPC) {
IntegralAP<false> I = IntegralAP<false>::deserialize(*OpPC);
OpPC += align(I.bytesToSerialize());
return I;
}

template <>
inline IntegralAP<true> ReadArg<IntegralAP<true>>(Program &P, CodePtr &OpPC) {
IntegralAP<true> I = IntegralAP<true>::deserialize(*OpPC);
OpPC += align(I.bytesToSerialize());
return I;
}

LLVM_DUMP_METHOD void Function::dump() const { dump(llvm::errs()); }

LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const {
Expand Down
30 changes: 30 additions & 0 deletions clang/lib/AST/Interp/IntegralAP.h
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,31 @@ template <bool Signed> class IntegralAP final {
*R = IntegralAP(A.V.lshr(ShiftAmount));
}

// === Serialization support ===
size_t bytesToSerialize() const {
// 4 bytes for the BitWidth followed by N bytes for the actual APInt.
return sizeof(uint32_t) + (V.getBitWidth() / CHAR_BIT);
}

void serialize(std::byte *Buff) const {
assert(V.getBitWidth() < std::numeric_limits<uint8_t>::max());
uint32_t BitWidth = V.getBitWidth();

std::memcpy(Buff, &BitWidth, sizeof(uint32_t));
llvm::StoreIntToMemory(V, (uint8_t *)(Buff + sizeof(uint32_t)),
BitWidth / CHAR_BIT);
}

static IntegralAP<Signed> deserialize(const std::byte *Buff) {
uint32_t BitWidth;
std::memcpy(&BitWidth, Buff, sizeof(uint32_t));
IntegralAP<Signed> Val(APInt(BitWidth, 0ull, !Signed));

llvm::LoadIntFromMemory(Val.V, (const uint8_t *)Buff + sizeof(uint32_t),
BitWidth / CHAR_BIT);
return Val;
}

private:
template <template <typename T> class Op>
static bool CheckAddSubMulUB(const IntegralAP &A, const IntegralAP &B,
Expand All @@ -289,6 +314,11 @@ inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
return OS;
}

template <bool Signed>
IntegralAP<Signed> getSwappedBytes(IntegralAP<Signed> F) {
return F;
}

} // namespace interp
} // namespace clang

Expand Down
16 changes: 16 additions & 0 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -2041,6 +2041,22 @@ template <> inline Floating ReadArg<Floating>(InterpState &S, CodePtr &OpPC) {
return F;
}

template <>
inline IntegralAP<false> ReadArg<IntegralAP<false>>(InterpState &S,
CodePtr &OpPC) {
IntegralAP<false> I = IntegralAP<false>::deserialize(*OpPC);
OpPC += align(I.bytesToSerialize());
return I;
}

template <>
inline IntegralAP<true> ReadArg<IntegralAP<true>>(InterpState &S,
CodePtr &OpPC) {
IntegralAP<true> I = IntegralAP<true>::deserialize(*OpPC);
OpPC += align(I.bytesToSerialize());
return I;
}

} // namespace interp
} // namespace clang

Expand Down
4 changes: 4 additions & 0 deletions clang/lib/AST/Interp/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ def ArgUint32 : ArgType { let Name = "uint32_t"; }
def ArgSint64 : ArgType { let Name = "int64_t"; }
def ArgUint64 : ArgType { let Name = "uint64_t"; }
def ArgFloat : ArgType { let Name = "Floating"; }
def ArgIntAP : ArgType { let Name = "IntegralAP<false>"; }
def ArgIntAPS : ArgType { let Name = "IntegralAP<true>"; }
def ArgBool : ArgType { let Name = "bool"; }

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

// [] -> [Integer]
Expand Down
14 changes: 14 additions & 0 deletions clang/test/AST/Interp/intap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,20 @@ namespace i128 {
constexpr uint128_t ui128Zero{};
static_assert(ui128Zero == 0, "");


enum LargeEnum : signed __int128 {
LV = (signed __int128)1 << 127,
};

constexpr LargeEnum F = LV;
static_assert(F == (signed __int128)1 << 127, "");
constexpr LargeEnum getLargeEnum() {
return LV;
}
static_assert(getLargeEnum() == (signed __int128)1 << 127, "");



#if __cplusplus >= 201402L
template <typename T>
constexpr T CastFrom(__int128_t A) {
Expand Down