Skip to content

Commit

Permalink
[clang] Fix __builtin_popcountg not matching GCC (#83313)
Browse files Browse the repository at this point in the history
Our implementation previously accepted signed arguments and performed
integer promotion on the argument. GCC's implementation requires an
unsigned argument and does not perform integer promotion on it.
  • Loading branch information
overmighty committed Feb 28, 2024
1 parent e3b93a1 commit fc8d481
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 28 deletions.
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))}}
}

0 comments on commit fc8d481

Please sign in to comment.