Skip to content

Commit fc8d481

Browse files
authored
[clang] Fix __builtin_popcountg not matching GCC (#83313)
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.
1 parent e3b93a1 commit fc8d481

File tree

6 files changed

+43
-28
lines changed

6 files changed

+43
-28
lines changed

clang/docs/LanguageExtensions.rst

+4-4
Original file line numberDiff line numberDiff line change
@@ -3477,7 +3477,7 @@ builtin, the mangler emits their usual pattern without any special treatment.
34773477
-----------------------
34783478
34793479
``__builtin_popcountg`` returns the number of 1 bits in the argument. The
3480-
argument can be of any integer type.
3480+
argument can be of any unsigned integer type.
34813481
34823482
**Syntax**:
34833483
@@ -3489,20 +3489,20 @@ argument can be of any integer type.
34893489
34903490
.. code-block:: c++
34913491
3492-
int x = 1;
3492+
unsigned int x = 1;
34933493
int x_pop = __builtin_popcountg(x);
34943494
34953495
unsigned long y = 3;
34963496
int y_pop = __builtin_popcountg(y);
34973497
3498-
_BitInt(128) z = 7;
3498+
unsigned _BitInt(128) z = 7;
34993499
int z_pop = __builtin_popcountg(z);
35003500
35013501
**Description**:
35023502
35033503
``__builtin_popcountg`` is meant to be a type-generic alternative to the
35043504
``__builtin_popcount{,l,ll}`` builtins, with support for other integer types,
3505-
such as ``__int128`` and C23 ``_BitInt(N)``.
3505+
such as ``unsigned __int128`` and C23 ``unsigned _BitInt(N)``.
35063506
35073507
Multiprecision Arithmetic Builtins
35083508
----------------------------------

clang/include/clang/Basic/Builtins.td

+1-1
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,7 @@ def Popcount : Builtin, BitInt_Long_LongLongTemplate {
690690

691691
def Popcountg : Builtin {
692692
let Spellings = ["__builtin_popcountg"];
693-
let Attributes = [NoThrow, Const];
693+
let Attributes = [NoThrow, Const, CustomTypeChecking];
694694
let Prototype = "int(...)";
695695
}
696696

clang/include/clang/Basic/DiagnosticSemaKinds.td

+1-1
Original file line numberDiff line numberDiff line change
@@ -11984,7 +11984,7 @@ def err_builtin_invalid_arg_type: Error <
1198411984
"signed integer or floating point type|vector type|"
1198511985
"floating point type|"
1198611986
"vector of integers|"
11987-
"type of integer}1 (was %2)">;
11987+
"type of unsigned integer}1 (was %2)">;
1198811988

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

clang/lib/Sema/SemaChecking.cpp

+10-4
Original file line numberDiff line numberDiff line change
@@ -2190,17 +2190,23 @@ static bool SemaBuiltinCpu(Sema &S, const TargetInfo &TI, CallExpr *TheCall,
21902190
}
21912191

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

2198-
Expr *Arg = TheCall->getArg(0);
2198+
ExprResult ArgRes = S.DefaultLvalueConversion(TheCall->getArg(0));
2199+
if (ArgRes.isInvalid())
2200+
return true;
2201+
2202+
Expr *Arg = ArgRes.get();
2203+
TheCall->setArg(0, Arg);
2204+
21992205
QualType ArgTy = Arg->getType();
22002206

2201-
if (!ArgTy->isIntegerType()) {
2207+
if (!ArgTy->isUnsignedIntegerType()) {
22022208
S.Diag(Arg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
2203-
<< 1 << /*integer ty*/ 7 << ArgTy;
2209+
<< 1 << /*unsigned integer ty*/ 7 << ArgTy;
22042210
return true;
22052211
}
22062212
return false;

clang/test/CodeGen/builtins.c

+14-14
Original file line numberDiff line numberDiff line change
@@ -948,38 +948,38 @@ void test_builtin_popcountg(unsigned char uc, unsigned short us,
948948
volatile int pop;
949949
pop = __builtin_popcountg(uc);
950950
// CHECK: %1 = load i8, ptr %uc.addr, align 1
951-
// CHECK-NEXT: %conv = zext i8 %1 to i32
952-
// CHECK-NEXT: %2 = call i32 @llvm.ctpop.i32(i32 %conv)
953-
// CHECK-NEXT: store volatile i32 %2, ptr %pop, align 4
951+
// CHECK-NEXT: %2 = call i8 @llvm.ctpop.i8(i8 %1)
952+
// CHECK-NEXT: %cast = sext i8 %2 to i32
953+
// CHECK-NEXT: store volatile i32 %cast, ptr %pop, align 4
954954
pop = __builtin_popcountg(us);
955955
// CHECK-NEXT: %3 = load i16, ptr %us.addr, align 2
956-
// CHECK-NEXT: %conv1 = zext i16 %3 to i32
957-
// CHECK-NEXT: %4 = call i32 @llvm.ctpop.i32(i32 %conv1)
958-
// CHECK-NEXT: store volatile i32 %4, ptr %pop, align 4
956+
// CHECK-NEXT: %4 = call i16 @llvm.ctpop.i16(i16 %3)
957+
// CHECK-NEXT: %cast1 = sext i16 %4 to i32
958+
// CHECK-NEXT: store volatile i32 %cast1, ptr %pop, align 4
959959
pop = __builtin_popcountg(ui);
960960
// CHECK-NEXT: %5 = load i32, ptr %ui.addr, align 4
961961
// CHECK-NEXT: %6 = call i32 @llvm.ctpop.i32(i32 %5)
962962
// CHECK-NEXT: store volatile i32 %6, ptr %pop, align 4
963963
pop = __builtin_popcountg(ul);
964964
// CHECK-NEXT: %7 = load i64, ptr %ul.addr, align 8
965965
// CHECK-NEXT: %8 = call i64 @llvm.ctpop.i64(i64 %7)
966-
// CHECK-NEXT: %cast = trunc i64 %8 to i32
967-
// CHECK-NEXT: store volatile i32 %cast, ptr %pop, align 4
966+
// CHECK-NEXT: %cast2 = trunc i64 %8 to i32
967+
// CHECK-NEXT: store volatile i32 %cast2, ptr %pop, align 4
968968
pop = __builtin_popcountg(ull);
969969
// CHECK-NEXT: %9 = load i64, ptr %ull.addr, align 8
970970
// CHECK-NEXT: %10 = call i64 @llvm.ctpop.i64(i64 %9)
971-
// CHECK-NEXT: %cast2 = trunc i64 %10 to i32
972-
// CHECK-NEXT: store volatile i32 %cast2, ptr %pop, align 4
971+
// CHECK-NEXT: %cast3 = trunc i64 %10 to i32
972+
// CHECK-NEXT: store volatile i32 %cast3, ptr %pop, align 4
973973
pop = __builtin_popcountg(ui128);
974974
// CHECK-NEXT: %11 = load i128, ptr %ui128.addr, align 16
975975
// CHECK-NEXT: %12 = call i128 @llvm.ctpop.i128(i128 %11)
976-
// CHECK-NEXT: %cast3 = trunc i128 %12 to i32
977-
// CHECK-NEXT: store volatile i32 %cast3, ptr %pop, align 4
976+
// CHECK-NEXT: %cast4 = trunc i128 %12 to i32
977+
// CHECK-NEXT: store volatile i32 %cast4, ptr %pop, align 4
978978
pop = __builtin_popcountg(ubi128);
979979
// CHECK-NEXT: %13 = load i128, ptr %ubi128.addr, align 8
980980
// CHECK-NEXT: %14 = call i128 @llvm.ctpop.i128(i128 %13)
981-
// CHECK-NEXT: %cast4 = trunc i128 %14 to i32
982-
// CHECK-NEXT: store volatile i32 %cast4, ptr %pop, align 4
981+
// CHECK-NEXT: %cast5 = trunc i128 %14 to i32
982+
// CHECK-NEXT: store volatile i32 %cast5, ptr %pop, align 4
983983
// CHECK-NEXT: ret void
984984
}
985985

clang/test/Sema/builtin-popcountg.c

+13-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
1-
// RUN: %clang_cc1 -triple=x86_64-pc-linux-gnu -fsyntax-only -verify -Wpedantic %s
1+
// RUN: %clang_cc1 -std=c23 -triple=x86_64-pc-linux-gnu -fsyntax-only -verify -Wpedantic %s
22

33
typedef int int2 __attribute__((ext_vector_type(2)));
44

5-
void test_builtin_popcountg(int i, double d, int2 i2) {
5+
void test_builtin_popcountg(short s, int i, __int128 i128, _BitInt(128) bi128,
6+
double d, int2 i2) {
67
__builtin_popcountg();
78
// expected-error@-1 {{too few arguments to function call, expected 1, have 0}}
89
__builtin_popcountg(i, i);
910
// expected-error@-1 {{too many arguments to function call, expected 1, have 2}}
11+
__builtin_popcountg(s);
12+
// expected-error@-1 {{1st argument must be a type of unsigned integer (was 'short')}}
13+
__builtin_popcountg(i);
14+
// expected-error@-1 {{1st argument must be a type of unsigned integer (was 'int')}}
15+
__builtin_popcountg(i128);
16+
// expected-error@-1 {{1st argument must be a type of unsigned integer (was '__int128')}}
17+
__builtin_popcountg(bi128);
18+
// expected-error@-1 {{1st argument must be a type of unsigned integer (was '_BitInt(128)')}}
1019
__builtin_popcountg(d);
11-
// expected-error@-1 {{1st argument must be a type of integer (was 'double')}}
20+
// expected-error@-1 {{1st argument must be a type of unsigned integer (was 'double')}}
1221
__builtin_popcountg(i2);
13-
// expected-error@-1 {{1st argument must be a type of integer (was 'int2' (vector of 2 'int' values))}}
22+
// expected-error@-1 {{1st argument must be a type of unsigned integer (was 'int2' (vector of 2 'int' values))}}
1423
}

0 commit comments

Comments
 (0)