Skip to content

Commit f210fc1

Browse files
authored
[Clang] Add __builtin_bswapg (#162433)
Add a new builtin function __builtin_bswapg. It works on any integral types that has a multiple of 16 bits as well as a single byte. Closes #160266
1 parent 95c93f4 commit f210fc1

File tree

13 files changed

+457
-7
lines changed

13 files changed

+457
-7
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,10 @@ Non-comprehensive list of changes in this release
312312
allocator-level heap organization strategies. A feature to instrument all
313313
allocation functions with a token ID can be enabled via the
314314
``-fsanitize=alloc-token`` flag.
315+
316+
- A new generic byte swap builtin function ``__builtin_bswapg`` that extends the existing
317+
__builtin_bswap{16,32,64} function family to support all standard integer types.
318+
315319
- A builtin ``__builtin_infer_alloc_token(<args>, ...)`` is provided to allow
316320
compile-time querying of allocation token IDs, where the builtin arguments
317321
mirror those normally passed to an allocation function.

clang/include/clang/Basic/Builtins.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,12 @@ def BSwap : Builtin, Template<["unsigned short", "uint32_t", "uint64_t"],
755755
let Prototype = "T(T)";
756756
}
757757

758+
def BSwapg : Builtin {
759+
let Spellings = ["__builtin_bswapg"];
760+
let Attributes = [NoThrow, Const, Constexpr, CustomTypeChecking];
761+
let Prototype = "int(...)";
762+
}
763+
758764
def Bitreverse : BitInt8_16_32_64BuiltinsTemplate, Builtin {
759765
let Spellings = ["__builtin_bitreverse"];
760766
let Attributes = [NoThrow, Const, Constexpr];

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12967,6 +12967,9 @@ def err_builtin_invalid_arg_type: Error<
1296712967
"%plural{0:|: }3"
1296812968
"%plural{[0,3]:type|:types}1 (was %4)">;
1296912969

12970+
def err_bswapg_invalid_bit_width : Error<
12971+
"_BitInt type %0 (%1 bits) must be a multiple of 16 bits for byte swapping">;
12972+
1297012973
def err_builtin_trivially_relocate_invalid_arg_type: Error <
1297112974
"first%select{||| and second}0 argument%select{|||s}0 to "
1297212975
"'__builtin_trivially_relocate' must be"

clang/lib/AST/ByteCode/InterpBuiltin.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -972,9 +972,10 @@ static bool interp__builtin_bswap(InterpState &S, CodePtr OpPC,
972972
const InterpFrame *Frame,
973973
const CallExpr *Call) {
974974
const APSInt &Val = popToAPSInt(S, Call->getArg(0));
975-
assert(Val.getActiveBits() <= 64);
976-
977-
pushInteger(S, Val.byteSwap(), Call->getType());
975+
if (Val.getBitWidth() == 8)
976+
pushInteger(S, Val, Call->getType());
977+
else
978+
pushInteger(S, Val.byteSwap(), Call->getType());
978979
return true;
979980
}
980981

@@ -3687,7 +3688,7 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
36873688
case Builtin::BI__builtin_elementwise_ctzg:
36883689
return interp__builtin_elementwise_countzeroes(S, OpPC, Frame, Call,
36893690
BuiltinID);
3690-
3691+
case Builtin::BI__builtin_bswapg:
36913692
case Builtin::BI__builtin_bswap16:
36923693
case Builtin::BI__builtin_bswap32:
36933694
case Builtin::BI__builtin_bswap64:

clang/lib/AST/ExprConstant.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15306,13 +15306,15 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
1530615306

1530715307
return Success(Val.reverseBits(), E);
1530815308
}
15309-
15309+
case Builtin::BI__builtin_bswapg:
1531015310
case Builtin::BI__builtin_bswap16:
1531115311
case Builtin::BI__builtin_bswap32:
1531215312
case Builtin::BI__builtin_bswap64: {
1531315313
APSInt Val;
1531415314
if (!EvaluateInteger(E->getArg(0), Val, Info))
1531515315
return false;
15316+
if (Val.getBitWidth() == 8)
15317+
return Success(Val, E);
1531615318

1531715319
return Success(Val.byteSwap(), E);
1531815320
}

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3618,6 +3618,19 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
36183618
Builder.CreateArithmeticFence(ArgValue, ConvertType(ArgType)));
36193619
return RValue::get(ArgValue);
36203620
}
3621+
case Builtin::BI__builtin_bswapg: {
3622+
Value *ArgValue = EmitScalarExpr(E->getArg(0));
3623+
llvm::IntegerType *IntTy = cast<llvm::IntegerType>(ArgValue->getType());
3624+
assert(IntTy && "LLVM's __builtin_bswapg only supports integer variants");
3625+
assert(((IntTy->getBitWidth() % 16 == 0 && IntTy->getBitWidth() != 0) ||
3626+
IntTy->getBitWidth() == 8) &&
3627+
"LLVM's __builtin_bswapg only supports integer variants that has a "
3628+
"multiple of 16 bits as well as a single byte");
3629+
if (IntTy->getBitWidth() == 8)
3630+
return RValue::get(ArgValue);
3631+
return RValue::get(
3632+
emitBuiltinWithOneOverloadedType<1>(*this, E, Intrinsic::bswap));
3633+
}
36213634
case Builtin::BI__builtin_bswap16:
36223635
case Builtin::BI__builtin_bswap32:
36233636
case Builtin::BI__builtin_bswap64:

clang/lib/Sema/SemaChecking.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2218,6 +2218,39 @@ static bool BuiltinCpu(Sema &S, const TargetInfo &TI, CallExpr *TheCall,
22182218
return false;
22192219
}
22202220

2221+
/// Checks that __builtin_bswapg was called with a single argument, which is an
2222+
/// unsigned integer, and overrides the return value type to the integer type.
2223+
static bool BuiltinBswapg(Sema &S, CallExpr *TheCall) {
2224+
if (S.checkArgCount(TheCall, 1))
2225+
return true;
2226+
ExprResult ArgRes = S.DefaultLvalueConversion(TheCall->getArg(0));
2227+
if (ArgRes.isInvalid())
2228+
return true;
2229+
2230+
Expr *Arg = ArgRes.get();
2231+
TheCall->setArg(0, Arg);
2232+
if (Arg->isTypeDependent())
2233+
return false;
2234+
2235+
QualType ArgTy = Arg->getType();
2236+
2237+
if (!ArgTy->isIntegerType()) {
2238+
S.Diag(Arg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
2239+
<< 1 << /*scalar=*/1 << /*unsigned integer=*/1 << /*floating point=*/0
2240+
<< ArgTy;
2241+
return true;
2242+
}
2243+
if (const auto *BT = dyn_cast<BitIntType>(ArgTy)) {
2244+
if (BT->getNumBits() % 16 != 0 && BT->getNumBits() != 8) {
2245+
S.Diag(Arg->getBeginLoc(), diag::err_bswapg_invalid_bit_width)
2246+
<< ArgTy << BT->getNumBits();
2247+
return true;
2248+
}
2249+
}
2250+
TheCall->setType(ArgTy);
2251+
return false;
2252+
}
2253+
22212254
/// Checks that __builtin_popcountg was called with a single argument, which is
22222255
/// an unsigned integer.
22232256
static bool BuiltinPopcountg(Sema &S, CallExpr *TheCall) {
@@ -3522,6 +3555,10 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
35223555
}
35233556
break;
35243557
}
3558+
case Builtin::BI__builtin_bswapg:
3559+
if (BuiltinBswapg(*this, TheCall))
3560+
return ExprError();
3561+
break;
35253562
case Builtin::BI__builtin_popcountg:
35263563
if (BuiltinPopcountg(*this, TheCall))
35273564
return ExprError();

clang/test/AST/ByteCode/builtin-functions.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,39 @@ namespace bswap {
837837
int h3 = __builtin_bswap16(0x1234) == 0x3412 ? 1 : f();
838838
int h4 = __builtin_bswap32(0x1234) == 0x34120000 ? 1 : f();
839839
int h5 = __builtin_bswap64(0x1234) == 0x3412000000000000 ? 1 : f();
840+
int h6 = __builtin_bswapg(0x12) == 0x12 ? 1 : f();
841+
int h7 = __builtin_bswapg(0x1234) == 0x3412 ? 1 : f();
842+
int h8 = __builtin_bswapg(0x00001234) == 0x34120000 ? 1 : f();
843+
int h9 = __builtin_bswapg(0x0000000000001234) == 0x3412000000000000 ? 1 : f();
844+
#ifndef __AVR__
845+
int h10 = __builtin_bswapg((_BitInt(8))0x12) == (_BitInt(8))0x12 ? 1 : f();
846+
int h11 = __builtin_bswapg((_BitInt(16))0x1234) == (_BitInt(16))0x3412 ? 1 : f();
847+
int h12 = __builtin_bswapg((_BitInt(32))0x00001234) == (_BitInt(32))0x34120000 ? 1 : f();
848+
int h13 = __builtin_bswapg((_BitInt(64))0x0000000000001234) == (_BitInt(64))0x3412000000000000 ? 1 : f();
849+
int h14 = __builtin_bswapg(~(_BitInt(128))0) == (~(_BitInt(128))0) ? 1 : f();
850+
int h15 = __builtin_bswapg((_BitInt(24))0x1234) == (_BitInt(24))0x3412 ? 1 : f();
851+
// expected-error@-1 {{_BitInt type '_BitInt(24)' (24 bits) must be a multiple of 16 bits for byte swapping}}
852+
// ref-error@-2 {{_BitInt type '_BitInt(24)' (24 bits) must be a multiple of 16 bits for byte swapping}}
853+
#endif
854+
855+
constexpr const int const_expr = 0x1234;
856+
857+
void test_constexpr_reference() {
858+
const int expr = 0x1234;
859+
const int& ref = expr; // #declare
860+
861+
constexpr const int& const_ref = const_expr;
862+
863+
constexpr auto result2 = __builtin_bswapg(ref);
864+
//expected-error@-1 {{constexpr variable 'result2' must be initialized by a constant expression}}
865+
//expected-note@-2 {{initializer of 'ref' is not a constant expression}}
866+
//expected-note@#declare {{declared here}}
867+
//ref-error@-4 {{constexpr variable 'result2' must be initialized by a constant expression}}
868+
//ref-note@-5 {{initializer of 'ref' is not a constant expression}}
869+
//ref-note@#declare {{declared here}}
870+
871+
constexpr auto result3 = __builtin_bswapg(const_ref);
872+
}
840873
}
841874

842875
#define CFSTR __builtin___CFStringMakeConstantString

clang/test/CodeGen/builtins.c

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,15 @@ int main(void) {
130130
P(object_size, (s0, 3));
131131

132132
// Whatever
133-
133+
P(bswapg, ((char)N));
134+
P(bswapg, ((short)N));
135+
P(bswapg, ((int)N));
136+
P(bswapg, ((unsigned long)N));
137+
P(bswapg, ((_BitInt(8))N));
138+
P(bswapg, ((_BitInt(16))N));
139+
P(bswapg, ((_BitInt(32))N));
140+
P(bswapg, ((_BitInt(64))N));
141+
P(bswapg, ((_BitInt(128))N));
134142
P(bswap16, (N));
135143
P(bswap32, (N));
136144
P(bswap64, (N));
@@ -1277,3 +1285,35 @@ void test_builtin_ctzg(unsigned char uc, unsigned short us, unsigned int ui,
12771285
}
12781286

12791287
#endif
1288+
1289+
// CHECK-LABEL: define{{.*}} void @test_builtin_bswapg
1290+
void test_builtin_bswapg(unsigned char uc, unsigned short us, unsigned int ui,
1291+
unsigned long ul, unsigned long long ull,
1292+
unsigned __int128 ui128, _BitInt(8) bi8,
1293+
_BitInt(16) bi16, _BitInt(32) bi32,
1294+
_BitInt(64) bi64, _BitInt(128) bi128) {
1295+
uc = __builtin_bswapg(uc);
1296+
// CHECK: %1 = load i8, ptr %uc.addr
1297+
// CHECK: store i8 %1, ptr %uc.addr
1298+
us = __builtin_bswapg(us);
1299+
// CHECK: call i16 @llvm.bswap.i16
1300+
ui = __builtin_bswapg(ui);
1301+
// CHECK: call i32 @llvm.bswap.i32
1302+
ul = __builtin_bswapg(ul);
1303+
// CHECK: call i64 @llvm.bswap.i64
1304+
ull = __builtin_bswapg(ull);
1305+
// CHECK: call i64 @llvm.bswap.i64
1306+
ui128 = __builtin_bswapg(ui128);
1307+
// CHECK: call i128 @llvm.bswap.i128
1308+
bi8 = __builtin_bswapg(bi8);
1309+
// CHECK: %17 = load i8, ptr %bi8.addr, align 1
1310+
// CHECK: store i8 %17, ptr %bi8.addr
1311+
bi16 = __builtin_bswapg(bi16);
1312+
// CHECK: call i16 @llvm.bswap.i16
1313+
bi32 = __builtin_bswapg(bi32);
1314+
// CHECK: call i32 @llvm.bswap.i32
1315+
bi64 = __builtin_bswapg(bi64);
1316+
// CHECK: call i64 @llvm.bswap.i64
1317+
bi128 = __builtin_bswapg(bi128);
1318+
// CHECK: call i128 @llvm.bswap.i128
1319+
}

clang/test/CodeGenCXX/builtins.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,59 @@ int structured_binding_size() {
8383
return __builtin_structured_binding_size(S2);
8484
// CHECK: ret i32 2
8585
}
86+
87+
void test_int_reference(int& a) {
88+
__builtin_bswapg(a);
89+
}
90+
// CHECK-LABEL: @_Z18test_int_referenceRi
91+
// CHECK: store ptr %a, ptr
92+
// CHECK: load ptr, ptr
93+
// CHECK: load i32, ptr
94+
// CHECK: call i32 @llvm.bswap.i32
95+
96+
void test_long_reference(long& a) {
97+
__builtin_bswapg(a);
98+
}
99+
// CHECK-LABEL: @_Z19test_long_referenceRl
100+
// CHECK: store ptr %a, ptr
101+
// CHECK: load ptr, ptr
102+
// CHECK: load i64, ptr
103+
// CHECK: call i64 @llvm.bswap.i64
104+
105+
void test_short_reference(short& a) {
106+
__builtin_bswapg(a);
107+
}
108+
// CHECK-LABEL: @_Z20test_short_referenceRs
109+
// CHECK: store ptr %a, ptr
110+
// CHECK: load ptr, ptr
111+
// CHECK: load i16, ptr
112+
// CHECK: call i16 @llvm.bswap.i16
113+
114+
void test_char_reference(char& a) {
115+
__builtin_bswapg(a);
116+
}
117+
// CHECK-LABEL: @_Z19test_char_referenceRc
118+
// CHECK: store ptr %a, ptr
119+
// CHECK: load ptr, ptr
120+
// CHECK-NOT: call i8 @llvm.bswap.i8
121+
// CHECK: ret void
122+
123+
void test_bitint() {
124+
_BitInt(8) a = 0x12;
125+
__builtin_bswapg(a);
126+
_BitInt(16) b = 0x1234;
127+
__builtin_bswapg(b);
128+
_BitInt(32) c = 0x00001234;
129+
__builtin_bswapg(c);
130+
_BitInt(64) d = 0x0000000000001234;
131+
__builtin_bswapg(d);
132+
_BitInt(128) e = ~(_BitInt(128))0;
133+
__builtin_bswapg(e);
134+
}
135+
// CHECK-LABEL: @_Z11test_bitintv
136+
// CHECK-NOT: call i8 @llvm.bswap.i8
137+
// CHECK: call i16 @llvm.bswap.i16
138+
// CHECK: call i32 @llvm.bswap.i32
139+
// CHECK: call i64 @llvm.bswap.i64
140+
// CHECK: call i128 @llvm.bswap.i128
141+
// CHECK: ret void

0 commit comments

Comments
 (0)