Skip to content

Commit ac1af75

Browse files
authored
[clang] Implement constexpr support for __builtin_{clzg,ctzg} (#86577)
Fixes #86549.
1 parent a3f21a3 commit ac1af75

File tree

5 files changed

+153
-11
lines changed

5 files changed

+153
-11
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5420,10 +5420,12 @@ The following builtin intrinsics can be used in constant expressions:
54205420
* ``__builtin_clzl``
54215421
* ``__builtin_clzll``
54225422
* ``__builtin_clzs``
5423+
* ``__builtin_clzg``
54235424
* ``__builtin_ctz``
54245425
* ``__builtin_ctzl``
54255426
* ``__builtin_ctzll``
54265427
* ``__builtin_ctzs``
5428+
* ``__builtin_ctzg``
54275429
* ``__builtin_ffs``
54285430
* ``__builtin_ffsl``
54295431
* ``__builtin_ffsll``

clang/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,11 @@ Non-comprehensive list of changes in this release
188188

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

191+
- Added ``__builtin_clzg`` and ``__builtin_ctzg`` as type-generic alternatives
192+
to ``__builtin_clz{,s,l,ll}`` and ``__builtin_ctz{,s,l,ll}`` respectively,
193+
with support for any unsigned integer type. Like the previous builtins, these
194+
new builtins are constexpr and may be used in constant expressions.
195+
191196
New Compiler Flags
192197
------------------
193198

clang/include/clang/Basic/Builtins.td

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -678,7 +678,7 @@ def Clz : Builtin, BitShort_Int_Long_LongLongTemplate {
678678

679679
def Clzg : Builtin {
680680
let Spellings = ["__builtin_clzg"];
681-
let Attributes = [NoThrow, Const, CustomTypeChecking];
681+
let Attributes = [NoThrow, Const, Constexpr, CustomTypeChecking];
682682
let Prototype = "int(...)";
683683
}
684684

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

691691
def Ctzg : Builtin {
692692
let Spellings = ["__builtin_ctzg"];
693-
let Attributes = [NoThrow, Const, CustomTypeChecking];
693+
let Attributes = [NoThrow, Const, Constexpr, CustomTypeChecking];
694694
let Prototype = "int(...)";
695695
}
696696

clang/lib/AST/ExprConstant.cpp

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12353,21 +12353,31 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
1235312353
case Builtin::BI__builtin_clzl:
1235412354
case Builtin::BI__builtin_clzll:
1235512355
case Builtin::BI__builtin_clzs:
12356+
case Builtin::BI__builtin_clzg:
1235612357
case Builtin::BI__lzcnt16: // Microsoft variants of count leading-zeroes
1235712358
case Builtin::BI__lzcnt:
1235812359
case Builtin::BI__lzcnt64: {
1235912360
APSInt Val;
1236012361
if (!EvaluateInteger(E->getArg(0), Val, Info))
1236112362
return false;
1236212363

12363-
// When the argument is 0, the result of GCC builtins is undefined, whereas
12364-
// for Microsoft intrinsics, the result is the bit-width of the argument.
12365-
bool ZeroIsUndefined = BuiltinOp != Builtin::BI__lzcnt16 &&
12366-
BuiltinOp != Builtin::BI__lzcnt &&
12367-
BuiltinOp != Builtin::BI__lzcnt64;
12364+
if (!Val) {
12365+
if (BuiltinOp == Builtin::BI__builtin_clzg && E->getNumArgs() > 1) {
12366+
if (!EvaluateInteger(E->getArg(1), Val, Info))
12367+
return false;
12368+
return Success(Val, E);
12369+
}
1236812370

12369-
if (ZeroIsUndefined && !Val)
12370-
return Error(E);
12371+
// When the argument is 0, the result of GCC builtins is undefined,
12372+
// whereas for Microsoft intrinsics, the result is the bit-width of the
12373+
// argument.
12374+
bool ZeroIsUndefined = BuiltinOp != Builtin::BI__lzcnt16 &&
12375+
BuiltinOp != Builtin::BI__lzcnt &&
12376+
BuiltinOp != Builtin::BI__lzcnt64;
12377+
12378+
if (ZeroIsUndefined)
12379+
return Error(E);
12380+
}
1237112381

1237212382
return Success(Val.countl_zero(), E);
1237312383
}
@@ -12409,12 +12419,21 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
1240912419
case Builtin::BI__builtin_ctz:
1241012420
case Builtin::BI__builtin_ctzl:
1241112421
case Builtin::BI__builtin_ctzll:
12412-
case Builtin::BI__builtin_ctzs: {
12422+
case Builtin::BI__builtin_ctzs:
12423+
case Builtin::BI__builtin_ctzg: {
1241312424
APSInt Val;
1241412425
if (!EvaluateInteger(E->getArg(0), Val, Info))
1241512426
return false;
12416-
if (!Val)
12427+
12428+
if (!Val) {
12429+
if (BuiltinOp == Builtin::BI__builtin_ctzg && E->getNumArgs() > 1) {
12430+
if (!EvaluateInteger(E->getArg(1), Val, Info))
12431+
return false;
12432+
return Success(Val, E);
12433+
}
12434+
1241712435
return Error(E);
12436+
}
1241812437

1241912438
return Success(Val.countr_zero(), E);
1242012439
}

clang/test/Sema/constant-builtins-2.c

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,64 @@ char clz6[__builtin_clzll(0xFFLL) == BITSIZE(long long) - 8 ? 1 : -1];
218218
char clz7[__builtin_clzs(0x1) == BITSIZE(short) - 1 ? 1 : -1];
219219
char clz8[__builtin_clzs(0xf) == BITSIZE(short) - 4 ? 1 : -1];
220220
char clz9[__builtin_clzs(0xfff) == BITSIZE(short) - 12 ? 1 : -1];
221+
int clz10 = __builtin_clzg((unsigned char)0); // expected-error {{not a compile-time constant}}
222+
char clz11[__builtin_clzg((unsigned char)0, 42) == 42 ? 1 : -1];
223+
char clz12[__builtin_clzg((unsigned char)0x1) == BITSIZE(char) - 1 ? 1 : -1];
224+
char clz13[__builtin_clzg((unsigned char)0x1, 42) == BITSIZE(char) - 1 ? 1 : -1];
225+
char clz14[__builtin_clzg((unsigned char)0xf) == BITSIZE(char) - 4 ? 1 : -1];
226+
char clz15[__builtin_clzg((unsigned char)0xf, 42) == BITSIZE(char) - 4 ? 1 : -1];
227+
char clz16[__builtin_clzg((unsigned char)(1 << (BITSIZE(char) - 1))) == 0 ? 1 : -1];
228+
char clz17[__builtin_clzg((unsigned char)(1 << (BITSIZE(char) - 1)), 42) == 0 ? 1 : -1];
229+
int clz18 = __builtin_clzg((unsigned short)0); // expected-error {{not a compile-time constant}}
230+
char clz19[__builtin_clzg((unsigned short)0, 42) == 42 ? 1 : -1];
231+
char clz20[__builtin_clzg((unsigned short)0x1) == BITSIZE(short) - 1 ? 1 : -1];
232+
char clz21[__builtin_clzg((unsigned short)0x1, 42) == BITSIZE(short) - 1 ? 1 : -1];
233+
char clz22[__builtin_clzg((unsigned short)0xf) == BITSIZE(short) - 4 ? 1 : -1];
234+
char clz23[__builtin_clzg((unsigned short)0xf, 42) == BITSIZE(short) - 4 ? 1 : -1];
235+
char clz24[__builtin_clzg((unsigned short)(1 << (BITSIZE(short) - 1))) == 0 ? 1 : -1];
236+
char clz25[__builtin_clzg((unsigned short)(1 << (BITSIZE(short) - 1)), 42) == 0 ? 1 : -1];
237+
int clz26 = __builtin_clzg(0U); // expected-error {{not a compile-time constant}}
238+
char clz27[__builtin_clzg(0U, 42) == 42 ? 1 : -1];
239+
char clz28[__builtin_clzg(0x1U) == BITSIZE(int) - 1 ? 1 : -1];
240+
char clz29[__builtin_clzg(0x1U, 42) == BITSIZE(int) - 1 ? 1 : -1];
241+
char clz30[__builtin_clzg(0xfU) == BITSIZE(int) - 4 ? 1 : -1];
242+
char clz31[__builtin_clzg(0xfU, 42) == BITSIZE(int) - 4 ? 1 : -1];
243+
char clz32[__builtin_clzg(1U << (BITSIZE(int) - 1)) == 0 ? 1 : -1];
244+
char clz33[__builtin_clzg(1U << (BITSIZE(int) - 1), 42) == 0 ? 1 : -1];
245+
int clz34 = __builtin_clzg(0UL); // expected-error {{not a compile-time constant}}
246+
char clz35[__builtin_clzg(0UL, 42) == 42 ? 1 : -1];
247+
char clz36[__builtin_clzg(0x1UL) == BITSIZE(long) - 1 ? 1 : -1];
248+
char clz37[__builtin_clzg(0x1UL, 42) == BITSIZE(long) - 1 ? 1 : -1];
249+
char clz38[__builtin_clzg(0xfUL) == BITSIZE(long) - 4 ? 1 : -1];
250+
char clz39[__builtin_clzg(0xfUL, 42) == BITSIZE(long) - 4 ? 1 : -1];
251+
char clz40[__builtin_clzg(1UL << (BITSIZE(long) - 1)) == 0 ? 1 : -1];
252+
char clz41[__builtin_clzg(1UL << (BITSIZE(long) - 1), 42) == 0 ? 1 : -1];
253+
int clz42 = __builtin_clzg(0ULL); // expected-error {{not a compile-time constant}}
254+
char clz43[__builtin_clzg(0ULL, 42) == 42 ? 1 : -1];
255+
char clz44[__builtin_clzg(0x1ULL) == BITSIZE(long long) - 1 ? 1 : -1];
256+
char clz45[__builtin_clzg(0x1ULL, 42) == BITSIZE(long long) - 1 ? 1 : -1];
257+
char clz46[__builtin_clzg(0xfULL) == BITSIZE(long long) - 4 ? 1 : -1];
258+
char clz47[__builtin_clzg(0xfULL, 42) == BITSIZE(long long) - 4 ? 1 : -1];
259+
char clz48[__builtin_clzg(1ULL << (BITSIZE(long long) - 1)) == 0 ? 1 : -1];
260+
char clz49[__builtin_clzg(1ULL << (BITSIZE(long long) - 1), 42) == 0 ? 1 : -1];
261+
#ifdef __SIZEOF_INT128__
262+
int clz50 = __builtin_clzg((unsigned __int128)0); // expected-error {{not a compile-time constant}}
263+
char clz51[__builtin_clzg((unsigned __int128)0, 42) == 42 ? 1 : -1];
264+
char clz52[__builtin_clzg((unsigned __int128)0x1) == BITSIZE(__int128) - 1 ? 1 : -1];
265+
char clz53[__builtin_clzg((unsigned __int128)0x1, 42) == BITSIZE(__int128) - 1 ? 1 : -1];
266+
char clz54[__builtin_clzg((unsigned __int128)0xf) == BITSIZE(__int128) - 4 ? 1 : -1];
267+
char clz55[__builtin_clzg((unsigned __int128)0xf, 42) == BITSIZE(__int128) - 4 ? 1 : -1];
268+
char clz56[__builtin_clzg((unsigned __int128)(1 << (BITSIZE(__int128) - 1))) == 0 ? 1 : -1];
269+
char clz57[__builtin_clzg((unsigned __int128)(1 << (BITSIZE(__int128) - 1)), 42) == 0 ? 1 : -1];
270+
#endif
271+
int clz58 = __builtin_clzg((unsigned _BitInt(128))0); // expected-error {{not a compile-time constant}}
272+
char clz59[__builtin_clzg((unsigned _BitInt(128))0, 42) == 42 ? 1 : -1];
273+
char clz60[__builtin_clzg((unsigned _BitInt(128))0x1) == BITSIZE(_BitInt(128)) - 1 ? 1 : -1];
274+
char clz61[__builtin_clzg((unsigned _BitInt(128))0x1, 42) == BITSIZE(_BitInt(128)) - 1 ? 1 : -1];
275+
char clz62[__builtin_clzg((unsigned _BitInt(128))0xf) == BITSIZE(_BitInt(128)) - 4 ? 1 : -1];
276+
char clz63[__builtin_clzg((unsigned _BitInt(128))0xf, 42) == BITSIZE(_BitInt(128)) - 4 ? 1 : -1];
277+
char clz64[__builtin_clzg((unsigned _BitInt(128))(1 << (BITSIZE(_BitInt(128)) - 1))) == 0 ? 1 : -1];
278+
char clz65[__builtin_clzg((unsigned _BitInt(128))(1 << (BITSIZE(_BitInt(128)) - 1)), 42) == 0 ? 1 : -1];
221279

222280
char ctz1[__builtin_ctz(1) == 0 ? 1 : -1];
223281
char ctz2[__builtin_ctz(8) == 3 ? 1 : -1];
@@ -226,6 +284,64 @@ int ctz4 = __builtin_ctz(0); // expected-error {{not a compile-time constant}}
226284
char ctz5[__builtin_ctzl(0x10L) == 4 ? 1 : -1];
227285
char ctz6[__builtin_ctzll(0x100LL) == 8 ? 1 : -1];
228286
char ctz7[__builtin_ctzs(1 << (BITSIZE(short) - 1)) == BITSIZE(short) - 1 ? 1 : -1];
287+
int ctz8 = __builtin_ctzg((unsigned char)0); // expected-error {{not a compile-time constant}}
288+
char ctz9[__builtin_ctzg((unsigned char)0, 42) == 42 ? 1 : -1];
289+
char ctz10[__builtin_ctzg((unsigned char)0x1) == 0 ? 1 : -1];
290+
char ctz11[__builtin_ctzg((unsigned char)0x1, 42) == 0 ? 1 : -1];
291+
char ctz12[__builtin_ctzg((unsigned char)0x10) == 4 ? 1 : -1];
292+
char ctz13[__builtin_ctzg((unsigned char)0x10, 42) == 4 ? 1 : -1];
293+
char ctz14[__builtin_ctzg((unsigned char)(1 << (BITSIZE(char) - 1))) == BITSIZE(char) - 1 ? 1 : -1];
294+
char ctz15[__builtin_ctzg((unsigned char)(1 << (BITSIZE(char) - 1)), 42) == BITSIZE(char) - 1 ? 1 : -1];
295+
int ctz16 = __builtin_ctzg((unsigned short)0); // expected-error {{not a compile-time constant}}
296+
char ctz17[__builtin_ctzg((unsigned short)0, 42) == 42 ? 1 : -1];
297+
char ctz18[__builtin_ctzg((unsigned short)0x1) == 0 ? 1 : -1];
298+
char ctz19[__builtin_ctzg((unsigned short)0x1, 42) == 0 ? 1 : -1];
299+
char ctz20[__builtin_ctzg((unsigned short)0x10) == 4 ? 1 : -1];
300+
char ctz21[__builtin_ctzg((unsigned short)0x10, 42) == 4 ? 1 : -1];
301+
char ctz22[__builtin_ctzg((unsigned short)(1 << (BITSIZE(short) - 1))) == BITSIZE(short) - 1 ? 1 : -1];
302+
char ctz23[__builtin_ctzg((unsigned short)(1 << (BITSIZE(short) - 1)), 42) == BITSIZE(short) - 1 ? 1 : -1];
303+
int ctz24 = __builtin_ctzg(0U); // expected-error {{not a compile-time constant}}
304+
char ctz25[__builtin_ctzg(0U, 42) == 42 ? 1 : -1];
305+
char ctz26[__builtin_ctzg(0x1U) == 0 ? 1 : -1];
306+
char ctz27[__builtin_ctzg(0x1U, 42) == 0 ? 1 : -1];
307+
char ctz28[__builtin_ctzg(0x10U) == 4 ? 1 : -1];
308+
char ctz29[__builtin_ctzg(0x10U, 42) == 4 ? 1 : -1];
309+
char ctz30[__builtin_ctzg(1U << (BITSIZE(int) - 1)) == BITSIZE(int) - 1 ? 1 : -1];
310+
char ctz31[__builtin_ctzg(1U << (BITSIZE(int) - 1), 42) == BITSIZE(int) - 1 ? 1 : -1];
311+
int ctz32 = __builtin_ctzg(0UL); // expected-error {{not a compile-time constant}}
312+
char ctz33[__builtin_ctzg(0UL, 42) == 42 ? 1 : -1];
313+
char ctz34[__builtin_ctzg(0x1UL) == 0 ? 1 : -1];
314+
char ctz35[__builtin_ctzg(0x1UL, 42) == 0 ? 1 : -1];
315+
char ctz36[__builtin_ctzg(0x10UL) == 4 ? 1 : -1];
316+
char ctz37[__builtin_ctzg(0x10UL, 42) == 4 ? 1 : -1];
317+
char ctz38[__builtin_ctzg(1UL << (BITSIZE(long) - 1)) == BITSIZE(long) - 1 ? 1 : -1];
318+
char ctz39[__builtin_ctzg(1UL << (BITSIZE(long) - 1), 42) == BITSIZE(long) - 1 ? 1 : -1];
319+
int ctz40 = __builtin_ctzg(0ULL); // expected-error {{not a compile-time constant}}
320+
char ctz41[__builtin_ctzg(0ULL, 42) == 42 ? 1 : -1];
321+
char ctz42[__builtin_ctzg(0x1ULL) == 0 ? 1 : -1];
322+
char ctz43[__builtin_ctzg(0x1ULL, 42) == 0 ? 1 : -1];
323+
char ctz44[__builtin_ctzg(0x10ULL) == 4 ? 1 : -1];
324+
char ctz45[__builtin_ctzg(0x10ULL, 42) == 4 ? 1 : -1];
325+
char ctz46[__builtin_ctzg(1ULL << (BITSIZE(long long) - 1)) == BITSIZE(long long) - 1 ? 1 : -1];
326+
char ctz47[__builtin_ctzg(1ULL << (BITSIZE(long long) - 1), 42) == BITSIZE(long long) - 1 ? 1 : -1];
327+
#ifdef __SIZEOF_INT128__
328+
int ctz48 = __builtin_ctzg((unsigned __int128)0); // expected-error {{not a compile-time constant}}
329+
char ctz49[__builtin_ctzg((unsigned __int128)0, 42) == 42 ? 1 : -1];
330+
char ctz50[__builtin_ctzg((unsigned __int128)0x1) == 0 ? 1 : -1];
331+
char ctz51[__builtin_ctzg((unsigned __int128)0x1, 42) == 0 ? 1 : -1];
332+
char ctz52[__builtin_ctzg((unsigned __int128)0x10) == 4 ? 1 : -1];
333+
char ctz53[__builtin_ctzg((unsigned __int128)0x10, 42) == 4 ? 1 : -1];
334+
char ctz54[__builtin_ctzg((unsigned __int128)1 << (BITSIZE(__int128) - 1)) == BITSIZE(__int128) - 1 ? 1 : -1];
335+
char ctz55[__builtin_ctzg((unsigned __int128)1 << (BITSIZE(__int128) - 1), 42) == BITSIZE(__int128) - 1 ? 1 : -1];
336+
#endif
337+
int ctz56 = __builtin_ctzg((unsigned _BitInt(128))0); // expected-error {{not a compile-time constant}}
338+
char ctz57[__builtin_ctzg((unsigned _BitInt(128))0, 42) == 42 ? 1 : -1];
339+
char ctz58[__builtin_ctzg((unsigned _BitInt(128))0x1) == 0 ? 1 : -1];
340+
char ctz59[__builtin_ctzg((unsigned _BitInt(128))0x1, 42) == 0 ? 1 : -1];
341+
char ctz60[__builtin_ctzg((unsigned _BitInt(128))0x10) == 4 ? 1 : -1];
342+
char ctz61[__builtin_ctzg((unsigned _BitInt(128))0x10, 42) == 4 ? 1 : -1];
343+
char ctz62[__builtin_ctzg((unsigned _BitInt(128))1 << (BITSIZE(_BitInt(128)) - 1)) == BITSIZE(_BitInt(128)) - 1 ? 1 : -1];
344+
char ctz63[__builtin_ctzg((unsigned _BitInt(128))1 << (BITSIZE(_BitInt(128)) - 1), 42) == BITSIZE(_BitInt(128)) - 1 ? 1 : -1];
229345

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

0 commit comments

Comments
 (0)