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

>From e262508ece2cfc8897ef23854f63b13a23f5e35e Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnag...@protonmail.com>
Date: Tue, 23 Sep 2025 02:17:49 -0700
Subject: [PATCH] [clang] Implement __builtin_stdc_rotate_{left,right}

Resolves #122819
---
 clang/docs/LanguageExtensions.rst             | 29 ++++++++
 clang/include/clang/Basic/Builtins.td         | 12 +++
 clang/lib/AST/ExprConstant.cpp                |  2 +
 clang/lib/CodeGen/CGBuiltin.cpp               |  2 +
 clang/lib/Sema/SemaChecking.cpp               | 48 ++++++++++++
 clang/test/CodeGen/builtin-rotate.c           | 73 +++++++++++++++++++
 clang/test/Sema/builtin-stdc-rotate.c         | 18 +++++
 .../SemaCXX/constexpr-builtin-stdc-rotate.cpp | 45 ++++++++++++
 8 files changed, 229 insertions(+)
 create mode 100644 clang/test/Sema/builtin-stdc-rotate.c
 create mode 100644 clang/test/SemaCXX/constexpr-builtin-stdc-rotate.cpp

diff --git a/clang/docs/LanguageExtensions.rst 
b/clang/docs/LanguageExtensions.rst
index 25f4e3b3fbd26..4c5f8d03558cd 100644
--- a/clang/docs/LanguageExtensions.rst
+++ b/clang/docs/LanguageExtensions.rst
@@ -3679,6 +3679,35 @@ 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 taken modulo the bit-width of the value being rotated. These builtins
+can be used within constant expressions.
+
+**Example of use**:
+
+.. code-block:: c
+
+  unsigned char rotated_left = __builtin_stdc_rotate_left((unsigned char)0xB1, 
3);
+  unsigned int rotated_right = __builtin_stdc_rotate_right(0x12345678U, 8);
+
+  unsigned _BitInt(128) big_val = 0x123456789ULL;
+  unsigned _BitInt(128) rotated = __builtin_stdc_rotate_left(big_val, 5);
+
 ``__builtin_unreachable``
 -------------------------
 
diff --git a/clang/include/clang/Basic/Builtins.td 
b/clang/include/clang/Basic/Builtins.td
index 35d2c3e19fdf9..49987df6a380b 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -767,12 +767,24 @@ def RotateLeft : BitInt8_16_32_64BuiltinsTemplate, 
Builtin {
   let Prototype = "T(T, T)";
 }
 
+def StdcRotateLeft : Builtin {
+  let Spellings = ["__builtin_stdc_rotate_left"];
+  let Attributes = [NoThrow, Const, Constexpr, CustomTypeChecking];
+  let Prototype = "void(...)";
+}
+
 def RotateRight : BitInt8_16_32_64BuiltinsTemplate, Builtin {
   let Spellings = ["__builtin_rotateright"];
   let Attributes = [NoThrow, Const, Constexpr];
   let Prototype = "T(T, T)";
 }
 
+def StdcRotateRight : Builtin {
+  let Spellings = ["__builtin_stdc_rotate_right"];
+  let Attributes = [NoThrow, Const, Constexpr, CustomTypeChecking];
+  let Prototype = "void(...)";
+}
+
 // Random GCC builtins
 // FIXME: The builtins marked FunctionWithBuiltinPrefix below should be
 //        merged with the library definitions. They are currently not because
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index a4fca1811c92b..974ad68434030 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -14139,6 +14139,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const 
CallExpr *E,
   case Builtin::BI__builtin_rotateleft16:
   case Builtin::BI__builtin_rotateleft32:
   case Builtin::BI__builtin_rotateleft64:
+  case Builtin::BI__builtin_stdc_rotate_left:
   case Builtin::BI_rotl8: // Microsoft variants of rotate right
   case Builtin::BI_rotl16:
   case Builtin::BI_rotl:
@@ -14156,6 +14157,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const 
CallExpr *E,
   case Builtin::BI__builtin_rotateright16:
   case Builtin::BI__builtin_rotateright32:
   case Builtin::BI__builtin_rotateright64:
+  case Builtin::BI__builtin_stdc_rotate_right:
   case Builtin::BI_rotr8: // Microsoft variants of rotate right
   case Builtin::BI_rotr16:
   case Builtin::BI_rotr:
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index f7c3dea257d50..9f96b6f940b5c 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -3642,6 +3642,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 +3654,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 e73e81c440cc1..43a77a53fb436 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 Vector = true) {
   QualType MaskTy = MaskArg->getType();
@@ -3416,6 +3458,12 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, 
unsigned BuiltinID,
       return ExprError();
     break;
 
+  case Builtin::BI__builtin_stdc_rotate_left:
+  case Builtin::BI__builtin_stdc_rotate_right:
+    if (BuiltinRotateGeneric(*this, TheCall))
+      return ExprError();
+    break;
+
   case Builtin::BI__builtin_allow_runtime_check: {
     Expr *Arg = TheCall->getArg(0);
     // Check if the argument is a string literal.
diff --git a/clang/test/CodeGen/builtin-rotate.c 
b/clang/test/CodeGen/builtin-rotate.c
index 8fc1701c6c9bb..5a3e5501e9c45 100644
--- a/clang/test/CodeGen/builtin-rotate.c
+++ b/clang/test/CodeGen/builtin-rotate.c
@@ -32,6 +32,43 @@ unsigned long long rotl64(unsigned long long x, long long y) 
{
   return __builtin_rotateleft64(x, y);
 }
 
+// CHECK-LABEL: test_builtin_stdc_rotate_left
+void test_builtin_stdc_rotate_left(unsigned char uc, unsigned short us,
+                               unsigned int ui, unsigned long ul,
+                               unsigned long long ull, unsigned __int128 ui128,
+                               unsigned _BitInt(128) ubi128) {
+
+volatile unsigned char result_uc;
+volatile unsigned int result_ui;
+volatile unsigned short result_us;
+volatile unsigned long result_ul;
+volatile unsigned long long result_ull;
+volatile unsigned __int128 result_ui128;
+volatile unsigned _BitInt(128) result_ubi128;
+
+  // CHECK: call i8 @llvm.fshl.i8(i8 %{{.*}}, i8 %{{.*}}, i8 3)
+  result_uc = __builtin_stdc_rotate_left(uc, 3);
+
+  // CHECK: call i16 @llvm.fshl.i16(i16 %{{.*}}, i16 %{{.*}}, i16 5)
+  result_us = __builtin_stdc_rotate_left(us, 5);
+
+  // CHECK: call i32 @llvm.fshl.i32(i32 %{{.*}}, i32 %{{.*}}, i32 8)
+  result_ui = __builtin_stdc_rotate_left(ui, 8);
+
+  // CHECK: call i{{32|64}} @llvm.fshl.i{{32|64}}(i{{32|64}} %{{.*}}, 
i{{32|64}} %{{.*}}, i{{32|64}} 8)
+  result_ul = __builtin_stdc_rotate_left(ul, 8);
+
+  // CHECK: call i64 @llvm.fshl.i64(i64 %{{.*}}, i64 %{{.*}}, i64 16)
+  result_ull = __builtin_stdc_rotate_left(ull, 16);
+
+  // CHECK: call i128 @llvm.fshl.i128(i128 %{{.*}}, i128 %{{.*}}, i128 32)
+  result_ui128 = __builtin_stdc_rotate_left(ui128, 32);
+
+  // CHECK: call i128 @llvm.fshl.i128(i128 %{{.*}}, i128 %{{.*}}, i128 64)
+  result_ubi128 = __builtin_stdc_rotate_left(ubi128, 64);
+
+}
+
 char rotr8(char x, char y) {
 // CHECK-LABEL: rotr8
 // CHECK: [[F:%.*]] = call i8 @llvm.fshr.i8(i8 [[X:%.*]], i8 [[X]], i8 
[[Y:%.*]])
@@ -64,3 +101,39 @@ long long rotr64(long long x, unsigned long long y) {
   return __builtin_rotateright64(x, y);
 }
 
+// CHECK-LABEL: test_builtin_stdc_rotate_right
+void test_builtin_stdc_rotate_right(unsigned char uc, unsigned short us,
+                               unsigned int ui, unsigned long ul,
+                               unsigned long long ull, unsigned __int128 ui128,
+                               unsigned _BitInt(128) ubi128) {
+
+  volatile unsigned char result_uc;
+  volatile unsigned int result_ui;
+  volatile unsigned short result_us;
+  volatile unsigned long result_ul;
+  volatile unsigned long long result_ull;
+  volatile unsigned __int128 result_ui128;
+  volatile unsigned _BitInt(128) result_ubi128;
+
+  // CHECK: call i8 @llvm.fshr.i8(i8 %{{.*}}, i8 %{{.*}}, i8 3)
+  result_uc = __builtin_stdc_rotate_right(uc, 3);
+
+  // CHECK: call i16 @llvm.fshr.i16(i16 %{{.*}}, i16 %{{.*}}, i16 5)
+  result_us = __builtin_stdc_rotate_right(us, 5);
+
+  // CHECK: call i32 @llvm.fshr.i32(i32 %{{.*}}, i32 %{{.*}}, i32 8)
+  result_ui = __builtin_stdc_rotate_right(ui, 8);
+
+  // CHECK: call i{{32|64}} @llvm.fshr.i{{32|64}}(i{{32|64}} %{{.*}}, 
i{{32|64}} %{{.*}}, i{{32|64}} 8)
+  result_ul = __builtin_stdc_rotate_right(ul, 8);
+
+  // CHECK: call i64 @llvm.fshr.i64(i64 %{{.*}}, i64 %{{.*}}, i64 16)
+  result_ull = __builtin_stdc_rotate_right(ull, 16);
+
+  // CHECK: call i128 @llvm.fshr.i128(i128 %{{.*}}, i128 %{{.*}}, i128 32)
+  result_ui128 = __builtin_stdc_rotate_right(ui128, 32);
+
+  // CHECK: call i128 @llvm.fshr.i128(i128 %{{.*}}, i128 %{{.*}}, i128 64)
+  result_ubi128 = __builtin_stdc_rotate_right(ubi128, 64);
+
+}
diff --git a/clang/test/Sema/builtin-stdc-rotate.c 
b/clang/test/Sema/builtin-stdc-rotate.c
new file mode 100644
index 0000000000000..9e64e29faaade
--- /dev/null
+++ b/clang/test/Sema/builtin-stdc-rotate.c
@@ -0,0 +1,18 @@
+// 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), "");
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..caca850a4201f
--- /dev/null
+++ b/clang/test/SemaCXX/constexpr-builtin-stdc-rotate.cpp
@@ -0,0 +1,45 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c++14 -fsyntax-only 
-verify %s
+// expected-no-diagnostics
+
+namespace test_constexpr_stdc_rotate {
+
+static_assert(__builtin_stdc_rotate_left((unsigned char)0b10110001, 3) == 
(unsigned char)0b10001101, "");
+static_assert(__builtin_stdc_rotate_left((unsigned short)0x1234, 4) == 
(unsigned short)0x2341, "");
+static_assert(__builtin_stdc_rotate_left(0x12345678U, 8) == 0x34567812U, "");
+static_assert(__builtin_stdc_rotate_left(0x123456789ABCDEF0ULL, 16) == 
0x56789ABCDEF01234ULL, "");
+static_assert(__builtin_stdc_rotate_right((unsigned char)0b10110001, 3) == 
(unsigned char)0b00110110, "");
+static_assert(__builtin_stdc_rotate_right(0x12345678U, 8) == 0x78123456U, "");
+static_assert(__builtin_stdc_rotate_right(0x123456789ABCDEF0ULL, 16) == 
0xDEF0123456789ABCULL, "");
+static_assert(__builtin_stdc_rotate_left(0x12345678U, 0) == 0x12345678U, "");
+static_assert(__builtin_stdc_rotate_left(0x12345678U, 32) == 0x12345678U, "");
+static_assert(__builtin_stdc_rotate_left(0x80000000U, 1) == 0x00000001U, "");
+static_assert(__builtin_stdc_rotate_right(__builtin_stdc_rotate_left(0x12345678U,
 8), 8) == 0x12345678U, "");
+static_assert(__builtin_stdc_rotate_left(0x12345678U, 40) == 
__builtin_stdc_rotate_left(0x12345678U, 8), "");
+static_assert(__builtin_stdc_rotate_left(0x00000000U, 7) == 0x00000000U, "");
+static_assert(__builtin_stdc_rotate_left((unsigned char)0x01, 2) == (unsigned 
char)0x04, "");
+static_assert(__builtin_stdc_rotate_left((unsigned char)0xAA, 1) == (unsigned 
char)0x55, "");
+static_assert(__builtin_stdc_rotate_left((unsigned char)0x12, 4) == (unsigned 
char)0x21, "");
+static_assert(__builtin_stdc_rotate_left((unsigned short)0x1234, 4) == 
(unsigned short)0x2341, "");
+static_assert(__builtin_stdc_rotate_left(0x12345678U, 4) == 0x23456781U, "");
+
+namespace test_int128 {
+
+static_assert(__builtin_stdc_rotate_left((unsigned __int128)1, 127) == 
(unsigned __int128)1 << 127, "");
+
+constexpr unsigned __int128 test_pattern = 0x123456789ABCDEF0ULL;
+static_assert(__builtin_stdc_rotate_left(test_pattern, 1) == test_pattern << 
1, "");
+
+} // namespace test_int128
+
+namespace test_bitint {
+
+static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(37))1, 36) == 
((unsigned _BitInt(37))1 << 36), "");
+
+constexpr unsigned _BitInt(128) bi128_pattern = 0x123456789ABCDEF0ULL;
+static_assert(__builtin_stdc_rotate_left(bi128_pattern, 1) == bi128_pattern << 
1, "");
+
+static_assert(__builtin_stdc_rotate_left((unsigned _BitInt(3))0b101, 1) == 
(unsigned _BitInt(3))0b011, "");
+
+} // namespace test_bitint
+
+} // namespace test_constexpr_stdc_rotate

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to