-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[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
Conversation
@llvm/pr-subscribers-platform-windows @llvm/pr-subscribers-clang Author: Timm Baeder (tbaederr) ChangesAdd (de)serialization support for them, like we do for Floating values. Unfortunately I wasn't able to eliminate the code duplication coming from the signed/unsigned split here. I was trying to avoid adding a third function and calling that from the other two, but maybe that's the best thing to do. Full diff: https://github.com/llvm/llvm-project/pull/79747.diff 7 Files Affected:
diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.cpp b/clang/lib/AST/Interp/ByteCodeEmitter.cpp
index fd2a92d9d3f91e0..4f7ad51773a6546 100644
--- a/clang/lib/AST/Interp/ByteCodeEmitter.cpp
+++ b/clang/lib/AST/Interp/ByteCodeEmitter.cpp
@@ -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"
@@ -228,6 +229,42 @@ 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 IntegralAP<false> &Val, bool &Success) {
+ size_t Size = Val.bytesToSerialize();
+
+ if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {
+ Success = false;
+ return;
+ }
+
+ // Access must be aligned!
+ size_t ValPos = align(Code.size());
+ Size = align(Size);
+ assert(aligned(ValPos + Size));
+ Code.resize(ValPos + Size);
+ Val.serialize(Code.data() + ValPos);
+}
+
+template <>
+void emit(Program &P, std::vector<std::byte> &Code, const IntegralAP<true> &Val,
+ bool &Success) {
+ size_t Size = Val.bytesToSerialize();
+
+ if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {
+ Success = false;
+ return;
+ }
+
+ // Access must be aligned!
+ size_t ValPos = align(Code.size());
+ Size = align(Size);
+ assert(aligned(ValPos + Size));
+ Code.resize(ValPos + Size);
+ Val.serialize(Code.data() + ValPos);
+}
+
template <typename... Tys>
bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &... Args, const SourceInfo &SI) {
bool Success = true;
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index d4501cefb2131da..c2110834f64c045 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -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;
}
@@ -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);
+ else 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);
diff --git a/clang/lib/AST/Interp/Disasm.cpp b/clang/lib/AST/Interp/Disasm.cpp
index d276df8f2926229..eba437e05f59d47 100644
--- a/clang/lib/AST/Interp/Disasm.cpp
+++ b/clang/lib/AST/Interp/Disasm.cpp
@@ -12,6 +12,7 @@
#include "Floating.h"
#include "Function.h"
+#include "IntegralAP.h"
#include "Opcode.h"
#include "PrimType.h"
#include "Program.h"
@@ -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 {
diff --git a/clang/lib/AST/Interp/IntegralAP.h b/clang/lib/AST/Interp/IntegralAP.h
index 55e29caa1cd7471..cf040c5d818d23d 100644
--- a/clang/lib/AST/Interp/IntegralAP.h
+++ b/clang/lib/AST/Interp/IntegralAP.h
@@ -263,6 +263,30 @@ template <bool Signed> class IntegralAP final {
*R = IntegralAP(A.V.lshr(ShiftAmount));
}
+ // === Serialization support ===
+ size_t bytesToSerialize() const {
+ // 1 byte for the BitWidth followed by N bytes for the actual APInt.
+ // FIXME: Is 1 byte enough? APInt::getBitWidth() returns an unsigned.
+ return 1 + (V.getBitWidth() / CHAR_BIT);
+ }
+
+ void serialize(std::byte *Buff) const {
+ assert(V.getBitWidth() < std::numeric_limits<uint8_t>::max());
+ uint8_t BitWidth = V.getBitWidth();
+
+ *Buff = static_cast<std::byte>(BitWidth);
+ llvm::StoreIntToMemory(V, (uint8_t *)(Buff + 1), BitWidth / CHAR_BIT);
+ }
+
+ static IntegralAP<Signed> deserialize(const std::byte *Buff) {
+ uint8_t BitWidth = static_cast<uint8_t>(*Buff);
+ IntegralAP<Signed> Val(APInt(BitWidth, 0ull, !Signed));
+
+ llvm::LoadIntFromMemory(Val.V, (const uint8_t *)Buff + 1,
+ BitWidth / CHAR_BIT);
+ return Val;
+ }
+
private:
template <template <typename T> class Op>
static bool CheckAddSubMulUB(const IntegralAP &A, const IntegralAP &B,
@@ -289,6 +313,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
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 65c54ed9c89b61c..ec43481ebe6cfc7 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -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
diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td
index 24747b6b98c163e..b0fafb973dff45c 100644
--- a/clang/lib/AST/Interp/Opcodes.td
+++ b/clang/lib/AST/Interp/Opcodes.td
@@ -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 *"; }
@@ -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]
diff --git a/clang/test/AST/Interp/intap.cpp b/clang/test/AST/Interp/intap.cpp
index 118dc21b67e875f..d4440124856915d 100644
--- a/clang/test/AST/Interp/intap.cpp
+++ b/clang/test/AST/Interp/intap.cpp
@@ -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) {
|
beb4bcd
to
b0ae1f2
Compare
✅ With the latest revision this PR passed the C/C++ code formatter. |
b0ae1f2
to
13b8fa2
Compare
Add (de)serialization support for them, like we do for Floating values.
13b8fa2
to
943c395
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM aside from a minor formatting nit.
943c395
to
7af3bdf
Compare
Add (de)serialization support for them, like we do for Floating values.
Unfortunately I wasn't able to eliminate the code duplication coming from the signed/unsigned split here. I was trying to avoid adding a third function and calling that from the other two, but maybe that's the best thing to do.