Skip to content

[clang] Implement C2y stdc_memreverse8 and stdc_memreverse8u{8,16,32,64} builtins#197358

Open
chaitanyav wants to merge 3 commits into
llvm:mainfrom
chaitanyav:memory_reversal
Open

[clang] Implement C2y stdc_memreverse8 and stdc_memreverse8u{8,16,32,64} builtins#197358
chaitanyav wants to merge 3 commits into
llvm:mainfrom
chaitanyav:memory_reversal

Conversation

@chaitanyav
Copy link
Copy Markdown
Contributor

Implements the C2y <stdbit.h> memory reversal functions stdc_memreverse8 and stdc_memreverse8u{8,16,32,64}. The typed variants lower to llvm.bswap and support constexpr evaluation.

@chaitanyav chaitanyav self-assigned this May 13, 2026
@chaitanyav chaitanyav marked this pull request as ready for review May 13, 2026 14:09
@llvmorg-github-actions llvmorg-github-actions Bot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" clang:codegen IR generation bugs: mangling, exceptions, etc. clang:bytecode Issues for the clang bytecode constexpr interpreter labels May 13, 2026
@llvmorg-github-actions
Copy link
Copy Markdown

llvmorg-github-actions Bot commented May 13, 2026

@llvm/pr-subscribers-clang-codegen

@llvm/pr-subscribers-clang

Author: NagaChaitanya Vellanki (chaitanyav)

Changes

Implements the C2y <stdbit.h> memory reversal functions stdc_memreverse8 and stdc_memreverse8u{8,16,32,64}. The typed variants lower to llvm.bswap and support constexpr evaluation.


Full diff: https://github.com/llvm/llvm-project/pull/197358.diff

11 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+5)
  • (modified) clang/include/clang/Basic/Builtins.td (+16)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+2)
  • (modified) clang/lib/AST/ByteCode/InterpBuiltin.cpp (+4)
  • (modified) clang/lib/AST/ExprConstant.cpp (+5-1)
  • (modified) clang/lib/CodeGen/CGBuiltin.cpp (+58)
  • (modified) clang/lib/Sema/SemaChecking.cpp (+12)
  • (modified) clang/test/CodeGen/Inputs/stdbit.h (+6)
  • (added) clang/test/CodeGen/builtin-stdc-memreverse8.c (+96)
  • (modified) clang/test/Sema/Inputs/stdbit.h (+6)
  • (added) clang/test/Sema/builtin-stdc-memreverse8.c (+84)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 4b0eb5b9d8505..85d21e549cd65 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -182,6 +182,11 @@ C2y Feature Support
   ``stdc_rotate_left_{uc,us,ui,ul,ull}`` and
   ``stdc_rotate_right_{uc,us,ui,ul,ull}``.
 
+- Implemented C2y ``<stdbit.h>`` memory reversal functions:
+  ``__builtin_stdc_memreverse8`` / ``stdc_memreverse8`` (in-place byte
+  reversal of a byte array) and ``stdc_memreverse8u{8,16,32,64}`` (byte-swap
+  of an exact-width unsigned integer value, usable in constant expressions).
+
 C23 Feature Support
 ^^^^^^^^^^^^^^^^^^^
 - Clang now allows C23 ``constexpr`` struct member access through the dot operator in constant expressions. (#GH178349)
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 40ec94ab75046..1a9c5facfd43e 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -34,6 +34,10 @@ class IntBitUtilTemplate : Template<
      "unsigned long int", "unsigned long long int"],
     ["_uc", "_us", "_ui", "_ul", "_ull"]>;
 
+class MemReverse8Template : Template<
+    ["unsigned char", "unsigned short", "uint32_t", "uint64_t"],
+    ["u8",            "u16",            "u32",      "u64"]>;
+
 class MSInt8_16_32Template : Template<["char", "short", "msint32_t"],
                                       ["8",    "16",    ""]>;
 
@@ -986,6 +990,18 @@ def StdcRotateRightTyped : LibBuiltin<"stdbit.h", "C2Y_LANG">, IntBitUtilTemplat
   let Prototype = "T(T, unsigned int)";
 }
 
+def StdcMemReverse8: Builtin {
+  let Spellings = ["__builtin_stdc_memreverse8"];
+  let Attributes = [NoThrow, NonNull<NonOptimizing, [1]>];
+  let Prototype = "void(size_t, unsigned char*)";
+}
+
+def StdcMemReverse8Typed : LibBuiltin<"stdbit.h", "C2Y_LANG">, MemReverse8Template {
+  let Spellings = ["stdc_memreverse8"];
+  let Attributes = [NoThrow, Const, Constexpr];
+  let Prototype = "T(T)";
+}
+
 
 // Random GCC builtins
 // FIXME: The builtins marked FunctionWithBuiltinPrefix below should be
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 5c225dc56ddd8..0647036d8c1df 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11512,6 +11512,8 @@ def err_builtin_setjmp_unsupported : Error<
 def err_builtin_longjmp_invalid_val : Error<
   "argument to __builtin_longjmp must be a constant 1">;
 def err_builtin_requires_language : Error<"'%0' is only available in %1">;
+def err_builtin_requires_char_bit_8 : Error<
+  "'%0' is only available on targets where CHAR_BIT == 8">;
 
 def err_constant_integer_arg_type : Error<
   "argument to %0 must be a constant integer">;
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index e05c9aed39b14..f19d2aca4e70c 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -4953,6 +4953,10 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
   case Builtin::BI__builtin_bswap16:
   case Builtin::BI__builtin_bswap32:
   case Builtin::BI__builtin_bswap64:
+  case Builtin::BIstdc_memreverse8u8:
+  case Builtin::BIstdc_memreverse8u16:
+  case Builtin::BIstdc_memreverse8u32:
+  case Builtin::BIstdc_memreverse8u64:
     return interp__builtin_bswap(S, OpPC, Frame, Call);
 
   case Builtin::BI__atomic_always_lock_free:
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 5f09c9ea4a7b8..9da83fb0b3016 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -16483,7 +16483,11 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
   case Builtin::BI__builtin_bswapg:
   case Builtin::BI__builtin_bswap16:
   case Builtin::BI__builtin_bswap32:
-  case Builtin::BI__builtin_bswap64: {
+  case Builtin::BI__builtin_bswap64:
+  case Builtin::BIstdc_memreverse8u8:
+  case Builtin::BIstdc_memreverse8u16:
+  case Builtin::BIstdc_memreverse8u32:
+  case Builtin::BIstdc_memreverse8u64: {
     APSInt Val;
     if (!EvaluateInteger(E->getArg(0), Val, Info))
       return false;
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 1f97c67f26c8e..16f99975be64a 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -3949,6 +3949,64 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     return RValue::get(Phi);
   }
 
+  // stdc_memreverse8u8 is a no-op (single byte, nothing to swap).
+  case Builtin::BIstdc_memreverse8u8:
+    return RValue::get(EmitScalarExpr(E->getArg(0)));
+
+  case Builtin::BIstdc_memreverse8u16:
+  case Builtin::BIstdc_memreverse8u32:
+  case Builtin::BIstdc_memreverse8u64:
+    return RValue::get(
+        emitBuiltinWithOneOverloadedType<1>(*this, E, Intrinsic::bswap));
+
+  case Builtin::BI__builtin_stdc_memreverse8: {
+    Value *N = EmitScalarExpr(E->getArg(0));
+    Value *Ptr = EmitScalarExpr(E->getArg(1));
+
+    if (auto *CI = dyn_cast<ConstantInt>(N)) {
+      uint64_t Size = CI->getZExtValue();
+      if (Size == 2 || Size == 4 || Size == 8) {
+        llvm::Type *IntTy = Builder.getIntNTy(Size * 8);
+        CharUnits Align = CharUnits::One();
+        Address Addr(Ptr, IntTy, Align);
+        Value *Val = Builder.CreateLoad(Addr);
+        Function *F = CGM.getIntrinsic(Intrinsic::bswap, IntTy);
+        Value *Swapped = Builder.CreateCall(F, Val);
+        Builder.CreateStore(Swapped, Addr);
+        return RValue::get(nullptr);
+      }
+    }
+
+    // General case: emit a loop swapping ptr[i] and ptr[n-i-1].
+    BasicBlock *EntryBB = Builder.GetInsertBlock();
+    BasicBlock *LoopBB = createBasicBlock("memreverse8.loop", CurFn);
+    BasicBlock *AfterBB = createBasicBlock("memreverse8.after", CurFn);
+    Value *Half = Builder.CreateLShr(N, ConstantInt::get(N->getType(), 1));
+    Value *IsEmpty =
+        Builder.CreateICmpEQ(Half, ConstantInt::get(Half->getType(), 0));
+    Builder.CreateCondBr(IsEmpty, AfterBB, LoopBB);
+
+    Builder.SetInsertPoint(LoopBB);
+    PHINode *Idx = Builder.CreatePHI(Half->getType(), 2, "i");
+    Idx->addIncoming(ConstantInt::get(Half->getType(), 0), EntryBB);
+    Value *J = Builder.CreateSub(Builder.CreateSub(N, Idx),
+                                 ConstantInt::get(N->getType(), 1));
+    Address AddrI(Builder.CreateGEP(Int8Ty, Ptr, Idx), Int8Ty,
+                  CharUnits::One());
+    Address AddrJ(Builder.CreateGEP(Int8Ty, Ptr, J), Int8Ty, CharUnits::One());
+    Value *XI = Builder.CreateLoad(AddrI);
+    Value *XJ = Builder.CreateLoad(AddrJ);
+    Builder.CreateStore(XJ, AddrI);
+    Builder.CreateStore(XI, AddrJ);
+    Value *Next = Builder.CreateAdd(Idx, ConstantInt::get(Half->getType(), 1));
+    Idx->addIncoming(Next, LoopBB);
+    Value *Done = Builder.CreateICmpEQ(Next, Half);
+    Builder.CreateCondBr(Done, AfterBB, LoopBB);
+
+    Builder.SetInsertPoint(AfterBB);
+    return RValue::get(nullptr);
+  }
+
   case Builtin::BI__builtin_constant_p: {
     llvm::Type *ResultType = ConvertType(E->getType());
 
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 530587208cce8..685298d33ddb8 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -3863,6 +3863,18 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
       return ExprError();
     break;
 
+  case Builtin::BI__builtin_stdc_memreverse8:
+  case Builtin::BIstdc_memreverse8u8:
+  case Builtin::BIstdc_memreverse8u16:
+  case Builtin::BIstdc_memreverse8u32:
+  case Builtin::BIstdc_memreverse8u64:
+    if (Context.getTargetInfo().getCharWidth() != 8) {
+      Diag(TheCall->getBeginLoc(), diag::err_builtin_requires_char_bit_8)
+          << TheCall->getDirectCallee()->getName();
+      return ExprError();
+    }
+    break;
+
   case Builtin::BI__builtin_stdc_bit_floor:
   case Builtin::BI__builtin_stdc_bit_ceil:
     if (BuiltinStdCBuiltin(*this, TheCall, QualType()))
diff --git a/clang/test/CodeGen/Inputs/stdbit.h b/clang/test/CodeGen/Inputs/stdbit.h
index bdabb9ec8ee7d..ca8b87deb67e3 100644
--- a/clang/test/CodeGen/Inputs/stdbit.h
+++ b/clang/test/CodeGen/Inputs/stdbit.h
@@ -112,4 +112,10 @@ unsigned int stdc_rotate_right_ui(unsigned int, unsigned int);
 unsigned long stdc_rotate_right_ul(unsigned long, unsigned int);
 unsigned long long stdc_rotate_right_ull(unsigned long long, unsigned int);
 
+void stdc_memreverse8(__SIZE_TYPE__, unsigned char *);
+unsigned char stdc_memreverse8u8(unsigned char);
+unsigned short stdc_memreverse8u16(unsigned short);
+__UINT32_TYPE__ stdc_memreverse8u32(__UINT32_TYPE__);
+__UINT64_TYPE__ stdc_memreverse8u64(__UINT64_TYPE__);
+
 #endif
diff --git a/clang/test/CodeGen/builtin-stdc-memreverse8.c b/clang/test/CodeGen/builtin-stdc-memreverse8.c
new file mode 100644
index 0000000000000..730f29bb9664a
--- /dev/null
+++ b/clang/test/CodeGen/builtin-stdc-memreverse8.c
@@ -0,0 +1,96 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c2y -emit-llvm -o - %s | FileCheck %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c2y -isystem %S/Inputs -DTEST_C2Y_LIB_SPELLINGS %s -emit-llvm -o - | FileCheck %s --check-prefix=C2Y
+
+#ifndef TEST_C2Y_LIB_SPELLINGS
+
+// N=0 and N=1: IsEmpty folds to true, branch to after without entering loop.
+// CHECK-LABEL: test_memreverse8_const0
+// CHECK: br i1 true,
+void test_memreverse8_const0(unsigned char *p) {
+  __builtin_stdc_memreverse8(0, p);
+}
+
+// CHECK-LABEL: test_memreverse8_const1
+// CHECK: br i1 true,
+void test_memreverse8_const1(unsigned char *p) {
+  __builtin_stdc_memreverse8(1, p);
+}
+
+// N=2, 4, 8: lowered to a single bswap with unaligned load/store.
+// CHECK-LABEL: test_memreverse8_const2
+// CHECK: load i16, ptr {{.*}}, align 1
+// CHECK: call i16 @llvm.bswap.i16
+// CHECK: store i16 {{.*}}, ptr {{.*}}, align 1
+void test_memreverse8_const2(unsigned char *p) {
+  __builtin_stdc_memreverse8(2, p);
+}
+
+// CHECK-LABEL: test_memreverse8_const4
+// CHECK: load i32, ptr {{.*}}, align 1
+// CHECK: call i32 @llvm.bswap.i32
+// CHECK: store i32 {{.*}}, ptr {{.*}}, align 1
+void test_memreverse8_const4(unsigned char *p) {
+  __builtin_stdc_memreverse8(4, p);
+}
+
+// CHECK-LABEL: test_memreverse8_const8
+// CHECK: load i64, ptr {{.*}}, align 1
+// CHECK: call i64 @llvm.bswap.i64
+// CHECK: store i64 {{.*}}, ptr {{.*}}, align 1
+void test_memreverse8_const8(unsigned char *p) {
+  __builtin_stdc_memreverse8(8, p);
+}
+
+// Constant N=3: not bswap-optimized, falls back to the loop.
+// CHECK-LABEL: test_memreverse8_const3
+// CHECK: br i1 false,
+// CHECK-NOT: @llvm.bswap
+void test_memreverse8_const3(unsigned char *p) {
+  __builtin_stdc_memreverse8(3, p);
+}
+
+// CHECK-LABEL: test_memreverse8_runtime
+// CHECK: lshr
+// CHECK: memreverse8.loop:
+// CHECK: getelementptr
+// CHECK: getelementptr
+// CHECK: load i8
+// CHECK: load i8
+// CHECK: store i8
+// CHECK: store i8
+// CHECK: memreverse8.after:
+void test_memreverse8_runtime(__SIZE_TYPE__ n, unsigned char *p) {
+  __builtin_stdc_memreverse8(n, p);
+}
+
+#endif // !TEST_C2Y_LIB_SPELLINGS
+
+#ifdef TEST_C2Y_LIB_SPELLINGS
+#include <stdbit.h>
+
+// u8 is a no-op: single byte, nothing to swap.
+// C2Y-LABEL: test_typed_memreverse8u8
+// C2Y-NOT:   @llvm.bswap
+unsigned char test_typed_memreverse8u8(unsigned char x) {
+  return stdc_memreverse8u8(x);
+}
+
+// C2Y-LABEL: test_typed_memreverse8u16
+// C2Y: call i16 @llvm.bswap.i16
+unsigned short test_typed_memreverse8u16(unsigned short x) {
+  return stdc_memreverse8u16(x);
+}
+
+// C2Y-LABEL: test_typed_memreverse8u32
+// C2Y: call i32 @llvm.bswap.i32
+unsigned int test_typed_memreverse8u32(unsigned int x) {
+  return stdc_memreverse8u32(x);
+}
+
+// C2Y-LABEL: test_typed_memreverse8u64
+// C2Y: call i64 @llvm.bswap.i64
+__UINT64_TYPE__ test_typed_memreverse8u64(__UINT64_TYPE__ x) {
+  return stdc_memreverse8u64(x);
+}
+
+#endif // TEST_C2Y_LIB_SPELLINGS
diff --git a/clang/test/Sema/Inputs/stdbit.h b/clang/test/Sema/Inputs/stdbit.h
index bdabb9ec8ee7d..ca8b87deb67e3 100644
--- a/clang/test/Sema/Inputs/stdbit.h
+++ b/clang/test/Sema/Inputs/stdbit.h
@@ -112,4 +112,10 @@ unsigned int stdc_rotate_right_ui(unsigned int, unsigned int);
 unsigned long stdc_rotate_right_ul(unsigned long, unsigned int);
 unsigned long long stdc_rotate_right_ull(unsigned long long, unsigned int);
 
+void stdc_memreverse8(__SIZE_TYPE__, unsigned char *);
+unsigned char stdc_memreverse8u8(unsigned char);
+unsigned short stdc_memreverse8u16(unsigned short);
+__UINT32_TYPE__ stdc_memreverse8u32(__UINT32_TYPE__);
+__UINT64_TYPE__ stdc_memreverse8u64(__UINT64_TYPE__);
+
 #endif
diff --git a/clang/test/Sema/builtin-stdc-memreverse8.c b/clang/test/Sema/builtin-stdc-memreverse8.c
new file mode 100644
index 0000000000000..6b0017ab7540c
--- /dev/null
+++ b/clang/test/Sema/builtin-stdc-memreverse8.c
@@ -0,0 +1,84 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c2y -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c2y -isystem %S/Inputs -fsyntax-only -verify %s
+
+void test_errors(void) {
+  unsigned char buf[4];
+
+  __builtin_stdc_memreverse8(4);           // expected-error {{too few arguments to function call}}
+  __builtin_stdc_memreverse8(4, buf, buf); // expected-error {{too many arguments to function call}}
+}
+
+void test_null_pointer(void) {
+  __builtin_stdc_memreverse8(4, 0);                  // expected-warning {{null passed to a callee that requires a non-null argument}}
+  __builtin_stdc_memreverse8(4, (unsigned char *)0); // expected-warning {{null passed to a callee that requires a non-null argument}}
+}
+
+void test_uintN_pointer_types(void) {
+  __UINT8_TYPE__  *p8  = 0;
+  __UINT16_TYPE__ *p16 = 0;
+  __UINT32_TYPE__ *p32 = 0;
+  __UINT64_TYPE__ *p64 = 0;
+
+  __builtin_stdc_memreverse8(4, p8);   // uint8_t* == unsigned char*, no diagnostic
+  __builtin_stdc_memreverse8(4, p16);  // expected-error {{incompatible pointer types}}
+  __builtin_stdc_memreverse8(4, p32);  // expected-error {{incompatible pointer types}}
+  __builtin_stdc_memreverse8(4, p64);  // expected-error {{incompatible pointer types}}
+}
+
+void test_pointer_types(void) {
+  char *cp = 0;
+  int *ip = 0;
+  void *vp = 0;
+  const unsigned char *ccp = 0;
+  signed char *scp = 0;
+  unsigned char **ucpp = 0;
+
+  __builtin_stdc_memreverse8(4, cp);   // expected-warning {{converts between pointers to integer types}}
+  __builtin_stdc_memreverse8(4, ip);   // expected-error {{incompatible pointer types}}
+  __builtin_stdc_memreverse8(4, vp);   // void* implicitly converts, no diagnostic
+  __builtin_stdc_memreverse8(4, ccp);  // expected-warning {{discards qualifiers}}
+  __builtin_stdc_memreverse8(4, scp);  // expected-warning {{converts between pointers to integer types}}
+  __builtin_stdc_memreverse8(4, ucpp); // expected-error {{incompatible pointer types}}
+}
+
+#ifdef __has_include
+#if __has_include(<stdbit.h>)
+#include <stdbit.h>
+
+_Static_assert(stdc_memreverse8u8(0xAB) == 0xAB, "");
+_Static_assert(stdc_memreverse8u16(0x1234) == 0x3412, "");
+_Static_assert(stdc_memreverse8u32(0x12345678U) == 0x78563412U, "");
+_Static_assert(stdc_memreverse8u64(0x123456789ABCDEF0ULL) == 0xF0DEBC9A78563412ULL, "");
+
+_Static_assert(stdc_memreverse8u16(0) == 0, "");
+_Static_assert(stdc_memreverse8u32(0) == 0, "");
+_Static_assert(stdc_memreverse8u64(0) == 0, "");
+_Static_assert(stdc_memreverse8u16(0xFFFF) == 0xFFFF, "");
+_Static_assert(stdc_memreverse8u32(0xFFFFFFFFU) == 0xFFFFFFFFU, "");
+_Static_assert(stdc_memreverse8u64(0xFFFFFFFFFFFFFFFFULL) == 0xFFFFFFFFFFFFFFFFULL, "");
+
+_Static_assert(stdc_memreverse8u16(stdc_memreverse8u16(0x1234)) == 0x1234, "");
+_Static_assert(stdc_memreverse8u32(stdc_memreverse8u32(0x12345678U)) == 0x12345678U, "");
+_Static_assert(stdc_memreverse8u64(stdc_memreverse8u64(0x123456789ABCDEF0ULL)) == 0x123456789ABCDEF0ULL, "");
+_Static_assert(stdc_memreverse8u8(0xAB) == 0xAB, "");
+_Static_assert(stdc_memreverse8u16(0xDEAD) == 0xADDE, "");
+_Static_assert(stdc_memreverse8u32(0xDEADBEEFU) == 0xEFBEADDEU, "");
+_Static_assert(stdc_memreverse8u64(0x0102030405060708ULL) == 0x0807060504030201ULL, "");
+
+_Static_assert(stdc_memreverse8u8(0xAA) == 0xAA, "");
+_Static_assert(stdc_memreverse8u16(0xABAB) == 0xABAB, "");
+_Static_assert(stdc_memreverse8u32(0xABCDCDABU) == 0xABCDCDABU, "");
+_Static_assert(stdc_memreverse8u64(0x0102030404030201ULL) == 0x0102030404030201ULL, "");
+
+void test_typed_variant_errors(void) {
+  stdc_memreverse8u8();         // expected-error {{too few arguments to function call}}
+  stdc_memreverse8u8(1, 2);     // expected-error {{too many arguments to function call}}
+  stdc_memreverse8u16();        // expected-error {{too few arguments to function call}}
+  stdc_memreverse8u16(1, 2);    // expected-error {{too many arguments to function call}}
+  stdc_memreverse8u32();        // expected-error {{too few arguments to function call}}
+  stdc_memreverse8u32(1, 2);    // expected-error {{too many arguments to function call}}
+  stdc_memreverse8u64();        // expected-error {{too few arguments to function call}}
+  stdc_memreverse8u64(1, 2);    // expected-error {{too many arguments to function call}}
+}
+#endif
+#endif

["_uc", "_us", "_ui", "_ul", "_ull"]>;

class MemReverse8Template : Template<
["unsigned char", "unsigned short", "uint32_t", "uint64_t"],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit interesting to see uint32_t but unsigned char instead of uint8_t, do we not support the smaller bit widths here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i am seeing this build error when using uint8_t

/home/naga/llvm-project/clang/include/clang/Basic/Builtins.td:1002:7: error: Unknown Type: uint8_t
  let Prototype = "T(T)";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's because we haven't added support for them yet, but I think we should if we intend to use them:

void ParseType(StringRef T) {

// Z -> int32_t (require a native 32-bit integer type on the target)

WDYT @efriedma-quic ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't have much practical impact, but it's probably worth trying to be consistent with the C standard definitions, sure.

def StdcMemReverse8: Builtin {
let Spellings = ["__builtin_stdc_memreverse8"];
let Attributes = [NoThrow, NonNull<NonOptimizing, [1]>];
let Prototype = "void(size_t, unsigned char*)";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this prototype is reasonable as-is, but the official prototype is:

void stdc_memreverse8(size_t n, unsigned char ptr[static n]);

and that actually associates some good bounds information that could be used by other analyses, at least in C. Nothing to change, but it might be nice if we had a way to use the correct signature for C and fall back to the decayed signature for C++.


def StdcMemReverse8: Builtin {
let Spellings = ["__builtin_stdc_memreverse8"];
let Attributes = [NoThrow, NonNull<NonOptimizing, [1]>];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this one also be pure?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the function is modifying the buffer pointed to by the ptr and the return type is void

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added Pure to the typed variants

"argument to __builtin_longjmp must be a constant 1">;
def err_builtin_requires_language : Error<"'%0' is only available in %1">;
def err_builtin_requires_char_bit_8 : Error<
"'%0' is only available on targets where CHAR_BIT == 8">;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"'%0' is only available on targets where CHAR_BIT == 8">;
"'%0' is only available on targets where 'CHAR_BIT == 8'">;

case Builtin::BIstdc_memreverse8u32:
case Builtin::BIstdc_memreverse8u64:
if (Context.getTargetInfo().getCharWidth() != 8) {
Diag(TheCall->getBeginLoc(), diag::err_builtin_requires_char_bit_8)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with the code but I also don't think we have a way to test it either; we don't have options to let you control CHAR_BIT and we don't have targets that aren't 8-bit currently.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 15, 2026

🐧 Linux x64 Test Results

  • 88019 tests passed
  • 1572 tests skipped
  • 1 test failed

Failed Tests

(click on a test name to see its output)

lldb-api

lldb-api.python_api/run_locker/TestRunLocker.py
Script:
--
/usr/bin/python3 /home/gha/actions-runner/_work/llvm-project/llvm-project/lldb/test/API/dotest.py -u CXXFLAGS -u CFLAGS --env LLVM_LIBS_DIR=/home/gha/actions-runner/_work/llvm-project/llvm-project/build/./lib --env LLVM_INCLUDE_DIR=/home/gha/actions-runner/_work/llvm-project/llvm-project/build/include --env LLVM_TOOLS_DIR=/home/gha/actions-runner/_work/llvm-project/llvm-project/build/./bin --libcxx-include-dir /home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/c++/v1 --libcxx-include-target-dir /home/gha/actions-runner/_work/llvm-project/llvm-project/build/include/x86_64-unknown-linux-gnu/c++/v1 --libcxx-library-dir /home/gha/actions-runner/_work/llvm-project/llvm-project/build/./lib/x86_64-unknown-linux-gnu --triple x86_64-unknown-linux-gnu --build-dir /home/gha/actions-runner/_work/llvm-project/llvm-project/build/lldb-test-build.noindex --lldb-module-cache-dir /home/gha/actions-runner/_work/llvm-project/llvm-project/build/lldb-test-build.noindex/module-cache-lldb/lldb-api --clang-module-cache-dir /home/gha/actions-runner/_work/llvm-project/llvm-project/build/lldb-test-build.noindex/module-cache-clang/lldb-api --executable /home/gha/actions-runner/_work/llvm-project/llvm-project/build/./bin/lldb --compiler /home/gha/actions-runner/_work/llvm-project/llvm-project/build/./bin/clang --dsymutil /home/gha/actions-runner/_work/llvm-project/llvm-project/build/./bin/dsymutil --make /usr/bin/gmake --llvm-tools-dir /home/gha/actions-runner/_work/llvm-project/llvm-project/build/./bin --lldb-obj-root /home/gha/actions-runner/_work/llvm-project/llvm-project/build/tools/lldb --lldb-libs-dir /home/gha/actions-runner/_work/llvm-project/llvm-project/build/./lib --cmake-build-type Release /home/gha/actions-runner/_work/llvm-project/llvm-project/lldb/test/API/python_api/run_locker -p TestRunLocker.py
--
Exit Code: 1

Command Output (stdout):
--
lldb version 23.0.0git (https://github.com/llvm/llvm-project revision a461c369eeeb843a1d0df017299db020660317a1)
  clang revision a461c369eeeb843a1d0df017299db020660317a1
  llvm revision a461c369eeeb843a1d0df017299db020660317a1
"can't evaluate expressions when the process is running."
Skipping the following test categories: msvcstl, dsym, pdb, gmodules, debugserver, objc

--
Command Output (stderr):
--
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'EvaluateExpression'
FAIL: LLDB (/home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/clang-x86_64) :: test_run_locker (TestRunLocker.TestRunLocker.test_run_locker)
Log Files:
 - /home/gha/actions-runner/_work/llvm-project/llvm-project/build/lldb-test-build.noindex/python_api/run_locker/TestRunLocker/Failure_test_run_locker.log
PASS: LLDB (/home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/clang-x86_64) :: test_run_locker_stop_at_entry (TestRunLocker.TestRunLocker.test_run_locker_stop_at_entry)
======================================================================
FAIL: test_run_locker (TestRunLocker.TestRunLocker.test_run_locker)
   Test that the run locker is set correctly when we launch
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/gha/actions-runner/_work/llvm-project/llvm-project/lldb/packages/Python/lldbsuite/test/decorators.py", line 160, in wrapper
    return func(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/home/gha/actions-runner/_work/llvm-project/llvm-project/lldb/test/API/python_api/run_locker/TestRunLocker.py", line 23, in test_run_locker
    self.runlocker_test(False)
  File "/home/gha/actions-runner/_work/llvm-project/llvm-project/lldb/test/API/python_api/run_locker/TestRunLocker.py", line 110, in runlocker_test
    self.assertIn(
AssertionError: "can't evaluate expressions when the process is running" not found in ''
Config=x86_64-/home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/clang
----------------------------------------------------------------------
Ran 2 tests in 0.640s

FAILED (failures=1)

--

If these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the infrastructure label.

…64} builtins

Implements the C2y <stdbit.h> memory reversal functions stdc_memreverse8
and stdc_memreverse8u{8,16,32,64}. The typed variants lower to llvm.bswap
and support constexpr evaluation.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:bytecode Issues for the clang bytecode constexpr interpreter clang:codegen IR generation bugs: mangling, exceptions, etc. clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants