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

>From bfb45397062138511f9eff3af171361eea4a7f27 Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <[email protected]>
Date: Mon, 8 Jun 2026 10:00:52 -0700
Subject: [PATCH 1/7] [clang] Add constexpr support for stdc_memreverse8

Implements constant expression evaluation in both the AST interpreter
(ExprConstant.cpp) and the bytecode interpreter (InterpBuiltin.cpp).

Adds constexpr tests for the typed variants stdc_memreverse8u{8,16,32,64}

Follow up:#197358.
---
 clang/include/clang/Basic/Builtins.td         |   2 +-
 clang/lib/AST/ByteCode/InterpBuiltin.cpp      |  74 +++++
 clang/lib/AST/ExprConstant.cpp                |  59 ++++
 ...constexpr-builtin-stdc-memreverse8-typed.c |  37 +++
 .../constexpr-builtin-stdc-memreverse8.cpp    | 273 ++++++++++++++++++
 5 files changed, 444 insertions(+), 1 deletion(-)
 create mode 100644 clang/test/Sema/constexpr-builtin-stdc-memreverse8-typed.c
 create mode 100644 clang/test/SemaCXX/constexpr-builtin-stdc-memreverse8.cpp

diff --git a/clang/include/clang/Basic/Builtins.td 
b/clang/include/clang/Basic/Builtins.td
index 63cdb787bea16..d65bb1047c1ad 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -992,7 +992,7 @@ def StdcRotateRightTyped : LibBuiltin<"stdbit.h", 
"C2Y_LANG">, IntBitUtilTemplat
 
 def StdcMemReverse8: LibBuiltin<"stdbit.h", "C2Y_LANG"> {
   let Spellings = ["stdc_memreverse8"];
-  let Attributes = [NoThrow, NonNull<NonOptimizing, [1]>];
+  let Attributes = [NoThrow, Constexpr, NonNull<NonOptimizing, [1]>];
   let Prototype = "void(size_t, unsigned char*)";
   let AddBuiltinPrefixedAlias = 1;
 }
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index b76f13833da14..951dab5efb4e3 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2058,6 +2058,76 @@ static bool isOneByteCharacterType(QualType T) {
   return T->isCharType() || T->isChar8Type();
 }
 
+// stdc_memreverse8(size_t N, unsigned char *P)
+static bool interp__builtin_stdc_memreverse8(InterpState &S, CodePtr OpPC,
+                                             const InterpFrame *Frame,
+                                             const CallExpr *Call) {
+  Pointer Ptr = S.Stk.pop<Pointer>().expand();
+
+  uint64_t NElems;
+  if (!popToUInt64(S, Call->getArg(0), NElems))
+    return false;
+
+  if (Ptr.isZero()) {
+    S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_null)
+        << AK_Assign;
+    return false;
+  }
+
+  if (Ptr.isDummy() || !Ptr.isBlockPointer() || !Ptr.isLive())
+    return false;
+
+  QualType ElemTy = Ptr.getFieldDesc()->isArray()
+                        ? Ptr.getFieldDesc()->getElemQualType()
+                        : Ptr.getFieldDesc()->getType();
+  if (ElemTy->isIncompleteType()) {
+    S.FFDiag(S.Current->getSource(OpPC),
+             diag::note_constexpr_ltor_incomplete_type)
+        << ElemTy;
+    return false;
+  }
+  if (!isOneByteCharacterType(ElemTy)) {
+    S.FFDiag(S.Current->getSource(OpPC),
+             diag::note_constexpr_memchr_unsupported)
+        << 
S.getASTContext().BuiltinInfo.getQuotedName(Call->getBuiltinCallee())
+        << ElemTy;
+    return false;
+  }
+
+  size_t BaseIdx = Ptr.getIndex();
+  size_t ArraySize = Ptr.getNumElems();
+  size_t RemainingElems = ArraySize - BaseIdx;
+  if (NElems > RemainingElems) {
+    S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
+        << (int64_t)(BaseIdx + NElems - 1) << /*isArray=*/0
+        << (uint64_t)ArraySize;
+    return false;
+  }
+
+  if (NElems <= 1)
+    return true;
+
+  PrimType ElemT = *S.getContext().classify(ElemTy);
+
+  for (uint64_t I = 0, Half = NElems / 2; I < Half; ++I) {
+    Pointer LoPtr = Ptr.atIndex(BaseIdx + I);
+    Pointer HiPtr = Ptr.atIndex(BaseIdx + NElems - 1 - I);
+
+    if (!CheckLoad(S, OpPC, LoPtr, AK_Read) ||
+        !CheckLoad(S, OpPC, HiPtr, AK_Read))
+      return false;
+
+    INT_TYPE_SWITCH_NO_BOOL(ElemT, {
+      T LoVal = LoPtr.deref<T>();
+      LoPtr.deref<T>() = HiPtr.deref<T>();
+      HiPtr.deref<T>() = LoVal;
+      LoPtr.initialize();
+      HiPtr.initialize();
+    });
+  }
+  return true;
+}
+
 static bool interp__builtin_memcmp(InterpState &S, CodePtr OpPC,
                                    const InterpFrame *Frame,
                                    const CallExpr *Call, unsigned ID) {
@@ -5085,6 +5155,10 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, 
const CallExpr *Call,
   case Builtin::BIstdc_memreverse8u64:
     return interp__builtin_bswap(S, OpPC, Frame, Call);
 
+  case Builtin::BIstdc_memreverse8:
+  case Builtin::BI__builtin_stdc_memreverse8:
+    return interp__builtin_stdc_memreverse8(S, OpPC, Frame, Call);
+
   case Builtin::BI__atomic_always_lock_free:
   case Builtin::BI__atomic_is_lock_free:
     return interp__builtin_atomic_lock_free(S, OpPC, Frame, Call, BuiltinID);
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 1d359339b9104..314429b949c8b 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -21102,6 +21102,65 @@ class VoidExprEvaluator
     case Builtin::BI__builtin_operator_delete:
       return HandleOperatorDeleteCall(Info, E);
 
+    case Builtin::BIstdc_memreverse8:
+    case Builtin::BI__builtin_stdc_memreverse8: {
+      APSInt N;
+      if (!EvaluateInteger(E->getArg(0), N, Info))
+        return false;
+      uint64_t NElems = N.getZExtValue();
+
+      LValue Ptr;
+      if (!EvaluatePointer(E->getArg(1), Ptr, Info))
+        return false;
+
+      if (!Ptr.checkNullPointerForFoldAccess(Info, E, AK_Assign) ||
+          Ptr.Designator.Invalid)
+        return false;
+
+      QualType CharTy = Ptr.Designator.getType(Info.Ctx);
+      if (CharTy->isIncompleteType()) {
+        Info.FFDiag(E, diag::note_constexpr_ltor_incomplete_type) << CharTy;
+        return false;
+      }
+      if (!isOneByteCharacterType(CharTy)) {
+        Info.FFDiag(E, diag::note_constexpr_memchr_unsupported)
+            << Info.Ctx.BuiltinInfo.getQuotedName(E->getBuiltinCallee())
+            << CharTy;
+        return false;
+      }
+
+      uint64_t RemainingElems = Ptr.Designator.validIndexAdjustments().second;
+      if (NElems > RemainingElems) {
+        uint64_t ArrayIndex =
+            Ptr.Designator.MostDerivedIsArrayElement
+                ? Ptr.Designator.Entries.back().getAsArrayIndex()
+                : (uint64_t)Ptr.Designator.IsOnePastTheEnd;
+        APSInt Index = APSInt::get(ArrayIndex + NElems - 1);
+        Ptr.Designator.diagnosePointerArithmetic(Info, E, Index);
+        return false;
+      }
+
+      if (NElems <= 1)
+        return true;
+
+      LValue Lo = Ptr;
+      LValue Hi = Ptr;
+      if (!HandleLValueArrayAdjustment(Info, E, Hi, CharTy, NElems - 1))
+        return false;
+
+      for (uint64_t I = 0, Half = NElems / 2; I < Half; ++I) {
+        APValue LoVal, HiVal;
+        if (!handleLValueToRValueConversion(Info, E, CharTy, Lo, LoVal) ||
+            !handleLValueToRValueConversion(Info, E, CharTy, Hi, HiVal) ||
+            !handleAssignment(Info, E, Lo, CharTy, HiVal) ||
+            !handleAssignment(Info, E, Hi, CharTy, LoVal) ||
+            !HandleLValueArrayAdjustment(Info, E, Lo, CharTy, 1) ||
+            !HandleLValueArrayAdjustment(Info, E, Hi, CharTy, -1))
+          return false;
+      }
+      return true;
+    }
+
     default:
       return false;
     }
diff --git a/clang/test/Sema/constexpr-builtin-stdc-memreverse8-typed.c 
b/clang/test/Sema/constexpr-builtin-stdc-memreverse8-typed.c
new file mode 100644
index 0000000000000..d6779f74b9620
--- /dev/null
+++ b/clang/test/Sema/constexpr-builtin-stdc-memreverse8-typed.c
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c2y -isystem %S/Inputs 
-fsyntax-only %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c2y -isystem %S/Inputs 
-fsyntax-only %s -fexperimental-new-constant-interpreter
+
+#include <stdbit.h>
+
+constexpr __UINT8_TYPE__ u8_0xAB = stdc_memreverse8u8((__UINT8_TYPE__)0xAB);
+_Static_assert(u8_0xAB == (__UINT8_TYPE__)0xAB, "");
+constexpr __UINT8_TYPE__ u8_0x00 = stdc_memreverse8u8((__UINT8_TYPE__)0x00);
+_Static_assert(u8_0x00 == (__UINT8_TYPE__)0x00, "");
+constexpr __UINT8_TYPE__ u8_0xFF = stdc_memreverse8u8((__UINT8_TYPE__)0xFF);
+_Static_assert(u8_0xFF == (__UINT8_TYPE__)0xFF, "");
+
+constexpr __UINT16_TYPE__ u16_0x1234 = 
stdc_memreverse8u16((__UINT16_TYPE__)0x1234);
+_Static_assert(u16_0x1234 == (__UINT16_TYPE__)0x3412, "");
+constexpr __UINT16_TYPE__ u16_0x0000 = 
stdc_memreverse8u16((__UINT16_TYPE__)0x0000);
+_Static_assert(u16_0x0000 == (__UINT16_TYPE__)0x0000, "");
+constexpr __UINT16_TYPE__ u16_0xAAAA = 
stdc_memreverse8u16((__UINT16_TYPE__)0xAAAA);
+_Static_assert(u16_0xAAAA == (__UINT16_TYPE__)0xAAAA, "");
+
+constexpr __UINT32_TYPE__ u32_0x12345678 = 
stdc_memreverse8u32((__UINT32_TYPE__)0x12345678);
+_Static_assert(u32_0x12345678 == (__UINT32_TYPE__)0x78563412, "");
+constexpr __UINT32_TYPE__ u32_0x00000000 = 
stdc_memreverse8u32((__UINT32_TYPE__)0x00000000);
+_Static_assert(u32_0x00000000 == (__UINT32_TYPE__)0x00000000, "");
+constexpr __UINT32_TYPE__ u32_0xDEADBEEF = 
stdc_memreverse8u32((__UINT32_TYPE__)0xDEADBEEF);
+_Static_assert(u32_0xDEADBEEF == (__UINT32_TYPE__)0xEFBEADDE, "");
+
+constexpr __UINT64_TYPE__ u64_0x0102030405060708 = 
stdc_memreverse8u64((__UINT64_TYPE__)0x0102030405060708ULL);
+_Static_assert(u64_0x0102030405060708 == 
(__UINT64_TYPE__)0x0807060504030201ULL, "");
+constexpr __UINT64_TYPE__ u64_0x0000000000000000 = 
stdc_memreverse8u64((__UINT64_TYPE__)0x0000000000000000ULL);
+_Static_assert(u64_0x0000000000000000 == 
(__UINT64_TYPE__)0x0000000000000000ULL, "");
+
+constexpr __UINT16_TYPE__ u16_rt = 
stdc_memreverse8u16(stdc_memreverse8u16((__UINT16_TYPE__)0xABCD));
+_Static_assert(u16_rt == (__UINT16_TYPE__)0xABCD, "");
+constexpr __UINT32_TYPE__ u32_rt = 
stdc_memreverse8u32(stdc_memreverse8u32((__UINT32_TYPE__)0xDEADBEEF));
+_Static_assert(u32_rt == (__UINT32_TYPE__)0xDEADBEEF, "");
+constexpr __UINT64_TYPE__ u64_rt = 
stdc_memreverse8u64(stdc_memreverse8u64((__UINT64_TYPE__)0xCAFEBABEDEADBEEFULL));
+_Static_assert(u64_rt == (__UINT64_TYPE__)0xCAFEBABEDEADBEEFULL, "");
diff --git a/clang/test/SemaCXX/constexpr-builtin-stdc-memreverse8.cpp 
b/clang/test/SemaCXX/constexpr-builtin-stdc-memreverse8.cpp
new file mode 100644
index 0000000000000..8064b74fc2b85
--- /dev/null
+++ b/clang/test/SemaCXX/constexpr-builtin-stdc-memreverse8.cpp
@@ -0,0 +1,273 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only 
-verify=ref,both -std=c++20 %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only 
-verify=expected,both -std=c++20 %s -fexperimental-new-constant-interpreter
+
+namespace test_noop {
+
+// N=0: no bytes touched.
+constexpr unsigned char test_n0() {
+  unsigned char buf[4] = {0x12, 0x34, 0x56, 0x78};
+  __builtin_stdc_memreverse8(0, buf);
+  return buf[0];
+}
+static_assert(test_n0() == 0x12, "");
+
+// N=1: single byte is its own reverse.
+constexpr unsigned char test_n1() {
+  unsigned char buf[4] = {0x12, 0x34, 0x56, 0x78};
+  __builtin_stdc_memreverse8(1, buf);
+  return buf[0];
+}
+static_assert(test_n1() == 0x12, "");
+
+} // namespace test_noop
+
+namespace test_basic {
+
+// N=2: swap two bytes.
+constexpr bool test_n2() {
+  unsigned char buf[2] = {0x12, 0x34};
+  __builtin_stdc_memreverse8(2, buf);
+  return buf[0] == 0x34 && buf[1] == 0x12;
+}
+static_assert(test_n2(), "");
+
+// N=4: full 4-byte reversal.
+constexpr bool test_n4() {
+  unsigned char buf[4] = {0x12, 0x34, 0x56, 0x78};
+  __builtin_stdc_memreverse8(4, buf);
+  return buf[0] == 0x78 && buf[1] == 0x56 &&
+         buf[2] == 0x34 && buf[3] == 0x12;
+}
+static_assert(test_n4(), "");
+
+// N=8: 8-byte reversal.
+constexpr bool test_n8() {
+  unsigned char buf[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
+  __builtin_stdc_memreverse8(8, buf);
+  return buf[0] == 0x08 && buf[1] == 0x07 && buf[2] == 0x06 &&
+         buf[3] == 0x05 && buf[4] == 0x04 && buf[5] == 0x03 &&
+         buf[6] == 0x02 && buf[7] == 0x01;
+}
+static_assert(test_n8(), "");
+
+} // namespace test_basic
+
+namespace test_odd_length {
+
+// N=3: odd-length reversal, middle byte unchanged.
+constexpr bool test_n3() {
+  unsigned char buf[3] = {0xAA, 0xBB, 0xCC};
+  __builtin_stdc_memreverse8(3, buf);
+  return buf[0] == 0xCC && buf[1] == 0xBB && buf[2] == 0xAA;
+}
+static_assert(test_n3(), "");
+
+// N=5: odd-length, middle byte stays.
+constexpr bool test_n5() {
+  unsigned char buf[5] = {0x01, 0x02, 0x03, 0x04, 0x05};
+  __builtin_stdc_memreverse8(5, buf);
+  return buf[0] == 0x05 && buf[1] == 0x04 && buf[2] == 0x03 &&
+         buf[3] == 0x02 && buf[4] == 0x01;
+}
+static_assert(test_n5(), "");
+
+} // namespace test_odd_length
+
+namespace test_idempotent {
+
+// All same bytes: reversal is a no-op.
+constexpr bool test_all_same() {
+  unsigned char buf[4] = {0xAB, 0xAB, 0xAB, 0xAB};
+  __builtin_stdc_memreverse8(4, buf);
+  return buf[0] == 0xAB && buf[1] == 0xAB &&
+         buf[2] == 0xAB && buf[3] == 0xAB;
+}
+static_assert(test_all_same(), "");
+
+// Palindrome: reversal produces same sequence.
+constexpr bool test_palindrome() {
+  unsigned char buf[4] = {0x12, 0x34, 0x34, 0x12};
+  __builtin_stdc_memreverse8(4, buf);
+  return buf[0] == 0x12 && buf[1] == 0x34 &&
+         buf[2] == 0x34 && buf[3] == 0x12;
+}
+static_assert(test_palindrome(), "");
+
+} // namespace test_idempotent
+
+namespace test_double_reverse {
+
+// Reversing twice restores the original.
+constexpr bool test_round_trip_4() {
+  unsigned char buf[4] = {0x12, 0x34, 0x56, 0x78};
+  __builtin_stdc_memreverse8(4, buf);
+  __builtin_stdc_memreverse8(4, buf);
+  return buf[0] == 0x12 && buf[1] == 0x34 &&
+         buf[2] == 0x56 && buf[3] == 0x78;
+}
+static_assert(test_round_trip_4(), "");
+
+constexpr bool test_round_trip_3() {
+  unsigned char buf[3] = {0xDE, 0xAD, 0xBE};
+  __builtin_stdc_memreverse8(3, buf);
+  __builtin_stdc_memreverse8(3, buf);
+  return buf[0] == 0xDE && buf[1] == 0xAD && buf[2] == 0xBE;
+}
+static_assert(test_round_trip_3(), "");
+
+} // namespace test_double_reverse
+
+namespace test_partial {
+
+// Reverse only part of a larger buffer; rest is untouched.
+constexpr bool test_partial_reverse() {
+  unsigned char buf[6] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
+  __builtin_stdc_memreverse8(4, buf);
+  return buf[0] == 0x04 && buf[1] == 0x03 && buf[2] == 0x02 &&
+         buf[3] == 0x01 && buf[4] == 0x05 && buf[5] == 0x06;
+}
+static_assert(test_partial_reverse(), "");
+
+// ptr points into the middle of a larger buffer; bytes outside the range are 
untouched.
+constexpr bool test_mid_array() {
+  unsigned char buf[6] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};
+  __builtin_stdc_memreverse8(4, buf + 1);
+  return buf[0] == 0x01 && buf[1] == 0x05 && buf[2] == 0x04 &&
+         buf[3] == 0x03 && buf[4] == 0x02 && buf[5] == 0x06;
+}
+static_assert(test_mid_array(), "");
+
+// N=1 with a pointer-to-scalar (not an array): single byte is a no-op.
+constexpr bool test_scalar_n1() {
+  unsigned char c = 0x42;
+  __builtin_stdc_memreverse8(1, &c);
+  return c == 0x42;
+}
+static_assert(test_scalar_n1(), "");
+
+// N=0 with a pointer-to-scalar: no-op.
+constexpr bool test_scalar_n0() {
+  unsigned char c = 0x42;
+  __builtin_stdc_memreverse8(0, &c);
+  return c == 0x42;
+}
+static_assert(test_scalar_n0(), "");
+
+} // namespace test_partial
+
+namespace test_side_effects {
+
+// n++ passes n=4 to the reversal, then increments n to 5.
+constexpr bool test_n_postincrement() {
+  unsigned char buf[4] = {0x01, 0x02, 0x03, 0x04};
+  __SIZE_TYPE__ n = 4;
+  __builtin_stdc_memreverse8(n++, buf);
+  return n == 5 &&
+         buf[0] == 0x04 && buf[1] == 0x03 && buf[2] == 0x02 && buf[3] == 0x01;
+}
+static_assert(test_n_postincrement(), "");
+
+// ++n increments n to 4 first, then passes n=4 to the reversal.
+constexpr bool test_n_preincrement() {
+  unsigned char buf[4] = {0x01, 0x02, 0x03, 0x04};
+  __SIZE_TYPE__ n = 3;
+  __builtin_stdc_memreverse8(++n, buf);
+  return n == 4 &&
+         buf[0] == 0x04 && buf[1] == 0x03 && buf[2] == 0x02 && buf[3] == 0x01;
+}
+static_assert(test_n_preincrement(), "");
+
+// p++ passes buf to the reversal, then advances p.
+constexpr bool test_p_postincrement() {
+  unsigned char buf[4] = {0x01, 0x02, 0x03, 0x04};
+  unsigned char *p = buf;
+  __builtin_stdc_memreverse8(4, p++);
+  return buf[0] == 0x04 && buf[1] == 0x03 && buf[2] == 0x02 && buf[3] == 0x01 
&&
+         p == buf + 1;
+}
+static_assert(test_p_postincrement(), "");
+
+// Both arguments have side effects; each is evaluated once before the call.
+constexpr bool test_both_postincrement() {
+  unsigned char buf[5] = {0x01, 0x02, 0x03, 0x04, 0x05};
+  unsigned char *p = buf;
+  __SIZE_TYPE__ n = 4;
+  __builtin_stdc_memreverse8(n++, p++);
+  return n == 5 && p == buf + 1 &&
+         buf[0] == 0x04 && buf[1] == 0x03 && buf[2] == 0x02 && buf[3] == 0x01 
&&
+         buf[4] == 0x05;
+}
+static_assert(test_both_postincrement(), "");
+
+} // namespace test_side_effects
+
+namespace test_side_effects_zero_or_one {
+
+// Side effects in the second argument must be evaluated even if N == 0.
+constexpr bool test_n0_side_effect() {
+  unsigned char buf[4] = {0x12, 0x34, 0x56, 0x78};
+  unsigned char *p = buf;
+  __builtin_stdc_memreverse8(0, p++);
+  return p == buf + 1;
+}
+static_assert(test_n0_side_effect(), "");
+
+// Side effects in the second argument must be evaluated even if N == 1.
+constexpr bool test_n1_side_effect() {
+  unsigned char buf[4] = {0x12, 0x34, 0x56, 0x78};
+  unsigned char *p = buf;
+  __builtin_stdc_memreverse8(1, p++);
+  return p == buf + 1;
+}
+static_assert(test_n1_side_effect(), "");
+
+} // namespace test_side_effects_zero_or_one
+
+namespace test_negative {
+
+// N exceeds the array size: out-of-bounds access.
+constexpr bool test_oob() { // both-error{{constexpr function never produces a 
constant expression}}
+  unsigned char buf[2] = {0x01, 0x02};
+  __builtin_stdc_memreverse8(4, buf); // both-note 2{{cannot refer to element 
3 of array of 2 elements in a constant expression}}
+  return true;
+}
+static_assert(test_oob(), ""); // both-error{{not an integral constant 
expression}} both-note{{in call to 'test_oob()'}}
+
+// Null pointer: assignment through null is not allowed in a constant 
expression.
+constexpr bool test_null_ptr() { // both-error{{constexpr function never 
produces a constant expression}}
+  __builtin_stdc_memreverse8(4, (unsigned char *)0); // both-warning{{null 
passed to a callee that requires a non-null argument}} both-note 2{{assignment 
to dereferenced null pointer is not allowed in a constant expression}}
+  return true;
+}
+static_assert(test_null_ptr(), ""); // both-error{{not an integral constant 
expression}} both-note{{in call to 'test_null_ptr()'}}
+
+// N=0 but null pointer passed.
+constexpr bool test_null_ptr_n0() { // both-error{{constexpr function never 
produces a constant expression}}
+  __builtin_stdc_memreverse8(0, (unsigned char *)0); // both-warning{{null 
passed to a callee that requires a non-null argument}} both-note 2{{assignment 
to dereferenced null pointer is not allowed in a constant expression}}
+  return true;
+}
+static_assert(test_null_ptr_n0(), ""); // both-error{{not an integral constant 
expression}} both-note{{in call to 'test_null_ptr_n0()'}}
+
+// N=1 but null pointer passed.
+constexpr bool test_null_ptr_n1() { // both-error{{constexpr function never 
produces a constant expression}}
+  __builtin_stdc_memreverse8(1, (unsigned char *)0); // both-warning{{null 
passed to a callee that requires a non-null argument}} both-note 2{{assignment 
to dereferenced null pointer is not allowed in a constant expression}}
+  return true;
+}
+static_assert(test_null_ptr_n1(), ""); // both-error{{not an integral constant 
expression}} both-note{{in call to 'test_null_ptr_n1()'}}
+
+// N=1 but pointer is one-past-the-end.
+constexpr bool test_oob_n1() { // both-error{{constexpr function never 
produces a constant expression}}
+  unsigned char buf[2] = {0x01, 0x02};
+  __builtin_stdc_memreverse8(1, buf + 2); // both-note 2{{cannot refer to 
element 2 of array of 2 elements in a constant expression}}
+  return true;
+}
+static_assert(test_oob_n1(), ""); // both-error{{not an integral constant 
expression}} both-note{{in call to 'test_oob_n1()'}}
+
+// Swapping uninitialized memory.
+constexpr bool test_uninit() {
+  unsigned char buf[2]; // both-note {{declared here}}
+  __builtin_stdc_memreverse8(2, buf); // both-note {{read of uninitialized 
object is not allowed in a constant expression}}
+  return true;
+}
+static_assert(test_uninit(), ""); // both-error{{not an integral constant 
expression}} both-note{{in call to 'test_uninit()'}}
+
+} // namespace test_negative

>From 72a3e918710d8a73d2389a64afa80a2ba00f97cc Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <[email protected]>
Date: Tue, 9 Jun 2026 08:32:08 -0700
Subject: [PATCH 2/7] Address review feedback for stdc_memreverse8 constexpr
 support

- Use isReadable() with isOnePastEnd() instead of the
  isDummy/isBlockPointer/isLive checks
- Use std::swap instead of manual three-line swap
- Move initialize() calls outside INT_TYPE_SWITCH_NO_BOOL
- Add test_n0_one_past_end to cover N=0 with a one-past-end pointer
---
 clang/lib/AST/ByteCode/InterpBuiltin.cpp      | 51 ++++++++++---------
 .../constexpr-builtin-stdc-memreverse8.cpp    | 20 +++++---
 2 files changed, 40 insertions(+), 31 deletions(-)

diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 951dab5efb4e3..9a5c7b142b400 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2062,7 +2062,7 @@ static bool isOneByteCharacterType(QualType T) {
 static bool interp__builtin_stdc_memreverse8(InterpState &S, CodePtr OpPC,
                                              const InterpFrame *Frame,
                                              const CallExpr *Call) {
-  Pointer Ptr = S.Stk.pop<Pointer>().expand();
+  Pointer Ptr = S.Stk.pop<Pointer>();
 
   uint64_t NElems;
   if (!popToUInt64(S, Call->getArg(0), NElems))
@@ -2074,25 +2074,15 @@ static bool 
interp__builtin_stdc_memreverse8(InterpState &S, CodePtr OpPC,
     return false;
   }
 
-  if (Ptr.isDummy() || !Ptr.isBlockPointer() || !Ptr.isLive())
+  if (!isReadable(Ptr) && !Ptr.isOnePastEnd())
     return false;
 
-  QualType ElemTy = Ptr.getFieldDesc()->isArray()
-                        ? Ptr.getFieldDesc()->getElemQualType()
-                        : Ptr.getFieldDesc()->getType();
-  if (ElemTy->isIncompleteType()) {
-    S.FFDiag(S.Current->getSource(OpPC),
-             diag::note_constexpr_ltor_incomplete_type)
-        << ElemTy;
-    return false;
-  }
-  if (!isOneByteCharacterType(ElemTy)) {
-    S.FFDiag(S.Current->getSource(OpPC),
-             diag::note_constexpr_memchr_unsupported)
-        << 
S.getASTContext().BuiltinInfo.getQuotedName(Call->getBuiltinCallee())
-        << ElemTy;
-    return false;
-  }
+  const Descriptor *Desc = Ptr.getFieldDesc();
+  bool IsArray = Desc->isArray();
+  QualType ElemTy = IsArray ? Desc->getElemQualType() : Desc->getType();
+
+  if (IsArray)
+    Ptr = Ptr.expand();
 
   size_t BaseIdx = Ptr.getIndex();
   size_t ArraySize = Ptr.getNumElems();
@@ -2107,6 +2097,20 @@ static bool interp__builtin_stdc_memreverse8(InterpState 
&S, CodePtr OpPC,
   if (NElems <= 1)
     return true;
 
+  if (ElemTy->isIncompleteType()) {
+    S.FFDiag(S.Current->getSource(OpPC),
+             diag::note_constexpr_ltor_incomplete_type)
+        << ElemTy;
+    return false;
+  }
+  if (!isOneByteCharacterType(ElemTy)) {
+    S.FFDiag(S.Current->getSource(OpPC),
+             diag::note_constexpr_memchr_unsupported)
+        << 
S.getASTContext().BuiltinInfo.getQuotedName(Call->getBuiltinCallee())
+        << ElemTy;
+    return false;
+  }
+
   PrimType ElemT = *S.getContext().classify(ElemTy);
 
   for (uint64_t I = 0, Half = NElems / 2; I < Half; ++I) {
@@ -2117,13 +2121,10 @@ static bool 
interp__builtin_stdc_memreverse8(InterpState &S, CodePtr OpPC,
         !CheckLoad(S, OpPC, HiPtr, AK_Read))
       return false;
 
-    INT_TYPE_SWITCH_NO_BOOL(ElemT, {
-      T LoVal = LoPtr.deref<T>();
-      LoPtr.deref<T>() = HiPtr.deref<T>();
-      HiPtr.deref<T>() = LoVal;
-      LoPtr.initialize();
-      HiPtr.initialize();
-    });
+    INT_TYPE_SWITCH_NO_BOOL(ElemT,
+                            { std::swap(LoPtr.deref<T>(), HiPtr.deref<T>()); 
});
+    LoPtr.initialize();
+    HiPtr.initialize();
   }
   return true;
 }
diff --git a/clang/test/SemaCXX/constexpr-builtin-stdc-memreverse8.cpp 
b/clang/test/SemaCXX/constexpr-builtin-stdc-memreverse8.cpp
index 8064b74fc2b85..c1c2f1a6a0dea 100644
--- a/clang/test/SemaCXX/constexpr-builtin-stdc-memreverse8.cpp
+++ b/clang/test/SemaCXX/constexpr-builtin-stdc-memreverse8.cpp
@@ -4,20 +4,28 @@
 namespace test_noop {
 
 // N=0: no bytes touched.
-constexpr unsigned char test_n0() {
+constexpr bool test_n0() {
   unsigned char buf[4] = {0x12, 0x34, 0x56, 0x78};
   __builtin_stdc_memreverse8(0, buf);
-  return buf[0];
+  return buf[0] == 0x12;
 }
-static_assert(test_n0() == 0x12, "");
+static_assert(test_n0(), "");
 
 // N=1: single byte is its own reverse.
-constexpr unsigned char test_n1() {
+constexpr bool test_n1() {
   unsigned char buf[4] = {0x12, 0x34, 0x56, 0x78};
   __builtin_stdc_memreverse8(1, buf);
-  return buf[0];
+  return buf[0] == 0x12;
 }
-static_assert(test_n1() == 0x12, "");
+static_assert(test_n1(), "");
+
+// N=0 with a one-past-the-end pointer: valid since no bytes are accessed.
+constexpr bool test_n0_one_past_end() {
+  unsigned char buf[2] = {0x12, 0x34};
+  __builtin_stdc_memreverse8(0, buf + 2);
+  return buf[0] == 0x12 && buf[1] == 0x34;
+}
+static_assert(test_n0_one_past_end(), "");
 
 } // namespace test_noop
 

>From 3eb5c71817cb13736cedfb9c15979acf847fd0e8 Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <[email protected]>
Date: Tue, 9 Jun 2026 19:28:50 -0700
Subject: [PATCH 3/7] Make sure interp__builtin_stdc_memreverse8 consistent
 with ExprConstant

---
 clang/lib/AST/ByteCode/InterpBuiltin.cpp | 36 +++++++++++++-----------
 1 file changed, 20 insertions(+), 16 deletions(-)

diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 9a5c7b142b400..0cbb4dfb2b22b 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2081,22 +2081,6 @@ static bool interp__builtin_stdc_memreverse8(InterpState 
&S, CodePtr OpPC,
   bool IsArray = Desc->isArray();
   QualType ElemTy = IsArray ? Desc->getElemQualType() : Desc->getType();
 
-  if (IsArray)
-    Ptr = Ptr.expand();
-
-  size_t BaseIdx = Ptr.getIndex();
-  size_t ArraySize = Ptr.getNumElems();
-  size_t RemainingElems = ArraySize - BaseIdx;
-  if (NElems > RemainingElems) {
-    S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
-        << (int64_t)(BaseIdx + NElems - 1) << /*isArray=*/0
-        << (uint64_t)ArraySize;
-    return false;
-  }
-
-  if (NElems <= 1)
-    return true;
-
   if (ElemTy->isIncompleteType()) {
     S.FFDiag(S.Current->getSource(OpPC),
              diag::note_constexpr_ltor_incomplete_type)
@@ -2111,6 +2095,26 @@ static bool interp__builtin_stdc_memreverse8(InterpState 
&S, CodePtr OpPC,
     return false;
   }
 
+  if (IsArray)
+    Ptr = Ptr.expand();
+
+  size_t BaseIdx = Ptr.getIndex();
+  size_t ArraySize = Ptr.getNumElems();
+  size_t RemainingElems = ArraySize - BaseIdx;
+  if (NElems > RemainingElems) {
+    if (IsArray)
+      S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
+          << (uint64_t)(BaseIdx + NElems - 1) << /*array*/ 0
+          << (uint64_t)ArraySize;
+    else
+      S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
+          << (uint64_t)(BaseIdx + NElems - 1) << /*non-array*/ 1;
+    return false;
+  }
+
+  if (NElems <= 1)
+    return true;
+
   PrimType ElemT = *S.getContext().classify(ElemTy);
 
   for (uint64_t I = 0, Half = NElems / 2; I < Half; ++I) {

>From 4b93f08d6a72f9e52098d34ad3ad62ea6ef09218 Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <[email protected]>
Date: Wed, 10 Jun 2026 10:39:29 -0700
Subject: [PATCH 4/7] Remove dead isIncompleteType check

The argument to stdc_memreverse8 is unsigned char *, it cannot
be incompletetype.
---
 clang/lib/AST/ByteCode/InterpBuiltin.cpp | 6 ------
 clang/lib/AST/ExprConstant.cpp           | 4 ----
 2 files changed, 10 deletions(-)

diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 0cbb4dfb2b22b..d4bcbb0dc7f2a 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2081,12 +2081,6 @@ static bool interp__builtin_stdc_memreverse8(InterpState 
&S, CodePtr OpPC,
   bool IsArray = Desc->isArray();
   QualType ElemTy = IsArray ? Desc->getElemQualType() : Desc->getType();
 
-  if (ElemTy->isIncompleteType()) {
-    S.FFDiag(S.Current->getSource(OpPC),
-             diag::note_constexpr_ltor_incomplete_type)
-        << ElemTy;
-    return false;
-  }
   if (!isOneByteCharacterType(ElemTy)) {
     S.FFDiag(S.Current->getSource(OpPC),
              diag::note_constexpr_memchr_unsupported)
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 314429b949c8b..7c54e77ef0317 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -21118,10 +21118,6 @@ class VoidExprEvaluator
         return false;
 
       QualType CharTy = Ptr.Designator.getType(Info.Ctx);
-      if (CharTy->isIncompleteType()) {
-        Info.FFDiag(E, diag::note_constexpr_ltor_incomplete_type) << CharTy;
-        return false;
-      }
       if (!isOneByteCharacterType(CharTy)) {
         Info.FFDiag(E, diag::note_constexpr_memchr_unsupported)
             << Info.Ctx.BuiltinInfo.getQuotedName(E->getBuiltinCallee())

>From 177565f45b6db0eef98e58a8ca5b54bd8d0d0d03 Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <[email protected]>
Date: Wed, 10 Jun 2026 10:51:36 -0700
Subject: [PATCH 5/7] Add test for case when N exceeds the size of scalar type

---
 clang/test/SemaCXX/constexpr-builtin-stdc-memreverse8.cpp | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/clang/test/SemaCXX/constexpr-builtin-stdc-memreverse8.cpp 
b/clang/test/SemaCXX/constexpr-builtin-stdc-memreverse8.cpp
index c1c2f1a6a0dea..3b0bc02b37dd9 100644
--- a/clang/test/SemaCXX/constexpr-builtin-stdc-memreverse8.cpp
+++ b/clang/test/SemaCXX/constexpr-builtin-stdc-memreverse8.cpp
@@ -270,6 +270,14 @@ constexpr bool test_oob_n1() { // both-error{{constexpr 
function never produces
 }
 static_assert(test_oob_n1(), ""); // both-error{{not an integral constant 
expression}} both-note{{in call to 'test_oob_n1()'}}
 
+// N exceeds the size of a scalar (non-array) object.
+constexpr bool test_scalar_oob() { // both-error{{constexpr function never 
produces a constant expression}}
+  unsigned char c = 0x42;
+  __builtin_stdc_memreverse8(4, &c); // both-note 2{{cannot refer to element 3 
of non-array object in a constant expression}}
+  return true;
+}
+static_assert(test_scalar_oob(), ""); // both-error{{not an integral constant 
expression}} both-note{{in call to 'test_scalar_oob()'}}
+
 // Swapping uninitialized memory.
 constexpr bool test_uninit() {
   unsigned char buf[2]; // both-note {{declared here}}

>From 78892c3a905608b800b4bf130dd5c7b8fe0c018c Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <[email protected]>
Date: Wed, 10 Jun 2026 12:42:50 -0700
Subject: [PATCH 6/7] Add check in Interp to detect if the ptr is a const

---
 clang/lib/AST/ByteCode/InterpBuiltin.cpp                  | 7 +++++++
 clang/test/SemaCXX/constexpr-builtin-stdc-memreverse8.cpp | 8 ++++++++
 2 files changed, 15 insertions(+)

diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index d4bcbb0dc7f2a..9f29860739425 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2109,6 +2109,13 @@ static bool interp__builtin_stdc_memreverse8(InterpState 
&S, CodePtr OpPC,
   if (NElems <= 1)
     return true;
 
+  Pointer FirstPtr = Ptr.atIndex(BaseIdx);
+  if (FirstPtr.isConst()) {
+    S.FFDiag(S.Current->getSource(OpPC), 
diag::note_constexpr_modify_const_type)
+        << FirstPtr.getType();
+    return false;
+  }
+
   PrimType ElemT = *S.getContext().classify(ElemTy);
 
   for (uint64_t I = 0, Half = NElems / 2; I < Half; ++I) {
diff --git a/clang/test/SemaCXX/constexpr-builtin-stdc-memreverse8.cpp 
b/clang/test/SemaCXX/constexpr-builtin-stdc-memreverse8.cpp
index 3b0bc02b37dd9..25ebb919497c3 100644
--- a/clang/test/SemaCXX/constexpr-builtin-stdc-memreverse8.cpp
+++ b/clang/test/SemaCXX/constexpr-builtin-stdc-memreverse8.cpp
@@ -286,4 +286,12 @@ constexpr bool test_uninit() {
 }
 static_assert(test_uninit(), ""); // both-error{{not an integral constant 
expression}} both-note{{in call to 'test_uninit()'}}
 
+// Reversing const-qualified array: must be rejected in a constant expression.
+constexpr bool test_const_violation_array() { // both-error{{constexpr 
function never produces a constant expression}}
+  const unsigned char buf[4] = {0x01, 0x02, 0x03, 0x04};
+  __builtin_stdc_memreverse8(4, const_cast<unsigned char *>(buf)); // 
both-note 2{{modification of object of const-qualified type 'const unsigned 
char' is not allowed in a constant expression}}
+  return true;
+}
+static_assert(test_const_violation_array(), ""); // both-error{{not an 
integral constant expression}} both-note{{in call to 
'test_const_violation_array()'}}
+
 } // namespace test_negative

>From 9eedc9460505ed67ad5daaafa22f4281c2f7ad61 Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <[email protected]>
Date: Thu, 25 Jun 2026 17:48:44 -0700
Subject: [PATCH 7/7] Remove redundant isOneByteCharacterType check from
 stdc_memreverse8 evaluators

The parameter type is unsigned char *, so the type system already
rejects any other pointer type at the call site. The check in the constexpr
evaluators is dead code.
---
 clang/lib/AST/ByteCode/InterpBuiltin.cpp | 8 --------
 clang/lib/AST/ExprConstant.cpp           | 7 -------
 2 files changed, 15 deletions(-)

diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp 
b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 9f29860739425..3c469fd4f2b70 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2081,14 +2081,6 @@ static bool interp__builtin_stdc_memreverse8(InterpState 
&S, CodePtr OpPC,
   bool IsArray = Desc->isArray();
   QualType ElemTy = IsArray ? Desc->getElemQualType() : Desc->getType();
 
-  if (!isOneByteCharacterType(ElemTy)) {
-    S.FFDiag(S.Current->getSource(OpPC),
-             diag::note_constexpr_memchr_unsupported)
-        << 
S.getASTContext().BuiltinInfo.getQuotedName(Call->getBuiltinCallee())
-        << ElemTy;
-    return false;
-  }
-
   if (IsArray)
     Ptr = Ptr.expand();
 
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 7c54e77ef0317..4b6a8263748e7 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -21118,13 +21118,6 @@ class VoidExprEvaluator
         return false;
 
       QualType CharTy = Ptr.Designator.getType(Info.Ctx);
-      if (!isOneByteCharacterType(CharTy)) {
-        Info.FFDiag(E, diag::note_constexpr_memchr_unsupported)
-            << Info.Ctx.BuiltinInfo.getQuotedName(E->getBuiltinCallee())
-            << CharTy;
-        return false;
-      }
-
       uint64_t RemainingElems = Ptr.Designator.validIndexAdjustments().second;
       if (NElems > RemainingElems) {
         uint64_t ArrayIndex =

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to