Skip to content

Commit

Permalink
[clang] Implement constexpr support for __builtin_{clzg,ctzg} (llvm#8…
Browse files Browse the repository at this point in the history
  • Loading branch information
overmighty authored Mar 26, 2024
1 parent a3f21a3 commit ac1af75
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 11 deletions.
2 changes: 2 additions & 0 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5420,10 +5420,12 @@ The following builtin intrinsics can be used in constant expressions:
* ``__builtin_clzl``
* ``__builtin_clzll``
* ``__builtin_clzs``
* ``__builtin_clzg``
* ``__builtin_ctz``
* ``__builtin_ctzl``
* ``__builtin_ctzll``
* ``__builtin_ctzs``
* ``__builtin_ctzg``
* ``__builtin_ffs``
* ``__builtin_ffsl``
* ``__builtin_ffsll``
Expand Down
5 changes: 5 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,11 @@ Non-comprehensive list of changes in this release

- Lambda expressions are now accepted in C++03 mode as an extension.

- Added ``__builtin_clzg`` and ``__builtin_ctzg`` as type-generic alternatives
to ``__builtin_clz{,s,l,ll}`` and ``__builtin_ctz{,s,l,ll}`` respectively,
with support for any unsigned integer type. Like the previous builtins, these
new builtins are constexpr and may be used in constant expressions.

New Compiler Flags
------------------

Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,7 @@ def Clz : Builtin, BitShort_Int_Long_LongLongTemplate {

def Clzg : Builtin {
let Spellings = ["__builtin_clzg"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Attributes = [NoThrow, Const, Constexpr, CustomTypeChecking];
let Prototype = "int(...)";
}

Expand All @@ -690,7 +690,7 @@ def Ctz : Builtin, BitShort_Int_Long_LongLongTemplate {

def Ctzg : Builtin {
let Spellings = ["__builtin_ctzg"];
let Attributes = [NoThrow, Const, CustomTypeChecking];
let Attributes = [NoThrow, Const, Constexpr, CustomTypeChecking];
let Prototype = "int(...)";
}

Expand Down
37 changes: 28 additions & 9 deletions clang/lib/AST/ExprConstant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12353,21 +12353,31 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
case Builtin::BI__builtin_clzl:
case Builtin::BI__builtin_clzll:
case Builtin::BI__builtin_clzs:
case Builtin::BI__builtin_clzg:
case Builtin::BI__lzcnt16: // Microsoft variants of count leading-zeroes
case Builtin::BI__lzcnt:
case Builtin::BI__lzcnt64: {
APSInt Val;
if (!EvaluateInteger(E->getArg(0), Val, Info))
return false;

// 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.
bool ZeroIsUndefined = BuiltinOp != Builtin::BI__lzcnt16 &&
BuiltinOp != Builtin::BI__lzcnt &&
BuiltinOp != Builtin::BI__lzcnt64;
if (!Val) {
if (BuiltinOp == Builtin::BI__builtin_clzg && E->getNumArgs() > 1) {
if (!EvaluateInteger(E->getArg(1), Val, Info))
return false;
return Success(Val, E);
}

if (ZeroIsUndefined && !Val)
return Error(E);
// 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.
bool ZeroIsUndefined = BuiltinOp != Builtin::BI__lzcnt16 &&
BuiltinOp != Builtin::BI__lzcnt &&
BuiltinOp != Builtin::BI__lzcnt64;

if (ZeroIsUndefined)
return Error(E);
}

return Success(Val.countl_zero(), E);
}
Expand Down Expand Up @@ -12409,12 +12419,21 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
case Builtin::BI__builtin_ctz:
case Builtin::BI__builtin_ctzl:
case Builtin::BI__builtin_ctzll:
case Builtin::BI__builtin_ctzs: {
case Builtin::BI__builtin_ctzs:
case Builtin::BI__builtin_ctzg: {
APSInt Val;
if (!EvaluateInteger(E->getArg(0), Val, Info))
return false;
if (!Val)

if (!Val) {
if (BuiltinOp == Builtin::BI__builtin_ctzg && E->getNumArgs() > 1) {
if (!EvaluateInteger(E->getArg(1), Val, Info))
return false;
return Success(Val, E);
}

return Error(E);
}

return Success(Val.countr_zero(), E);
}
Expand Down
116 changes: 116 additions & 0 deletions clang/test/Sema/constant-builtins-2.c
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,64 @@ char clz6[__builtin_clzll(0xFFLL) == BITSIZE(long long) - 8 ? 1 : -1];
char clz7[__builtin_clzs(0x1) == BITSIZE(short) - 1 ? 1 : -1];
char clz8[__builtin_clzs(0xf) == BITSIZE(short) - 4 ? 1 : -1];
char clz9[__builtin_clzs(0xfff) == BITSIZE(short) - 12 ? 1 : -1];
int clz10 = __builtin_clzg((unsigned char)0); // expected-error {{not a compile-time constant}}
char clz11[__builtin_clzg((unsigned char)0, 42) == 42 ? 1 : -1];
char clz12[__builtin_clzg((unsigned char)0x1) == BITSIZE(char) - 1 ? 1 : -1];
char clz13[__builtin_clzg((unsigned char)0x1, 42) == BITSIZE(char) - 1 ? 1 : -1];
char clz14[__builtin_clzg((unsigned char)0xf) == BITSIZE(char) - 4 ? 1 : -1];
char clz15[__builtin_clzg((unsigned char)0xf, 42) == BITSIZE(char) - 4 ? 1 : -1];
char clz16[__builtin_clzg((unsigned char)(1 << (BITSIZE(char) - 1))) == 0 ? 1 : -1];
char clz17[__builtin_clzg((unsigned char)(1 << (BITSIZE(char) - 1)), 42) == 0 ? 1 : -1];
int clz18 = __builtin_clzg((unsigned short)0); // expected-error {{not a compile-time constant}}
char clz19[__builtin_clzg((unsigned short)0, 42) == 42 ? 1 : -1];
char clz20[__builtin_clzg((unsigned short)0x1) == BITSIZE(short) - 1 ? 1 : -1];
char clz21[__builtin_clzg((unsigned short)0x1, 42) == BITSIZE(short) - 1 ? 1 : -1];
char clz22[__builtin_clzg((unsigned short)0xf) == BITSIZE(short) - 4 ? 1 : -1];
char clz23[__builtin_clzg((unsigned short)0xf, 42) == BITSIZE(short) - 4 ? 1 : -1];
char clz24[__builtin_clzg((unsigned short)(1 << (BITSIZE(short) - 1))) == 0 ? 1 : -1];
char clz25[__builtin_clzg((unsigned short)(1 << (BITSIZE(short) - 1)), 42) == 0 ? 1 : -1];
int clz26 = __builtin_clzg(0U); // expected-error {{not a compile-time constant}}
char clz27[__builtin_clzg(0U, 42) == 42 ? 1 : -1];
char clz28[__builtin_clzg(0x1U) == BITSIZE(int) - 1 ? 1 : -1];
char clz29[__builtin_clzg(0x1U, 42) == BITSIZE(int) - 1 ? 1 : -1];
char clz30[__builtin_clzg(0xfU) == BITSIZE(int) - 4 ? 1 : -1];
char clz31[__builtin_clzg(0xfU, 42) == BITSIZE(int) - 4 ? 1 : -1];
char clz32[__builtin_clzg(1U << (BITSIZE(int) - 1)) == 0 ? 1 : -1];
char clz33[__builtin_clzg(1U << (BITSIZE(int) - 1), 42) == 0 ? 1 : -1];
int clz34 = __builtin_clzg(0UL); // expected-error {{not a compile-time constant}}
char clz35[__builtin_clzg(0UL, 42) == 42 ? 1 : -1];
char clz36[__builtin_clzg(0x1UL) == BITSIZE(long) - 1 ? 1 : -1];
char clz37[__builtin_clzg(0x1UL, 42) == BITSIZE(long) - 1 ? 1 : -1];
char clz38[__builtin_clzg(0xfUL) == BITSIZE(long) - 4 ? 1 : -1];
char clz39[__builtin_clzg(0xfUL, 42) == BITSIZE(long) - 4 ? 1 : -1];
char clz40[__builtin_clzg(1UL << (BITSIZE(long) - 1)) == 0 ? 1 : -1];
char clz41[__builtin_clzg(1UL << (BITSIZE(long) - 1), 42) == 0 ? 1 : -1];
int clz42 = __builtin_clzg(0ULL); // expected-error {{not a compile-time constant}}
char clz43[__builtin_clzg(0ULL, 42) == 42 ? 1 : -1];
char clz44[__builtin_clzg(0x1ULL) == BITSIZE(long long) - 1 ? 1 : -1];
char clz45[__builtin_clzg(0x1ULL, 42) == BITSIZE(long long) - 1 ? 1 : -1];
char clz46[__builtin_clzg(0xfULL) == BITSIZE(long long) - 4 ? 1 : -1];
char clz47[__builtin_clzg(0xfULL, 42) == BITSIZE(long long) - 4 ? 1 : -1];
char clz48[__builtin_clzg(1ULL << (BITSIZE(long long) - 1)) == 0 ? 1 : -1];
char clz49[__builtin_clzg(1ULL << (BITSIZE(long long) - 1), 42) == 0 ? 1 : -1];
#ifdef __SIZEOF_INT128__
int clz50 = __builtin_clzg((unsigned __int128)0); // expected-error {{not a compile-time constant}}
char clz51[__builtin_clzg((unsigned __int128)0, 42) == 42 ? 1 : -1];
char clz52[__builtin_clzg((unsigned __int128)0x1) == BITSIZE(__int128) - 1 ? 1 : -1];
char clz53[__builtin_clzg((unsigned __int128)0x1, 42) == BITSIZE(__int128) - 1 ? 1 : -1];
char clz54[__builtin_clzg((unsigned __int128)0xf) == BITSIZE(__int128) - 4 ? 1 : -1];
char clz55[__builtin_clzg((unsigned __int128)0xf, 42) == BITSIZE(__int128) - 4 ? 1 : -1];
char clz56[__builtin_clzg((unsigned __int128)(1 << (BITSIZE(__int128) - 1))) == 0 ? 1 : -1];
char clz57[__builtin_clzg((unsigned __int128)(1 << (BITSIZE(__int128) - 1)), 42) == 0 ? 1 : -1];
#endif
int clz58 = __builtin_clzg((unsigned _BitInt(128))0); // expected-error {{not a compile-time constant}}
char clz59[__builtin_clzg((unsigned _BitInt(128))0, 42) == 42 ? 1 : -1];
char clz60[__builtin_clzg((unsigned _BitInt(128))0x1) == BITSIZE(_BitInt(128)) - 1 ? 1 : -1];
char clz61[__builtin_clzg((unsigned _BitInt(128))0x1, 42) == BITSIZE(_BitInt(128)) - 1 ? 1 : -1];
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];
char clz64[__builtin_clzg((unsigned _BitInt(128))(1 << (BITSIZE(_BitInt(128)) - 1))) == 0 ? 1 : -1];
char clz65[__builtin_clzg((unsigned _BitInt(128))(1 << (BITSIZE(_BitInt(128)) - 1)), 42) == 0 ? 1 : -1];

char ctz1[__builtin_ctz(1) == 0 ? 1 : -1];
char ctz2[__builtin_ctz(8) == 3 ? 1 : -1];
Expand All @@ -226,6 +284,64 @@ int ctz4 = __builtin_ctz(0); // expected-error {{not a compile-time constant}}
char ctz5[__builtin_ctzl(0x10L) == 4 ? 1 : -1];
char ctz6[__builtin_ctzll(0x100LL) == 8 ? 1 : -1];
char ctz7[__builtin_ctzs(1 << (BITSIZE(short) - 1)) == BITSIZE(short) - 1 ? 1 : -1];
int ctz8 = __builtin_ctzg((unsigned char)0); // expected-error {{not a compile-time constant}}
char ctz9[__builtin_ctzg((unsigned char)0, 42) == 42 ? 1 : -1];
char ctz10[__builtin_ctzg((unsigned char)0x1) == 0 ? 1 : -1];
char ctz11[__builtin_ctzg((unsigned char)0x1, 42) == 0 ? 1 : -1];
char ctz12[__builtin_ctzg((unsigned char)0x10) == 4 ? 1 : -1];
char ctz13[__builtin_ctzg((unsigned char)0x10, 42) == 4 ? 1 : -1];
char ctz14[__builtin_ctzg((unsigned char)(1 << (BITSIZE(char) - 1))) == BITSIZE(char) - 1 ? 1 : -1];
char ctz15[__builtin_ctzg((unsigned char)(1 << (BITSIZE(char) - 1)), 42) == BITSIZE(char) - 1 ? 1 : -1];
int ctz16 = __builtin_ctzg((unsigned short)0); // expected-error {{not a compile-time constant}}
char ctz17[__builtin_ctzg((unsigned short)0, 42) == 42 ? 1 : -1];
char ctz18[__builtin_ctzg((unsigned short)0x1) == 0 ? 1 : -1];
char ctz19[__builtin_ctzg((unsigned short)0x1, 42) == 0 ? 1 : -1];
char ctz20[__builtin_ctzg((unsigned short)0x10) == 4 ? 1 : -1];
char ctz21[__builtin_ctzg((unsigned short)0x10, 42) == 4 ? 1 : -1];
char ctz22[__builtin_ctzg((unsigned short)(1 << (BITSIZE(short) - 1))) == BITSIZE(short) - 1 ? 1 : -1];
char ctz23[__builtin_ctzg((unsigned short)(1 << (BITSIZE(short) - 1)), 42) == BITSIZE(short) - 1 ? 1 : -1];
int ctz24 = __builtin_ctzg(0U); // expected-error {{not a compile-time constant}}
char ctz25[__builtin_ctzg(0U, 42) == 42 ? 1 : -1];
char ctz26[__builtin_ctzg(0x1U) == 0 ? 1 : -1];
char ctz27[__builtin_ctzg(0x1U, 42) == 0 ? 1 : -1];
char ctz28[__builtin_ctzg(0x10U) == 4 ? 1 : -1];
char ctz29[__builtin_ctzg(0x10U, 42) == 4 ? 1 : -1];
char ctz30[__builtin_ctzg(1U << (BITSIZE(int) - 1)) == BITSIZE(int) - 1 ? 1 : -1];
char ctz31[__builtin_ctzg(1U << (BITSIZE(int) - 1), 42) == BITSIZE(int) - 1 ? 1 : -1];
int ctz32 = __builtin_ctzg(0UL); // expected-error {{not a compile-time constant}}
char ctz33[__builtin_ctzg(0UL, 42) == 42 ? 1 : -1];
char ctz34[__builtin_ctzg(0x1UL) == 0 ? 1 : -1];
char ctz35[__builtin_ctzg(0x1UL, 42) == 0 ? 1 : -1];
char ctz36[__builtin_ctzg(0x10UL) == 4 ? 1 : -1];
char ctz37[__builtin_ctzg(0x10UL, 42) == 4 ? 1 : -1];
char ctz38[__builtin_ctzg(1UL << (BITSIZE(long) - 1)) == BITSIZE(long) - 1 ? 1 : -1];
char ctz39[__builtin_ctzg(1UL << (BITSIZE(long) - 1), 42) == BITSIZE(long) - 1 ? 1 : -1];
int ctz40 = __builtin_ctzg(0ULL); // expected-error {{not a compile-time constant}}
char ctz41[__builtin_ctzg(0ULL, 42) == 42 ? 1 : -1];
char ctz42[__builtin_ctzg(0x1ULL) == 0 ? 1 : -1];
char ctz43[__builtin_ctzg(0x1ULL, 42) == 0 ? 1 : -1];
char ctz44[__builtin_ctzg(0x10ULL) == 4 ? 1 : -1];
char ctz45[__builtin_ctzg(0x10ULL, 42) == 4 ? 1 : -1];
char ctz46[__builtin_ctzg(1ULL << (BITSIZE(long long) - 1)) == BITSIZE(long long) - 1 ? 1 : -1];
char ctz47[__builtin_ctzg(1ULL << (BITSIZE(long long) - 1), 42) == BITSIZE(long long) - 1 ? 1 : -1];
#ifdef __SIZEOF_INT128__
int ctz48 = __builtin_ctzg((unsigned __int128)0); // expected-error {{not a compile-time constant}}
char ctz49[__builtin_ctzg((unsigned __int128)0, 42) == 42 ? 1 : -1];
char ctz50[__builtin_ctzg((unsigned __int128)0x1) == 0 ? 1 : -1];
char ctz51[__builtin_ctzg((unsigned __int128)0x1, 42) == 0 ? 1 : -1];
char ctz52[__builtin_ctzg((unsigned __int128)0x10) == 4 ? 1 : -1];
char ctz53[__builtin_ctzg((unsigned __int128)0x10, 42) == 4 ? 1 : -1];
char ctz54[__builtin_ctzg((unsigned __int128)1 << (BITSIZE(__int128) - 1)) == BITSIZE(__int128) - 1 ? 1 : -1];
char ctz55[__builtin_ctzg((unsigned __int128)1 << (BITSIZE(__int128) - 1), 42) == BITSIZE(__int128) - 1 ? 1 : -1];
#endif
int ctz56 = __builtin_ctzg((unsigned _BitInt(128))0); // expected-error {{not a compile-time constant}}
char ctz57[__builtin_ctzg((unsigned _BitInt(128))0, 42) == 42 ? 1 : -1];
char ctz58[__builtin_ctzg((unsigned _BitInt(128))0x1) == 0 ? 1 : -1];
char ctz59[__builtin_ctzg((unsigned _BitInt(128))0x1, 42) == 0 ? 1 : -1];
char ctz60[__builtin_ctzg((unsigned _BitInt(128))0x10) == 4 ? 1 : -1];
char ctz61[__builtin_ctzg((unsigned _BitInt(128))0x10, 42) == 4 ? 1 : -1];
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];

char popcount1[__builtin_popcount(0) == 0 ? 1 : -1];
char popcount2[__builtin_popcount(0xF0F0) == 8 ? 1 : -1];
Expand Down

0 comments on commit ac1af75

Please sign in to comment.