[clang] [Clang] Add __builtin_bswapg (PR #162433)

via cfe-commits cfe-commits at lists.llvm.org
Thu Oct 9 20:39:14 PDT 2025


https://github.com/clingfei updated https://github.com/llvm/llvm-project/pull/162433

>From 92466f3789ce1849ebee8a405efd42e191c591f5 Mon Sep 17 00:00:00 2001
From: clingfei <1599101385 at qq.com>
Date: Wed, 8 Oct 2025 15:05:44 +0800
Subject: [PATCH 1/4] [Clang] Add __builtin_bswapg

---
 clang/include/clang/Basic/Builtins.td         |  6 ++++
 clang/lib/AST/ByteCode/InterpBuiltin.cpp      | 10 ++++++-
 clang/lib/AST/ExprConstant.cpp                | 11 ++++++++
 clang/lib/CodeGen/CGBuiltin.cpp               |  1 +
 clang/lib/Sema/SemaChecking.cpp               | 28 +++++++++++++++++++
 clang/test/AST/ByteCode/builtin-functions.cpp |  4 +++
 clang/test/CodeGen/builtins.c                 |  2 +-
 clang/test/Sema/constant-builtins-2.c         |  4 +++
 clang/test/Sema/constant-builtins.c           |  5 +++-
 9 files changed, 68 insertions(+), 3 deletions(-)

diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 468121f7d20ab..e65ed2f20be97 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -755,6 +755,12 @@ def BSwap : Builtin, Template<["unsigned short", "uint32_t", "uint64_t"],
   let Prototype = "T(T)";
 }
 
+def BSwapg : Builtin {
+  let Spellings = ["__builtin_bswapg"];
+  let Attributes = [NoThrow, Const, Constexpr, CustomTypeChecking];
+  let Prototype = "int(...)";
+}
+
 def Bitreverse : BitInt8_16_32_64BuiltinsTemplate, Builtin {
   let Spellings = ["__builtin_bitreverse"];
   let Attributes = [NoThrow, Const, Constexpr];
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 1eea813b8c556..b8d17fbce6d4e 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -3288,7 +3288,15 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
   case Builtin::BI__builtin_elementwise_ctzg:
     return interp__builtin_elementwise_countzeroes(S, OpPC, Frame, Call,
                                                    BuiltinID);
-
+  case Builtin::BI__builtin_bswapg: {
+    const APSInt &Val = popToAPSInt(S, Call->getArg(0));
+      assert(Val.getActiveBits() <= 64);
+    if (Val.getBitWidth() == 8)
+        pushInteger(S, Val, Call->getType());
+    else
+        pushInteger(S, Val.byteSwap(), Call->getType());
+    return true;
+  }
   case Builtin::BI__builtin_bswap16:
   case Builtin::BI__builtin_bswap32:
   case Builtin::BI__builtin_bswap64:
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 618e1636e9e53..058905e7fd3c0 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -13982,6 +13982,17 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
 
     return Success(Val.reverseBits(), E);
   }
+  case Builtin::BI__builtin_bswapg: {
+    APSInt Val;
+    if (!EvaluateInteger(E->getArg(0), Val, Info))
+      return false;
+    if (Val.getBitWidth() == 8) {
+        bool ret =  Success(Val, E);
+        return ret;
+    }
+        
+    return Success(Val.byteSwap(), E);
+  }
 
   case Builtin::BI__builtin_bswap16:
   case Builtin::BI__builtin_bswap32:
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 9ee810c9d5775..7733f4dc15f5d 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -3622,6 +3622,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
           Builder.CreateArithmeticFence(ArgValue, ConvertType(ArgType)));
     return RValue::get(ArgValue);
   }
+  case Builtin::BI__builtin_bswapg:
   case Builtin::BI__builtin_bswap16:
   case Builtin::BI__builtin_bswap32:
   case Builtin::BI__builtin_bswap64:
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 063db05665af1..362b53676feaa 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -2200,6 +2200,30 @@ static bool BuiltinCpu(Sema &S, const TargetInfo &TI, CallExpr *TheCall,
   return false;
 }
 
+/// Checks that __builtin_bswapg was called with a single argument, which is an
+/// unsigned integer, and overrides the return value type to the integer type.
+static bool BuiltinBswapg(Sema &S, CallExpr *TheCall) {
+  if (S.checkArgCount(TheCall, 1))
+    return true;
+  ExprResult ArgRes = S.DefaultLvalueConversion(TheCall->getArg(0));
+  if (ArgRes.isInvalid())
+    return true;
+
+  Expr *Arg = ArgRes.get();
+  TheCall->setArg(0, Arg);
+
+  QualType ArgTy = Arg->getType();
+
+  if (!ArgTy->isIntegerType()) {
+    S.Diag(Arg->getBeginLoc(), diag::err_builtin_invalid_arg_type)
+        << 1 << /* scalar */ 1 << /* unsigned integer ty */ 1 << /* no fp */ 0
+        << ArgTy;
+    return true;
+  }
+  TheCall->setType(ArgTy);
+  return false;
+}
+
 /// Checks that __builtin_popcountg was called with a single argument, which is
 /// an unsigned integer.
 static bool BuiltinPopcountg(Sema &S, CallExpr *TheCall) {
@@ -3448,6 +3472,10 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
     }
     break;
   }
+  case Builtin::BI__builtin_bswapg:
+    if (BuiltinBswapg(*this, TheCall))
+        return ExprError();
+    break;
   case Builtin::BI__builtin_popcountg:
     if (BuiltinPopcountg(*this, TheCall))
       return ExprError();
diff --git a/clang/test/AST/ByteCode/builtin-functions.cpp b/clang/test/AST/ByteCode/builtin-functions.cpp
index f47bc49d9a1a8..84ff9cc137bf3 100644
--- a/clang/test/AST/ByteCode/builtin-functions.cpp
+++ b/clang/test/AST/ByteCode/builtin-functions.cpp
@@ -824,6 +824,10 @@ namespace bswap {
   int h3 = __builtin_bswap16(0x1234) == 0x3412 ? 1 : f();
   int h4 = __builtin_bswap32(0x1234) == 0x34120000 ? 1 : f();
   int h5 = __builtin_bswap64(0x1234) == 0x3412000000000000 ? 1 : f();
+  int h6 = __builtin_bswapg(0x12) == 0x12 ? 1 : f();
+  int h7 = __builtin_bswapg(0x1234) == 0x3412 ? 1 : f();
+  int h8 = __builtin_bswapg(0x00001234) == 0x34120000 ? 1 : f();
+  int h9 = __builtin_bswapg(0x0000000000001234) == 0x3412000000000000 ? 1 : f();
 }
 
 #define CFSTR __builtin___CFStringMakeConstantString
diff --git a/clang/test/CodeGen/builtins.c b/clang/test/CodeGen/builtins.c
index 738814c88bf56..11d8302816c84 100644
--- a/clang/test/CodeGen/builtins.c
+++ b/clang/test/CodeGen/builtins.c
@@ -130,7 +130,7 @@ int main(void) {
   P(object_size, (s0, 3));
 
   // Whatever
-
+  P(bswapg, (N));
   P(bswap16, (N));
   P(bswap32, (N));
   P(bswap64, (N));
diff --git a/clang/test/Sema/constant-builtins-2.c b/clang/test/Sema/constant-builtins-2.c
index e465a3c5f0ad8..96bd2c966c2ef 100644
--- a/clang/test/Sema/constant-builtins-2.c
+++ b/clang/test/Sema/constant-builtins-2.c
@@ -479,6 +479,10 @@ int h0 = __builtin_types_compatible_p(int, float);
 int h3 = __builtin_bswap16(0x1234) == 0x3412 ? 1 : f();
 int h4 = __builtin_bswap32(0x1234) == 0x34120000 ? 1 : f();
 int h5 = __builtin_bswap64(0x1234) == 0x3412000000000000 ? 1 : f();
+int h6 = __builtin_bswapg((char)(0x12)) == (char)(0x12) ? 1 : f();
+int h7 = __builtin_bswapg((short)(0x1234)) == (short)(0x3412) ? 1 : f();
+int h8 = __builtin_bswapg(0x00001234) == 0x34120000 ? 1 : f();
+int h9 = __builtin_bswapg(0x0000000000001234ULL) == 0x3412000000000000 ? 1 : f();
 extern long int bi0;
 extern __typeof__(__builtin_expect(0, 0)) bi0;
 
diff --git a/clang/test/Sema/constant-builtins.c b/clang/test/Sema/constant-builtins.c
index 964ab59e787c4..6c13fe96b6b4a 100644
--- a/clang/test/Sema/constant-builtins.c
+++ b/clang/test/Sema/constant-builtins.c
@@ -25,7 +25,10 @@ int h0 = __builtin_types_compatible_p(int,float);
 int h3 = __builtin_bswap16(0x1234) == 0x3412 ? 1 : f();
 int h4 = __builtin_bswap32(0x1234) == 0x34120000 ? 1 : f();
 int h5 = __builtin_bswap64(0x1234) == 0x3412000000000000 ? 1 : f();
-
+int h6 = __builtin_bswapg((char)0x12) == (char)0x12 ? 1 : f();
+int h7 = __builtin_bswapg((short)(0x1234)) == (short)(0x3412) ? 1 : f();
+int h8 = __builtin_bswapg(0x00001234) == 0x34120000 ? 1 : f();
+int h9 = __builtin_bswapg(0x0000000000001234ULL) == 0x3412000000000000 ? 1 : f();
 short somefunc(void);
 
 short t = __builtin_constant_p(5353) ? 42 : somefunc();

>From d4ea8686cd97c203ec458e4cd56cd67f941bbfd0 Mon Sep 17 00:00:00 2001
From: clingfei <1599101385 at qq.com>
Date: Thu, 9 Oct 2025 13:14:08 +0800
Subject: [PATCH 2/4] handle special case 8 in interp__builtin_bswap and fix
 extra space in VisitBuiltinCallExpr

---
 clang/lib/AST/ByteCode/InterpBuiltin.cpp | 16 +++++-----------
 clang/lib/AST/ExprConstant.cpp           |  6 ++----
 2 files changed, 7 insertions(+), 15 deletions(-)

diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index b8d17fbce6d4e..01f2cd280b175 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -1004,8 +1004,10 @@ static bool interp__builtin_bswap(InterpState &S, CodePtr OpPC,
                                   const CallExpr *Call) {
   const APSInt &Val = popToAPSInt(S, Call->getArg(0));
   assert(Val.getActiveBits() <= 64);
-
-  pushInteger(S, Val.byteSwap(), Call->getType());
+  if (Val.getBitWidth() == 8)
+    pushInteger(S, Val, Call->getType());
+  else
+    pushInteger(S, Val.byteSwap(), Call->getType());
   return true;
 }
 
@@ -3288,15 +3290,7 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
   case Builtin::BI__builtin_elementwise_ctzg:
     return interp__builtin_elementwise_countzeroes(S, OpPC, Frame, Call,
                                                    BuiltinID);
-  case Builtin::BI__builtin_bswapg: {
-    const APSInt &Val = popToAPSInt(S, Call->getArg(0));
-      assert(Val.getActiveBits() <= 64);
-    if (Val.getBitWidth() == 8)
-        pushInteger(S, Val, Call->getType());
-    else
-        pushInteger(S, Val.byteSwap(), Call->getType());
-    return true;
-  }
+  case Builtin::BI__builtin_bswapg:
   case Builtin::BI__builtin_bswap16:
   case Builtin::BI__builtin_bswap32:
   case Builtin::BI__builtin_bswap64:
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 058905e7fd3c0..88c3a1cbcd833 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -13986,10 +13986,8 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
     APSInt Val;
     if (!EvaluateInteger(E->getArg(0), Val, Info))
       return false;
-    if (Val.getBitWidth() == 8) {
-        bool ret =  Success(Val, E);
-        return ret;
-    }
+    if (Val.getBitWidth() == 8)
+      return Success(Val, E);
         
     return Success(Val.byteSwap(), E);
   }

>From fa016a1bf3eb112754d24aac1823abe4e326509f Mon Sep 17 00:00:00 2001
From: clingfei <1599101385 at qq.com>
Date: Thu, 9 Oct 2025 22:54:57 +0800
Subject: [PATCH 3/4] add dependent check for argument

---
 clang/lib/Sema/SemaChecking.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 362b53676feaa..486fc8d083125 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -2211,6 +2211,8 @@ static bool BuiltinBswapg(Sema &S, CallExpr *TheCall) {
 
   Expr *Arg = ArgRes.get();
   TheCall->setArg(0, Arg);
+  if (Arg->isTypeDependent())
+    return false;
 
   QualType ArgTy = Arg->getType();
 

>From c0d0f3c179ff0dd0ccf3683cf075de492fbef12c Mon Sep 17 00:00:00 2001
From: clingfei <1599101385 at qq.com>
Date: Fri, 10 Oct 2025 10:56:22 +0800
Subject: [PATCH 4/4] fix bswapg codegen and add c++ tests

---
 clang/lib/AST/ExprConstant.cpp     |   2 +-
 clang/lib/CodeGen/CGBuiltin.cpp    |  10 ++-
 clang/lib/Sema/SemaChecking.cpp    |   2 +-
 clang/test/CodeGen/builtins.c      |   5 +-
 clang/test/Sema/builtin-bswapg.cpp | 121 +++++++++++++++++++++++++++++
 5 files changed, 136 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/Sema/builtin-bswapg.cpp

diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 88c3a1cbcd833..745e8d9201183 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -13988,7 +13988,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
       return false;
     if (Val.getBitWidth() == 8)
       return Success(Val, E);
-        
+
     return Success(Val.byteSwap(), E);
   }
 
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 7733f4dc15f5d..124351e7f1e59 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -3622,7 +3622,15 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
           Builder.CreateArithmeticFence(ArgValue, ConvertType(ArgType)));
     return RValue::get(ArgValue);
   }
-  case Builtin::BI__builtin_bswapg:
+  case Builtin::BI__builtin_bswapg: {
+    Value *ArgValue = EmitScalarExpr(E->getArg(0));
+    llvm::IntegerType *IntTy = cast<llvm::IntegerType>(ArgValue->getType());
+    assert(IntTy && "LLVM's __builtin_bswapg only supports integer variants");
+    if (IntTy->getBitWidth() == 8)
+      return RValue::get(ArgValue);
+    return RValue::get(
+        emitBuiltinWithOneOverloadedType<1>(*this, E, Intrinsic::bswap));
+  }
   case Builtin::BI__builtin_bswap16:
   case Builtin::BI__builtin_bswap32:
   case Builtin::BI__builtin_bswap64:
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 486fc8d083125..80afa08f3f3af 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -3476,7 +3476,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
   }
   case Builtin::BI__builtin_bswapg:
     if (BuiltinBswapg(*this, TheCall))
-        return ExprError();
+      return ExprError();
     break;
   case Builtin::BI__builtin_popcountg:
     if (BuiltinPopcountg(*this, TheCall))
diff --git a/clang/test/CodeGen/builtins.c b/clang/test/CodeGen/builtins.c
index 11d8302816c84..66ac8ad7431fb 100644
--- a/clang/test/CodeGen/builtins.c
+++ b/clang/test/CodeGen/builtins.c
@@ -130,7 +130,10 @@ int main(void) {
   P(object_size, (s0, 3));
 
   // Whatever
-  P(bswapg, (N));
+  P(bswapg, ((char)N));
+  P(bswapg, ((short)N));
+	P(bswapg, ((int)N));
+	P(bswapg, ((unsigned long)N));
   P(bswap16, (N));
   P(bswap32, (N));
   P(bswap64, (N));
diff --git a/clang/test/Sema/builtin-bswapg.cpp b/clang/test/Sema/builtin-bswapg.cpp
new file mode 100644
index 0000000000000..c7fa6f2410023
--- /dev/null
+++ b/clang/test/Sema/builtin-bswapg.cpp
@@ -0,0 +1,121 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify -fexperimental-new-constant-interpreter %s
+// expected-no-diagnostics
+template <class A, class B> 
+static constexpr bool is_same_type = false;
+
+template <class A> 
+static constexpr bool is_same_type<A, A> = true;
+
+void test_basic_type_checks() {
+  static_assert(is_same_type<char, decltype(__builtin_bswapg((char)0))>, "");
+  static_assert(is_same_type<unsigned char, decltype(__builtin_bswapg((unsigned char)0))>, "");
+  static_assert(is_same_type<short, decltype(__builtin_bswapg((short)0))>, "");
+  static_assert(is_same_type<unsigned short, decltype(__builtin_bswapg((unsigned short)0))>, "");
+  static_assert(is_same_type<int, decltype(__builtin_bswapg((int)0))>, "");
+  static_assert(is_same_type<unsigned int, decltype(__builtin_bswapg((unsigned int)0))>, "");
+  static_assert(is_same_type<long, decltype(__builtin_bswapg((long)0))>, "");
+  static_assert(is_same_type<unsigned long, decltype(__builtin_bswapg((unsigned long)0))>, "");
+}
+
+template<typename T>
+void test_template_type_check() {
+  static_assert(is_same_type<T, decltype(__builtin_bswapg(T{}))>, 
+                "bswapg should return the same type as its argument");
+  constexpr T zero{};
+  constexpr T max = ~T{};
+  constexpr T one = T{1};
+    
+  static_assert(is_same_type<T, decltype(__builtin_bswapg(zero))>, "");
+  static_assert(is_same_type<T, decltype(__builtin_bswapg(max))>, "");
+  static_assert(is_same_type<T, decltype(__builtin_bswapg(one))>, "");
+}
+template void test_template_type_check<char>();
+template void test_template_type_check<unsigned char>();
+template void test_template_type_check<short>();
+template void test_template_type_check<unsigned short>();
+template void test_template_type_check<int>();
+template void test_template_type_check<unsigned int>();
+template void test_template_type_check<long>();
+template void test_template_type_check<unsigned long>();
+
+void test_lambda_type_checks() {
+  auto lambda = [](auto x) {
+    static_assert(is_same_type<decltype(x), decltype(__builtin_bswapg(x))>, 
+                  "bswapg in lambda should preserve type");
+    return __builtin_bswapg(x);
+  };
+  auto result_long = lambda(42UL);
+  static_assert(is_same_type<unsigned long, decltype(result_long)>, "");
+    
+  auto result_int = lambda(42);
+  static_assert(is_same_type<int, decltype(result_int)>, "");
+    
+  auto result_short = lambda(static_cast<short>(42));
+  static_assert(is_same_type<short, decltype(result_short)>, "");
+
+  auto result_char = lambda(static_cast<char>(42));
+  static_assert(is_same_type<char, decltype(result_char)>, "");
+}
+
+auto test_auto_return_type_long(long x) {
+  auto result = __builtin_bswapg(x);
+  static_assert(is_same_type<long, decltype(result)>, "");
+  return result;
+}
+
+auto test_auto_return_type_int(int x) {
+  auto result = __builtin_bswapg(x);
+  static_assert(is_same_type<int, decltype(result)>, "");
+  return result;
+}
+
+auto test_auto_return_type_short(short x) {
+  auto result = __builtin_bswapg(x);
+  static_assert(is_same_type<short, decltype(result)>, "");
+  return result;
+}
+
+auto test_auto_return_type_char(char x) {
+  auto result = __builtin_bswapg(x);
+  static_assert(is_same_type<char, decltype(result)>, "");
+  return result;
+}
+
+void test_auto_return_type() {
+  test_auto_return_type_long(42);
+  test_auto_return_type_int(42);
+  test_auto_return_type_short(42);
+  test_auto_return_type_char(42);
+}
+
+decltype(auto) test_decltype_auto(int x) {
+  return __builtin_bswapg(x);
+}
+
+void test_decltype_auto_check() {
+  int x = 42;
+  auto result = test_decltype_auto(x);
+  static_assert(is_same_type<int, decltype(result)>, "");
+}
+
+template<auto Value>
+struct ValueTemplateTypeTest {
+  using value_type = decltype(Value);
+  using result_type = decltype(__builtin_bswapg(Value));
+    
+  static constexpr bool type_matches = is_same_type<value_type, result_type>;
+  static_assert(type_matches, "Value template bswapg should preserve type");
+    
+  static constexpr auto swapped_value = __builtin_bswapg(Value);
+};
+
+template<auto... Values>
+void test_template_pack_types() {
+  static_assert((is_same_type<decltype(Values), decltype(__builtin_bswapg(Values))> && ...), "All pack elements should preserve type");
+}
+
+template struct ValueTemplateTypeTest<0x1234>;
+template struct ValueTemplateTypeTest<0x12345678UL>;
+template struct ValueTemplateTypeTest<(short)0x1234>;
+template struct ValueTemplateTypeTest<(char)0x12>;
\ No newline at end of file



More information about the cfe-commits mailing list