[clang] [clang][Interp] Add basic support for _BitInt (PR #68069)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 2 23:43:51 PDT 2023
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
<details>
<summary>Changes</summary>
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.
---
Full diff: https://github.com/llvm/llvm-project/pull/68069.diff
8 Files Affected:
- (modified) clang/lib/AST/Interp/ByteCodeExprGen.cpp (+12)
- (modified) clang/lib/AST/Interp/Context.h (+2)
- (modified) clang/lib/AST/Interp/IntegralAP.h (+9-7)
- (modified) clang/lib/AST/Interp/Interp.h (+57-1)
- (modified) clang/lib/AST/Interp/InterpBuiltin.cpp (+2-1)
- (modified) clang/lib/AST/Interp/Opcodes.td (+28-2)
- (added) clang/test/AST/Interp/intap.cpp (+83)
- (modified) clang/test/AST/Interp/literals.cpp (-75)
``````````diff
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;
``````````
</details>
https://github.com/llvm/llvm-project/pull/68069
More information about the cfe-commits
mailing list