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

Timm Baeder via cfe-commits cfe-commits at lists.llvm.org
Tue Oct 10 04:06:25 PDT 2023


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

>From c93895b7e1b0727327178d73adc93cb988ad3966 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          | 13 ++++--
 7 files changed, 119 insertions(+), 15 deletions(-)

diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 9bd040a208b3f95..cf82ec9c15eca1e 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 47dc1d08c9c4d8b..438cfb5a7eb90a4 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -1568,6 +1568,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,
@@ -1608,6 +1624,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>();
@@ -1697,7 +1753,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 50b6c0ac154de30..9d390fed152417f 100644
--- a/clang/lib/AST/Interp/Opcodes.td
+++ b/clang/lib/AST/Interp/Opcodes.td
@@ -564,7 +564,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 {
@@ -577,6 +577,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];
@@ -586,11 +602,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
index b3f02d2b769531d..2afd1900da43735 100644
--- a/clang/test/AST/Interp/intap.cpp
+++ b/clang/test/AST/Interp/intap.cpp
@@ -1,7 +1,12 @@
-// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -fms-extensions -std=c++11 -verify %s
-// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -fms-extensions -std=c++20 -verify %s
-// RUN: %clang_cc1 -std=c++11 -fms-extensions -verify=ref %s
-// RUN: %clang_cc1 -std=c++20 -fms-extensions -verify=ref %s
+// 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 {



More information about the cfe-commits mailing list