Skip to content

[clang] Fix __builtin_popcountg not matching GCC #83313

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions clang/docs/LanguageExtensions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3477,7 +3477,7 @@ builtin, the mangler emits their usual pattern without any special treatment.
-----------------------

``__builtin_popcountg`` returns the number of 1 bits in the argument. The
argument can be of any integer type.
argument can be of any unsigned integer type.

**Syntax**:

Expand All @@ -3489,20 +3489,20 @@ argument can be of any integer type.

.. code-block:: c++

int x = 1;
unsigned int x = 1;
int x_pop = __builtin_popcountg(x);

unsigned long y = 3;
int y_pop = __builtin_popcountg(y);

_BitInt(128) z = 7;
unsigned _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)``.
such as ``unsigned __int128`` and C23 ``unsigned _BitInt(N)``.

Multiprecision Arithmetic Builtins
----------------------------------
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/Builtins.td
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,7 @@ def Popcount : Builtin, BitInt_Long_LongLongTemplate {

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

Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -11984,7 +11984,7 @@ def err_builtin_invalid_arg_type: Error <
"signed integer or floating point type|vector type|"
"floating point type|"
"vector of integers|"
"type of integer}1 (was %2)">;
"type of unsigned integer}1 (was %2)">;

def err_builtin_matrix_disabled: Error<
"matrix types extension is disabled. Pass -fenable-matrix to enable it">;
Expand Down
14 changes: 10 additions & 4 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2190,17 +2190,23 @@ static bool SemaBuiltinCpu(Sema &S, const TargetInfo &TI, CallExpr *TheCall,
}

/// Checks that __builtin_popcountg was called with a single argument, which is
/// an integer.
/// an unsigned integer.
static bool SemaBuiltinPopcountg(Sema &S, CallExpr *TheCall) {
if (checkArgCount(S, TheCall, 1))
return true;

Expr *Arg = TheCall->getArg(0);
ExprResult ArgRes = S.DefaultLvalueConversion(TheCall->getArg(0));
if (ArgRes.isInvalid())
return true;

Expr *Arg = ArgRes.get();
TheCall->setArg(0, Arg);

QualType ArgTy = Arg->getType();

if (!ArgTy->isIntegerType()) {
if (!ArgTy->isUnsignedIntegerType()) {
S.Diag(Arg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
<< 1 << /*integer ty*/ 7 << ArgTy;
<< 1 << /*unsigned integer ty*/ 7 << ArgTy;
return true;
}
return false;
Expand Down
28 changes: 14 additions & 14 deletions clang/test/CodeGen/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -948,38 +948,38 @@ void test_builtin_popcountg(unsigned char uc, unsigned short us,
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
// CHECK-NEXT: %2 = call i8 @llvm.ctpop.i8(i8 %1)
// CHECK-NEXT: %cast = sext i8 %2 to i32
// CHECK-NEXT: store volatile i32 %cast, 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
// CHECK-NEXT: %4 = call i16 @llvm.ctpop.i16(i16 %3)
// CHECK-NEXT: %cast1 = sext i16 %4 to i32
// CHECK-NEXT: store volatile i32 %cast1, 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
// CHECK-NEXT: %cast2 = trunc i64 %8 to i32
// CHECK-NEXT: store volatile i32 %cast2, 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
// CHECK-NEXT: %cast3 = trunc i64 %10 to i32
// CHECK-NEXT: store volatile i32 %cast3, 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
// CHECK-NEXT: %cast4 = trunc i128 %12 to i32
// CHECK-NEXT: store volatile i32 %cast4, 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: %cast5 = trunc i128 %14 to i32
// CHECK-NEXT: store volatile i32 %cast5, ptr %pop, align 4
// CHECK-NEXT: ret void
}

Expand Down
17 changes: 13 additions & 4 deletions clang/test/Sema/builtin-popcountg.c
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
// RUN: %clang_cc1 -triple=x86_64-pc-linux-gnu -fsyntax-only -verify -Wpedantic %s
// RUN: %clang_cc1 -std=c23 -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) {
void test_builtin_popcountg(short s, int i, __int128 i128, _BitInt(128) bi128,
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(s);
// expected-error@-1 {{1st argument must be a type of unsigned integer (was 'short')}}
__builtin_popcountg(i);
// expected-error@-1 {{1st argument must be a type of unsigned integer (was 'int')}}
__builtin_popcountg(i128);
// expected-error@-1 {{1st argument must be a type of unsigned integer (was '__int128')}}
__builtin_popcountg(bi128);
// expected-error@-1 {{1st argument must be a type of unsigned integer (was '_BitInt(128)')}}
__builtin_popcountg(d);
// expected-error@-1 {{1st argument must be a type of integer (was 'double')}}
// expected-error@-1 {{1st argument must be a type of unsigned integer (was 'double')}}
__builtin_popcountg(i2);
// expected-error@-1 {{1st argument must be a type of integer (was 'int2' (vector of 2 'int' values))}}
// expected-error@-1 {{1st argument must be a type of unsigned integer (was 'int2' (vector of 2 'int' values))}}
}