Skip to content

Commit 26d9f85

Browse files
authored
[clang][Interp] Add basic support for _BitInt (#68069)
Make sure we pass the expected bitwidth around when casting to IntAP/IntAPS. This makes it easier to test the `IntegralAP` code for different bit widths than 128.
1 parent d97947e commit 26d9f85

File tree

7 files changed

+125
-11
lines changed

7 files changed

+125
-11
lines changed

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,13 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
138138
if (!this->visit(SubExpr))
139139
return false;
140140

141+
if (ToT == PT_IntAP)
142+
return this->emitCastFloatingIntegralAP(Ctx.getBitWidth(CE->getType()),
143+
CE);
144+
if (ToT == PT_IntAPS)
145+
return this->emitCastFloatingIntegralAPS(Ctx.getBitWidth(CE->getType()),
146+
CE);
147+
141148
return this->emitCastFloatingIntegral(*ToT, CE);
142149
}
143150

@@ -183,6 +190,11 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
183190
return true;
184191
}
185192

193+
if (ToT == PT_IntAP)
194+
return this->emitCastAP(*FromT, Ctx.getBitWidth(CE->getType()), CE);
195+
if (ToT == PT_IntAPS)
196+
return this->emitCastAPS(*FromT, Ctx.getBitWidth(CE->getType()), CE);
197+
186198
return this->emitCast(*FromT, *ToT, CE);
187199
}
188200

clang/lib/AST/Interp/Context.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ class Context final {
6464
unsigned getCharBit() const;
6565
/// Return the floating-point semantics for T.
6666
const llvm::fltSemantics &getFloatSemantics(QualType T) const;
67+
/// Return the size of T in bits.
68+
uint32_t getBitWidth(QualType T) const { return Ctx.getIntWidth(T); }
6769

6870
/// Classifies an expression.
6971
std::optional<PrimType> classify(QualType T) const;

clang/lib/AST/Interp/IntegralAP.h

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,12 @@ template <bool Signed> class IntegralAP final {
3737
APSInt V;
3838

3939
template <typename T> static T truncateCast(const APSInt &V) {
40-
return std::is_signed_v<T> ? V.trunc(sizeof(T) * 8).getSExtValue()
41-
: V.trunc(sizeof(T) * 8).getZExtValue();
40+
constexpr unsigned BitSize = sizeof(T) * 8;
41+
if (BitSize >= V.getBitWidth())
42+
return std::is_signed_v<T> ? V.getSExtValue() : V.getZExtValue();
43+
44+
return std::is_signed_v<T> ? V.trunc(BitSize).getSExtValue()
45+
: V.trunc(BitSize).getZExtValue();
4246
}
4347

4448
public:
@@ -89,10 +93,9 @@ template <bool Signed> class IntegralAP final {
8993
}
9094

9195
template <unsigned Bits, bool InputSigned>
92-
static IntegralAP from(Integral<Bits, InputSigned> I) {
93-
// FIXME: Take bits parameter.
96+
static IntegralAP from(Integral<Bits, InputSigned> I, unsigned BitWidth) {
9497
APSInt Copy =
95-
APSInt(APInt(128, static_cast<int64_t>(I), InputSigned), !Signed);
98+
APSInt(APInt(BitWidth, static_cast<int64_t>(I), InputSigned), !Signed);
9699
Copy.setIsSigned(Signed);
97100

98101
assert(Copy.isSigned() == Signed);
@@ -108,8 +111,7 @@ template <bool Signed> class IntegralAP final {
108111
return IntegralAP(0);
109112
}
110113

111-
// FIXME: This can't be static if the bitwidth depends on V.
112-
static constexpr unsigned bitWidth() { return 128; }
114+
constexpr unsigned bitWidth() const { return V.getBitWidth(); }
113115

114116
APSInt toAPSInt(unsigned Bits = 0) const { return V; }
115117
APValue toAPValue() const { return APValue(V); }

clang/lib/AST/Interp/Interp.h

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1568,6 +1568,22 @@ inline bool CastFP(InterpState &S, CodePtr OpPC, const llvm::fltSemantics *Sem,
15681568
return true;
15691569
}
15701570

1571+
/// Like Cast(), but we cast to an arbitrary-bitwidth integral, so we need
1572+
/// to know what bitwidth the result should be.
1573+
template <PrimType Name, class T = typename PrimConv<Name>::T>
1574+
bool CastAP(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
1575+
S.Stk.push<IntegralAP<false>>(
1576+
IntegralAP<false>::from(S.Stk.pop<T>(), BitWidth));
1577+
return true;
1578+
}
1579+
1580+
template <PrimType Name, class T = typename PrimConv<Name>::T>
1581+
bool CastAPS(InterpState &S, CodePtr OpPC, uint32_t BitWidth) {
1582+
S.Stk.push<IntegralAP<true>>(
1583+
IntegralAP<true>::from(S.Stk.pop<T>(), BitWidth));
1584+
return true;
1585+
}
1586+
15711587
template <PrimType Name, class T = typename PrimConv<Name>::T>
15721588
bool CastIntegralFloating(InterpState &S, CodePtr OpPC,
15731589
const llvm::fltSemantics *Sem,
@@ -1608,6 +1624,46 @@ bool CastFloatingIntegral(InterpState &S, CodePtr OpPC) {
16081624
}
16091625
}
16101626

1627+
static inline bool CastFloatingIntegralAP(InterpState &S, CodePtr OpPC,
1628+
uint32_t BitWidth) {
1629+
const Floating &F = S.Stk.pop<Floating>();
1630+
1631+
APSInt Result(BitWidth, /*IsUnsigned=*/true);
1632+
auto Status = F.convertToInteger(Result);
1633+
1634+
// Float-to-Integral overflow check.
1635+
if ((Status & APFloat::opStatus::opInvalidOp) && F.isFinite()) {
1636+
const Expr *E = S.Current->getExpr(OpPC);
1637+
QualType Type = E->getType();
1638+
1639+
S.CCEDiag(E, diag::note_constexpr_overflow) << F.getAPFloat() << Type;
1640+
return S.noteUndefinedBehavior();
1641+
}
1642+
1643+
S.Stk.push<IntegralAP<true>>(IntegralAP<true>(Result));
1644+
return CheckFloatResult(S, OpPC, F, Status);
1645+
}
1646+
1647+
static inline bool CastFloatingIntegralAPS(InterpState &S, CodePtr OpPC,
1648+
uint32_t BitWidth) {
1649+
const Floating &F = S.Stk.pop<Floating>();
1650+
1651+
APSInt Result(BitWidth, /*IsUnsigned=*/false);
1652+
auto Status = F.convertToInteger(Result);
1653+
1654+
// Float-to-Integral overflow check.
1655+
if ((Status & APFloat::opStatus::opInvalidOp) && F.isFinite()) {
1656+
const Expr *E = S.Current->getExpr(OpPC);
1657+
QualType Type = E->getType();
1658+
1659+
S.CCEDiag(E, diag::note_constexpr_overflow) << F.getAPFloat() << Type;
1660+
return S.noteUndefinedBehavior();
1661+
}
1662+
1663+
S.Stk.push<IntegralAP<true>>(IntegralAP<true>(Result));
1664+
return CheckFloatResult(S, OpPC, F, Status);
1665+
}
1666+
16111667
template <PrimType Name, class T = typename PrimConv<Name>::T>
16121668
bool CastPointerIntegral(InterpState &S, CodePtr OpPC) {
16131669
const Pointer &Ptr = S.Stk.pop<Pointer>();
@@ -1697,7 +1753,7 @@ inline bool Shl(InterpState &S, CodePtr OpPC) {
16971753

16981754
typename LT::AsUnsigned R;
16991755
LT::AsUnsigned::shiftLeft(LT::AsUnsigned::from(LHS),
1700-
LT::AsUnsigned::from(RHS), Bits, &R);
1756+
LT::AsUnsigned::from(RHS, Bits), Bits, &R);
17011757
S.Stk.push<LT>(LT::from(R));
17021758
return true;
17031759
}

clang/lib/AST/Interp/InterpBuiltin.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ static APSInt peekToAPSInt(InterpStack &Stk, PrimType T, size_t Offset = 0) {
4141
APSInt R;
4242
INT_TYPE_SWITCH(T, {
4343
T Val = Stk.peek<T>(Offset);
44-
R = APSInt(APInt(T::bitWidth(), static_cast<uint64_t>(Val), T::isSigned()));
44+
R = APSInt(
45+
APInt(Val.bitWidth(), static_cast<uint64_t>(Val), T::isSigned()));
4546
});
4647

4748
return R;

clang/lib/AST/Interp/Opcodes.td

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ def FromCastTypeClass : TypeClass {
564564
}
565565

566566
def ToCastTypeClass : TypeClass {
567-
let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool, IntAP, IntAPS];
567+
let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool];
568568
}
569569

570570
def Cast: Opcode {
@@ -577,6 +577,22 @@ def CastFP : Opcode {
577577
let Args = [ArgFltSemantics, ArgRoundingMode];
578578
}
579579

580+
def FixedSizeIntegralTypes : TypeClass {
581+
let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool];
582+
}
583+
584+
def CastAP : Opcode {
585+
let Types = [AluTypeClass];
586+
let Args = [ArgUint32];
587+
let HasGroup = 1;
588+
}
589+
590+
def CastAPS : Opcode {
591+
let Types = [AluTypeClass];
592+
let Args = [ArgUint32];
593+
let HasGroup = 1;
594+
}
595+
580596
// Cast an integer to a floating type
581597
def CastIntegralFloating : Opcode {
582598
let Types = [AluTypeClass];
@@ -586,11 +602,21 @@ def CastIntegralFloating : Opcode {
586602

587603
// Cast a floating to an integer type
588604
def CastFloatingIntegral : Opcode {
589-
let Types = [AluTypeClass];
605+
let Types = [FixedSizeIntegralTypes];
590606
let Args = [];
591607
let HasGroup = 1;
592608
}
593609

610+
def CastFloatingIntegralAP : Opcode {
611+
let Types = [];
612+
let Args = [ArgUint32];
613+
}
614+
615+
def CastFloatingIntegralAPS : Opcode {
616+
let Types = [];
617+
let Args = [ArgUint32];
618+
}
619+
594620
def CastPointerIntegral : Opcode {
595621
let Types = [AluTypeClass];
596622
let Args = [];

clang/test/AST/Interp/intap.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,21 @@
33
// RUN: %clang_cc1 -std=c++11 -fms-extensions -verify=ref %s
44
// RUN: %clang_cc1 -std=c++20 -fms-extensions -verify=ref %s
55

6+
7+
using MaxBitInt = _BitInt(8388608);
8+
9+
constexpr _BitInt(2) A = 0;
10+
constexpr _BitInt(2) B = A + 1;
11+
constexpr _BitInt(2) C = B + 1; // expected-warning {{from 2 to -2}} \
12+
// ref-warning {{from 2 to -2}}
13+
static_assert(C == -2, "");
14+
15+
16+
constexpr MaxBitInt A_ = 0;
17+
constexpr MaxBitInt B_ = A_ + 1;
18+
static_assert(B_ == 1, "");
19+
20+
621
#ifdef __SIZEOF_INT128__
722
namespace i128 {
823
typedef __int128 int128_t;

0 commit comments

Comments
 (0)