-
Notifications
You must be signed in to change notification settings - Fork 15k
[Clang] Add __builtin_bswapg #162433
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
base: main
Are you sure you want to change the base?
[Clang] Add __builtin_bswapg #162433
Changes from 4 commits
92466f3
d4ea868
fa016a1
c0d0f3c
1e9895c
bca24f2
ba83b56
a2a6191
dfd3385
6afe334
ed8c33a
6333668
2b4e009
42301e8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3622,6 +3622,15 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, | |
| Builder.CreateArithmeticFence(ArgValue, ConvertType(ArgType))); | ||
| return RValue::get(ArgValue); | ||
| } | ||
| case Builtin::BI__builtin_bswapg: { | ||
| Value *ArgValue = EmitScalarExpr(E->getArg(0)); | ||
| llvm::IntegerType *IntTy = cast<llvm::IntegerType>(ArgValue->getType()); | ||
| assert(IntTy && "LLVM's __builtin_bswapg only supports integer variants"); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should probably have a getBitWidth() % 8 == 0 and getBitWidth() > 0, as a defense for future support of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As @philnik777 described in #160266, bswapg should work on any integral types that have a multiple of 16 bits as well as a single byte. Thus, the condition should be (getBitWidth() % 16 == 0 and getBitWidth() > 0) or getBitWidth() == 8? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think mod 16 is right either, swapping Would it be more reasonable to simply say it must be a power of 2 number of bytes (N=8*2^^N for some positive N)? @philnik777 does that sound reasonable? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It sounds reasonable, but the llvm intrinsic has the constraint on 16 bits. I don't think we should block introducing the builtin on extending the llvm intrinsic. |
||
| if (IntTy->getBitWidth() == 8) | ||
| return RValue::get(ArgValue); | ||
| return RValue::get( | ||
| emitBuiltinWithOneOverloadedType<1>(*this, E, Intrinsic::bswap)); | ||
| } | ||
| case Builtin::BI__builtin_bswap16: | ||
| case Builtin::BI__builtin_bswap32: | ||
| case Builtin::BI__builtin_bswap64: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,121 @@ | ||
| // RUN: %clang_cc1 -fsyntax-only -verify %s | ||
clingfei marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| // RUN: %clang_cc1 -fsyntax-only -verify -fexperimental-new-constant-interpreter %s | ||
| // expected-no-diagnostics | ||
| template <class A, class B> | ||
| static constexpr bool is_same_type = false; | ||
|
|
||
| template <class A> | ||
| static constexpr bool is_same_type<A, A> = true; | ||
|
|
||
| void test_basic_type_checks() { | ||
| static_assert(is_same_type<char, decltype(__builtin_bswapg((char)0))>, ""); | ||
clingfei marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| static_assert(is_same_type<unsigned char, decltype(__builtin_bswapg((unsigned char)0))>, ""); | ||
| static_assert(is_same_type<short, decltype(__builtin_bswapg((short)0))>, ""); | ||
| static_assert(is_same_type<unsigned short, decltype(__builtin_bswapg((unsigned short)0))>, ""); | ||
| static_assert(is_same_type<int, decltype(__builtin_bswapg((int)0))>, ""); | ||
| static_assert(is_same_type<unsigned int, decltype(__builtin_bswapg((unsigned int)0))>, ""); | ||
| static_assert(is_same_type<long, decltype(__builtin_bswapg((long)0))>, ""); | ||
| static_assert(is_same_type<unsigned long, decltype(__builtin_bswapg((unsigned long)0))>, ""); | ||
| } | ||
|
|
||
| template<typename T> | ||
| void test_template_type_check() { | ||
| static_assert(is_same_type<T, decltype(__builtin_bswapg(T{}))>, | ||
| "bswapg should return the same type as its argument"); | ||
| constexpr T zero{}; | ||
| constexpr T max = ~T{}; | ||
| constexpr T one = T{1}; | ||
|
|
||
| static_assert(is_same_type<T, decltype(__builtin_bswapg(zero))>, ""); | ||
| static_assert(is_same_type<T, decltype(__builtin_bswapg(max))>, ""); | ||
| static_assert(is_same_type<T, decltype(__builtin_bswapg(one))>, ""); | ||
| } | ||
| template void test_template_type_check<char>(); | ||
| template void test_template_type_check<unsigned char>(); | ||
| template void test_template_type_check<short>(); | ||
| template void test_template_type_check<unsigned short>(); | ||
| template void test_template_type_check<int>(); | ||
| template void test_template_type_check<unsigned int>(); | ||
| template void test_template_type_check<long>(); | ||
| template void test_template_type_check<unsigned long>(); | ||
|
|
||
| void test_lambda_type_checks() { | ||
| auto lambda = [](auto x) { | ||
| static_assert(is_same_type<decltype(x), decltype(__builtin_bswapg(x))>, | ||
| "bswapg in lambda should preserve type"); | ||
| return __builtin_bswapg(x); | ||
| }; | ||
| auto result_long = lambda(42UL); | ||
| static_assert(is_same_type<unsigned long, decltype(result_long)>, ""); | ||
|
|
||
| auto result_int = lambda(42); | ||
| static_assert(is_same_type<int, decltype(result_int)>, ""); | ||
|
|
||
| auto result_short = lambda(static_cast<short>(42)); | ||
| static_assert(is_same_type<short, decltype(result_short)>, ""); | ||
|
|
||
| auto result_char = lambda(static_cast<char>(42)); | ||
| static_assert(is_same_type<char, decltype(result_char)>, ""); | ||
| } | ||
|
|
||
| auto test_auto_return_type_long(long x) { | ||
| auto result = __builtin_bswapg(x); | ||
| static_assert(is_same_type<long, decltype(result)>, ""); | ||
| return result; | ||
| } | ||
clingfei marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| auto test_auto_return_type_int(int x) { | ||
| auto result = __builtin_bswapg(x); | ||
| static_assert(is_same_type<int, decltype(result)>, ""); | ||
| return result; | ||
| } | ||
|
|
||
| auto test_auto_return_type_short(short x) { | ||
| auto result = __builtin_bswapg(x); | ||
| static_assert(is_same_type<short, decltype(result)>, ""); | ||
| return result; | ||
| } | ||
|
|
||
| auto test_auto_return_type_char(char x) { | ||
| auto result = __builtin_bswapg(x); | ||
| static_assert(is_same_type<char, decltype(result)>, ""); | ||
| return result; | ||
| } | ||
|
|
||
| void test_auto_return_type() { | ||
| test_auto_return_type_long(42); | ||
| test_auto_return_type_int(42); | ||
| test_auto_return_type_short(42); | ||
| test_auto_return_type_char(42); | ||
| } | ||
|
|
||
| decltype(auto) test_decltype_auto(int x) { | ||
| return __builtin_bswapg(x); | ||
| } | ||
|
|
||
| void test_decltype_auto_check() { | ||
| int x = 42; | ||
| auto result = test_decltype_auto(x); | ||
| static_assert(is_same_type<int, decltype(result)>, ""); | ||
| } | ||
|
|
||
| template<auto Value> | ||
| struct ValueTemplateTypeTest { | ||
| using value_type = decltype(Value); | ||
| using result_type = decltype(__builtin_bswapg(Value)); | ||
|
|
||
| static constexpr bool type_matches = is_same_type<value_type, result_type>; | ||
| static_assert(type_matches, "Value template bswapg should preserve type"); | ||
|
|
||
| static constexpr auto swapped_value = __builtin_bswapg(Value); | ||
| }; | ||
|
|
||
| template<auto... Values> | ||
| void test_template_pack_types() { | ||
| static_assert((is_same_type<decltype(Values), decltype(__builtin_bswapg(Values))> && ...), "All pack elements should preserve type"); | ||
| } | ||
|
|
||
| template struct ValueTemplateTypeTest<0x1234>; | ||
| template struct ValueTemplateTypeTest<0x12345678UL>; | ||
| template struct ValueTemplateTypeTest<(short)0x1234>; | ||
| template struct ValueTemplateTypeTest<(char)0x12>; | ||
clingfei marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
clingfei marked this conversation as resolved.
Show resolved
Hide resolved
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this match the other generic ones?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, this will cause a build error:
llvm-project/clang/include/clang/Basic/Builtins.td:761:7: error: Not a template
let Prototype = "T(T)";
^
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC
T(T)assumes an explicit set of type mappings. In principle I think this could be labeled asT(T)with the customtypechecking flag. That would be more correct thanint(..), and the Sema work is already doing the custom checking.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder whether we should have a
CustomTypeCheckingBuiltinthat doesn't ask for a prototype. I don't think that would work without any modification to Clang though. Some builtins might assume the return type is correct and won't set it again during custom type checking.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be nice if we could automate type checking for truly template/generic builtins. I feel like builtins using CustomTypeChecking should have an accurate prototype, but I agree that no prototype is better than a fake one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, some more infrastructure for this would be nice, especially since we seem to go in the direction of "all the builtins are generic".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In fact, at first I attempted to use T(T) with an explicit set of type mappings, but the automatically generated builtin functions are bswapg16, bswapg32, etc., rather than a single generic bswapg. I'm not sure if I can use T(T) without making more modifications to clang. It would be nice if that worked.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry, missed this reply - yeah it exists to automate the bindings for multiple types rather than just specifying a generic function :-/