Skip to content

Commit 5e37b3b

Browse files
committed
[clang] Implement __builtin_{clzg,ctzg}
Fixes #83075, fixes #83076.
1 parent c089fa5 commit 5e37b3b

File tree

9 files changed

+445
-36
lines changed

9 files changed

+445
-36
lines changed

clang/docs/LanguageExtensions.rst

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3504,6 +3504,47 @@ argument can be of any unsigned integer type.
35043504
``__builtin_popcount{,l,ll}`` builtins, with support for other integer types,
35053505
such as ``unsigned __int128`` and C23 ``unsigned _BitInt(N)``.
35063506
3507+
``__builtin_clzg`` and ``__builtin_ctzg``
3508+
-----------------------------------------
3509+
3510+
``__builtin_clzg`` (respectively ``__builtin_ctzg``) returns the number of
3511+
leading (respectively trailing) 0 bits in the first argument. The first argument
3512+
can be of any unsigned integer type.
3513+
3514+
If the first argument is 0 and an optional second argument of ``int`` type is
3515+
provided, then the second argument is returned. If the first argument is 0, but
3516+
only one argument is provided, then the returned value is undefined.
3517+
3518+
**Syntax**:
3519+
3520+
.. code-block:: c++
3521+
3522+
int __builtin_clzg(type x[, int fallback])
3523+
int __builtin_ctzg(type x[, int fallback])
3524+
3525+
**Examples**:
3526+
3527+
.. code-block:: c++
3528+
3529+
unsigned int x = 1;
3530+
int x_lz = __builtin_clzg(x);
3531+
int x_tz = __builtin_ctzg(x);
3532+
3533+
unsigned long y = 2;
3534+
int y_lz = __builtin_clzg(y);
3535+
int y_tz = __builtin_ctzg(y);
3536+
3537+
unsigned _BitInt(128) z = 4;
3538+
int z_lz = __builtin_clzg(z);
3539+
int z_tz = __builtin_ctzg(z);
3540+
3541+
**Description**:
3542+
3543+
``__builtin_clzg`` (respectively ``__builtin_ctzg``) is meant to be a
3544+
type-generic alternative to the ``__builtin_clz{,l,ll}`` (respectively
3545+
``__builtin_ctz{,l,ll}``) builtins, with support for other integer types, such
3546+
as ``unsigned __int128`` and C23 ``unsigned _BitInt(N)``.
3547+
35073548
Multiprecision Arithmetic Builtins
35083549
----------------------------------
35093550

clang/include/clang/Basic/Builtins.td

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,12 @@ def Clz : Builtin, BitShort_Int_Long_LongLongTemplate {
662662

663663
// FIXME: Add int clzimax(uintmax_t)
664664

665+
def Clzg : Builtin {
666+
let Spellings = ["__builtin_clzg"];
667+
let Attributes = [NoThrow, Const, CustomTypeChecking];
668+
let Prototype = "int(...)";
669+
}
670+
665671
def Ctz : Builtin, BitShort_Int_Long_LongLongTemplate {
666672
let Spellings = ["__builtin_ctz"];
667673
let Attributes = [NoThrow, Const, Constexpr];
@@ -670,6 +676,12 @@ def Ctz : Builtin, BitShort_Int_Long_LongLongTemplate {
670676

671677
// FIXME: Add int ctzimax(uintmax_t)
672678

679+
def Ctzg : Builtin {
680+
let Spellings = ["__builtin_ctzg"];
681+
let Attributes = [NoThrow, Const, CustomTypeChecking];
682+
let Prototype = "int(...)";
683+
}
684+
673685
def FFS : Builtin, BitInt_Long_LongLongTemplate {
674686
let Spellings = ["__builtin_ffs"];
675687
let Attributes = [FunctionWithBuiltinPrefix, NoThrow, Const, Constexpr];

clang/include/clang/Basic/DiagnosticSemaKinds.td

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

1198011980
def err_builtin_invalid_arg_type: Error <
11981-
"%ordinal0 argument must be a "
11982-
"%select{vector, integer or floating point type|matrix|"
11983-
"pointer to a valid matrix element type|"
11984-
"signed integer or floating point type|vector type|"
11985-
"floating point type|"
11986-
"vector of integers|"
11987-
"type of unsigned integer}1 (was %2)">;
11981+
"%ordinal0 argument must be "
11982+
"%select{a vector, integer or floating point type|a matrix|"
11983+
"a pointer to a valid matrix element type|"
11984+
"a signed integer or floating point type|a vector type|"
11985+
"a floating point type|"
11986+
"a vector of integers|"
11987+
"an unsigned integer|"
11988+
"an 'int'}1 (was %2)">;
1198811989

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

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3128,8 +3128,14 @@ 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);
@@ -3140,13 +3146,27 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
31403146
if (Result->getType() != ResultType)
31413147
Result = Builder.CreateIntCast(Result, ResultType, /*isSigned*/true,
31423148
"cast");
3143-
return RValue::get(Result);
3149+
if (!HasFallback)
3150+
return RValue::get(Result);
3151+
3152+
Value *Zero = Constant::getNullValue(ArgType);
3153+
Value *IsZero = Builder.CreateICmpEQ(ArgValue, Zero, "iszero");
3154+
Value *FallbackValue = EmitScalarExpr(E->getArg(1));
3155+
Value *ResultOrFallback =
3156+
Builder.CreateSelect(IsZero, FallbackValue, Result, "ctzg");
3157+
return RValue::get(ResultOrFallback);
31443158
}
31453159
case Builtin::BI__builtin_clzs:
31463160
case Builtin::BI__builtin_clz:
31473161
case Builtin::BI__builtin_clzl:
3148-
case Builtin::BI__builtin_clzll: {
3149-
Value *ArgValue = EmitCheckedArgForBuiltin(E->getArg(0), BCK_CLZPassedZero);
3162+
case Builtin::BI__builtin_clzll:
3163+
case Builtin::BI__builtin_clzg: {
3164+
bool HasFallback = BuiltinIDIfNoAsmLabel == Builtin::BI__builtin_clzg &&
3165+
E->getNumArgs() > 1;
3166+
3167+
Value *ArgValue =
3168+
HasFallback ? EmitScalarExpr(E->getArg(0))
3169+
: EmitCheckedArgForBuiltin(E->getArg(0), BCK_CLZPassedZero);
31503170

31513171
llvm::Type *ArgType = ArgValue->getType();
31523172
Function *F = CGM.getIntrinsic(Intrinsic::ctlz, ArgType);
@@ -3157,7 +3177,15 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
31573177
if (Result->getType() != ResultType)
31583178
Result = Builder.CreateIntCast(Result, ResultType, /*isSigned*/true,
31593179
"cast");
3160-
return RValue::get(Result);
3180+
if (!HasFallback)
3181+
return RValue::get(Result);
3182+
3183+
Value *Zero = Constant::getNullValue(ArgType);
3184+
Value *IsZero = Builder.CreateICmpEQ(ArgValue, Zero, "iszero");
3185+
Value *FallbackValue = EmitScalarExpr(E->getArg(1));
3186+
Value *ResultOrFallback =
3187+
Builder.CreateSelect(IsZero, FallbackValue, Result, "clzg");
3188+
return RValue::get(ResultOrFallback);
31613189
}
31623190
case Builtin::BI__builtin_ffs:
31633191
case Builtin::BI__builtin_ffsl:

clang/lib/Sema/SemaChecking.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2214,6 +2214,54 @@ static bool SemaBuiltinPopcountg(Sema &S, CallExpr *TheCall) {
22142214
return false;
22152215
}
22162216

2217+
/// Checks that __builtin_{clzg,ctzg} was called with a first argument, which is
2218+
/// an unsigned integer, and an optional second argument, which is promoted to
2219+
/// an 'int'.
2220+
static bool SemaBuiltinCountZeroBitsGeneric(Sema &S, CallExpr *TheCall) {
2221+
if (checkArgCountRange(S, TheCall, 1, 2))
2222+
return true;
2223+
2224+
ExprResult Arg0Res = S.DefaultLvalueConversion(TheCall->getArg(0));
2225+
if (Arg0Res.isInvalid())
2226+
return true;
2227+
2228+
Expr *Arg0 = Arg0Res.get();
2229+
TheCall->setArg(0, Arg0);
2230+
2231+
QualType Arg0Ty = Arg0->getType();
2232+
2233+
if (!Arg0Ty->isUnsignedIntegerType()) {
2234+
S.Diag(Arg0->getBeginLoc(), diag::err_builtin_invalid_arg_type)
2235+
<< 1 << /*unsigned integer ty*/ 7 << Arg0Ty;
2236+
return true;
2237+
}
2238+
2239+
if (TheCall->getNumArgs() > 1) {
2240+
ExprResult Arg1Res = S.DefaultLvalueConversion(TheCall->getArg(1));
2241+
if (Arg1Res.isInvalid())
2242+
return true;
2243+
2244+
Expr *Arg1 = Arg1Res.get();
2245+
TheCall->setArg(1, Arg1);
2246+
2247+
QualType Arg1Ty = Arg1->getType();
2248+
2249+
if (S.Context.isPromotableIntegerType(Arg1Ty)) {
2250+
Arg1Ty = S.Context.getPromotedIntegerType(Arg1Ty);
2251+
Arg1 = S.ImpCastExprToType(Arg1, Arg1Ty, CK_IntegralCast).get();
2252+
TheCall->setArg(1, Arg1);
2253+
}
2254+
2255+
if (!Arg1Ty->isSpecificBuiltinType(BuiltinType::Int)) {
2256+
S.Diag(Arg1->getBeginLoc(), diag::err_builtin_invalid_arg_type)
2257+
<< 2 << /*'int' ty*/ 8 << Arg1Ty;
2258+
return true;
2259+
}
2260+
}
2261+
2262+
return false;
2263+
}
2264+
22172265
ExprResult
22182266
Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
22192267
CallExpr *TheCall) {
@@ -2990,6 +3038,11 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
29903038
if (SemaBuiltinPopcountg(*this, TheCall))
29913039
return ExprError();
29923040
break;
3041+
case Builtin::BI__builtin_clzg:
3042+
case Builtin::BI__builtin_ctzg:
3043+
if (SemaBuiltinCountZeroBitsGeneric(*this, TheCall))
3044+
return ExprError();
3045+
break;
29933046
}
29943047

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

0 commit comments

Comments
 (0)