Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4182,7 +4182,7 @@ builtin, the mangler emits their usual pattern without any special treatment.
-----------------------

``__builtin_popcountg`` returns the number of 1 bits in the argument. The
argument can be of any unsigned integer type.
argument can be of any unsigned integer type or fixed boolean vector.

**Syntax**:

Expand Down Expand Up @@ -4214,7 +4214,9 @@ such as ``unsigned __int128`` and C23 ``unsigned _BitInt(N)``.

``__builtin_clzg`` (respectively ``__builtin_ctzg``) returns the number of
leading (respectively trailing) 0 bits in the first argument. The first argument
can be of any unsigned integer type.
can be of any unsigned integer type or fixed boolean vector. Boolean vectors
behave like a bit field where the least significant bits are trailing and the
most significant bits are leading.

If the first argument is 0 and an optional second argument of ``int`` type is
provided, then the second argument is returned. If the first argument is 0, but
Expand Down
4 changes: 3 additions & 1 deletion clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -143,10 +143,12 @@ Non-comprehensive list of changes in this release
- Added ``__builtin_masked_load`` and ``__builtin_masked_store`` for conditional
memory loads from vectors. Binds to the LLVM intrinsic of the same name.

- The ``__builtin_popcountg``, ``__builtin_ctzg``, and ``__builtin_clzg``
functions now accept fixed-size boolean vectors.

- Use of ``__has_feature`` to detect the ``ptrauth_qualifier`` and ``ptrauth_intrinsics``
features has been deprecated, and is restricted to the arm64e target only. The
correct method to check for these features is to test for the ``__PTRAUTH__``
macro.

- Added a new builtin, ``__builtin_dedup_pack``, to remove duplicate types from a parameter pack.
This feature is particularly useful in template metaprogramming for normalizing type lists.
Expand Down
46 changes: 40 additions & 6 deletions clang/lib/AST/ByteCode/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,22 @@ static void diagnoseNonConstexprBuiltin(InterpState &S, CodePtr OpPC,
S.CCEDiag(Loc, diag::note_invalid_subexpr_in_const_expr);
}

static llvm::APSInt convertBoolVectorToInt(const Pointer &Val) {
assert(Val.getFieldDesc()->isPrimitiveArray() &&
Val.getFieldDesc()->getElemQualType()->isBooleanType() &&
"Not a boolean vector");
unsigned NumElts = Val.getNumElems();

// Each element is one bit, so create an integer with NumElts bits.
llvm::APSInt Result(NumElts, 0);
for (unsigned I = 0; I < NumElts; ++I) {
if (Val.elem<bool>(I))
Result.setBit(I);
}

return Result;
}

static bool interp__builtin_is_constant_evaluated(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const CallExpr *Call) {
Expand Down Expand Up @@ -638,8 +654,14 @@ static bool interp__builtin_abs(InterpState &S, CodePtr OpPC,
static bool interp__builtin_popcount(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
const CallExpr *Call) {
PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType());
APSInt Val = popToAPSInt(S.Stk, ArgT);
APSInt Val;
if (Call->getArg(0)->getType()->isExtVectorBoolType()) {
const Pointer &Arg = S.Stk.pop<Pointer>();
Val = convertBoolVectorToInt(Arg);
} else {
PrimType ArgT = *S.getContext().classify(Call->getArg(0)->getType());
Val = popToAPSInt(S.Stk, ArgT);
}
pushInteger(S, Val.popcount(), Call->getType());
return true;
}
Expand Down Expand Up @@ -935,8 +957,14 @@ static bool interp__builtin_clz(InterpState &S, CodePtr OpPC,
PrimType FallbackT = *S.getContext().classify(Call->getArg(1));
Fallback = popToAPSInt(S.Stk, FallbackT);
}
PrimType ValT = *S.getContext().classify(Call->getArg(0));
const APSInt &Val = popToAPSInt(S.Stk, ValT);
APSInt Val;
if (Call->getArg(0)->getType()->isExtVectorBoolType()) {
const Pointer &Arg = S.Stk.pop<Pointer>();
Val = convertBoolVectorToInt(Arg);
} else {
PrimType ValT = *S.getContext().classify(Call->getArg(0));
Val = popToAPSInt(S.Stk, ValT);
}

// When the argument is 0, the result of GCC builtins is undefined, whereas
// for Microsoft intrinsics, the result is the bit-width of the argument.
Expand Down Expand Up @@ -966,8 +994,14 @@ static bool interp__builtin_ctz(InterpState &S, CodePtr OpPC,
PrimType FallbackT = *S.getContext().classify(Call->getArg(1));
Fallback = popToAPSInt(S.Stk, FallbackT);
}
PrimType ValT = *S.getContext().classify(Call->getArg(0));
const APSInt &Val = popToAPSInt(S.Stk, ValT);
APSInt Val;
if (Call->getArg(0)->getType()->isExtVectorBoolType()) {
const Pointer &Arg = S.Stk.pop<Pointer>();
Val = convertBoolVectorToInt(Arg);
} else {
PrimType ValT = *S.getContext().classify(Call->getArg(0));
Val = popToAPSInt(S.Stk, ValT);
}

if (Val == 0) {
if (Fallback) {
Expand Down
42 changes: 39 additions & 3 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11314,6 +11314,24 @@ static bool EvaluateVector(const Expr* E, APValue& Result, EvalInfo &Info) {
return VectorExprEvaluator(Info, Result).Visit(E);
}

static llvm::APInt ConvertBoolVectorToInt(const APValue &Val) {
assert(Val.isVector() && "expected vector APValue");
unsigned NumElts = Val.getVectorLength();

// Each element is one bit, so create an integer with NumElts bits.
llvm::APInt Result(NumElts, 0);

for (unsigned I = 0; I < NumElts; ++I) {
const APValue &Elt = Val.getVectorElt(I);
assert(Elt.isInt() && "expected integer element in bool vector");

if (Elt.getInt().getBoolValue())
Result.setBit(I);
}

return Result;
}

bool VectorExprEvaluator::VisitCastExpr(const CastExpr *E) {
const VectorType *VTy = E->getType()->castAs<VectorType>();
unsigned NElts = VTy->getNumElements();
Expand Down Expand Up @@ -13456,8 +13474,14 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
case Builtin::BI__lzcnt:
case Builtin::BI__lzcnt64: {
APSInt Val;
if (!EvaluateInteger(E->getArg(0), Val, Info))
if (E->getArg(0)->getType()->isExtVectorBoolType()) {
APValue Vec;
if (!EvaluateVector(E->getArg(0), Vec, Info))
return false;
Val = ConvertBoolVectorToInt(Vec);
} else if (!EvaluateInteger(E->getArg(0), Val, Info)) {
return false;
}

std::optional<APSInt> Fallback;
if ((BuiltinOp == Builtin::BI__builtin_clzg ||
Expand Down Expand Up @@ -13542,8 +13566,14 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
case Builtin::BI__builtin_ctzg:
case Builtin::BI__builtin_elementwise_cttz: {
APSInt Val;
if (!EvaluateInteger(E->getArg(0), Val, Info))
if (E->getArg(0)->getType()->isExtVectorBoolType()) {
APValue Vec;
if (!EvaluateVector(E->getArg(0), Vec, Info))
return false;
Val = ConvertBoolVectorToInt(Vec);
} else if (!EvaluateInteger(E->getArg(0), Val, Info)) {
return false;
}

std::optional<APSInt> Fallback;
if ((BuiltinOp == Builtin::BI__builtin_ctzg ||
Expand Down Expand Up @@ -13758,8 +13788,14 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
case Builtin::BI__popcnt:
case Builtin::BI__popcnt64: {
APSInt Val;
if (!EvaluateInteger(E->getArg(0), Val, Info))
if (E->getArg(0)->getType()->isExtVectorBoolType()) {
APValue Vec;
if (!EvaluateVector(E->getArg(0), Vec, Info))
return false;
Val = ConvertBoolVectorToInt(Vec);
} else if (!EvaluateInteger(E->getArg(0), Val, Info)) {
return false;
}

return Success(Val.popcount(), E);
}
Expand Down
28 changes: 24 additions & 4 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1693,6 +1693,26 @@ getBitTestAtomicOrdering(BitTest::InterlockingKind I) {
llvm_unreachable("invalid interlocking");
}

static llvm::Value *EmitBitCountExpr(CodeGenFunction &CGF, const Expr *E) {
llvm::Value *ArgValue = CGF.EmitScalarExpr(E);
llvm::Type *ArgType = ArgValue->getType();

// Boolean vectors can be casted directly to its bitfield representation. We
// intentionally do not round up to the next power of two size and let LLVM
// handle the trailing bits.
if (auto *VT = dyn_cast<llvm::FixedVectorType>(ArgType);
VT && VT->getElementType()->isIntegerTy(1)) {
llvm::Type *StorageType =
llvm::Type::getIntNTy(CGF.getLLVMContext(), VT->getNumElements());
ArgValue = CGF.emitBoolVecConversion(
ArgValue, StorageType->getPrimitiveSizeInBits(), "insertvec");
ArgValue = CGF.Builder.CreateBitCast(ArgValue, StorageType);
ArgType = ArgValue->getType();
}

return ArgValue;
}

/// Emit a _bittest* intrinsic. These intrinsics take a pointer to an array of
/// bits and a bit position and read and optionally modify the bit at that
/// position. The position index can be arbitrarily large, i.e. it can be larger
Expand Down Expand Up @@ -2020,7 +2040,7 @@ Value *CodeGenFunction::EmitCheckedArgForBuiltin(const Expr *E,
assert((Kind == BCK_CLZPassedZero || Kind == BCK_CTZPassedZero) &&
"Unsupported builtin check kind");

Value *ArgValue = EmitScalarExpr(E);
Value *ArgValue = EmitBitCountExpr(*this, E);
if (!SanOpts.has(SanitizerKind::Builtin))
return ArgValue;

Expand Down Expand Up @@ -3334,7 +3354,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
E->getNumArgs() > 1;

Value *ArgValue =
HasFallback ? EmitScalarExpr(E->getArg(0))
HasFallback ? EmitBitCountExpr(*this, E->getArg(0))
: EmitCheckedArgForBuiltin(E->getArg(0), BCK_CTZPassedZero);

llvm::Type *ArgType = ArgValue->getType();
Expand Down Expand Up @@ -3371,7 +3391,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
E->getNumArgs() > 1;

Value *ArgValue =
HasFallback ? EmitScalarExpr(E->getArg(0))
HasFallback ? EmitBitCountExpr(*this, E->getArg(0))
: EmitCheckedArgForBuiltin(E->getArg(0), BCK_CLZPassedZero);

llvm::Type *ArgType = ArgValue->getType();
Expand Down Expand Up @@ -3456,7 +3476,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
case Builtin::BI__builtin_popcountl:
case Builtin::BI__builtin_popcountll:
case Builtin::BI__builtin_popcountg: {
Value *ArgValue = EmitScalarExpr(E->getArg(0));
Value *ArgValue = EmitBitCountExpr(*this, E->getArg(0));

llvm::Type *ArgType = ArgValue->getType();
Function *F = CGM.getIntrinsic(Intrinsic::ctpop, ArgType);
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2214,7 +2214,7 @@ static bool BuiltinPopcountg(Sema &S, CallExpr *TheCall) {

QualType ArgTy = Arg->getType();

if (!ArgTy->isUnsignedIntegerType()) {
if (!ArgTy->isUnsignedIntegerType() && !ArgTy->isExtVectorBoolType()) {
S.Diag(Arg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
<< 1 << /* scalar */ 1 << /* unsigned integer ty */ 3 << /* no fp */ 0
<< ArgTy;
Expand All @@ -2239,7 +2239,7 @@ static bool BuiltinCountZeroBitsGeneric(Sema &S, CallExpr *TheCall) {

QualType Arg0Ty = Arg0->getType();

if (!Arg0Ty->isUnsignedIntegerType()) {
if (!Arg0Ty->isUnsignedIntegerType() && !Arg0Ty->isExtVectorBoolType()) {
S.Diag(Arg0->getBeginLoc(), diag::err_builtin_invalid_arg_type)
<< 1 << /* scalar */ 1 << /* unsigned integer ty */ 3 << /* no fp */ 0
<< Arg0Ty;
Expand Down
4 changes: 4 additions & 0 deletions clang/test/AST/ByteCode/builtin-functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,7 @@ namespace SourceLocation {
}

#define BITSIZE(x) (sizeof(x) * 8)
constexpr bool __attribute__((ext_vector_type(4))) v4b{};
namespace popcount {
static_assert(__builtin_popcount(~0u) == __CHAR_BIT__ * sizeof(unsigned int), "");
static_assert(__builtin_popcount(0) == 0, "");
Expand All @@ -471,6 +472,7 @@ namespace popcount {
static_assert(__builtin_popcountg(0ul) == 0, "");
static_assert(__builtin_popcountg(~0ull) == __CHAR_BIT__ * sizeof(unsigned long long), "");
static_assert(__builtin_popcountg(0ull) == 0, "");
static_assert(__builtin_popcountg(v4b) == 0, "");
#ifdef __SIZEOF_INT128__
static_assert(__builtin_popcountg(~(unsigned __int128)0) == __CHAR_BIT__ * sizeof(unsigned __int128), "");
static_assert(__builtin_popcountg((unsigned __int128)0) == 0, "");
Expand Down Expand Up @@ -743,6 +745,7 @@ namespace clz {
char clz62[__builtin_clzg((unsigned _BitInt(128))0xf) == BITSIZE(_BitInt(128)) - 4 ? 1 : -1];
char clz63[__builtin_clzg((unsigned _BitInt(128))0xf, 42) == BITSIZE(_BitInt(128)) - 4 ? 1 : -1];
#endif
char clz64[__builtin_clzg(v4b, 0) == 0 ? 1 : -1];
}

namespace ctz {
Expand Down Expand Up @@ -813,6 +816,7 @@ namespace ctz {
char ctz62[__builtin_ctzg((unsigned _BitInt(128))1 << (BITSIZE(_BitInt(128)) - 1)) == BITSIZE(_BitInt(128)) - 1 ? 1 : -1];
char ctz63[__builtin_ctzg((unsigned _BitInt(128))1 << (BITSIZE(_BitInt(128)) - 1), 42) == BITSIZE(_BitInt(128)) - 1 ? 1 : -1];
#endif
char clz64[__builtin_ctzg(v4b, 0) == 0 ? 1 : -1];
}

namespace bswap {
Expand Down
Loading
Loading