[clang] [clang] Implement __builtin_stdc_rotate_left, __builtin_stdc_rotate_right (PR #160259)

NagaChaitanya Vellanki via cfe-commits cfe-commits at lists.llvm.org
Fri Sep 26 16:46:01 PDT 2025


https://github.com/chaitanyav updated https://github.com/llvm/llvm-project/pull/160259

>From db50769a3a42fd5a23a5900d2ac8497f0aece893 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] [clang] Implement __builtin_stdc_rotate_{left,right}

Resolves #122819
---
 clang/docs/LanguageExtensions.rst             |  38 +++++
 clang/include/clang/Basic/Builtins.td         |  12 ++
 clang/lib/AST/ExprConstant.cpp                |  56 ++++++--
 clang/lib/CodeGen/CGBuiltin.cpp               |  26 +++-
 clang/lib/Sema/SemaChecking.cpp               |  48 +++++++
 clang/test/CodeGen/builtin-rotate.c           | 130 ++++++++++++++++++
 clang/test/Sema/builtin-stdc-rotate.c         |  56 ++++++++
 .../SemaCXX/constexpr-builtin-stdc-rotate.cpp | 107 ++++++++++++++
 8 files changed, 458 insertions(+), 15 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 b503283559db4..22ca42994b620 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -3680,6 +3680,44 @@ 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 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_rotate1 = __builtin_stdc_rotate_left((unsigned char)0xB1, -1);
+  unsigned char neg_rotate2 = __builtin_stdc_rotate_left((unsigned char)0xB1, 7);
+
+  unsigned _BitInt(17) odd_val = 0x1ABCD;
+  unsigned _BitInt(17) rotated_odd = __builtin_stdc_rotate_left(odd_val, 5);
+  unsigned _BitInt(17) neg_rotated_odd = __builtin_stdc_rotate_right(odd_val, -3);
+
+  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 468121f7d20ab..bc5be5551318b 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 b706b14945b6d..c604a50a5b45a 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -14139,23 +14139,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.urem(Val.getBitWidth())), 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:
@@ -14166,7 +14160,41 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
         !EvaluateInteger(E->getArg(1), Amt, Info))
       return false;
 
-    return Success(Val.rotr(Amt.urem(Val.getBitWidth())), E);
+    // Normalize shift amount to [0, BitWidth) range to match runtime behavior
+    unsigned BitWidth = Val.getBitWidth();
+    APSInt Divisor(llvm::APInt(Amt.getBitWidth(), BitWidth), Amt.isUnsigned());
+    APSInt NormalizedAmt = Amt % Divisor;
+    if (NormalizedAmt.isNegative()) {
+      NormalizedAmt += Divisor;
+    }
+
+    // Determine rotation direction
+    bool IsRotateRight;
+    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:
+      IsRotateRight = true;
+      break;
+    default:
+      IsRotateRight = false;
+      break;
+    }
+
+    // Perform the rotation
+    APSInt Result =
+        IsRotateRight
+            ? APSInt(Val.rotr(NormalizedAmt.getZExtValue()), Val.isUnsigned())
+            : APSInt(Val.rotl(NormalizedAmt.getZExtValue()), Val.isUnsigned());
+
+    return Success(Result, E);
   }
 
   case Builtin::BI__builtin_elementwise_add_sat: {
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 9ee810c9d5775..7096c2eaeea5b 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -2492,12 +2492,34 @@ 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();
+
+  unsigned BitWidth = Ty->getIntegerBitWidth();
+
+  if (E->getArg(1)->getType()->isSignedIntegerType()) {
+    // converting BitWidth to the type of ShiftAmt
+    llvm::Type *ShiftTy = ShiftAmt->getType();
+    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);
+  }
+
+  llvm::Type *ShiftTy = ShiftAmt->getType();
+  llvm::Value *BitWidthInShiftTy = ConstantInt::get(ShiftTy, BitWidth);
+  ShiftAmt = Builder.CreateURem(ShiftAmt, BitWidthInShiftTy);
   ShiftAmt = Builder.CreateIntCast(ShiftAmt, Ty, false);
 
   // 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.
@@ -3642,6 +3664,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 +3676,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 39c3aa2243338..2acd983edaa04 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, bool AllowConst,
                                    bool AllowAS) {
@@ -3458,6 +3500,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..a95699bd62f23 100644
--- a/clang/test/CodeGen/builtin-rotate.c
+++ b/clang/test/CodeGen/builtin-rotate.c
@@ -32,6 +32,84 @@ 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, unsigned _BitInt(9) val9,
+                                   unsigned _BitInt(37) val37) {
+
+  volatile unsigned char result_uc;
+  volatile unsigned short result_us;
+  volatile unsigned int result_ui;
+  volatile unsigned long result_ul;
+  volatile unsigned long long result_ull;
+  volatile unsigned __int128 result_ui128;
+  volatile unsigned _BitInt(128) result_ubi128;
+  volatile unsigned _BitInt(9) result_9;
+  volatile unsigned _BitInt(37) result_37;
+
+  // 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);
+
+  // CHECK: call i8 @llvm.fshl.i8(i8 %{{.*}}, i8 %{{.*}}, i8 7)
+  result_uc = __builtin_stdc_rotate_left(uc, -1);
+
+  // CHECK: call i16 @llvm.fshl.i16(i16 %{{.*}}, i16 %{{.*}}, i16 11)
+  result_us = __builtin_stdc_rotate_left(us, -5);
+
+  // CHECK: call i32 @llvm.fshl.i32(i32 %{{.*}}, i32 %{{.*}}, i32 29)
+  result_ui = __builtin_stdc_rotate_left(ui, -3);
+
+  // CHECK: call i8 @llvm.fshl.i8(i8 %{{.*}}, i8 %{{.*}}, i8 0)
+  result_uc = __builtin_stdc_rotate_left(uc, -65536);
+
+  // CHECK: call i32 @llvm.fshl.i32(i32 %{{.*}}, i32 %{{.*}}, i32 27)
+  result_ui = __builtin_stdc_rotate_left(ui, -4294967333LL);
+
+  int negative_shift = -5;
+  // CHECK: call i8 @llvm.fshl.i8(i8 %{{.*}}, i8 %{{.*}}, i8 %{{.*}})
+  result_uc = __builtin_stdc_rotate_left(uc, negative_shift);
+
+  // CHECK: call i32 @llvm.fshl.i32(i32 %{{.*}}, i32 %{{.*}}, i32 %{{.*}})
+  result_ui = __builtin_stdc_rotate_left(ui, negative_shift);
+
+  int var = 3;
+  // CHECK: call i32 @llvm.fshl.i32(i32 42, i32 42, i32 %{{.*}})
+  result_ui = __builtin_stdc_rotate_left(42U, var);
+
+  // CHECK: call i9 @llvm.fshl.i9(i9 %{{.*}}, i9 %{{.*}}, i9 1)
+  result_9 = __builtin_stdc_rotate_left(val9, 1);
+
+  // CHECK: call i37 @llvm.fshl.i37(i37 %{{.*}}, i37 %{{.*}}, i37 36)
+  result_37 = __builtin_stdc_rotate_left(val37, 36);
+
+  // CHECK: call i9 @llvm.fshl.i9(i9 %{{.*}}, i9 %{{.*}}, i9 8)
+  result_9 = __builtin_stdc_rotate_left(val9, -1);
+
+  // CHECK: call i37 @llvm.fshl.i37(i37 %{{.*}}, i37 %{{.*}}, i37 32)
+  result_37 = __builtin_stdc_rotate_left(val37, -5);
+
+}
+
 char rotr8(char x, char y) {
 // CHECK-LABEL: rotr8
 // CHECK: [[F:%.*]] = call i8 @llvm.fshr.i8(i8 [[X:%.*]], i8 [[X]], i8 [[Y:%.*]])
@@ -64,3 +142,55 @@ 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, unsigned _BitInt(9) val9) {
+
+  volatile unsigned char result_uc;
+  volatile unsigned short result_us;
+  volatile unsigned int result_ui;
+  volatile unsigned long result_ul;
+  volatile unsigned long long result_ull;
+  volatile unsigned __int128 result_ui128;
+  volatile unsigned _BitInt(128) result_ubi128;
+  volatile unsigned _BitInt(9) result_9;
+
+  // 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);
+
+  // CHECK: call i8 @llvm.fshr.i8(i8 %{{.*}}, i8 %{{.*}}, i8 7)
+  result_uc = __builtin_stdc_rotate_right(uc, -1);
+
+  // CHECK: call i16 @llvm.fshr.i16(i16 %{{.*}}, i16 %{{.*}}, i16 13)
+  result_us = __builtin_stdc_rotate_right(us, -3);
+
+  // CHECK: call i64 @llvm.fshr.i64(i64 %{{.*}}, i64 %{{.*}}, i64 48)
+  result_ull = __builtin_stdc_rotate_right(ull, -16);
+
+  // CHECK: call i9 @llvm.fshr.i9(i9 %{{.*}}, i9 %{{.*}}, i9 1)
+  result_9 = __builtin_stdc_rotate_right(val9, 1);
+
+  // CHECK: call i9 @llvm.fshr.i9(i9 %{{.*}}, i9 %{{.*}}, i9 8)
+  result_9 = __builtin_stdc_rotate_right(val9, -1);
+
+}
diff --git a/clang/test/Sema/builtin-stdc-rotate.c b/clang/test/Sema/builtin-stdc-rotate.c
new file mode 100644
index 0000000000000..e061e20cf4893
--- /dev/null
+++ b/clang/test/Sema/builtin-stdc-rotate.c
@@ -0,0 +1,56 @@
+// 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 _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, "");
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..ca6663f6edc57
--- /dev/null
+++ b/clang/test/SemaCXX/constexpr-builtin-stdc-rotate.cpp
@@ -0,0 +1,107 @@
+// 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(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_constexpr_stdc_rotate



More information about the cfe-commits mailing list