diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index 711baf45f449a..2a177814c4df7 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3473,6 +3473,37 @@ builtin, the mangler emits their usual pattern without any special treatment. // Computes a unique stable name for the given type. constexpr const char * __builtin_sycl_unique_stable_name( type-id ); +``__builtin_popcountg`` +----------------------- + +``__builtin_popcountg`` returns the number of 1 bits in the argument. The +argument can be of any integer type. + +**Syntax**: + +.. code-block:: c++ + + int __builtin_popcountg(type x) + +**Examples**: + +.. code-block:: c++ + + int x = 1; + int x_pop = __builtin_popcountg(x); + + unsigned long y = 3; + int y_pop = __builtin_popcountg(y); + + _BitInt(128) z = 7; + int z_pop = __builtin_popcountg(z); + +**Description**: + +``__builtin_popcountg`` is meant to be a type-generic alternative to the +``__builtin_popcount{,l,ll}`` builtins, with support for other integer types, +such as ``__int128`` and C23 ``_BitInt(N)``. + Multiprecision Arithmetic Builtins ---------------------------------- diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index df74026c5d2d5..c29132b737d59 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -688,6 +688,12 @@ def Popcount : Builtin, BitInt_Long_LongLongTemplate { let Prototype = "int(unsigned T)"; } +def Popcountg : Builtin { + let Spellings = ["__builtin_popcountg"]; + let Attributes = [NoThrow, Const]; + let Prototype = "int(...)"; +} + def Clrsb : Builtin, BitInt_Long_LongLongTemplate { let Spellings = ["__builtin_clrsb"]; let Attributes = [NoThrow, Const, Constexpr]; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index ebda201361fb0..5fb26e6f7feef 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11976,7 +11976,8 @@ def err_builtin_invalid_arg_type: Error < "pointer to a valid matrix element type|" "signed integer or floating point type|vector type|" "floating point type|" - "vector of integers}1 (was %2)">; + "vector of integers|" + "type of integer}1 (was %2)">; def err_builtin_matrix_disabled: Error< "matrix types extension is disabled. Pass -fenable-matrix to enable it">; diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index d8b2115f1e5e3..c63a46b6916d4 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -3216,7 +3216,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, case Builtin::BI__popcnt64: case Builtin::BI__builtin_popcount: case Builtin::BI__builtin_popcountl: - case Builtin::BI__builtin_popcountll: { + case Builtin::BI__builtin_popcountll: + case Builtin::BI__builtin_popcountg: { Value *ArgValue = EmitScalarExpr(E->getArg(0)); llvm::Type *ArgType = ArgValue->getType(); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 7fa295ebd9404..4d71614a1e2a2 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -2188,6 +2188,23 @@ static bool SemaBuiltinCpu(Sema &S, const TargetInfo &TI, CallExpr *TheCall, return false; } +/// Checks that __builtin_popcountg was called with a single argument, which is +/// an integer. +static bool SemaBuiltinPopcountg(Sema &S, CallExpr *TheCall) { + if (checkArgCount(S, TheCall, 1)) + return true; + + Expr *Arg = TheCall->getArg(0); + QualType ArgTy = Arg->getType(); + + if (!ArgTy->isIntegerType()) { + S.Diag(Arg->getBeginLoc(), diag::err_builtin_invalid_arg_type) + << 1 << /*integer ty*/ 7 << ArgTy; + return true; + } + return false; +} + ExprResult Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, CallExpr *TheCall) { @@ -2958,7 +2975,12 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, diag::err_hip_invalid_args_builtin_mangled_name); return ExprError(); } + break; } + case Builtin::BI__builtin_popcountg: + if (SemaBuiltinPopcountg(*this, TheCall)) + return ExprError(); + break; } // Since the target specific builtins for each arch overlap, only check those diff --git a/clang/test/CodeGen/builtins.c b/clang/test/CodeGen/builtins.c index 88282120283b8..73866116e07e7 100644 --- a/clang/test/CodeGen/builtins.c +++ b/clang/test/CodeGen/builtins.c @@ -940,4 +940,47 @@ void test_builtin_os_log_long_double(void *buf, long double ld) { // CHECK: %[[V3:.*]] = load i128, ptr %[[ARG0_ADDR]], align 16 // CHECK: store i128 %[[V3]], ptr %[[ARGDATA]], align 1 +// CHECK-LABEL: define{{.*}} void @test_builtin_popcountg +void test_builtin_popcountg(unsigned char uc, unsigned short us, + unsigned int ui, unsigned long ul, + unsigned long long ull, unsigned __int128 ui128, + unsigned _BitInt(128) ubi128) { + volatile int pop; + pop = __builtin_popcountg(uc); + // CHECK: %1 = load i8, ptr %uc.addr, align 1 + // CHECK-NEXT: %conv = zext i8 %1 to i32 + // CHECK-NEXT: %2 = call i32 @llvm.ctpop.i32(i32 %conv) + // CHECK-NEXT: store volatile i32 %2, ptr %pop, align 4 + pop = __builtin_popcountg(us); + // CHECK-NEXT: %3 = load i16, ptr %us.addr, align 2 + // CHECK-NEXT: %conv1 = zext i16 %3 to i32 + // CHECK-NEXT: %4 = call i32 @llvm.ctpop.i32(i32 %conv1) + // CHECK-NEXT: store volatile i32 %4, ptr %pop, align 4 + pop = __builtin_popcountg(ui); + // CHECK-NEXT: %5 = load i32, ptr %ui.addr, align 4 + // CHECK-NEXT: %6 = call i32 @llvm.ctpop.i32(i32 %5) + // CHECK-NEXT: store volatile i32 %6, ptr %pop, align 4 + pop = __builtin_popcountg(ul); + // CHECK-NEXT: %7 = load i64, ptr %ul.addr, align 8 + // CHECK-NEXT: %8 = call i64 @llvm.ctpop.i64(i64 %7) + // CHECK-NEXT: %cast = trunc i64 %8 to i32 + // CHECK-NEXT: store volatile i32 %cast, ptr %pop, align 4 + pop = __builtin_popcountg(ull); + // CHECK-NEXT: %9 = load i64, ptr %ull.addr, align 8 + // CHECK-NEXT: %10 = call i64 @llvm.ctpop.i64(i64 %9) + // CHECK-NEXT: %cast2 = trunc i64 %10 to i32 + // CHECK-NEXT: store volatile i32 %cast2, ptr %pop, align 4 + pop = __builtin_popcountg(ui128); + // CHECK-NEXT: %11 = load i128, ptr %ui128.addr, align 16 + // CHECK-NEXT: %12 = call i128 @llvm.ctpop.i128(i128 %11) + // CHECK-NEXT: %cast3 = trunc i128 %12 to i32 + // CHECK-NEXT: store volatile i32 %cast3, ptr %pop, align 4 + pop = __builtin_popcountg(ubi128); + // CHECK-NEXT: %13 = load i128, ptr %ubi128.addr, align 8 + // CHECK-NEXT: %14 = call i128 @llvm.ctpop.i128(i128 %13) + // CHECK-NEXT: %cast4 = trunc i128 %14 to i32 + // CHECK-NEXT: store volatile i32 %cast4, ptr %pop, align 4 + // CHECK-NEXT: ret void +} + #endif diff --git a/clang/test/Sema/builtin-popcountg.c b/clang/test/Sema/builtin-popcountg.c new file mode 100644 index 0000000000000..e18b910046ff0 --- /dev/null +++ b/clang/test/Sema/builtin-popcountg.c @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -triple=x86_64-pc-linux-gnu -fsyntax-only -verify -Wpedantic %s + +typedef int int2 __attribute__((ext_vector_type(2))); + +void test_builtin_popcountg(int i, double d, int2 i2) { + __builtin_popcountg(); + // expected-error@-1 {{too few arguments to function call, expected 1, have 0}} + __builtin_popcountg(i, i); + // expected-error@-1 {{too many arguments to function call, expected 1, have 2}} + __builtin_popcountg(d); + // expected-error@-1 {{1st argument must be a type of integer (was 'double')}} + __builtin_popcountg(i2); + // expected-error@-1 {{1st argument must be a type of integer (was 'int2' (vector of 2 'int' values))}} +}