https://github.com/chaitanyav updated https://github.com/llvm/llvm-project/pull/160259
>From 3fae000a82a85a8d9a137b128c781cbc1ef76964 Mon Sep 17 00:00:00 2001 From: NagaChaitanya Vellanki <pnag...@protonmail.com> Date: Tue, 23 Sep 2025 02:17:49 -0700 Subject: [PATCH] [clang] Implement __builtin_stdc_rotate_{left,right} Resolves #122819 --- clang/docs/LanguageExtensions.rst | 31 ++++++++ clang/include/clang/Basic/Builtins.td | 12 +++ clang/lib/AST/ExprConstant.cpp | 2 + clang/lib/CodeGen/CGBuiltin.cpp | 8 ++ clang/lib/Sema/SemaChecking.cpp | 48 ++++++++++++ clang/test/CodeGen/builtin-rotate.c | 73 +++++++++++++++++++ clang/test/Sema/builtin-stdc-rotate.c | 30 ++++++++ .../SemaCXX/constexpr-builtin-stdc-rotate.cpp | 72 ++++++++++++++++++ 8 files changed, 276 insertions(+) create mode 100644 clang/test/Sema/builtin-stdc-rotate.c create mode 100644 clang/test/SemaCXX/constexpr-builtin-stdc-rotate.cpp diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index b503283559db4..5ef1176f60940 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -3680,6 +3680,37 @@ the arguments. Both arguments and the result have the bitwidth specified by the name of the builtin. These builtins can be used within constant expressions. +``__builtin_stdc_rotate_left`` and ``__builtin_stdc_rotate_right`` +------------------------------------------------------------------ + +**Syntax**: + +.. code-block:: c + + __builtin_stdc_rotate_left(value, count) + __builtin_stdc_rotate_right(value, count) + +**Description**: + +These builtins rotate the bits in ``value`` by ``count`` positions. The +``__builtin_stdc_rotate_left`` builtin rotates bits to the left, while +``__builtin_stdc_rotate_right`` rotates bits to the right. These builtins +accept any unsigned integer type, including ``_BitInt`` types. The rotation +count is treated as an unsigned value and taken modulo the bit-width of the +value being rotated. Negative rotation counts are converted to their unsigned +equivalent before the modulo operation. These builtins can be used within +constant expressions. + +**Example of use**: + +.. code-block:: c + + unsigned char rotated_left = __builtin_stdc_rotate_left((unsigned char)0xB1, 3); + unsigned int rotated_right = __builtin_stdc_rotate_right(0x12345678U, 8); + + unsigned _BitInt(128) big_val = 0x123456789ULL; + unsigned _BitInt(128) rotated = __builtin_stdc_rotate_left(big_val, 5); + ``__builtin_unreachable`` ------------------------- diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 9bc70ea5e5858..3550e14e6fd83 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -767,12 +767,24 @@ def RotateLeft : BitInt8_16_32_64BuiltinsTemplate, Builtin { let Prototype = "T(T, T)"; } +def StdcRotateLeft : Builtin { + let Spellings = ["__builtin_stdc_rotate_left"]; + let Attributes = [NoThrow, Const, Constexpr, CustomTypeChecking]; + let Prototype = "void(...)"; +} + def RotateRight : BitInt8_16_32_64BuiltinsTemplate, Builtin { let Spellings = ["__builtin_rotateright"]; let Attributes = [NoThrow, Const, Constexpr]; let Prototype = "T(T, T)"; } +def StdcRotateRight : Builtin { + let Spellings = ["__builtin_stdc_rotate_right"]; + let Attributes = [NoThrow, Const, Constexpr, CustomTypeChecking]; + let Prototype = "void(...)"; +} + // Random GCC builtins // FIXME: The builtins marked FunctionWithBuiltinPrefix below should be // merged with the library definitions. They are currently not because diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index a4fca1811c92b..974ad68434030 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -14139,6 +14139,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, case Builtin::BI__builtin_rotateleft16: case Builtin::BI__builtin_rotateleft32: case Builtin::BI__builtin_rotateleft64: + case Builtin::BI__builtin_stdc_rotate_left: case Builtin::BI_rotl8: // Microsoft variants of rotate right case Builtin::BI_rotl16: case Builtin::BI_rotl: @@ -14156,6 +14157,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, case Builtin::BI__builtin_rotateright16: case Builtin::BI__builtin_rotateright32: case Builtin::BI__builtin_rotateright64: + case Builtin::BI__builtin_stdc_rotate_right: case Builtin::BI_rotr8: // Microsoft variants of rotate right case Builtin::BI_rotr16: case Builtin::BI_rotr: diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 1cff1e0de71b7..c6ab721dfddb2 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -2494,6 +2494,12 @@ RValue CodeGenFunction::emitRotate(const CallExpr *E, bool IsRotateRight) { llvm::Type *Ty = Src->getType(); ShiftAmt = Builder.CreateIntCast(ShiftAmt, Ty, false); + unsigned BitWidth = Ty->getIntegerBitWidth(); + llvm::Value *BitWidthVal = ConstantInt::get(Ty, BitWidth); + llvm::Value *SignedRem = Builder.CreateSRem(ShiftAmt, BitWidthVal); + llvm::Value *WrappedShift = Builder.CreateAdd(SignedRem, BitWidthVal); + ShiftAmt = Builder.CreateURem(WrappedShift, BitWidthVal); + // Rotate is a special case of LLVM funnel shift - 1st 2 args are the same. unsigned IID = IsRotateRight ? Intrinsic::fshr : Intrinsic::fshl; Function *F = CGM.getIntrinsic(IID, Ty); @@ -3642,6 +3648,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, case Builtin::BI__builtin_rotateleft16: case Builtin::BI__builtin_rotateleft32: case Builtin::BI__builtin_rotateleft64: + case Builtin::BI__builtin_stdc_rotate_left: case Builtin::BI_rotl8: // Microsoft variants of rotate left case Builtin::BI_rotl16: case Builtin::BI_rotl: @@ -3653,6 +3660,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, case Builtin::BI__builtin_rotateright16: case Builtin::BI__builtin_rotateright32: case Builtin::BI__builtin_rotateright64: + case Builtin::BI__builtin_stdc_rotate_right: case Builtin::BI_rotr8: // Microsoft variants of rotate right case Builtin::BI_rotr16: case Builtin::BI_rotr: diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 0723516b38253..453e31ffe04ad 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -2267,6 +2267,48 @@ static bool BuiltinCountZeroBitsGeneric(Sema &S, CallExpr *TheCall) { return false; } +/// Checks that __builtin_stdc_rotate_{left,right} was called with two +/// arguments, that the first argument is an unsigned integer type, and that +/// the second argument is an integer type. +static bool BuiltinRotateGeneric(Sema &S, CallExpr *TheCall) { + if (S.checkArgCount(TheCall, 2)) + return true; + + ExprResult Arg0Res = S.DefaultLvalueConversion(TheCall->getArg(0)); + if (Arg0Res.isInvalid()) + return true; + + Expr *Arg0 = Arg0Res.get(); + TheCall->setArg(0, Arg0); + + QualType Arg0Ty = Arg0->getType(); + + if (!Arg0Ty->isUnsignedIntegerType()) { + S.Diag(Arg0->getBeginLoc(), diag::err_builtin_invalid_arg_type) + << 1 << /* scalar */ 1 << /* unsigned integer ty */ 3 << /* no fp */ 0 + << Arg0Ty; + return true; + } + + ExprResult Arg1Res = S.DefaultLvalueConversion(TheCall->getArg(1)); + if (Arg1Res.isInvalid()) + return true; + + Expr *Arg1 = Arg1Res.get(); + TheCall->setArg(1, Arg1); + + QualType Arg1Ty = Arg1->getType(); + + if (!Arg1Ty->isIntegerType()) { + S.Diag(Arg1->getBeginLoc(), diag::err_builtin_invalid_arg_type) + << 2 << /* scalar */ 1 << /* integer ty */ 2 << /* no fp */ 0 << Arg1Ty; + return true; + } + + TheCall->setType(Arg0Ty); + return false; +} + static bool CheckMaskedBuiltinArgs(Sema &S, Expr *MaskArg, Expr *PtrArg, unsigned Pos) { QualType MaskTy = MaskArg->getType(); @@ -3400,6 +3442,12 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, return ExprError(); break; + case Builtin::BI__builtin_stdc_rotate_left: + case Builtin::BI__builtin_stdc_rotate_right: + if (BuiltinRotateGeneric(*this, TheCall)) + return ExprError(); + break; + case Builtin::BI__builtin_allow_runtime_check: { Expr *Arg = TheCall->getArg(0); // Check if the argument is a string literal. diff --git a/clang/test/CodeGen/builtin-rotate.c b/clang/test/CodeGen/builtin-rotate.c index 8fc1701c6c9bb..5a3e5501e9c45 100644 --- a/clang/test/CodeGen/builtin-rotate.c +++ b/clang/test/CodeGen/builtin-rotate.c @@ -32,6 +32,43 @@ unsigned long long rotl64(unsigned long long x, long long y) { return __builtin_rotateleft64(x, y); } +// CHECK-LABEL: test_builtin_stdc_rotate_left +void test_builtin_stdc_rotate_left(unsigned char uc, unsigned short us, + unsigned int ui, unsigned long ul, + unsigned long long ull, unsigned __int128 ui128, + unsigned _BitInt(128) ubi128) { + +volatile unsigned char result_uc; +volatile unsigned int result_ui; +volatile unsigned short result_us; +volatile unsigned long result_ul; +volatile unsigned long long result_ull; +volatile unsigned __int128 result_ui128; +volatile unsigned _BitInt(128) result_ubi128; + + // CHECK: call i8 @llvm.fshl.i8(i8 %{{.*}}, i8 %{{.*}}, i8 3) + result_uc = __builtin_stdc_rotate_left(uc, 3); + + // CHECK: call i16 @llvm.fshl.i16(i16 %{{.*}}, i16 %{{.*}}, i16 5) + result_us = __builtin_stdc_rotate_left(us, 5); + + // CHECK: call i32 @llvm.fshl.i32(i32 %{{.*}}, i32 %{{.*}}, i32 8) + result_ui = __builtin_stdc_rotate_left(ui, 8); + + // CHECK: call i{{32|64}} @llvm.fshl.i{{32|64}}(i{{32|64}} %{{.*}}, i{{32|64}} %{{.*}}, i{{32|64}} 8) + result_ul = __builtin_stdc_rotate_left(ul, 8); + + // CHECK: call i64 @llvm.fshl.i64(i64 %{{.*}}, i64 %{{.*}}, i64 16) + result_ull = __builtin_stdc_rotate_left(ull, 16); + + // CHECK: call i128 @llvm.fshl.i128(i128 %{{.*}}, i128 %{{.*}}, i128 32) + result_ui128 = __builtin_stdc_rotate_left(ui128, 32); + + // CHECK: call i128 @llvm.fshl.i128(i128 %{{.*}}, i128 %{{.*}}, i128 64) + result_ubi128 = __builtin_stdc_rotate_left(ubi128, 64); + +} + char rotr8(char x, char y) { // CHECK-LABEL: rotr8 // CHECK: [[F:%.*]] = call i8 @llvm.fshr.i8(i8 [[X:%.*]], i8 [[X]], i8 [[Y:%.*]]) @@ -64,3 +101,39 @@ long long rotr64(long long x, unsigned long long y) { return __builtin_rotateright64(x, y); } +// CHECK-LABEL: test_builtin_stdc_rotate_right +void test_builtin_stdc_rotate_right(unsigned char uc, unsigned short us, + unsigned int ui, unsigned long ul, + unsigned long long ull, unsigned __int128 ui128, + unsigned _BitInt(128) ubi128) { + + volatile unsigned char result_uc; + volatile unsigned int result_ui; + volatile unsigned short result_us; + volatile unsigned long result_ul; + volatile unsigned long long result_ull; + volatile unsigned __int128 result_ui128; + volatile unsigned _BitInt(128) result_ubi128; + + // CHECK: call i8 @llvm.fshr.i8(i8 %{{.*}}, i8 %{{.*}}, i8 3) + result_uc = __builtin_stdc_rotate_right(uc, 3); + + // CHECK: call i16 @llvm.fshr.i16(i16 %{{.*}}, i16 %{{.*}}, i16 5) + result_us = __builtin_stdc_rotate_right(us, 5); + + // CHECK: call i32 @llvm.fshr.i32(i32 %{{.*}}, i32 %{{.*}}, i32 8) + result_ui = __builtin_stdc_rotate_right(ui, 8); + + // CHECK: call i{{32|64}} @llvm.fshr.i{{32|64}}(i{{32|64}} %{{.*}}, i{{32|64}} %{{.*}}, i{{32|64}} 8) + result_ul = __builtin_stdc_rotate_right(ul, 8); + + // CHECK: call i64 @llvm.fshr.i64(i64 %{{.*}}, i64 %{{.*}}, i64 16) + result_ull = __builtin_stdc_rotate_right(ull, 16); + + // CHECK: call i128 @llvm.fshr.i128(i128 %{{.*}}, i128 %{{.*}}, i128 32) + result_ui128 = __builtin_stdc_rotate_right(ui128, 32); + + // CHECK: call i128 @llvm.fshr.i128(i128 %{{.*}}, i128 %{{.*}}, i128 64) + result_ubi128 = __builtin_stdc_rotate_right(ubi128, 64); + +} diff --git a/clang/test/Sema/builtin-stdc-rotate.c b/clang/test/Sema/builtin-stdc-rotate.c new file mode 100644 index 0000000000000..1d3d53ef07e71 --- /dev/null +++ b/clang/test/Sema/builtin-stdc-rotate.c @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify %s +// expected-no-diagnostics + +_Static_assert(__builtin_stdc_rotate_left((unsigned char)0xB1, 3) == 0x8D, ""); +_Static_assert(__builtin_stdc_rotate_right((unsigned char)0xB1, 3) == 0x36, ""); +_Static_assert(__builtin_stdc_rotate_left((unsigned short)0x1234, 4) == 0x2341, ""); +_Static_assert(__builtin_stdc_rotate_right((unsigned short)0x1234, 4) == 0x4123, ""); +_Static_assert(__builtin_stdc_rotate_left(0x12345678U, 8) == 0x34567812U, ""); +_Static_assert(__builtin_stdc_rotate_right(0x12345678U, 8) == 0x78123456U, ""); +_Static_assert(__builtin_stdc_rotate_left(0x123456789ABCDEF0ULL, 16) == 0x56789ABCDEF01234ULL, ""); +_Static_assert(__builtin_stdc_rotate_right(0x123456789ABCDEF0ULL, 16) == 0xDEF0123456789ABCULL, ""); + +_Static_assert(__builtin_stdc_rotate_left((unsigned __int128)1, 127) == ((unsigned __int128)1 << 127), ""); +_Static_assert(__builtin_stdc_rotate_right(((unsigned __int128)1 << 127), 127) == (unsigned __int128)1, ""); + +_Static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(37))1, 36) == ((unsigned _BitInt(37))1 << 36), ""); +_Static_assert(__builtin_stdc_rotate_right((unsigned _BitInt(37))1, 36) == ((unsigned _BitInt(37))1 << 1), ""); +_Static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(128))1, 1) == ((unsigned _BitInt(128))2), ""); + +_Static_assert(__builtin_stdc_rotate_left((unsigned char)0x80, 9) == __builtin_stdc_rotate_left((unsigned char)0x80, 1), ""); +_Static_assert(__builtin_stdc_rotate_right((unsigned short)0x8000, 17) == __builtin_stdc_rotate_right((unsigned short)0x8000, 1), ""); +_Static_assert(__builtin_stdc_rotate_left(0x80000000U, 33) == __builtin_stdc_rotate_left(0x80000000U, 1), ""); + +_Static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(37))0x1000000000ULL, 40) == __builtin_stdc_rotate_left((unsigned _BitInt(37))0x1000000000ULL, 3), ""); +_Static_assert(__builtin_stdc_rotate_right((unsigned _BitInt(37))0x1000000000ULL, 74) == 0x1000000000ULL, ""); + +_Static_assert(__builtin_stdc_rotate_left((unsigned char)0x80, -1) == __builtin_stdc_rotate_left((unsigned char)0x80, 7), ""); +_Static_assert(__builtin_stdc_rotate_right((unsigned short)0x8000, -1) == __builtin_stdc_rotate_right((unsigned short)0x8000, 15), ""); +_Static_assert(__builtin_stdc_rotate_left(0x80000000U, -5) == __builtin_stdc_rotate_left(0x80000000U, 27), ""); +_Static_assert(__builtin_stdc_rotate_right((unsigned _BitInt(37))0x1000000000ULL, -10) == 0x4ULL, ""); diff --git a/clang/test/SemaCXX/constexpr-builtin-stdc-rotate.cpp b/clang/test/SemaCXX/constexpr-builtin-stdc-rotate.cpp new file mode 100644 index 0000000000000..1d3672e08d8c2 --- /dev/null +++ b/clang/test/SemaCXX/constexpr-builtin-stdc-rotate.cpp @@ -0,0 +1,72 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++14 -fsyntax-only -verify %s +// expected-no-diagnostics + +namespace test_constexpr_stdc_rotate { + +static_assert(__builtin_stdc_rotate_left((unsigned char)0b10110001, 3) == (unsigned char)0b10001101, ""); +static_assert(__builtin_stdc_rotate_left((unsigned short)0x1234, 4) == (unsigned short)0x2341, ""); +static_assert(__builtin_stdc_rotate_left(0x12345678U, 8) == 0x34567812U, ""); +static_assert(__builtin_stdc_rotate_left(0x123456789ABCDEF0ULL, 16) == 0x56789ABCDEF01234ULL, ""); +static_assert(__builtin_stdc_rotate_right((unsigned char)0b10110001, 3) == (unsigned char)0b00110110, ""); +static_assert(__builtin_stdc_rotate_right(0x12345678U, 8) == 0x78123456U, ""); +static_assert(__builtin_stdc_rotate_right(0x123456789ABCDEF0ULL, 16) == 0xDEF0123456789ABCULL, ""); +static_assert(__builtin_stdc_rotate_left(0x12345678U, 0) == 0x12345678U, ""); +static_assert(__builtin_stdc_rotate_left(0x12345678U, 32) == 0x12345678U, ""); +static_assert(__builtin_stdc_rotate_left(0x80000000U, 1) == 0x00000001U, ""); +static_assert(__builtin_stdc_rotate_right(__builtin_stdc_rotate_left(0x12345678U, 8), 8) == 0x12345678U, ""); +static_assert(__builtin_stdc_rotate_left(0x12345678U, 40) == __builtin_stdc_rotate_left(0x12345678U, 8), ""); +static_assert(__builtin_stdc_rotate_left(0x00000000U, 7) == 0x00000000U, ""); +static_assert(__builtin_stdc_rotate_left((unsigned char)0x01, 2) == (unsigned char)0x04, ""); +static_assert(__builtin_stdc_rotate_left((unsigned char)0xAA, 1) == (unsigned char)0x55, ""); +static_assert(__builtin_stdc_rotate_left((unsigned char)0x12, 4) == (unsigned char)0x21, ""); +static_assert(__builtin_stdc_rotate_left((unsigned short)0x1234, 4) == (unsigned short)0x2341, ""); +static_assert(__builtin_stdc_rotate_left(0x12345678U, 4) == 0x23456781U, ""); + +namespace test_int128 { + +static_assert(__builtin_stdc_rotate_left((unsigned __int128)1, 127) == (unsigned __int128)1 << 127, ""); + +constexpr unsigned __int128 test_pattern = 0x123456789ABCDEF0ULL; +static_assert(__builtin_stdc_rotate_left(test_pattern, 1) == test_pattern << 1, ""); + +} // namespace test_int128 + +namespace test_bitint { + +static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(37))1, 36) == ((unsigned _BitInt(37))1 << 36), ""); + +constexpr unsigned _BitInt(128) bi128_pattern = 0x123456789ABCDEF0ULL; +static_assert(__builtin_stdc_rotate_left(bi128_pattern, 1) == bi128_pattern << 1, ""); + +static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(3))0b101, 1) == (unsigned _BitInt(3))0b011, ""); + +} // namespace test_bitint + +namespace test_modulo_behavior { + +static_assert(__builtin_stdc_rotate_left((unsigned char)0x80, 9) == __builtin_stdc_rotate_left((unsigned char)0x80, 1), ""); +static_assert(__builtin_stdc_rotate_right((unsigned short)0x8000, 17) == __builtin_stdc_rotate_right((unsigned short)0x8000, 1), ""); +static_assert(__builtin_stdc_rotate_left(0x80000000U, 33) == __builtin_stdc_rotate_left(0x80000000U, 1), ""); +static_assert(__builtin_stdc_rotate_right(0x8000000000000000ULL, 65) == __builtin_stdc_rotate_right(0x8000000000000000ULL, 1), ""); + +static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(37))0x1000000000ULL, 40) == __builtin_stdc_rotate_left((unsigned _BitInt(37))0x1000000000ULL, 3), ""); +static_assert(__builtin_stdc_rotate_right((unsigned _BitInt(37))0x1000000000ULL, 74) == (unsigned _BitInt(37))0x1000000000ULL, ""); + +static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(37))0x123456789ULL, 0) == (unsigned _BitInt(37))0x123456789ULL, ""); +static_assert(__builtin_stdc_rotate_right((unsigned _BitInt(37))0x123456789ULL, 37) == (unsigned _BitInt(37))0x123456789ULL, ""); + +} // namespace test_modulo_behavior + +namespace test_negative_counts { + +static_assert(__builtin_stdc_rotate_left((unsigned char)0x80, -1) == __builtin_stdc_rotate_left((unsigned char)0x80, 7), ""); +static_assert(__builtin_stdc_rotate_right((unsigned short)0x8000, -1) == __builtin_stdc_rotate_right((unsigned short)0x8000, 15), ""); +static_assert(__builtin_stdc_rotate_left(0x80000000U, -5) == __builtin_stdc_rotate_left(0x80000000U, 27), ""); +static_assert(__builtin_stdc_rotate_right(0x8000000000000000ULL, -8) == __builtin_stdc_rotate_right(0x8000000000000000ULL, 56), ""); + +static_assert(__builtin_stdc_rotate_right((unsigned _BitInt(37))0x1000000000ULL, -10) == (unsigned _BitInt(37))0x4ULL, ""); +static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(37))0x800000000ULL, -3) == (unsigned _BitInt(37))0x4ULL, ""); + +} // namespace test_negative_counts + +} // namespace test_constexpr_stdc_rotate _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits