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

Conversation

tbaederr
Copy link
Contributor

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.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Jan 28, 2024
@llvmbot
Copy link
Member

llvmbot commented Jan 28, 2024

@llvm/pr-subscribers-platform-windows
@llvm/pr-subscribers-llvm-support

@llvm/pr-subscribers-clang

Author: Timm Baeder (tbaederr)

Changes

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.


Full diff: https://github.com/llvm/llvm-project/pull/79747.diff

7 Files Affected:

  • (modified) clang/lib/AST/Interp/ByteCodeEmitter.cpp (+37)
  • (modified) clang/lib/AST/Interp/ByteCodeExprGen.cpp (+7-4)
  • (modified) clang/lib/AST/Interp/Disasm.cpp (+15)
  • (modified) clang/lib/AST/Interp/IntegralAP.h (+29)
  • (modified) clang/lib/AST/Interp/Interp.h (+16)
  • (modified) clang/lib/AST/Interp/Opcodes.td (+4)
  • (modified) clang/test/AST/Interp/intap.cpp (+14)
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) {

Copy link

github-actions bot commented Jan 30, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

Add (de)serialization support for them, like we do for Floating values.
Copy link
Collaborator

@AaronBallman AaronBallman left a 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.

@tbaederr tbaederr merged commit 64a849a into llvm:main Jan 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category llvm:support platform:windows
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants