[clang] [clang] Implement __builtin_stdc_rotate_left, __builtin_stdc_rotate_right (PR #160259)
NagaChaitanya Vellanki via cfe-commits
cfe-commits at lists.llvm.org
Wed Nov 26 18:00:04 PST 2025
https://github.com/chaitanyav updated https://github.com/llvm/llvm-project/pull/160259
>From c66a213692435152de5d8b8aa664cc6578f9a286 Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Tue, 23 Sep 2025 02:17:49 -0700
Subject: [PATCH 1/5] [clang] Implement __builtin_stdc_rotate_left and
__builtin_stdc_rotate_right
This patch adds type-generic rotate builtins that accept any unsigned integer
type. These builtins provide:
- Support for all unsigned integer types, including _BitInt
- Constexpr evaluation capability
- Automatic normalization of rotation counts modulo the bit-width
- Proper handling of negative rotation counts (converted to equivalent
positive rotations in the opposite direction)
- Implicit conversion support for both arguments, allowing bool, float, and
types with conversion operators (not limited to record types)
The builtins follow C23 naming conventions.
Resolves #122819
---
clang/docs/LanguageExtensions.rst | 36 +++
clang/docs/ReleaseNotes.rst | 5 +
clang/include/clang/Basic/Builtins.td | 12 +
clang/lib/AST/ExprConstant.cpp | 60 +++--
clang/lib/CodeGen/CGBuiltin.cpp | 35 ++-
clang/lib/Sema/SemaChecking.cpp | 67 +++++
clang/test/CodeGen/builtin-rotate.c | 238 ++++++++++++++++++
clang/test/Sema/builtin-stdc-rotate.c | 126 ++++++++++
.../SemaCXX/constexpr-builtin-stdc-rotate.cpp | 199 +++++++++++++++
9 files changed, 762 insertions(+), 16 deletions(-)
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 80cea2166bc83..03ef8fff7e0d9 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -3695,6 +3695,42 @@ 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
+
+ T __builtin_stdc_rotate_left(T value, count)
+ T __builtin_stdc_rotate_right(T value, count)
+
+where ``T`` is any unsigned integer type and ``count`` is any integer type.
+
+**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. The first
+argument (``value``) must be an unsigned integer type, including ``_BitInt`` types.
+The second argument (``count``) can be any integer type. The rotation count is
+normalized modulo the bit-width of the value being rotated, with negative
+counts converted to equivalent positive rotations (e.g., rotating left
+by ``-1`` is equivalent to rotating left by ``BitWidth-1``). 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 char neg_rotate = __builtin_stdc_rotate_left((unsigned char)0xB1, -1);
+
+ unsigned _BitInt(20) value = 0xABCDE;
+ unsigned _BitInt(20) rotated = __builtin_stdc_rotate_left(value, 5);
+
``__builtin_unreachable``
-------------------------
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 4cfcd37df1866..104edb48f73ad 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -325,6 +325,11 @@ Non-comprehensive list of changes in this release
- Clang now rejects the invalid use of ``constexpr`` with ``auto`` and an explicit type in C. (#GH163090)
+- Added ``__builtin_stdc_rotate_left`` and ``__builtin_stdc_rotate_right``
+ for bit rotation of unsigned integers including ``_BitInt`` types. Rotation
+ counts are normalized modulo the bit-width and support negative values.
+ Usable in constant expressions.
+
New Compiler Flags
------------------
- New option ``-fno-sanitize-debug-trap-reasons`` added to disable emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``).
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 6d6104a3ddb8d..eec82e0c4a855 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -773,12 +773,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 88d7c79d3b99e..4f5172d199f77 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -15876,23 +15876,17 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
case Builtin::BI__builtin_rotateleft16:
case Builtin::BI__builtin_rotateleft32:
case Builtin::BI__builtin_rotateleft64:
- case Builtin::BI_rotl8: // Microsoft variants of rotate right
- case Builtin::BI_rotl16:
- case Builtin::BI_rotl:
- case Builtin::BI_lrotl:
- case Builtin::BI_rotl64: {
- APSInt Val, Amt;
- if (!EvaluateInteger(E->getArg(0), Val, Info) ||
- !EvaluateInteger(E->getArg(1), Amt, Info))
- return false;
-
- return Success(Val.rotl(Amt), E);
- }
-
case Builtin::BI__builtin_rotateright8:
case Builtin::BI__builtin_rotateright16:
case Builtin::BI__builtin_rotateright32:
case Builtin::BI__builtin_rotateright64:
+ case Builtin::BI__builtin_stdc_rotate_left:
+ case Builtin::BI__builtin_stdc_rotate_right:
+ case Builtin::BI_rotl8: // Microsoft variants of rotate left
+ case Builtin::BI_rotl16:
+ case Builtin::BI_rotl:
+ case Builtin::BI_lrotl:
+ case Builtin::BI_rotl64:
case Builtin::BI_rotr8: // Microsoft variants of rotate right
case Builtin::BI_rotr16:
case Builtin::BI_rotr:
@@ -15903,7 +15897,45 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
!EvaluateInteger(E->getArg(1), Amt, Info))
return false;
- return Success(Val.rotr(Amt), E);
+ // Normalize shift amount to [0, BitWidth) range to match runtime behavior
+ unsigned BitWidth = Val.getBitWidth();
+ unsigned AmtBitWidth = Amt.getBitWidth();
+ if (BitWidth == 1) {
+ // if BitWidth is 1, Amt % Divisor = 0
+ // No need for rotation
+ Amt = APSInt(APInt(AmtBitWidth, 0), Amt.isUnsigned());
+ } else {
+ APSInt Divisor;
+ if (AmtBitWidth > BitWidth) {
+ Divisor = APSInt(llvm::APInt(AmtBitWidth, BitWidth), Amt.isUnsigned());
+ } else {
+ Divisor = APSInt(llvm::APInt(BitWidth, BitWidth), Amt.isUnsigned());
+ if (AmtBitWidth < BitWidth) {
+ Amt = Amt.extend(BitWidth);
+ }
+ }
+
+ Amt = Amt % Divisor;
+ if (Amt.isNegative()) {
+ Amt += Divisor;
+ }
+ }
+
+ switch (BuiltinOp) {
+ case Builtin::BI__builtin_rotateright8:
+ 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:
+ case Builtin::BI_rotr16:
+ case Builtin::BI_rotr:
+ case Builtin::BI_lrotr:
+ case Builtin::BI_rotr64:
+ return Success(APSInt(Val.rotr(Amt.getZExtValue()), Val.isUnsigned()), E);
+ default:
+ return Success(APSInt(Val.rotl(Amt.getZExtValue()), Val.isUnsigned()), E);
+ }
}
case Builtin::BI__builtin_elementwise_add_sat: {
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index da72a43643a54..146f07321a2ff 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -2488,12 +2488,41 @@ RValue CodeGenFunction::emitRotate(const CallExpr *E, bool IsRotateRight) {
// The builtin's shift arg may have a different type than the source arg and
// result, but the LLVM intrinsic uses the same type for all values.
llvm::Type *Ty = Src->getType();
- ShiftAmt = Builder.CreateIntCast(ShiftAmt, Ty, false);
+ llvm::Type *ShiftTy = ShiftAmt->getType();
+
+ unsigned BitWidth = Ty->getIntegerBitWidth();
+
+ if (!llvm::isPowerOf2_32(BitWidth) &&
+ E->getArg(1)->getType()->isSignedIntegerType()) {
+ // converting BitWidth to the type of ShiftAmt
+ llvm::Value *BitWidthInShiftTy = ConstantInt::get(ShiftTy, BitWidth);
+
+ // if ShiftAmt is negative, normalize ShiftAmt to [0, BitWidth - 1]
+ llvm::Value *RemResult = Builder.CreateSRem(ShiftAmt, BitWidthInShiftTy);
+ llvm::Value *PositiveShift =
+ Builder.CreateAdd(RemResult, BitWidthInShiftTy);
+
+ llvm::Value *Zero = ConstantInt::get(ShiftTy, 0);
+ llvm::Value *IsRemNegative = Builder.CreateICmpSLT(RemResult, Zero);
+
+ ShiftAmt = Builder.CreateSelect(IsRemNegative, PositiveShift, RemResult);
+ }
+
+ unsigned ShiftAmtBitWidth = ShiftTy->getIntegerBitWidth();
+ if (ShiftAmtBitWidth > BitWidth) {
+ llvm::Value *BitWidthInShiftTy = ConstantInt::get(ShiftTy, BitWidth);
+ ShiftAmt = Builder.CreateURem(ShiftAmt, BitWidthInShiftTy);
+ ShiftAmt = Builder.CreateIntCast(ShiftAmt, Ty, false);
+ } else {
+ llvm::Value *BitWidthInShiftTy = ConstantInt::get(Ty, BitWidth);
+ ShiftAmt = Builder.CreateIntCast(ShiftAmt, Ty, false);
+ ShiftAmt = Builder.CreateURem(ShiftAmt, BitWidthInShiftTy);
+ }
// 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);
- return RValue::get(Builder.CreateCall(F, { Src, Src, ShiftAmt }));
+ return RValue::get(Builder.CreateCall(F, {Src, Src, ShiftAmt}));
}
// Map math builtins for long-double to f128 version.
@@ -3612,6 +3641,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:
@@ -3623,6 +3653,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 0ffb4854ba86d..2be6c19cb186f 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -2321,6 +2321,67 @@ 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()) {
+ ExprResult ConvArg0Res = S.PerformImplicitConversion(
+ TheCall->getArg(0), S.Context.UnsignedIntTy, AssignmentAction::Passing);
+ if (ConvArg0Res.isUsable()) {
+ Arg0 = ConvArg0Res.get();
+ Arg0Ty = Arg0->getType();
+ TheCall->setArg(0, Arg0);
+ }
+ }
+
+ 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()) {
+ ExprResult ConvArg1Res = S.PerformImplicitConversion(
+ TheCall->getArg(1), S.Context.IntTy, AssignmentAction::Passing);
+ if (ConvArg1Res.isUsable()) {
+ Arg1 = ConvArg1Res.get();
+ Arg1Ty = Arg1->getType();
+ TheCall->setArg(1, Arg1);
+ }
+ }
+
+ 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, bool AllowConst,
bool AllowAS) {
@@ -3572,6 +3633,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..ac47bf0082023 100644
--- a/clang/test/CodeGen/builtin-rotate.c
+++ b/clang/test/CodeGen/builtin-rotate.c
@@ -1,5 +1,7 @@
// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s
+#include<stdint.h>
+
unsigned char rotl8(unsigned char x, unsigned char y) {
// CHECK-LABEL: rotl8
// CHECK: [[F:%.*]] = call i8 @llvm.fshl.i8(i8 [[X:%.*]], i8 [[X]], i8 [[Y:%.*]])
@@ -64,3 +66,239 @@ long long rotr64(long long x, unsigned long long y) {
return __builtin_rotateright64(x, y);
}
+// CHECK-LABEL: test_builtin_stdc_rotate_left
+// CHECK: call i8 @llvm.fshl.i8(i8 %{{.*}}, i8 %{{.*}}, i8 3)
+// CHECK: call i16 @llvm.fshl.i16(i16 %{{.*}}, i16 %{{.*}}, i16 5)
+// CHECK: call i32 @llvm.fshl.i32(i32 %{{.*}}, i32 %{{.*}}, i32 8)
+// CHECK: call i64 @llvm.fshl.i64(i64 %{{.*}}, i64 %{{.*}}, i64 8)
+// CHECK: call i64 @llvm.fshl.i64(i64 %{{.*}}, i64 %{{.*}}, i64 16)
+// CHECK: call i128 @llvm.fshl.i128(i128 %{{.*}}, i128 %{{.*}}, i128 32)
+// CHECK: call i8 @llvm.fshl.i8(i8 %{{.*}}, i8 %{{.*}}, i8 7)
+// CHECK: call i16 @llvm.fshl.i16(i16 %{{.*}}, i16 %{{.*}}, i16 11)
+// CHECK: call i32 @llvm.fshl.i32(i32 %{{.*}}, i32 %{{.*}}, i32 29)
+// CHECK: call i8 @llvm.fshl.i8(i8 %{{.*}}, i8 %{{.*}}, i8 0)
+// CHECK: call i32 @llvm.fshl.i32(i32 %{{.*}}, i32 %{{.*}}, i32 27)
+// CHECK: call i32 @llvm.fshl.i32(i32 42, i32 42, i32 %{{.*}})
+// CHECK: call i9 @llvm.fshl.i9(i9 %{{.*}}, i9 %{{.*}}, i9 1)
+// CHECK: call i37 @llvm.fshl.i37(i37 %{{.*}}, i37 %{{.*}}, i37 36)
+// CHECK: call i9 @llvm.fshl.i9(i9 %{{.*}}, i9 %{{.*}}, i9 8)
+// CHECK: call i37 @llvm.fshl.i37(i37 %{{.*}}, i37 %{{.*}}, i37 32)
+// CHECK: call i10 @llvm.fshl.i10(i10 %{{.*}}, i10 %{{.*}}, i10 3)
+// CHECK: call i16 @llvm.fshl.i16(i16 %{{.*}}, i16 %{{.*}}, i16 8)
+// CHECK: call i24 @llvm.fshl.i24(i24 %{{.*}}, i24 %{{.*}}, i24 12)
+// CHECK: call i48 @llvm.fshl.i48(i48 %{{.*}}, i48 %{{.*}}, i48 24)
+// CHECK: call i8 @llvm.fshl.i8(i8 %{{.*}}, i8 %{{.*}}, i8 0)
+// CHECK: call i8 @llvm.fshr.i8(i8 %{{.*}}, i8 %{{.*}}, i8 0)
+// CHECK: call i32 @llvm.fshl.i32(i32 %{{.*}}, i32 %{{.*}}, i32 16)
+// CHECK: call i32 @llvm.fshl.i32(i32 %{{.*}}, i32 %{{.*}}, i32 31)
+// CHECK: call i8 @llvm.fshr.i8(i8 %{{.*}}, i8 %{{.*}}, i8 0)
+// CHECK: call i8 @llvm.fshl.i8(i8 %{{.*}}, i8 %{{.*}}, i8 0)
+void test_builtin_stdc_rotate_left(uint8_t u8, uint16_t u16,
+ uint32_t u32, uint64_t u64,
+ uint64_t u64_2, unsigned __int128 u128,
+ unsigned _BitInt(9) u9, unsigned _BitInt(37) u37,
+ unsigned _BitInt(10) u10, unsigned _BitInt(16) u16_bit,
+ unsigned _BitInt(24) u24, unsigned _BitInt(48) u48) {
+
+ volatile uint8_t result_u8;
+ volatile uint16_t result_u16;
+ volatile uint32_t result_u32;
+ volatile uint64_t result_u64;
+ volatile uint64_t result_u64_2;
+ volatile unsigned __int128 result_u128;
+ volatile unsigned _BitInt(9) result_u9;
+ volatile unsigned _BitInt(37) result_u37;
+ volatile unsigned _BitInt(10) result_u10;
+ volatile unsigned _BitInt(16) result_u16_bit;
+ volatile unsigned _BitInt(24) result_u24;
+ volatile unsigned _BitInt(48) result_u48;
+
+ result_u8 = __builtin_stdc_rotate_left(u8, 3);
+ result_u16 = __builtin_stdc_rotate_left(u16, 5);
+ result_u32 = __builtin_stdc_rotate_left(u32, 8);
+ result_u64 = __builtin_stdc_rotate_left(u64, 8);
+ result_u64_2 = __builtin_stdc_rotate_left(u64_2, 16);
+ result_u128 = __builtin_stdc_rotate_left(u128, 32);
+
+ result_u8 = __builtin_stdc_rotate_left(u8, -1);
+ result_u16 = __builtin_stdc_rotate_left(u16, -5);
+ result_u32 = __builtin_stdc_rotate_left(u32, -3);
+ result_u8 = __builtin_stdc_rotate_left(u8, -65536);
+ result_u32 = __builtin_stdc_rotate_left(u32, (int64_t)-4294967333LL);
+
+ int var = 3;
+ result_u32 = __builtin_stdc_rotate_left(42U, var);
+
+ result_u9 = __builtin_stdc_rotate_left(u9, 1);
+ result_u37 = __builtin_stdc_rotate_left(u37, 36);
+ result_u9 = __builtin_stdc_rotate_left(u9, -1);
+ result_u37 = __builtin_stdc_rotate_left(u37, -5);
+
+ result_u8 = __builtin_stdc_rotate_left((uint8_t)0xAB, 1000000);
+ result_u32 = __builtin_stdc_rotate_left(0x12345678U, 4294967295U);
+ result_u8 = __builtin_stdc_rotate_left((uint8_t)0xAB, -1000000);
+ result_u8 = __builtin_stdc_rotate_left((uint8_t)0x01, 2147483647);
+
+ result_u8 = __builtin_stdc_rotate_left((uint8_t)0xAB, 7);
+ result_u8 = __builtin_stdc_rotate_left((uint8_t)0xAB, 8);
+ result_u8 = __builtin_stdc_rotate_left((uint8_t)0xAB, 9);
+
+ result_u8 = __builtin_stdc_rotate_left((uint8_t)0xFF, 1073741824);
+ result_u32 = __builtin_stdc_rotate_left(0U, 2147483647);
+ result_u8 = __builtin_stdc_rotate_left((uint8_t)0x01, 1000000007);
+
+ result_u37 = __builtin_stdc_rotate_left((unsigned _BitInt(37))0x1000000000ULL, 1000000000);
+
+ result_u10 = __builtin_stdc_rotate_left(u10, 3);
+ result_u16_bit = __builtin_stdc_rotate_left(u16_bit, 8);
+ result_u24 = __builtin_stdc_rotate_left(u24, 12);
+ result_u48 = __builtin_stdc_rotate_left(u48, 24);
+
+ result_u10 = __builtin_stdc_rotate_left((unsigned _BitInt(10))0x3FF, -1);
+ result_u16_bit = __builtin_stdc_rotate_left((unsigned _BitInt(16))0xFFFF, -5);
+ result_u24 = __builtin_stdc_rotate_left((unsigned _BitInt(24))0xABCDEF, 1000000);
+ result_u48 = __builtin_stdc_rotate_left((unsigned _BitInt(48))0x123456789ABC, -2147483648);
+
+ uint8_t x = 0x42;
+ uint32_t z = 0x12345678;
+ result_u8 = __builtin_stdc_rotate_right(__builtin_stdc_rotate_left(x, 1000000), -1000000);
+ result_u32 = __builtin_stdc_rotate_left(__builtin_stdc_rotate_left(z, 50000), 4294967295U);
+
+ uint8_t temp = (uint8_t)x ^ __builtin_stdc_rotate_right((uint8_t)x, 1073741824);
+ result_u8 = __builtin_stdc_rotate_left(temp, 0x12345678);
+}
+
+// CHECK-LABEL: test_builtin_stdc_rotate_right
+// CHECK: call i8 @llvm.fshr.i8(i8 %{{.*}}, i8 %{{.*}}, i8 3)
+// CHECK: call i16 @llvm.fshr.i16(i16 %{{.*}}, i16 %{{.*}}, i16 5)
+// CHECK: call i32 @llvm.fshr.i32(i32 %{{.*}}, i32 %{{.*}}, i32 8)
+// CHECK: call i64 @llvm.fshr.i64(i64 %{{.*}}, i64 %{{.*}}, i64 8)
+// CHECK: call i64 @llvm.fshr.i64(i64 %{{.*}}, i64 %{{.*}}, i64 16)
+// CHECK: call i128 @llvm.fshr.i128(i128 %{{.*}}, i128 %{{.*}}, i128 32)
+// CHECK: call i8 @llvm.fshr.i8(i8 %{{.*}}, i8 %{{.*}}, i8 7)
+// CHECK: call i16 @llvm.fshr.i16(i16 %{{.*}}, i16 %{{.*}}, i16 13)
+// CHECK: call i64 @llvm.fshr.i64(i64 %{{.*}}, i64 %{{.*}}, i64 48)
+// CHECK: call i9 @llvm.fshr.i9(i9 %{{.*}}, i9 %{{.*}}, i9 1)
+// CHECK: call i9 @llvm.fshr.i9(i9 %{{.*}}, i9 %{{.*}}, i9 8)
+// CHECK: call i12 @llvm.fshr.i12(i12 %{{.*}}, i12 %{{.*}}, i12 6)
+// CHECK: call i20 @llvm.fshr.i20(i20 %{{.*}}, i20 %{{.*}}, i20 10)
+// CHECK: call i32 @llvm.fshr.i32(i32 %{{.*}}, i32 %{{.*}}, i32 16)
+// CHECK: call i16 @llvm.fshr.i16(i16 %{{.*}}, i16 %{{.*}}, i16 15)
+// CHECK: call i16 @llvm.fshl.i16(i16 %{{.*}}, i16 %{{.*}}, i16 1)
+void test_builtin_stdc_rotate_right(uint8_t u8, uint16_t u16,
+ uint32_t u32, uint64_t u64,
+ uint64_t u64_2, unsigned __int128 u128,
+ unsigned _BitInt(9) u9, unsigned _BitInt(12) u12,
+ unsigned _BitInt(20) u20, unsigned _BitInt(32) u32_bit) {
+
+ volatile uint8_t result_u8;
+ volatile uint16_t result_u16;
+ volatile uint32_t result_u32;
+ volatile uint64_t result_u64;
+ volatile uint64_t result_u64_2;
+ volatile unsigned __int128 result_u128;
+ volatile unsigned _BitInt(9) result_u9;
+ volatile unsigned _BitInt(12) result_u12;
+ volatile unsigned _BitInt(20) result_u20;
+ volatile unsigned _BitInt(32) result_u32_bit;
+
+ result_u8 = __builtin_stdc_rotate_right(u8, 3);
+ result_u16 = __builtin_stdc_rotate_right(u16, 5);
+ result_u32 = __builtin_stdc_rotate_right(u32, 8);
+ result_u64 = __builtin_stdc_rotate_right(u64, 8);
+ result_u64_2 = __builtin_stdc_rotate_right(u64_2, 16);
+ result_u128 = __builtin_stdc_rotate_right(u128, 32);
+
+ result_u8 = __builtin_stdc_rotate_right(u8, -1);
+ result_u16 = __builtin_stdc_rotate_right(u16, -3);
+ result_u64_2 = __builtin_stdc_rotate_right(u64_2, -16);
+
+ result_u9 = __builtin_stdc_rotate_right(u9, 1);
+ result_u9 = __builtin_stdc_rotate_right(u9, -1);
+
+ result_u16 = __builtin_stdc_rotate_right((uint16_t)0x1234, 2147483647);
+ result_u16 = __builtin_stdc_rotate_right((uint16_t)0x1234, -2147483647);
+ result_u8 = __builtin_stdc_rotate_right((uint8_t)0x80, -2147483648);
+
+ result_u16 = __builtin_stdc_rotate_right((uint16_t)0xFFFF, -1073741824);
+ result_u64_2 = __builtin_stdc_rotate_right(0ULL, -2147483648);
+ result_u8 = __builtin_stdc_rotate_right((uint8_t)0x80, -1000000007);
+
+ result_u12 = __builtin_stdc_rotate_right(u12, 6);
+ result_u20 = __builtin_stdc_rotate_right(u20, 10);
+ result_u32_bit = __builtin_stdc_rotate_right(u32_bit, 16);
+
+ result_u12 = __builtin_stdc_rotate_right((unsigned _BitInt(12))0xFFF, -3);
+ result_u20 = __builtin_stdc_rotate_right((unsigned _BitInt(20))0x12345, 1000000);
+ result_u32_bit = __builtin_stdc_rotate_right((unsigned _BitInt(32))0xABCDEF01, -2147483647);
+
+ uint16_t y = 0x1234;
+ result_u16 = __builtin_stdc_rotate_left(__builtin_stdc_rotate_right(y, 2147483647), -2147483647);
+}
+
+// Test _BitInt types with various bit widths
+// CHECK-LABEL: test_bitint_extremes
+// CHECK: call i3 @llvm.fshl.i3(i3 %{{.*}}, i3 %{{.*}}, i3 %{{.*}})
+// CHECK: call i23 @llvm.fshl.i23(i23 1193046, i23 1193046, i23 %{{.*}})
+// CHECK: call i37 @llvm.fshl.i37(i37 %{{.*}}, i37 %{{.*}}, i37 %{{.*}})
+// CHECK: call i67 @llvm.fshl.i67(i67 81985529216486895, i67 81985529216486895, i67 %{{.*}})
+// CHECK: call i127 @llvm.fshl.i127(i127 1, i127 1, i127 %{{.*}})
+// CHECK: call i3 @llvm.fshr.i3(i3 %{{.*}}, i3 %{{.*}}, i3 %{{.*}})
+// CHECK: call i23 @llvm.fshr.i23(i23 1193046, i23 1193046, i23 %{{.*}})
+// CHECK: call i37 @llvm.fshr.i37(i37 %{{.*}}, i37 %{{.*}}, i37 %{{.*}})
+// CHECK: call i67 @llvm.fshr.i67(i67 1311768467463790320, i67 1311768467463790320, i67 %{{.*}})
+// CHECK: call i127 @llvm.fshr.i127(i127 1, i127 1, i127 %{{.*}})
+void test_bitint_extremes(unsigned _BitInt(3) u3, unsigned _BitInt(37) u37, int shift) {
+ volatile unsigned _BitInt(3) result_u3;
+ volatile unsigned _BitInt(23) result_u23;
+ volatile unsigned _BitInt(37) result_u37;
+ volatile unsigned _BitInt(67) result_u67;
+ volatile unsigned _BitInt(127) result_u127;
+
+ result_u3 = __builtin_stdc_rotate_left(u3, shift);
+ result_u23 = __builtin_stdc_rotate_left((unsigned _BitInt(23))0x123456, shift);
+ result_u37 = __builtin_stdc_rotate_left(u37, shift);
+ result_u67 = __builtin_stdc_rotate_left((unsigned _BitInt(67))0x123456789ABCDEFULL, shift);
+ result_u127 = __builtin_stdc_rotate_left((unsigned _BitInt(127))1, shift);
+
+ result_u3 = __builtin_stdc_rotate_right(u3, shift);
+ result_u23 = __builtin_stdc_rotate_right((unsigned _BitInt(23))0x123456, shift);
+ result_u37 = __builtin_stdc_rotate_right(u37, shift);
+ result_u67 = __builtin_stdc_rotate_right((unsigned _BitInt(67))0x123456789ABCDEF0ULL, shift);
+ result_u127 = __builtin_stdc_rotate_right((unsigned _BitInt(127))1, shift);
+}
+
+// CHECK-LABEL: test_wider_shift_amount
+// CHECK: call i8 @llvm.fshl.i8(i8 %{{.*}}, i8 %{{.*}}, i8 7)
+// CHECK: call i8 @llvm.fshr.i8(i8 %{{.*}}, i8 %{{.*}}, i8 0)
+// CHECK: call i16 @llvm.fshl.i16(i16 %{{.*}}, i16 %{{.*}}, i16 11)
+// CHECK: call i16 @llvm.fshr.i16(i16 %{{.*}}, i16 %{{.*}}, i16 12)
+// CHECK: call i32 @llvm.fshl.i32(i32 %{{.*}}, i32 %{{.*}}, i32 0)
+// CHECK: call i32 @llvm.fshr.i32(i32 %{{.*}}, i32 %{{.*}}, i32 0)
+// CHECK: call i9 @llvm.fshl.i9(i9 %{{.*}}, i9 %{{.*}}, i9 8)
+// CHECK: call i9 @llvm.fshr.i9(i9 %{{.*}}, i9 %{{.*}}, i9 8)
+void test_wider_shift_amount(uint8_t u8, uint16_t u16, uint32_t u32, unsigned _BitInt(9) u9) {
+ volatile uint8_t result_u8;
+ volatile uint16_t result_u16;
+ volatile uint32_t result_u32;
+ volatile unsigned _BitInt(9) result_u9;
+
+ result_u8 = __builtin_stdc_rotate_left(u8, (int64_t)-1);
+ result_u8 = __builtin_stdc_rotate_right(u8, (int64_t)-1000);
+
+ result_u16 = __builtin_stdc_rotate_left(u16, (int64_t)-5);
+ result_u16 = __builtin_stdc_rotate_right(u16, (int64_t)-100);
+
+ result_u32 = __builtin_stdc_rotate_left(u32, (int64_t)-2147483648);
+ result_u32 = __builtin_stdc_rotate_right(u32, (int64_t)-1073741824);
+
+ result_u9 = __builtin_stdc_rotate_left(u9, (int64_t)-1);
+ result_u9 = __builtin_stdc_rotate_right(u9, (int64_t)-1000000);
+
+ result_u8 = __builtin_stdc_rotate_left((uint8_t)0xFF, (int64_t)-2147483648);
+ result_u16 = __builtin_stdc_rotate_right((uint16_t)0x1234, (int64_t)-1073741824);
+ result_u32 = __builtin_stdc_rotate_left(0x12345678U, (int64_t)-4294967296);
+
+ result_u9 = __builtin_stdc_rotate_left((unsigned _BitInt(9))0x1FF, (int64_t)-2147483647);
+}
+
diff --git a/clang/test/Sema/builtin-stdc-rotate.c b/clang/test/Sema/builtin-stdc-rotate.c
new file mode 100644
index 0000000000000..d9111c2c6f0a6
--- /dev/null
+++ b/clang/test/Sema/builtin-stdc-rotate.c
@@ -0,0 +1,126 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -verify %s
+
+_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 _BitInt(13))1, 12) == ((unsigned _BitInt(13))1 << 12), "");
+_Static_assert(__builtin_stdc_rotate_right((unsigned _BitInt(17))0x10000, 16) == ((unsigned _BitInt(17))1), "");
+_Static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(65))1, 64) == ((unsigned _BitInt(65))1 << 64), "");
+_Static_assert(__builtin_stdc_rotate_right((unsigned _BitInt(7))0x01, 1) == ((unsigned _BitInt(7))0x40), "");
+
+_Static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(9))0x100, 1) == ((unsigned _BitInt(9))0x001), "");
+_Static_assert(__builtin_stdc_rotate_right((unsigned _BitInt(9))0x001, 1) == ((unsigned _BitInt(9))0x100), "");
+_Static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(9))0x1FF, 4) == ((unsigned _BitInt(9))0x1FF), "");
+_Static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(9))1, 8) == ((unsigned _BitInt(9))0x100), "");
+_Static_assert(__builtin_stdc_rotate_right((unsigned _BitInt(9))0x100, 8) == ((unsigned _BitInt(9))1), "");
+_Static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(9))0x155, 1) == ((unsigned _BitInt(9))0xAB), "");
+_Static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(9))1, 9) == ((unsigned _BitInt(9))1), "");
+
+_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) == 0x200ULL, "");
+
+_Static_assert(__builtin_stdc_rotate_left((unsigned char)0xAB, -8) == (unsigned char)0xAB, "");
+_Static_assert(__builtin_stdc_rotate_left(0x12345678U, -32) == 0x12345678U, "");
+_Static_assert(__builtin_stdc_rotate_left((unsigned char)0x12, -25) == __builtin_stdc_rotate_left((unsigned char)0x12, 7), "");
+_Static_assert(__builtin_stdc_rotate_right(0x12345678U, -100) == __builtin_stdc_rotate_right(0x12345678U, 28), "");
+
+_Static_assert(__builtin_stdc_rotate_left((unsigned short)0x1234, -65541) == __builtin_stdc_rotate_left((unsigned short)0x1234, -5), "");
+_Static_assert(__builtin_stdc_rotate_right((unsigned char)0x80, -261) == __builtin_stdc_rotate_right((unsigned char)0x80, -5), "");
+_Static_assert(__builtin_stdc_rotate_left(0x12345678U, -4294967333LL) == __builtin_stdc_rotate_left(0x12345678U, -37), "");
+_Static_assert(__builtin_stdc_rotate_right((unsigned short)0x1234, 0) == (unsigned short)0x1234, "");
+_Static_assert(__builtin_stdc_rotate_left((unsigned long long)0x123456789ABCDEF0ULL, 0) == 0x123456789ABCDEF0ULL, "");
+_Static_assert(__builtin_stdc_rotate_left(0U, 15) == 0U, "");
+_Static_assert(__builtin_stdc_rotate_right(0U, 7) == 0U, "");
+
+_Static_assert(__builtin_stdc_rotate_left((unsigned char)0xAB, 1000000) == __builtin_stdc_rotate_left((unsigned char)0xAB, 1000000 % 8), "");
+_Static_assert(__builtin_stdc_rotate_right((unsigned short)0x1234, 2147483647) == __builtin_stdc_rotate_right((unsigned short)0x1234, 2147483647 % 16), "");
+_Static_assert(__builtin_stdc_rotate_left(0x12345678U, 4294967295U) == __builtin_stdc_rotate_left(0x12345678U, 4294967295U % 32), "");
+
+_Static_assert(__builtin_stdc_rotate_left((unsigned char)0xAB, -1000000) == __builtin_stdc_rotate_left((unsigned char)0xAB, -1000000 % 8), "");
+_Static_assert(__builtin_stdc_rotate_right((unsigned short)0x1234, -2147483647) == __builtin_stdc_rotate_right((unsigned short)0x1234, -2147483647 % 16), "");
+
+_Static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(127))1, 1000000) == __builtin_stdc_rotate_left((unsigned _BitInt(127))1, 1000000 % 127), "");
+_Static_assert(__builtin_stdc_rotate_right((unsigned _BitInt(127))1, -1000000) == __builtin_stdc_rotate_right((unsigned _BitInt(127))1, -1000000 % 127), "");
+
+_Static_assert(__builtin_stdc_rotate_left((unsigned char)0x01, 2147483647) == __builtin_stdc_rotate_left((unsigned char)0x01, 2147483647 % 8), "");
+_Static_assert(__builtin_stdc_rotate_right((unsigned char)0x80, -2147483648) == __builtin_stdc_rotate_right((unsigned char)0x80, -2147483648 % 8), "");
+
+_Static_assert(__builtin_stdc_rotate_left((unsigned char)0xAB, -9) == __builtin_stdc_rotate_left((unsigned char)0xAB, 7), "");
+_Static_assert(__builtin_stdc_rotate_left((unsigned char)0xAB, -17) == __builtin_stdc_rotate_left((unsigned char)0xAB, 7), "");
+_Static_assert(__builtin_stdc_rotate_left((unsigned char)0xAB, -25) == __builtin_stdc_rotate_left((unsigned char)0xAB, 7), "");
+
+_Static_assert(__builtin_stdc_rotate_left((unsigned char)0x12, 64 + 3) == __builtin_stdc_rotate_left((unsigned char)0x12, 3), "");
+_Static_assert(__builtin_stdc_rotate_right((unsigned short)0x1234, 128 + 5) == __builtin_stdc_rotate_right((unsigned short)0x1234, 5), "");
+
+_Static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(31))1, 1000) == __builtin_stdc_rotate_left((unsigned _BitInt(31))1, 1000 % 31), "");
+_Static_assert(__builtin_stdc_rotate_right((unsigned _BitInt(61))1, -1000) == __builtin_stdc_rotate_right((unsigned _BitInt(61))1, -1000 % 61), "");
+
+_Static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(128))0xFFFFFFFFFFFFFFFFULL, 50000) == __builtin_stdc_rotate_left((unsigned _BitInt(128))0xFFFFFFFFFFFFFFFFULL, 50000 % 128), "");
+_Static_assert(__builtin_stdc_rotate_right((unsigned _BitInt(128))0xAAAAAAAAAAAAAAAAULL, -50000) == __builtin_stdc_rotate_right((unsigned _BitInt(128))0xAAAAAAAAAAAAAAAAULL, -50000 % 128), "");
+
+_Static_assert(__builtin_stdc_rotate_left((unsigned char)0xAB, 7) == __builtin_stdc_rotate_left((unsigned char)0xAB, 7), "");
+_Static_assert(__builtin_stdc_rotate_left((unsigned char)0xAB, 8) == (unsigned char)0xAB, "");
+_Static_assert(__builtin_stdc_rotate_left((unsigned char)0xAB, 9) == __builtin_stdc_rotate_left((unsigned char)0xAB, 1), "");
+
+_Static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(9))0x155, 1000) == __builtin_stdc_rotate_left((unsigned _BitInt(9))0x155, 1000 % 9), "");
+_Static_assert(__builtin_stdc_rotate_right((unsigned _BitInt(9))0xAA, -1000) == __builtin_stdc_rotate_right((unsigned _BitInt(9))0xAA, -1000 % 9), "");
+
+_Static_assert(__builtin_stdc_rotate_left((unsigned char)0xFF, 1073741824) == (unsigned char)0xFF, "");
+_Static_assert(__builtin_stdc_rotate_right((unsigned short)0xFFFF, -1073741824) == (unsigned short)0xFFFF, "");
+
+_Static_assert(__builtin_stdc_rotate_left((unsigned char)0x42, 1000000000) == __builtin_stdc_rotate_left((unsigned char)0x42, 1000000000 % 8), "");
+_Static_assert(__builtin_stdc_rotate_right((unsigned char)0x42, -1000000000) == __builtin_stdc_rotate_right((unsigned char)0x42, -1000000000 % 8), "");
+
+_Static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(1))0, -1) == (unsigned _BitInt(1))0, "");
+_Static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(1))1, -1) == (unsigned _BitInt(1))1, "");
+_Static_assert(__builtin_stdc_rotate_right((unsigned _BitInt(1))0, 5) == (unsigned _BitInt(1))0, "");
+_Static_assert(__builtin_stdc_rotate_right((unsigned _BitInt(1))1, 5) == (unsigned _BitInt(1))1, "");
+
+void test_errors(int si, float f) {
+ unsigned int ui = 5;
+
+ __builtin_stdc_rotate_left(ui); // expected-error {{too few arguments to function call}}
+ __builtin_stdc_rotate_left(ui, 1, 2); // expected-error {{too many arguments to function call}}
+}
+
+void test_implicit_conversions(_Bool b, float f, int si) {
+ unsigned int ui = 5;
+
+ // Test implicit conversions for second argument
+ (void)__builtin_stdc_rotate_left(ui, b);
+ (void)__builtin_stdc_rotate_left(ui, f);
+ (void)__builtin_stdc_rotate_left(ui, 1.5); // expected-warning {{implicit conversion from 'double' to 'int' changes value from 1.5 to 1}}
+ (void)__builtin_stdc_rotate_right(ui, b);
+ (void)__builtin_stdc_rotate_right(ui, f);
+
+ // Test implicit conversions for first argument
+ (void)__builtin_stdc_rotate_left(si, 1);
+ (void)__builtin_stdc_rotate_left(-5, 1);
+ (void)__builtin_stdc_rotate_right(3.0, 1.5); // expected-warning {{implicit conversion from 'double' to 'int' changes value from 1.5 to 1}}
+
+ // Test narrowing conversion in assignment
+ unsigned _BitInt(17) rotated_odd = __builtin_stdc_rotate_left(0x1ABCD, 5); // expected-warning {{implicit conversion from 'unsigned int' to 'unsigned _BitInt(17)' changes value from 3504544 to 96672}}
+ (void)rotated_odd;
+}
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..ab2888fbcb271
--- /dev/null
+++ b/clang/test/SemaCXX/constexpr-builtin-stdc-rotate.cpp
@@ -0,0 +1,199 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++14 -fsyntax-only -verify %s
+
+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(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))0x200ULL, "");
+static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(37))0x800000000ULL, -3) == (unsigned _BitInt(37))0x100000000ULL, "");
+
+} // namespace test_negative_counts
+
+namespace test_boundaries {
+
+static_assert(__builtin_stdc_rotate_left((unsigned char)0x01, 7) == (unsigned char)0x80, "");
+static_assert(__builtin_stdc_rotate_right((unsigned char)0x80, 7) == (unsigned char)0x01, "");
+static_assert(__builtin_stdc_rotate_left((unsigned char)0xFF, 1) == (unsigned char)0xFF, "");
+static_assert(__builtin_stdc_rotate_left((unsigned short)0x0001, 15) == (unsigned short)0x8000, "");
+static_assert(__builtin_stdc_rotate_right((unsigned short)0x8000, 15) == (unsigned short)0x0001, "");
+static_assert(__builtin_stdc_rotate_left(0x00000001U, 31) == 0x80000000U, "");
+static_assert(__builtin_stdc_rotate_right(0x80000000U, 31) == 0x00000001U, "");
+static_assert(__builtin_stdc_rotate_left(0x0000000000000001ULL, 63) == 0x8000000000000000ULL, "");
+static_assert(__builtin_stdc_rotate_right(0x8000000000000000ULL, 63) == 0x0000000000000001ULL, "");
+
+static_assert(__builtin_stdc_rotate_left((unsigned char)0x01, -7) == (unsigned char)0x02, "");
+static_assert(__builtin_stdc_rotate_right((unsigned char)0x80, -7) == (unsigned char)0x40, "");
+static_assert(__builtin_stdc_rotate_left(0x00000001U, -31) == 0x00000002U, "");
+static_assert(__builtin_stdc_rotate_right(0x80000000U, -31) == 0x40000000U, "");
+
+static_assert(__builtin_stdc_rotate_left((unsigned char)0xAB, -8) == (unsigned char)0xAB, "");
+static_assert(__builtin_stdc_rotate_left(0x12345678U, -32) == 0x12345678U, "");
+static_assert(__builtin_stdc_rotate_left((unsigned char)0x12, -25) == __builtin_stdc_rotate_left((unsigned char)0x12, 7), "");
+static_assert(__builtin_stdc_rotate_left(0x12345678U, -100) == __builtin_stdc_rotate_left(0x12345678U, 28), "");
+
+constexpr unsigned __int128 int128_one = 1;
+constexpr unsigned __int128 int128_msb = int128_one << 127;
+static_assert(__builtin_stdc_rotate_left(int128_one, 127) == int128_msb, "");
+static_assert(__builtin_stdc_rotate_right(int128_msb, 127) == int128_one, "");
+static_assert(__builtin_stdc_rotate_left(int128_one, -127) == (int128_one << 1), "");
+
+constexpr unsigned _BitInt(37) bi37_one = 1;
+constexpr unsigned _BitInt(37) bi37_msb = bi37_one << 36;
+static_assert(__builtin_stdc_rotate_left(bi37_one, 36) == bi37_msb, "");
+static_assert(__builtin_stdc_rotate_right(bi37_msb, 36) == bi37_one, "");
+static_assert(__builtin_stdc_rotate_left(bi37_one, -36) == (bi37_one << 1), "");
+
+} // namespace test_boundaries
+
+namespace test_extreme_cases {
+
+static_assert(__builtin_stdc_rotate_left((unsigned char)0xAB, 1000000) == __builtin_stdc_rotate_left((unsigned char)0xAB, 1000000 % 8), "");
+static_assert(__builtin_stdc_rotate_right((unsigned short)0x1234, 2147483647) == __builtin_stdc_rotate_right((unsigned short)0x1234, 2147483647 % 16), "");
+static_assert(__builtin_stdc_rotate_left(0x12345678U, 4294967295U) == __builtin_stdc_rotate_left(0x12345678U, 4294967295U % 32), "");
+
+static_assert(__builtin_stdc_rotate_left((unsigned char)0xAB, -1000000) == __builtin_stdc_rotate_left((unsigned char)0xAB, -1000000 % 8), "");
+static_assert(__builtin_stdc_rotate_right((unsigned short)0x1234, -2147483647) == __builtin_stdc_rotate_right((unsigned short)0x1234, -2147483647 % 16), "");
+
+static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(127))1, 1000000) == __builtin_stdc_rotate_left((unsigned _BitInt(127))1, 1000000 % 127), "");
+static_assert(__builtin_stdc_rotate_right((unsigned _BitInt(127))1, -1000000) == __builtin_stdc_rotate_right((unsigned _BitInt(127))1, -1000000 % 127), "");
+
+static_assert(__builtin_stdc_rotate_left((unsigned char)0x01, 2147483647) == __builtin_stdc_rotate_left((unsigned char)0x01, 2147483647 % 8), "");
+static_assert(__builtin_stdc_rotate_right((unsigned char)0x80, -2147483648) == __builtin_stdc_rotate_right((unsigned char)0x80, -2147483648 % 8), "");
+
+static_assert(__builtin_stdc_rotate_left((unsigned char)0xAB, -9) == __builtin_stdc_rotate_left((unsigned char)0xAB, 7), "");
+static_assert(__builtin_stdc_rotate_left((unsigned char)0xAB, -17) == __builtin_stdc_rotate_left((unsigned char)0xAB, 7), "");
+static_assert(__builtin_stdc_rotate_left((unsigned char)0xAB, -25) == __builtin_stdc_rotate_left((unsigned char)0xAB, 7), "");
+
+static_assert(__builtin_stdc_rotate_left((unsigned char)0x12, 64 + 3) == __builtin_stdc_rotate_left((unsigned char)0x12, 3), "");
+static_assert(__builtin_stdc_rotate_right((unsigned short)0x1234, 128 + 5) == __builtin_stdc_rotate_right((unsigned short)0x1234, 5), "");
+
+static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(31))1, 1000) == __builtin_stdc_rotate_left((unsigned _BitInt(31))1, 1000 % 31), "");
+static_assert(__builtin_stdc_rotate_right((unsigned _BitInt(61))1, -1000) == __builtin_stdc_rotate_right((unsigned _BitInt(61))1, -1000 % 61), "");
+
+static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(128))0xFFFFFFFFFFFFFFFFULL, 50000) == __builtin_stdc_rotate_left((unsigned _BitInt(128))0xFFFFFFFFFFFFFFFFULL, 50000 % 128), "");
+static_assert(__builtin_stdc_rotate_right((unsigned _BitInt(128))0xAAAAAAAAAAAAAAAAULL, -50000) == __builtin_stdc_rotate_right((unsigned _BitInt(128))0xAAAAAAAAAAAAAAAAULL, -50000 % 128), "");
+
+static_assert(__builtin_stdc_rotate_left((unsigned char)0xAB, 7) == __builtin_stdc_rotate_left((unsigned char)0xAB, 7), "");
+static_assert(__builtin_stdc_rotate_left((unsigned char)0xAB, 8) == (unsigned char)0xAB, "");
+static_assert(__builtin_stdc_rotate_left((unsigned char)0xAB, 9) == __builtin_stdc_rotate_left((unsigned char)0xAB, 1), "");
+
+static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(9))0x155, 1000) == __builtin_stdc_rotate_left((unsigned _BitInt(9))0x155, 1000 % 9), "");
+static_assert(__builtin_stdc_rotate_right((unsigned _BitInt(9))0xAA, -1000) == __builtin_stdc_rotate_right((unsigned _BitInt(9))0xAA, -1000 % 9), "");
+
+static_assert(__builtin_stdc_rotate_left((unsigned char)0xFF, 1073741824) == (unsigned char)0xFF, "");
+static_assert(__builtin_stdc_rotate_right((unsigned short)0xFFFF, -1073741824) == (unsigned short)0xFFFF, "");
+
+static_assert(__builtin_stdc_rotate_left((unsigned char)0x42, 1000000000) == __builtin_stdc_rotate_left((unsigned char)0x42, 1000000000 % 8), "");
+static_assert(__builtin_stdc_rotate_right((unsigned char)0x42, -1000000000) == __builtin_stdc_rotate_right((unsigned char)0x42, -1000000000 % 8), "");
+
+static_assert(__builtin_stdc_rotate_left((unsigned char)0x12, -1000001) == __builtin_stdc_rotate_right((unsigned char)0x12, 1000001), "");
+static_assert(__builtin_stdc_rotate_right((unsigned short)0x1234, -65537) == __builtin_stdc_rotate_left((unsigned short)0x1234, 65537), "");
+
+constexpr unsigned _BitInt(67) large_pattern = 0x123456789ABCDEF0ULL;
+static_assert(__builtin_stdc_rotate_left(large_pattern, 1000000) == __builtin_stdc_rotate_left(large_pattern, 1000000 % 67), "");
+static_assert(__builtin_stdc_rotate_right(large_pattern, -1000000) == __builtin_stdc_rotate_right(large_pattern, -1000000 % 67), "");
+
+static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(23))0x123456, 1048576) == __builtin_stdc_rotate_left((unsigned _BitInt(23))0x123456, 1048576 % 23), "");
+static_assert(__builtin_stdc_rotate_right((unsigned _BitInt(23))0x123456, -1048576) == __builtin_stdc_rotate_right((unsigned _BitInt(23))0x123456, -1048576 % 23), "");
+
+} // namespace test_extreme_cases
+
+} // namespace test_constexpr_stdc_rotate
+
+namespace test_conversions {
+
+struct UnsignedWrapper {
+ operator unsigned int() const { return 42; }
+};
+
+struct RotateCount {
+ operator int() const { return 5; }
+};
+
+enum RotateAmount {
+ ROTATE_BY_4 = 4,
+ ROTATE_BY_8 = 8
+};
+
+struct NoConversion {};
+
+void test_implicit_conversions() {
+ UnsignedWrapper uw;
+ RotateCount rc;
+
+ auto result1 = __builtin_stdc_rotate_left(uw, 3);
+ auto result2 = __builtin_stdc_rotate_left(5U, rc);
+ auto result3 = __builtin_stdc_rotate_left(uw, rc);
+ auto result4 = __builtin_stdc_rotate_right(uw, RotateAmount::ROTATE_BY_4);
+
+ bool b = true;
+ float f = 3.7f;
+ auto result5 = __builtin_stdc_rotate_left(10U, b);
+ auto result6 = __builtin_stdc_rotate_left(10U, f);
+ auto result7 = __builtin_stdc_rotate_right(10U, 2.9f); // expected-warning {{implicit conversion from 'float' to 'int' changes value from 2.9000001 to 2}}
+}
+
+void test_no_conversions() {
+ NoConversion nc;
+ auto result1 = __builtin_stdc_rotate_left(5U, nc); // expected-error {{passing 'NoConversion' to parameter of incompatible type 'int'}} expected-error {{2nd argument must be a scalar signed integer type (was 'NoConversion')}}
+}
+
+} // namespace test_conversions
>From ad268dc781af564b8b7c35c46564babdbec050fa Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Tue, 18 Nov 2025 17:30:47 -0800
Subject: [PATCH 2/5] Fix rotate builtins as per review
* Make divisor unsigned and unify implementations
* Add implementation to InterpBuiltin.cpp
---
clang/lib/AST/ByteCode/InterpBuiltin.cpp | 67 +++++++++++++++++++++---
clang/lib/AST/ExprConstant.cpp | 22 +++++---
clang/lib/CodeGen/CGBuiltin.cpp | 52 ++++++++++--------
3 files changed, 105 insertions(+), 36 deletions(-)
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 659b6985d3157..5000a8434ffb4 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -3722,11 +3722,6 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
case Builtin::BI_rotl:
case Builtin::BI_lrotl:
case Builtin::BI_rotl64:
- return interp__builtin_elementwise_int_binop(
- S, OpPC, Call, [](const APSInt &Value, const APSInt &Amount) {
- return Value.rotl(Amount);
- });
-
case Builtin::BI__builtin_rotateright8:
case Builtin::BI__builtin_rotateright16:
case Builtin::BI__builtin_rotateright32:
@@ -3735,11 +3730,67 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
case Builtin::BI_rotr16:
case Builtin::BI_rotr:
case Builtin::BI_lrotr:
- case Builtin::BI_rotr64:
+ case Builtin::BI_rotr64: {
+ // Determine if this is a rotate right operation
+ bool IsRotateRight;
+ switch (BuiltinID) {
+ case Builtin::BI__builtin_rotateright8:
+ case Builtin::BI__builtin_rotateright16:
+ case Builtin::BI__builtin_rotateright32:
+ case Builtin::BI__builtin_rotateright64:
+ case Builtin::BI_rotr8:
+ case Builtin::BI_rotr16:
+ case Builtin::BI_rotr:
+ case Builtin::BI_lrotr:
+ case Builtin::BI_rotr64:
+ IsRotateRight = true;
+ break;
+ default:
+ IsRotateRight = false;
+ break;
+ }
+
return interp__builtin_elementwise_int_binop(
- S, OpPC, Call, [](const APSInt &Value, const APSInt &Amount) {
- return Value.rotr(Amount);
+ S, OpPC, Call, [IsRotateRight](const APSInt &Value, APSInt Amount) {
+ // Normalize shift amount to [0, BitWidth) range to match runtime
+ // behavior. This matches the algorithm in ExprConstant.cpp.
+ unsigned BitWidth = Value.getBitWidth();
+ unsigned AmtBitWidth = Amount.getBitWidth();
+ if (BitWidth == 1) {
+ // Rotating a 1-bit value is always a no-op
+ Amount = APSInt(llvm::APInt(AmtBitWidth, 0), Amount.isUnsigned());
+ } else {
+ // Divisor is always unsigned to avoid misinterpreting BitWidth as
+ // negative in small bit widths (e.g., BitWidth=2 would be -2 if
+ // signed).
+ APSInt Divisor;
+ if (AmtBitWidth > BitWidth) {
+ Divisor = APSInt(llvm::APInt(AmtBitWidth, BitWidth),
+ /*isUnsigned=*/true);
+ } else {
+ Divisor =
+ APSInt(llvm::APInt(BitWidth, BitWidth), /*isUnsigned=*/true);
+ if (AmtBitWidth < BitWidth) {
+ Amount = Amount.extend(BitWidth);
+ }
+ }
+
+ // Normalize to [0, BitWidth)
+ if (Amount.isSigned()) {
+ Amount = APSInt(Amount.srem(Divisor), /*isUnsigned=*/false);
+ if (Amount.isNegative()) {
+ APSInt SignedDivisor(Divisor, /*isUnsigned=*/false);
+ Amount += SignedDivisor;
+ }
+ } else {
+ Amount = APSInt(Amount.urem(Divisor), /*isUnsigned=*/true);
+ }
+ }
+
+ return IsRotateRight ? Value.rotr(Amount.getZExtValue())
+ : Value.rotl(Amount.getZExtValue());
});
+ }
case Builtin::BI__builtin_ffs:
case Builtin::BI__builtin_ffsl:
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 4f5172d199f77..115310d44ddde 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -15901,23 +15901,31 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
unsigned BitWidth = Val.getBitWidth();
unsigned AmtBitWidth = Amt.getBitWidth();
if (BitWidth == 1) {
- // if BitWidth is 1, Amt % Divisor = 0
- // No need for rotation
+ // Rotating a 1-bit value is always a no-op
Amt = APSInt(APInt(AmtBitWidth, 0), Amt.isUnsigned());
} else {
+ // Divisor is always unsigned to avoid misinterpreting BitWidth as
+ // negative in small bit widths (e.g., BitWidth=2 would be -2 if signed).
APSInt Divisor;
if (AmtBitWidth > BitWidth) {
- Divisor = APSInt(llvm::APInt(AmtBitWidth, BitWidth), Amt.isUnsigned());
+ Divisor =
+ APSInt(llvm::APInt(AmtBitWidth, BitWidth), /*isUnsigned=*/true);
} else {
- Divisor = APSInt(llvm::APInt(BitWidth, BitWidth), Amt.isUnsigned());
+ Divisor = APSInt(llvm::APInt(BitWidth, BitWidth), /*isUnsigned=*/true);
if (AmtBitWidth < BitWidth) {
Amt = Amt.extend(BitWidth);
}
}
- Amt = Amt % Divisor;
- if (Amt.isNegative()) {
- Amt += Divisor;
+ // Normalize to [0, BitWidth)
+ if (Amt.isSigned()) {
+ Amt = APSInt(Amt.srem(Divisor), /*isUnsigned=*/false);
+ if (Amt.isNegative()) {
+ APSInt SignedDivisor(Divisor, /*isUnsigned=*/false);
+ Amt += SignedDivisor;
+ }
+ } else {
+ Amt = APSInt(Amt.urem(Divisor), /*isUnsigned=*/true);
}
}
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 146f07321a2ff..c9fb20be0219b 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -2492,31 +2492,41 @@ RValue CodeGenFunction::emitRotate(const CallExpr *E, bool IsRotateRight) {
unsigned BitWidth = Ty->getIntegerBitWidth();
- if (!llvm::isPowerOf2_32(BitWidth) &&
- E->getArg(1)->getType()->isSignedIntegerType()) {
- // converting BitWidth to the type of ShiftAmt
- llvm::Value *BitWidthInShiftTy = ConstantInt::get(ShiftTy, BitWidth);
+ // Normalize shift amount to [0, BitWidth) range to match runtime behavior.
+ // This matches the algorithm in ExprConstant.cpp for constant evaluation.
+ if (BitWidth == 1) {
+ // Rotating a 1-bit value is always a no-op
+ ShiftAmt = ConstantInt::get(ShiftTy, 0);
+ } else {
+ unsigned ShiftAmtBitWidth = ShiftTy->getIntegerBitWidth();
+ bool ShiftAmtIsSigned = E->getArg(1)->getType()->isSignedIntegerType();
- // if ShiftAmt is negative, normalize ShiftAmt to [0, BitWidth - 1]
- llvm::Value *RemResult = Builder.CreateSRem(ShiftAmt, BitWidthInShiftTy);
- llvm::Value *PositiveShift =
- Builder.CreateAdd(RemResult, BitWidthInShiftTy);
+ // Choose the wider type for the divisor to avoid truncation
+ llvm::Type *DivisorTy = ShiftAmtBitWidth > BitWidth ? ShiftTy : Ty;
+ llvm::Value *Divisor = ConstantInt::get(DivisorTy, BitWidth);
- llvm::Value *Zero = ConstantInt::get(ShiftTy, 0);
- llvm::Value *IsRemNegative = Builder.CreateICmpSLT(RemResult, Zero);
+ // Extend ShiftAmt to match Divisor width if needed
+ if (ShiftAmtBitWidth < DivisorTy->getIntegerBitWidth()) {
+ ShiftAmt = Builder.CreateIntCast(ShiftAmt, DivisorTy, ShiftAmtIsSigned);
+ }
- ShiftAmt = Builder.CreateSelect(IsRemNegative, PositiveShift, RemResult);
- }
+ // Normalize to [0, BitWidth)
+ llvm::Value *RemResult;
+ if (ShiftAmtIsSigned) {
+ RemResult = Builder.CreateSRem(ShiftAmt, Divisor);
+ // Signed remainder can be negative, convert to positive equivalent
+ llvm::Value *Zero = ConstantInt::get(DivisorTy, 0);
+ llvm::Value *IsNegative = Builder.CreateICmpSLT(RemResult, Zero);
+ llvm::Value *PositiveShift = Builder.CreateAdd(RemResult, Divisor);
+ ShiftAmt = Builder.CreateSelect(IsNegative, PositiveShift, RemResult);
+ } else {
+ ShiftAmt = Builder.CreateURem(ShiftAmt, Divisor);
+ }
- unsigned ShiftAmtBitWidth = ShiftTy->getIntegerBitWidth();
- if (ShiftAmtBitWidth > BitWidth) {
- llvm::Value *BitWidthInShiftTy = ConstantInt::get(ShiftTy, BitWidth);
- ShiftAmt = Builder.CreateURem(ShiftAmt, BitWidthInShiftTy);
- ShiftAmt = Builder.CreateIntCast(ShiftAmt, Ty, false);
- } else {
- llvm::Value *BitWidthInShiftTy = ConstantInt::get(Ty, BitWidth);
- ShiftAmt = Builder.CreateIntCast(ShiftAmt, Ty, false);
- ShiftAmt = Builder.CreateURem(ShiftAmt, BitWidthInShiftTy);
+ // Convert to the source type if needed
+ if (ShiftAmt->getType() != Ty) {
+ ShiftAmt = Builder.CreateIntCast(ShiftAmt, Ty, false);
+ }
}
// Rotate is a special case of LLVM funnel shift - 1st 2 args are the same.
>From f6fac2c6378eabbc942091620e5cfc7cf3caed8b Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Wed, 26 Nov 2025 16:12:03 -0800
Subject: [PATCH 3/5] Address feedback from code review * Use APInt for Divisor
* Move rotation normalization code into ExprConstShared.h
---
clang/lib/AST/ByteCode/InterpBuiltin.cpp | 36 +----------
clang/lib/AST/ExprConstShared.h | 5 ++
clang/lib/AST/ExprConstant.cpp | 80 +++++++++++++-----------
3 files changed, 50 insertions(+), 71 deletions(-)
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 5000a8434ffb4..eb4f3d39441a7 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -3752,41 +3752,7 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
return interp__builtin_elementwise_int_binop(
S, OpPC, Call, [IsRotateRight](const APSInt &Value, APSInt Amount) {
- // Normalize shift amount to [0, BitWidth) range to match runtime
- // behavior. This matches the algorithm in ExprConstant.cpp.
- unsigned BitWidth = Value.getBitWidth();
- unsigned AmtBitWidth = Amount.getBitWidth();
- if (BitWidth == 1) {
- // Rotating a 1-bit value is always a no-op
- Amount = APSInt(llvm::APInt(AmtBitWidth, 0), Amount.isUnsigned());
- } else {
- // Divisor is always unsigned to avoid misinterpreting BitWidth as
- // negative in small bit widths (e.g., BitWidth=2 would be -2 if
- // signed).
- APSInt Divisor;
- if (AmtBitWidth > BitWidth) {
- Divisor = APSInt(llvm::APInt(AmtBitWidth, BitWidth),
- /*isUnsigned=*/true);
- } else {
- Divisor =
- APSInt(llvm::APInt(BitWidth, BitWidth), /*isUnsigned=*/true);
- if (AmtBitWidth < BitWidth) {
- Amount = Amount.extend(BitWidth);
- }
- }
-
- // Normalize to [0, BitWidth)
- if (Amount.isSigned()) {
- Amount = APSInt(Amount.srem(Divisor), /*isUnsigned=*/false);
- if (Amount.isNegative()) {
- APSInt SignedDivisor(Divisor, /*isUnsigned=*/false);
- Amount += SignedDivisor;
- }
- } else {
- Amount = APSInt(Amount.urem(Divisor), /*isUnsigned=*/true);
- }
- }
-
+ Amount = NormalizeRotateAmount(Value, Amount);
return IsRotateRight ? Value.rotr(Amount.getZExtValue())
: Value.rotl(Amount.getZExtValue());
});
diff --git a/clang/lib/AST/ExprConstShared.h b/clang/lib/AST/ExprConstShared.h
index 401ae629c86bf..28fd761c91bcb 100644
--- a/clang/lib/AST/ExprConstShared.h
+++ b/clang/lib/AST/ExprConstShared.h
@@ -18,6 +18,8 @@
namespace llvm {
class APFloat;
+class APSInt;
+class APInt;
}
namespace clang {
class QualType;
@@ -74,4 +76,7 @@ void HandleComplexComplexDiv(llvm::APFloat A, llvm::APFloat B, llvm::APFloat C,
CharUnits GetAlignOfExpr(const ASTContext &Ctx, const Expr *E,
UnaryExprOrTypeTrait ExprKind);
+llvm::APSInt NormalizeRotateAmount(const llvm::APSInt &Value,
+ const llvm::APSInt &Amount);
+
#endif
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 115310d44ddde..d4ba07439f5b1 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -15892,42 +15892,12 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
case Builtin::BI_rotr:
case Builtin::BI_lrotr:
case Builtin::BI_rotr64: {
- APSInt Val, Amt;
- if (!EvaluateInteger(E->getArg(0), Val, Info) ||
- !EvaluateInteger(E->getArg(1), Amt, Info))
+ APSInt Value, Amount;
+ if (!EvaluateInteger(E->getArg(0), Value, Info) ||
+ !EvaluateInteger(E->getArg(1), Amount, Info))
return false;
- // Normalize shift amount to [0, BitWidth) range to match runtime behavior
- unsigned BitWidth = Val.getBitWidth();
- unsigned AmtBitWidth = Amt.getBitWidth();
- if (BitWidth == 1) {
- // Rotating a 1-bit value is always a no-op
- Amt = APSInt(APInt(AmtBitWidth, 0), Amt.isUnsigned());
- } else {
- // Divisor is always unsigned to avoid misinterpreting BitWidth as
- // negative in small bit widths (e.g., BitWidth=2 would be -2 if signed).
- APSInt Divisor;
- if (AmtBitWidth > BitWidth) {
- Divisor =
- APSInt(llvm::APInt(AmtBitWidth, BitWidth), /*isUnsigned=*/true);
- } else {
- Divisor = APSInt(llvm::APInt(BitWidth, BitWidth), /*isUnsigned=*/true);
- if (AmtBitWidth < BitWidth) {
- Amt = Amt.extend(BitWidth);
- }
- }
-
- // Normalize to [0, BitWidth)
- if (Amt.isSigned()) {
- Amt = APSInt(Amt.srem(Divisor), /*isUnsigned=*/false);
- if (Amt.isNegative()) {
- APSInt SignedDivisor(Divisor, /*isUnsigned=*/false);
- Amt += SignedDivisor;
- }
- } else {
- Amt = APSInt(Amt.urem(Divisor), /*isUnsigned=*/true);
- }
- }
+ Amount = NormalizeRotateAmount(Value, Amount);
switch (BuiltinOp) {
case Builtin::BI__builtin_rotateright8:
@@ -15940,9 +15910,11 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
case Builtin::BI_rotr:
case Builtin::BI_lrotr:
case Builtin::BI_rotr64:
- return Success(APSInt(Val.rotr(Amt.getZExtValue()), Val.isUnsigned()), E);
+ return Success(
+ APSInt(Value.rotr(Amount.getZExtValue()), Value.isUnsigned()), E);
default:
- return Success(APSInt(Val.rotl(Amt.getZExtValue()), Val.isUnsigned()), E);
+ return Success(
+ APSInt(Value.rotl(Amount.getZExtValue()), Value.isUnsigned()), E);
}
}
@@ -19196,6 +19168,42 @@ void HandleComplexComplexDiv(APFloat A, APFloat B, APFloat C, APFloat D,
}
}
+APSInt NormalizeRotateAmount(const APSInt &Value, const APSInt &Amount) {
+ // Normalize shift amount to [0, BitWidth) range to match runtime behavior
+ APSInt NormAmt = Amount;
+ unsigned BitWidth = Value.getBitWidth();
+ unsigned AmtBitWidth = NormAmt.getBitWidth();
+ if (BitWidth == 1) {
+ // Rotating a 1-bit value is always a no-op
+ NormAmt = APSInt(APInt(AmtBitWidth, 0), NormAmt.isUnsigned());
+ } else {
+ // Divisor is always unsigned to avoid misinterpreting BitWidth as
+ // negative in small bit widths (e.g., BitWidth=2 would be -2 if signed).
+ APInt Divisor;
+ if (AmtBitWidth > BitWidth) {
+ Divisor = llvm::APInt(AmtBitWidth, BitWidth);
+ } else {
+ Divisor = llvm::APInt(BitWidth, BitWidth);
+ if (AmtBitWidth < BitWidth) {
+ NormAmt = NormAmt.extend(BitWidth);
+ }
+ }
+
+ // Normalize to [0, BitWidth)
+ if (NormAmt.isSigned()) {
+ NormAmt = APSInt(NormAmt.srem(Divisor), /*isUnsigned=*/false);
+ if (NormAmt.isNegative()) {
+ APSInt SignedDivisor(Divisor, /*isUnsigned=*/false);
+ NormAmt += SignedDivisor;
+ }
+ } else {
+ NormAmt = APSInt(NormAmt.urem(Divisor), /*isUnsigned=*/true);
+ }
+ }
+
+ return NormAmt;
+}
+
bool ComplexExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
if (E->isPtrMemOp() || E->isAssignmentOp() || E->getOpcode() == BO_Comma)
return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
>From 307710baf02356a02d6e7b10b7f74325030f5e06 Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Wed, 26 Nov 2025 16:29:37 -0800
Subject: [PATCH 4/5] Specify -ffreestanding to use the stdint header
---
clang/test/CodeGen/builtin-rotate.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/test/CodeGen/builtin-rotate.c b/clang/test/CodeGen/builtin-rotate.c
index ac47bf0082023..1f498c0294a58 100644
--- a/clang/test/CodeGen/builtin-rotate.c
+++ b/clang/test/CodeGen/builtin-rotate.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s
+// RUN: %clang_cc1 -ffreestanding %s -emit-llvm -o - | FileCheck %s
#include<stdint.h>
>From 45105a8c85f06fdd502d7385d35f8a26ff464a80 Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Wed, 26 Nov 2025 17:52:18 -0800
Subject: [PATCH 5/5] Allow implicit conversion for class/struct types with
conversion operators. Explicit conversion must be done for all other types.
---
clang/docs/ReleaseNotes.rst | 3 +-
clang/lib/Sema/SemaChecking.cpp | 102 ++++++++++++------
clang/test/Sema/builtin-stdc-rotate.c | 30 +++---
.../SemaCXX/constexpr-builtin-stdc-rotate.cpp | 11 +-
4 files changed, 96 insertions(+), 50 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 104edb48f73ad..88d58bc1f1e88 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -328,7 +328,8 @@ Non-comprehensive list of changes in this release
- Added ``__builtin_stdc_rotate_left`` and ``__builtin_stdc_rotate_right``
for bit rotation of unsigned integers including ``_BitInt`` types. Rotation
counts are normalized modulo the bit-width and support negative values.
- Usable in constant expressions.
+ Usable in constant expressions. Implicit conversion is supported for
+ class/struct types with conversion operators.
New Compiler Flags
------------------
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 2be6c19cb186f..40c5882f806fe 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -2321,6 +2321,66 @@ static bool BuiltinCountZeroBitsGeneric(Sema &S, CallExpr *TheCall) {
return false;
}
+class RotateIntegerConverter : public Sema::ContextualImplicitConverter {
+ unsigned ArgIndex;
+ bool OnlyUnsigned;
+
+ Sema::SemaDiagnosticBuilder emitError(Sema &S, SourceLocation Loc,
+ QualType T) {
+ return S.Diag(Loc, diag::err_builtin_invalid_arg_type)
+ << ArgIndex << /*scalar*/ 1
+ << (OnlyUnsigned ? /*unsigned integer*/ 3 : /*integer*/ 1)
+ << /*no fp*/ 0 << T;
+ }
+
+public:
+ RotateIntegerConverter(unsigned ArgIndex, bool OnlyUnsigned)
+ : ContextualImplicitConverter(/*Suppress=*/false,
+ /*SuppressConversion=*/true),
+ ArgIndex(ArgIndex), OnlyUnsigned(OnlyUnsigned) {}
+
+ bool match(QualType T) override {
+ return OnlyUnsigned ? T->isUnsignedIntegerType() : T->isIntegerType();
+ }
+
+ Sema::SemaDiagnosticBuilder diagnoseNoMatch(Sema &S, SourceLocation Loc,
+ QualType T) override {
+ return emitError(S, Loc, T);
+ }
+
+ Sema::SemaDiagnosticBuilder diagnoseIncomplete(Sema &S, SourceLocation Loc,
+ QualType T) override {
+ return emitError(S, Loc, T);
+ }
+
+ Sema::SemaDiagnosticBuilder diagnoseExplicitConv(Sema &S, SourceLocation Loc,
+ QualType T,
+ QualType ConvTy) override {
+ return emitError(S, Loc, T);
+ }
+
+ Sema::SemaDiagnosticBuilder noteExplicitConv(Sema &S, CXXConversionDecl *Conv,
+ QualType ConvTy) override {
+ return S.Diag(Conv->getLocation(), diag::note_conv_function_declared_at);
+ }
+
+ Sema::SemaDiagnosticBuilder diagnoseAmbiguous(Sema &S, SourceLocation Loc,
+ QualType T) override {
+ return emitError(S, Loc, T);
+ }
+
+ Sema::SemaDiagnosticBuilder noteAmbiguous(Sema &S, CXXConversionDecl *Conv,
+ QualType ConvTy) override {
+ return S.Diag(Conv->getLocation(), diag::note_conv_function_declared_at);
+ }
+
+ Sema::SemaDiagnosticBuilder diagnoseConversion(Sema &S, SourceLocation Loc,
+ QualType T,
+ QualType ConvTy) override {
+ llvm_unreachable("conversion functions are permitted");
+ }
+};
+
/// 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.
@@ -2328,7 +2388,10 @@ static bool BuiltinRotateGeneric(Sema &S, CallExpr *TheCall) {
if (S.checkArgCount(TheCall, 2))
return true;
- ExprResult Arg0Res = S.DefaultLvalueConversion(TheCall->getArg(0));
+ // First argument (value to rotate) must be unsigned integer type.
+ RotateIntegerConverter Arg0Converter(1, /*OnlyUnsigned=*/true);
+ ExprResult Arg0Res = S.PerformContextualImplicitConversion(
+ TheCall->getArg(0)->getBeginLoc(), TheCall->getArg(0), Arg0Converter);
if (Arg0Res.isInvalid())
return true;
@@ -2336,24 +2399,13 @@ static bool BuiltinRotateGeneric(Sema &S, CallExpr *TheCall) {
TheCall->setArg(0, Arg0);
QualType Arg0Ty = Arg0->getType();
- if (!Arg0Ty->isUnsignedIntegerType()) {
- ExprResult ConvArg0Res = S.PerformImplicitConversion(
- TheCall->getArg(0), S.Context.UnsignedIntTy, AssignmentAction::Passing);
- if (ConvArg0Res.isUsable()) {
- Arg0 = ConvArg0Res.get();
- Arg0Ty = Arg0->getType();
- TheCall->setArg(0, Arg0);
- }
- }
-
- if (!Arg0Ty->isUnsignedIntegerType()) {
- S.Diag(Arg0->getBeginLoc(), diag::err_builtin_invalid_arg_type)
- << 1 << /* scalar */ 1 << /* unsigned integer ty */ 3 << /* no fp */ 0
- << Arg0Ty;
+ if (!Arg0Ty->isUnsignedIntegerType())
return true;
- }
- ExprResult Arg1Res = S.DefaultLvalueConversion(TheCall->getArg(1));
+ // Second argument (rotation count) must be integer type.
+ RotateIntegerConverter Arg1Converter(2, /*OnlyUnsigned=*/false);
+ ExprResult Arg1Res = S.PerformContextualImplicitConversion(
+ TheCall->getArg(1)->getBeginLoc(), TheCall->getArg(1), Arg1Converter);
if (Arg1Res.isInvalid())
return true;
@@ -2361,22 +2413,8 @@ static bool BuiltinRotateGeneric(Sema &S, CallExpr *TheCall) {
TheCall->setArg(1, Arg1);
QualType Arg1Ty = Arg1->getType();
-
- if (!Arg1Ty->isIntegerType()) {
- ExprResult ConvArg1Res = S.PerformImplicitConversion(
- TheCall->getArg(1), S.Context.IntTy, AssignmentAction::Passing);
- if (ConvArg1Res.isUsable()) {
- Arg1 = ConvArg1Res.get();
- Arg1Ty = Arg1->getType();
- TheCall->setArg(1, Arg1);
- }
- }
-
- if (!Arg1Ty->isIntegerType()) {
- S.Diag(Arg1->getBeginLoc(), diag::err_builtin_invalid_arg_type)
- << 2 << /* scalar */ 1 << /* integer ty */ 2 << /* no fp */ 0 << Arg1Ty;
+ if (!Arg1Ty->isIntegerType())
return true;
- }
TheCall->setType(Arg0Ty);
return false;
diff --git a/clang/test/Sema/builtin-stdc-rotate.c b/clang/test/Sema/builtin-stdc-rotate.c
index d9111c2c6f0a6..d8b506dbd6e46 100644
--- a/clang/test/Sema/builtin-stdc-rotate.c
+++ b/clang/test/Sema/builtin-stdc-rotate.c
@@ -105,22 +105,28 @@ void test_errors(int si, float f) {
__builtin_stdc_rotate_left(ui, 1, 2); // expected-error {{too many arguments to function call}}
}
-void test_implicit_conversions(_Bool b, float f, int si) {
+void test_valid_conversions(_Bool b, int si) {
unsigned int ui = 5;
- // Test implicit conversions for second argument
+ // Valid: bool converts to int for second argument
(void)__builtin_stdc_rotate_left(ui, b);
- (void)__builtin_stdc_rotate_left(ui, f);
- (void)__builtin_stdc_rotate_left(ui, 1.5); // expected-warning {{implicit conversion from 'double' to 'int' changes value from 1.5 to 1}}
(void)__builtin_stdc_rotate_right(ui, b);
- (void)__builtin_stdc_rotate_right(ui, f);
- // Test implicit conversions for first argument
- (void)__builtin_stdc_rotate_left(si, 1);
- (void)__builtin_stdc_rotate_left(-5, 1);
- (void)__builtin_stdc_rotate_right(3.0, 1.5); // expected-warning {{implicit conversion from 'double' to 'int' changes value from 1.5 to 1}}
+ // Valid: signed int is an integer type for second argument
+ (void)__builtin_stdc_rotate_left(ui, si);
+ (void)__builtin_stdc_rotate_right(ui, si);
+}
+
+void test_invalid_types(float f, int si) {
+ unsigned int ui = 5;
+
+ // Invalid: float is not an integer type for second argument
+ (void)__builtin_stdc_rotate_left(ui, f); // expected-error {{2nd argument must be a scalar integer type (was 'float')}}
+ (void)__builtin_stdc_rotate_left(ui, 1.5); // expected-error {{2nd argument must be a scalar integer type (was 'double')}}
+ (void)__builtin_stdc_rotate_right(ui, f); // expected-error {{2nd argument must be a scalar integer type (was 'float')}}
- // Test narrowing conversion in assignment
- unsigned _BitInt(17) rotated_odd = __builtin_stdc_rotate_left(0x1ABCD, 5); // expected-warning {{implicit conversion from 'unsigned int' to 'unsigned _BitInt(17)' changes value from 3504544 to 96672}}
- (void)rotated_odd;
+ // Invalid: signed int is not unsigned for first argument
+ (void)__builtin_stdc_rotate_left(si, 1); // expected-error {{1st argument must be a scalar unsigned integer type (was 'int')}}
+ (void)__builtin_stdc_rotate_left(-5, 1); // expected-error {{1st argument must be a scalar unsigned integer type (was 'int')}}
+ (void)__builtin_stdc_rotate_right(3.0, 1); // expected-error {{1st argument must be a scalar unsigned integer type (was 'double')}}
}
diff --git a/clang/test/SemaCXX/constexpr-builtin-stdc-rotate.cpp b/clang/test/SemaCXX/constexpr-builtin-stdc-rotate.cpp
index ab2888fbcb271..4827c4091a18d 100644
--- a/clang/test/SemaCXX/constexpr-builtin-stdc-rotate.cpp
+++ b/clang/test/SemaCXX/constexpr-builtin-stdc-rotate.cpp
@@ -185,15 +185,16 @@ void test_implicit_conversions() {
auto result4 = __builtin_stdc_rotate_right(uw, RotateAmount::ROTATE_BY_4);
bool b = true;
- float f = 3.7f;
auto result5 = __builtin_stdc_rotate_left(10U, b);
- auto result6 = __builtin_stdc_rotate_left(10U, f);
- auto result7 = __builtin_stdc_rotate_right(10U, 2.9f); // expected-warning {{implicit conversion from 'float' to 'int' changes value from 2.9000001 to 2}}
}
-void test_no_conversions() {
+void test_invalid_types() {
+ float f = 3.7f;
+ auto result6 = __builtin_stdc_rotate_left(10U, f); // expected-error {{2nd argument must be a scalar integer type (was 'float')}}
+ auto result7 = __builtin_stdc_rotate_right(10U, 2.9f); // expected-error {{2nd argument must be a scalar integer type (was 'float')}}
+
NoConversion nc;
- auto result1 = __builtin_stdc_rotate_left(5U, nc); // expected-error {{passing 'NoConversion' to parameter of incompatible type 'int'}} expected-error {{2nd argument must be a scalar signed integer type (was 'NoConversion')}}
+ auto result1 = __builtin_stdc_rotate_left(5U, nc); // expected-error {{2nd argument must be a scalar integer type (was 'NoConversion')}}
}
} // namespace test_conversions
More information about the cfe-commits
mailing list