[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