[clang] [clang][Interp] Add basic support for _BitInt (PR #68069)

Timm Baeder via cfe-commits cfe-commits at lists.llvm.org
Mon Oct 2 23:42:48 PDT 2023


https://github.com/tbaederr created https://github.com/llvm/llvm-project/pull/68069

Make sure we pass the expected bitwidth around when casting to IntAP/IntAPS and move the tests to their own file.

This makes it easier to test the `IntegralAP` code for different bit widths than 128.

>From d7b0ac02cfd1bb2e679fb1ab086f38082d48963e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Timm=20B=C3=A4der?= <tbaeder at redhat.com>
Date: Tue, 3 Oct 2023 08:35:57 +0200
Subject: [PATCH] [clang][Interp] Add basic support for _BitInt

Make sure we pass the expected bitwidth around when casting to
IntAP/IntAPS and move the tests to their own file.
---
 clang/lib/AST/Interp/ByteCodeExprGen.cpp | 12 ++++
 clang/lib/AST/Interp/Context.h           |  2 +
 clang/lib/AST/Interp/IntegralAP.h        | 16 +++--
 clang/lib/AST/Interp/Interp.h            | 58 ++++++++++++++++-
 clang/lib/AST/Interp/InterpBuiltin.cpp   |  3 +-
 clang/lib/AST/Interp/Opcodes.td          | 30 ++++++++-
 clang/test/AST/Interp/intap.cpp          | 83 ++++++++++++++++++++++++
 clang/test/AST/Interp/literals.cpp       | 75 ---------------------
 8 files changed, 193 insertions(+), 86 deletions(-)
 create mode 100644 clang/test/AST/Interp/intap.cpp

diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index e266804a4e75dea..f899c2fe22ecd8f 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -138,6 +138,13 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
     if (!this->visit(SubExpr))
       return false;
 
+    if (ToT == PT_IntAP)
+      return this->emitCastFloatingIntegralAP(Ctx.getBitWidth(CE->getType()),
+                                              CE);
+    if (ToT == PT_IntAPS)
+      return this->emitCastFloatingIntegralAPS(Ctx.getBitWidth(CE->getType()),
+                                               CE);
+
     return this->emitCastFloatingIntegral(*ToT, CE);
   }
 
@@ -183,6 +190,11 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
       return true;
     }
 
+    if (ToT == PT_IntAP)
+      return this->emitCastAP(*FromT, Ctx.getBitWidth(CE->getType()), CE);
+    if (ToT == PT_IntAPS)
+      return this->emitCastAPS(*FromT, Ctx.getBitWidth(CE->getType()), CE);
+
     return this->emitCast(*FromT, *ToT, CE);
   }
 
diff --git a/clang/lib/AST/Interp/Context.h b/clang/lib/AST/Interp/Context.h
index 958b50b1615ad18..6df61e93ad83abc 100644
--- a/clang/lib/AST/Interp/Context.h
+++ b/clang/lib/AST/Interp/Context.h
@@ -64,6 +64,8 @@ class Context final {
   unsigned getCharBit() const;
   /// Return the floating-point semantics for T.
   const llvm::fltSemantics &getFloatSemantics(QualType T) const;
+  /// Return the size of T in bits.
+  uint32_t getBitWidth(QualType T) const { return Ctx.getIntWidth(T); }
 
   /// Classifies an expression.
   std::optional<PrimType> classify(QualType T) const;
diff --git a/clang/lib/AST/Interp/IntegralAP.h b/clang/lib/AST/Interp/IntegralAP.h
index a8df431bef11784..f9a33bbcd7bd7fa 100644
--- a/clang/lib/AST/Interp/IntegralAP.h
+++ b/clang/lib/AST/Interp/IntegralAP.h
@@ -37,8 +37,12 @@ template <bool Signed> class IntegralAP final {
   APSInt V;
 
   template <typename T> static T truncateCast(const APSInt &V) {
-    return std::is_signed_v<T> ? V.trunc(sizeof(T) * 8).getSExtValue()
-                               : V.trunc(sizeof(T) * 8).getZExtValue();
+    constexpr unsigned BitSize = sizeof(T) * 8;
+    if (BitSize >= V.getBitWidth())
+      return std::is_signed_v<T> ? V.getSExtValue() : V.getZExtValue();
+
+    return std::is_signed_v<T> ? V.trunc(BitSize).getSExtValue()
+                               : V.trunc(BitSize).getZExtValue();
   }
 
 public:
@@ -89,10 +93,9 @@ template <bool Signed> class IntegralAP final {
   }
 
   template <unsigned Bits, bool InputSigned>
-  static IntegralAP from(Integral<Bits, InputSigned> I) {
-    // FIXME: Take bits parameter.
+  static IntegralAP from(Integral<Bits, InputSigned> I, unsigned BitWidth) {
     APSInt Copy =
-        APSInt(APInt(128, static_cast<int64_t>(I), InputSigned), !Signed);
+        APSInt(APInt(BitWidth, static_cast<int64_t>(I), InputSigned), !Signed);
     Copy.setIsSigned(Signed);
 
     assert(Copy.isSigned() == Signed);
@@ -108,8 +111,7 @@ template <bool Signed> class IntegralAP final {
     return IntegralAP(0);
   }
 
-  // FIXME: This can't be static if the bitwidth depends on V.
-  static constexpr unsigned bitWidth() { return 128; }
+  constexpr unsigned bitWidth() const { return V.getBitWidth(); }
 
   APSInt toAPSInt(unsigned Bits = 0) const { return V; }
   APValue toAPValue() const { return APValue(V); }
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 9d5ec3315415cf7..d3ee28c0315cda8 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -1561,6 +1561,22 @@ inline bool CastFP(InterpState &S, CodePtr OpPC, const llvm::fltSemantics *Sem,
   return true;
 }
 
+/// Like Cast(), but we cast to an arbitrary-bitwidth integral, so we need
+/// to know what bitwidth the result should be.
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool CastAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
+  S.Stk.push<IntegralAP<false>>(
+      IntegralAP<false>::from(S.Stk.pop<T>(), BitWidth));
+  return true;
+}
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool CastAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
+  S.Stk.push<IntegralAP<true>>(
+      IntegralAP<true>::from(S.Stk.pop<T>(), BitWidth));
+  return true;
+}
+
 template <PrimType Name, class T = typename PrimConv<Name>::T>
 bool CastIntegralFloating(InterpState &S, CodePtr OpPC,
                           const llvm::fltSemantics *Sem,
@@ -1601,6 +1617,46 @@ bool CastFloatingIntegral(InterpState &S, CodePtr OpPC) {
   }
 }
 
+static inline bool CastFloatingIntegralAP(InterpState &S, CodePtr OpPC,
+                                          uint32_t BitWidth) {
+  const Floating &F = S.Stk.pop<Floating>();
+
+  APSInt Result(BitWidth, /*IsUnsigned=*/true);
+  auto Status = F.convertToInteger(Result);
+
+  // Float-to-Integral overflow check.
+  if ((Status & APFloat::opStatus::opInvalidOp) && F.isFinite()) {
+    const Expr *E = S.Current->getExpr(OpPC);
+    QualType Type = E->getType();
+
+    S.CCEDiag(E, diag::note_constexpr_overflow) << F.getAPFloat() << Type;
+    return S.noteUndefinedBehavior();
+  }
+
+  S.Stk.push<IntegralAP<true>>(IntegralAP<true>(Result));
+  return CheckFloatResult(S, OpPC, F, Status);
+}
+
+static inline bool CastFloatingIntegralAPS(InterpState &S, CodePtr OpPC,
+                                           uint32_t BitWidth) {
+  const Floating &F = S.Stk.pop<Floating>();
+
+  APSInt Result(BitWidth, /*IsUnsigned=*/false);
+  auto Status = F.convertToInteger(Result);
+
+  // Float-to-Integral overflow check.
+  if ((Status & APFloat::opStatus::opInvalidOp) && F.isFinite()) {
+    const Expr *E = S.Current->getExpr(OpPC);
+    QualType Type = E->getType();
+
+    S.CCEDiag(E, diag::note_constexpr_overflow) << F.getAPFloat() << Type;
+    return S.noteUndefinedBehavior();
+  }
+
+  S.Stk.push<IntegralAP<true>>(IntegralAP<true>(Result));
+  return CheckFloatResult(S, OpPC, F, Status);
+}
+
 template <PrimType Name, class T = typename PrimConv<Name>::T>
 bool CastPointerIntegral(InterpState &S, CodePtr OpPC) {
   const Pointer &Ptr = S.Stk.pop<Pointer>();
@@ -1690,7 +1746,7 @@ inline bool Shl(InterpState &S, CodePtr OpPC) {
 
   typename LT::AsUnsigned R;
   LT::AsUnsigned::shiftLeft(LT::AsUnsigned::from(LHS),
-                            LT::AsUnsigned::from(RHS), Bits, &R);
+                            LT::AsUnsigned::from(RHS, Bits), Bits, &R);
   S.Stk.push<LT>(LT::from(R));
   return true;
 }
diff --git a/clang/lib/AST/Interp/InterpBuiltin.cpp b/clang/lib/AST/Interp/InterpBuiltin.cpp
index bba0255219bc0d7..7552c1b88cff60c 100644
--- a/clang/lib/AST/Interp/InterpBuiltin.cpp
+++ b/clang/lib/AST/Interp/InterpBuiltin.cpp
@@ -41,7 +41,8 @@ static APSInt peekToAPSInt(InterpStack &Stk, PrimType T, size_t Offset = 0) {
   APSInt R;
   INT_TYPE_SWITCH(T, {
     T Val = Stk.peek<T>(Offset);
-    R = APSInt(APInt(T::bitWidth(), static_cast<uint64_t>(Val), T::isSigned()));
+    R = APSInt(
+        APInt(Val.bitWidth(), static_cast<uint64_t>(Val), T::isSigned()));
   });
 
   return R;
diff --git a/clang/lib/AST/Interp/Opcodes.td b/clang/lib/AST/Interp/Opcodes.td
index 9fc4938bb37bde8..ef6f730a21f027f 100644
--- a/clang/lib/AST/Interp/Opcodes.td
+++ b/clang/lib/AST/Interp/Opcodes.td
@@ -560,7 +560,7 @@ def FromCastTypeClass : TypeClass {
 }
 
 def ToCastTypeClass : TypeClass {
-  let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool, IntAP, IntAPS];
+  let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool];
 }
 
 def Cast: Opcode {
@@ -573,6 +573,22 @@ def CastFP : Opcode {
   let Args = [ArgFltSemantics, ArgRoundingMode];
 }
 
+def FixedSizeIntegralTypes : TypeClass {
+  let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool];
+}
+
+def CastAP : Opcode {
+  let Types = [AluTypeClass];
+  let Args = [ArgUint32];
+  let HasGroup = 1;
+}
+
+def CastAPS : Opcode {
+  let Types = [AluTypeClass];
+  let Args = [ArgUint32];
+  let HasGroup = 1;
+}
+
 // Cast an integer to a floating type
 def CastIntegralFloating : Opcode {
   let Types = [AluTypeClass];
@@ -582,11 +598,21 @@ def CastIntegralFloating : Opcode {
 
 // Cast a floating to an integer type
 def CastFloatingIntegral : Opcode {
-  let Types = [AluTypeClass];
+  let Types = [FixedSizeIntegralTypes];
   let Args = [];
   let HasGroup = 1;
 }
 
+def CastFloatingIntegralAP : Opcode {
+  let Types = [];
+  let Args = [ArgUint32];
+}
+
+def CastFloatingIntegralAPS : Opcode {
+  let Types = [];
+  let Args = [ArgUint32];
+}
+
 def CastPointerIntegral : Opcode {
   let Types = [AluTypeClass];
   let Args = [];
diff --git a/clang/test/AST/Interp/intap.cpp b/clang/test/AST/Interp/intap.cpp
new file mode 100644
index 000000000000000..8d8f3f27fc01672
--- /dev/null
+++ b/clang/test/AST/Interp/intap.cpp
@@ -0,0 +1,83 @@
+// RUN: %clang_cc1 -verify=ref %s
+// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify %s
+
+constexpr _BitInt(2) A = 0;
+constexpr _BitInt(2) B = A + 1;
+constexpr _BitInt(2) C = B + 1; // expected-warning {{from 2 to -2}} \
+                                // ref-warning {{from 2 to -2}}
+static_assert(C == -2, "");
+
+
+#ifdef __SIZEOF_INT128__
+namespace i128 {
+  typedef __int128 int128_t;
+  typedef unsigned __int128 uint128_t;
+  constexpr int128_t I128_1 = 12;
+  static_assert(I128_1 == 12, "");
+  static_assert(I128_1 != 10, "");
+  static_assert(I128_1 != 12, ""); // expected-error{{failed}} \
+                                   // ref-error{{failed}} \
+                                   // expected-note{{evaluates to}} \
+                                   // ref-note{{evaluates to}}
+
+  static const __uint128_t UINT128_MAX =__uint128_t(__int128_t(-1L));
+  static_assert(UINT128_MAX == -1, "");
+
+  static const __int128_t INT128_MAX = UINT128_MAX >> (__int128_t)1;
+  static_assert(INT128_MAX != 0, "");
+  static const __int128_t INT128_MIN = -INT128_MAX - 1;
+  constexpr __int128 A = INT128_MAX + 1; // expected-error {{must be initialized by a constant expression}} \
+                                         // expected-note {{outside the range}} \
+                                         // ref-error {{must be initialized by a constant expression}} \
+                                         // ref-note {{outside the range}}
+  constexpr int128_t Two = (int128_t)1 << 1ul;
+  static_assert(Two == 2, "");
+
+  constexpr uint128_t AllOnes = ~static_cast<uint128_t>(0);
+  static_assert(AllOnes == UINT128_MAX, "");
+
+#if __cplusplus >= 201402L
+  template <typename T>
+  constexpr T CastFrom(__int128_t A) {
+    T B = (T)A;
+    return B;
+  }
+  static_assert(CastFrom<char>(12) == 12, "");
+  static_assert(CastFrom<unsigned char>(12) == 12, "");
+  static_assert(CastFrom<long>(12) == 12, "");
+  static_assert(CastFrom<unsigned short>(12) == 12, "");
+  static_assert(CastFrom<int128_t>(12) == 12, "");
+  static_assert(CastFrom<float>(12) == 12, "");
+  static_assert(CastFrom<double>(12) == 12, "");
+  static_assert(CastFrom<long double>(12) == 12, "");
+
+  static_assert(CastFrom<char>(AllOnes) == -1, "");
+  static_assert(CastFrom<unsigned char>(AllOnes) == 0xFF, "");
+  static_assert(CastFrom<long>(AllOnes) == -1, "");
+  static_assert(CastFrom<unsigned short>(AllOnes) == 0xFFFF, "");
+  static_assert(CastFrom<int>(AllOnes) == -1, "");
+  static_assert(CastFrom<int128_t>(AllOnes) == -1, "");
+  static_assert(CastFrom<uint128_t>(AllOnes) == AllOnes, "");
+
+  template <typename T>
+  constexpr __int128 CastTo(T A) {
+    int128_t B = (int128_t)A;
+    return B;
+  }
+  static_assert(CastTo<char>(12) == 12, "");
+  static_assert(CastTo<unsigned char>(12) == 12, "");
+  static_assert(CastTo<long>(12) == 12, "");
+  static_assert(CastTo<unsigned long long>(12) == 12, "");
+  static_assert(CastTo<float>(12) == 12, "");
+  static_assert(CastTo<double>(12) == 12, "");
+  static_assert(CastTo<long double>(12) == 12, "");
+#endif
+
+  constexpr int128_t Error = __LDBL_MAX__; // ref-warning {{implicit conversion of out of range value}} \
+                                           // ref-error {{must be initialized by a constant expression}} \
+                                           // ref-note {{is outside the range of representable values of type}} \
+                                           // expected-warning {{implicit conversion of out of range value}} \
+                                           // expected-error {{must be initialized by a constant expression}} \
+                                           // expected-note {{is outside the range of representable values of type}}
+}
+#endif
diff --git a/clang/test/AST/Interp/literals.cpp b/clang/test/AST/Interp/literals.cpp
index 00875bcf44dc8e6..ceda59405ea9105 100644
--- a/clang/test/AST/Interp/literals.cpp
+++ b/clang/test/AST/Interp/literals.cpp
@@ -26,81 +26,6 @@ static_assert(number != 10, ""); // expected-error{{failed}} \
                                  // expected-note{{evaluates to}} \
                                  // ref-note{{evaluates to}}
 
-
-#ifdef __SIZEOF_INT128__
-namespace i128 {
-  typedef __int128 int128_t;
-  typedef unsigned __int128 uint128_t;
-  constexpr int128_t I128_1 = 12;
-  static_assert(I128_1 == 12, "");
-  static_assert(I128_1 != 10, "");
-  static_assert(I128_1 != 12, ""); // expected-error{{failed}} \
-                                   // ref-error{{failed}} \
-                                   // expected-note{{evaluates to}} \
-                                   // ref-note{{evaluates to}}
-
-  static const __uint128_t UINT128_MAX =__uint128_t(__int128_t(-1L));
-  static_assert(UINT128_MAX == -1, "");
-
-  static const __int128_t INT128_MAX = UINT128_MAX >> (__int128_t)1;
-  static_assert(INT128_MAX != 0, "");
-  static const __int128_t INT128_MIN = -INT128_MAX - 1;
-  constexpr __int128 A = INT128_MAX + 1; // expected-error {{must be initialized by a constant expression}} \
-                                         // expected-note {{outside the range}} \
-                                         // ref-error {{must be initialized by a constant expression}} \
-                                         // ref-note {{outside the range}}
-  constexpr int128_t Two = (int128_t)1 << 1ul;
-  static_assert(Two == 2, "");
-
-  constexpr uint128_t AllOnes = ~static_cast<uint128_t>(0);
-  static_assert(AllOnes == UINT128_MAX, "");
-
-#if __cplusplus >= 201402L
-  template <typename T>
-  constexpr T CastFrom(__int128_t A) {
-    T B = (T)A;
-    return B;
-  }
-  static_assert(CastFrom<char>(12) == 12, "");
-  static_assert(CastFrom<unsigned char>(12) == 12, "");
-  static_assert(CastFrom<long>(12) == 12, "");
-  static_assert(CastFrom<unsigned short>(12) == 12, "");
-  static_assert(CastFrom<int128_t>(12) == 12, "");
-  static_assert(CastFrom<float>(12) == 12, "");
-  static_assert(CastFrom<double>(12) == 12, "");
-  static_assert(CastFrom<long double>(12) == 12, "");
-
-  static_assert(CastFrom<char>(AllOnes) == -1, "");
-  static_assert(CastFrom<unsigned char>(AllOnes) == 0xFF, "");
-  static_assert(CastFrom<long>(AllOnes) == -1, "");
-  static_assert(CastFrom<unsigned short>(AllOnes) == 0xFFFF, "");
-  static_assert(CastFrom<int>(AllOnes) == -1, "");
-  static_assert(CastFrom<int128_t>(AllOnes) == -1, "");
-  static_assert(CastFrom<uint128_t>(AllOnes) == AllOnes, "");
-
-  template <typename T>
-  constexpr __int128 CastTo(T A) {
-    int128_t B = (int128_t)A;
-    return B;
-  }
-  static_assert(CastTo<char>(12) == 12, "");
-  static_assert(CastTo<unsigned char>(12) == 12, "");
-  static_assert(CastTo<long>(12) == 12, "");
-  static_assert(CastTo<unsigned long long>(12) == 12, "");
-  static_assert(CastTo<float>(12) == 12, "");
-  static_assert(CastTo<double>(12) == 12, "");
-  static_assert(CastTo<long double>(12) == 12, "");
-#endif
-
-constexpr int128_t Error = __LDBL_MAX__; // ref-warning {{implicit conversion of out of range value}} \
-                                         // ref-error {{must be initialized by a constant expression}} \
-                                         // ref-note {{is outside the range of representable values of type}} \
-                                         // expected-warning {{implicit conversion of out of range value}} \
-                                         // expected-error {{must be initialized by a constant expression}} \
-                                         // expected-note {{is outside the range of representable values of type}}
-}
-#endif
-
 constexpr bool b = number;
 static_assert(b, "");
 constexpr int one = true;



More information about the cfe-commits mailing list