Skip to content

Commit 7fc6a66

Browse files
overmightychencha3
authored andcommitted
[clang] Implement __builtin_{clzg,ctzg} (llvm#83431)
Fixes llvm#83075, fixes llvm#83076.
1 parent dbea6a7 commit 7fc6a66

File tree

9 files changed

+441
-40
lines changed

9 files changed

+441
-40
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3553,6 +3553,47 @@ argument can be of any unsigned integer type.
35533553
``__builtin_popcount{,l,ll}`` builtins, with support for other integer types,
35543554
such as ``unsigned __int128`` and C23 ``unsigned _BitInt(N)``.
35553555
3556+
``__builtin_clzg`` and ``__builtin_ctzg``
3557+
-----------------------------------------
3558+
3559+
``__builtin_clzg`` (respectively ``__builtin_ctzg``) returns the number of
3560+
leading (respectively trailing) 0 bits in the first argument. The first argument
3561+
can be of any unsigned integer type.
3562+
3563+
If the first argument is 0 and an optional second argument of ``int`` type is
3564+
provided, then the second argument is returned. If the first argument is 0, but
3565+
only one argument is provided, then the behavior is undefined.
3566+
3567+
**Syntax**:
3568+
3569+
.. code-block:: c++
3570+
3571+
int __builtin_clzg(type x[, int fallback])
3572+
int __builtin_ctzg(type x[, int fallback])
3573+
3574+
**Examples**:
3575+
3576+
.. code-block:: c++
3577+
3578+
unsigned int x = 1;
3579+
int x_lz = __builtin_clzg(x);
3580+
int x_tz = __builtin_ctzg(x);
3581+
3582+
unsigned long y = 2;
3583+
int y_lz = __builtin_clzg(y);
3584+
int y_tz = __builtin_ctzg(y);
3585+
3586+
unsigned _BitInt(128) z = 4;
3587+
int z_lz = __builtin_clzg(z);
3588+
int z_tz = __builtin_ctzg(z);
3589+
3590+
**Description**:
3591+
3592+
``__builtin_clzg`` (respectively ``__builtin_ctzg``) is meant to be a
3593+
type-generic alternative to the ``__builtin_clz{,l,ll}`` (respectively
3594+
``__builtin_ctz{,l,ll}``) builtins, with support for other integer types, such
3595+
as ``unsigned __int128`` and C23 ``unsigned _BitInt(N)``.
3596+
35563597
Multiprecision Arithmetic Builtins
35573598
----------------------------------
35583599

clang/include/clang/Basic/Builtins.td

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -676,15 +676,23 @@ def Clz : Builtin, BitShort_Int_Long_LongLongTemplate {
676676
let Prototype = "int(unsigned T)";
677677
}
678678

679-
// FIXME: Add int clzimax(uintmax_t)
679+
def Clzg : Builtin {
680+
let Spellings = ["__builtin_clzg"];
681+
let Attributes = [NoThrow, Const, CustomTypeChecking];
682+
let Prototype = "int(...)";
683+
}
680684

681685
def Ctz : Builtin, BitShort_Int_Long_LongLongTemplate {
682686
let Spellings = ["__builtin_ctz"];
683687
let Attributes = [NoThrow, Const, Constexpr];
684688
let Prototype = "int(unsigned T)";
685689
}
686690

687-
// FIXME: Add int ctzimax(uintmax_t)
691+
def Ctzg : Builtin {
692+
let Spellings = ["__builtin_ctzg"];
693+
let Attributes = [NoThrow, Const, CustomTypeChecking];
694+
let Prototype = "int(...)";
695+
}
688696

689697
def FFS : Builtin, BitInt_Long_LongLongTemplate {
690698
let Spellings = ["__builtin_ffs"];

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12020,13 +12020,14 @@ def err_builtin_launder_invalid_arg : Error<
1202012020
"'__builtin_launder' is not allowed">;
1202112021

1202212022
def err_builtin_invalid_arg_type: Error <
12023-
"%ordinal0 argument must be a "
12024-
"%select{vector, integer or floating point type|matrix|"
12025-
"pointer to a valid matrix element type|"
12026-
"signed integer or floating point type|vector type|"
12027-
"floating point type|"
12028-
"vector of integers|"
12029-
"type of unsigned integer}1 (was %2)">;
12023+
"%ordinal0 argument must be "
12024+
"%select{a vector, integer or floating point type|a matrix|"
12025+
"a pointer to a valid matrix element type|"
12026+
"a signed integer or floating point type|a vector type|"
12027+
"a floating point type|"
12028+
"a vector of integers|"
12029+
"an unsigned integer|"
12030+
"an 'int'}1 (was %2)">;
1203012031

1203112032
def err_builtin_matrix_disabled: Error<
1203212033
"matrix types extension is disabled. Pass -fenable-matrix to enable it">;

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3128,36 +3128,66 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
31283128
case Builtin::BI__builtin_ctzs:
31293129
case Builtin::BI__builtin_ctz:
31303130
case Builtin::BI__builtin_ctzl:
3131-
case Builtin::BI__builtin_ctzll: {
3132-
Value *ArgValue = EmitCheckedArgForBuiltin(E->getArg(0), BCK_CTZPassedZero);
3131+
case Builtin::BI__builtin_ctzll:
3132+
case Builtin::BI__builtin_ctzg: {
3133+
bool HasFallback = BuiltinIDIfNoAsmLabel == Builtin::BI__builtin_ctzg &&
3134+
E->getNumArgs() > 1;
3135+
3136+
Value *ArgValue =
3137+
HasFallback ? EmitScalarExpr(E->getArg(0))
3138+
: EmitCheckedArgForBuiltin(E->getArg(0), BCK_CTZPassedZero);
31333139

31343140
llvm::Type *ArgType = ArgValue->getType();
31353141
Function *F = CGM.getIntrinsic(Intrinsic::cttz, ArgType);
31363142

31373143
llvm::Type *ResultType = ConvertType(E->getType());
3138-
Value *ZeroUndef = Builder.getInt1(getTarget().isCLZForZeroUndef());
3144+
Value *ZeroUndef =
3145+
Builder.getInt1(HasFallback || getTarget().isCLZForZeroUndef());
31393146
Value *Result = Builder.CreateCall(F, {ArgValue, ZeroUndef});
31403147
if (Result->getType() != ResultType)
31413148
Result = Builder.CreateIntCast(Result, ResultType, /*isSigned*/true,
31423149
"cast");
3143-
return RValue::get(Result);
3150+
if (!HasFallback)
3151+
return RValue::get(Result);
3152+
3153+
Value *Zero = Constant::getNullValue(ArgType);
3154+
Value *IsZero = Builder.CreateICmpEQ(ArgValue, Zero, "iszero");
3155+
Value *FallbackValue = EmitScalarExpr(E->getArg(1));
3156+
Value *ResultOrFallback =
3157+
Builder.CreateSelect(IsZero, FallbackValue, Result, "ctzg");
3158+
return RValue::get(ResultOrFallback);
31443159
}
31453160
case Builtin::BI__builtin_clzs:
31463161
case Builtin::BI__builtin_clz:
31473162
case Builtin::BI__builtin_clzl:
3148-
case Builtin::BI__builtin_clzll: {
3149-
Value *ArgValue = EmitCheckedArgForBuiltin(E->getArg(0), BCK_CLZPassedZero);
3163+
case Builtin::BI__builtin_clzll:
3164+
case Builtin::BI__builtin_clzg: {
3165+
bool HasFallback = BuiltinIDIfNoAsmLabel == Builtin::BI__builtin_clzg &&
3166+
E->getNumArgs() > 1;
3167+
3168+
Value *ArgValue =
3169+
HasFallback ? EmitScalarExpr(E->getArg(0))
3170+
: EmitCheckedArgForBuiltin(E->getArg(0), BCK_CLZPassedZero);
31503171

31513172
llvm::Type *ArgType = ArgValue->getType();
31523173
Function *F = CGM.getIntrinsic(Intrinsic::ctlz, ArgType);
31533174

31543175
llvm::Type *ResultType = ConvertType(E->getType());
3155-
Value *ZeroUndef = Builder.getInt1(getTarget().isCLZForZeroUndef());
3176+
Value *ZeroUndef =
3177+
Builder.getInt1(HasFallback || getTarget().isCLZForZeroUndef());
31563178
Value *Result = Builder.CreateCall(F, {ArgValue, ZeroUndef});
31573179
if (Result->getType() != ResultType)
31583180
Result = Builder.CreateIntCast(Result, ResultType, /*isSigned*/true,
31593181
"cast");
3160-
return RValue::get(Result);
3182+
if (!HasFallback)
3183+
return RValue::get(Result);
3184+
3185+
Value *Zero = Constant::getNullValue(ArgType);
3186+
Value *IsZero = Builder.CreateICmpEQ(ArgValue, Zero, "iszero");
3187+
Value *FallbackValue = EmitScalarExpr(E->getArg(1));
3188+
Value *ResultOrFallback =
3189+
Builder.CreateSelect(IsZero, FallbackValue, Result, "clzg");
3190+
return RValue::get(ResultOrFallback);
31613191
}
31623192
case Builtin::BI__builtin_ffs:
31633193
case Builtin::BI__builtin_ffsl:

clang/lib/Sema/SemaChecking.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2399,6 +2399,48 @@ static bool SemaBuiltinPopcountg(Sema &S, CallExpr *TheCall) {
23992399
return false;
24002400
}
24012401

2402+
/// Checks that __builtin_{clzg,ctzg} was called with a first argument, which is
2403+
/// an unsigned integer, and an optional second argument, which is promoted to
2404+
/// an 'int'.
2405+
static bool SemaBuiltinCountZeroBitsGeneric(Sema &S, CallExpr *TheCall) {
2406+
if (checkArgCountRange(S, TheCall, 1, 2))
2407+
return true;
2408+
2409+
ExprResult Arg0Res = S.DefaultLvalueConversion(TheCall->getArg(0));
2410+
if (Arg0Res.isInvalid())
2411+
return true;
2412+
2413+
Expr *Arg0 = Arg0Res.get();
2414+
TheCall->setArg(0, Arg0);
2415+
2416+
QualType Arg0Ty = Arg0->getType();
2417+
2418+
if (!Arg0Ty->isUnsignedIntegerType()) {
2419+
S.Diag(Arg0->getBeginLoc(), diag::err_builtin_invalid_arg_type)
2420+
<< 1 << /*unsigned integer ty*/ 7 << Arg0Ty;
2421+
return true;
2422+
}
2423+
2424+
if (TheCall->getNumArgs() > 1) {
2425+
ExprResult Arg1Res = S.UsualUnaryConversions(TheCall->getArg(1));
2426+
if (Arg1Res.isInvalid())
2427+
return true;
2428+
2429+
Expr *Arg1 = Arg1Res.get();
2430+
TheCall->setArg(1, Arg1);
2431+
2432+
QualType Arg1Ty = Arg1->getType();
2433+
2434+
if (!Arg1Ty->isSpecificBuiltinType(BuiltinType::Int)) {
2435+
S.Diag(Arg1->getBeginLoc(), diag::err_builtin_invalid_arg_type)
2436+
<< 2 << /*'int' ty*/ 8 << Arg1Ty;
2437+
return true;
2438+
}
2439+
}
2440+
2441+
return false;
2442+
}
2443+
24022444
ExprResult
24032445
Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
24042446
CallExpr *TheCall) {
@@ -3187,6 +3229,11 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
31873229
if (SemaBuiltinPopcountg(*this, TheCall))
31883230
return ExprError();
31893231
break;
3232+
case Builtin::BI__builtin_clzg:
3233+
case Builtin::BI__builtin_ctzg:
3234+
if (SemaBuiltinCountZeroBitsGeneric(*this, TheCall))
3235+
return ExprError();
3236+
break;
31903237
}
31913238

31923239
if (getLangOpts().HLSL && CheckHLSLBuiltinFunctionCall(BuiltinID, TheCall))

0 commit comments

Comments
 (0)