[clang] [clang] Implement C2y stdc_load8_* endian-aware 8-bit load functions (PR #203666)

NagaChaitanya Vellanki via cfe-commits cfe-commits at lists.llvm.org
Thu Jun 25 21:12:45 PDT 2026


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

>From 42398e48acb7617496736d2bc29bd4634329f922 Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Tue, 9 Jun 2026 12:50:57 -0700
Subject: [PATCH 1/6] [clang] Implement C2y stdc_load8_* endian-aware 8-bit
 load functions
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Implement all 32 stdc_load8_* functions from C2y:
  stdc_load8_le{u,s}{8,16,32,64}
  stdc_load8_be{u,s}{8,16,32,64}
  stdc_load8_aligned_le{u,s}{8,16,32,64}
  stdc_load8_aligned_be{u,s}{8,16,32,64}

These functions read N bytes from a const unsigned char* and assemble
them into a signed or unsigned integer using little-endian or big-endian
byte order.

Constexpr evaluation is supported in both the AST and bytecode
interpreters using the §7.18.21 formula directly. Runtime code
generation emits an integer load followed by llvm.bswap when the
requested byte order differs from the target endianness; 8-bit variants
never need a swap.
---
 clang/docs/ReleaseNotes.rst                   |   7 ++
 clang/include/clang/Basic/Builtins.td         |  56 +++++++++
 clang/lib/AST/ByteCode/InterpBuiltin.cpp      | 103 ++++++++++++++++
 clang/lib/AST/ExprConstant.cpp                | 102 ++++++++++++++++
 clang/lib/CodeGen/CGBuiltin.cpp               |  86 ++++++++++++++
 clang/test/CodeGen/builtin-stdc-load8.c       |  97 +++++++++++++++
 clang/test/Sema/Inputs/stdbit.h               |  40 +++++++
 .../test/Sema/constexpr-builtin-stdc-load8.c  | 110 ++++++++++++++++++
 8 files changed, 601 insertions(+)
 create mode 100644 clang/test/CodeGen/builtin-stdc-load8.c
 create mode 100644 clang/test/Sema/constexpr-builtin-stdc-load8.c

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index a2439ccb0452a..3eb25e3fb25de 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -240,6 +240,13 @@ C2y Feature Support
   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).
 
+- Implemented the C2y ``<stdbit.h>`` endian-aware 8-bit load functions
+  (``stdc_load8_le``, ``stdc_load8_be``, ``stdc_load8_aligned_le``,
+  ``stdc_load8_aligned_be``, and their signed counterparts) with constexpr
+  evaluation support. These functions read N bytes from a
+  ``const unsigned char *`` and assemble them into an integer using
+  little-endian or big-endian byte order.
+
 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 63cdb787bea16..56a707f5cb19b 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -38,6 +38,14 @@ class MemReverse8Template : Template<
     ["uint8_t", "uint16_t", "uint32_t", "uint64_t"],
     ["u8",      "u16",      "u32",      "u64"]>;
 
+class Load8UnsignedTemplate : Template<
+    ["uint8_t", "uint16_t", "uint32_t", "uint64_t"],
+    ["8",       "16",       "32",       "64"]>;
+
+class Load8SignedTemplate : Template<
+    ["int8_t", "int16_t", "int32_t", "int64_t"],
+    ["8",      "16",      "32",      "64"]>;
+
 class MSInt8_16_32Template : Template<["char", "short", "msint32_t"],
                                       ["8",    "16",    ""]>;
 
@@ -1003,6 +1011,54 @@ def StdcMemReverse8Typed : LibBuiltin<"stdbit.h", "C2Y_LANG">, MemReverse8Templa
   let Prototype = "T(T)";
 }
 
+def StdcLoad8LeU : LibBuiltin<"stdbit.h", "C2Y_LANG">, Load8UnsignedTemplate {
+  let Spellings = ["stdc_load8_leu"];
+  let Attributes = [NoThrow, Pure, Constexpr, NonNull<NonOptimizing, [0]>];
+  let Prototype = "T(unsigned char const*)";
+}
+
+def StdcLoad8BeU : LibBuiltin<"stdbit.h", "C2Y_LANG">, Load8UnsignedTemplate {
+  let Spellings = ["stdc_load8_beu"];
+  let Attributes = [NoThrow, Pure, Constexpr, NonNull<NonOptimizing, [0]>];
+  let Prototype = "T(unsigned char const*)";
+}
+
+def StdcLoad8AlignedLeU : LibBuiltin<"stdbit.h", "C2Y_LANG">, Load8UnsignedTemplate {
+  let Spellings = ["stdc_load8_aligned_leu"];
+  let Attributes = [NoThrow, Pure, Constexpr, NonNull<NonOptimizing, [0]>];
+  let Prototype = "T(unsigned char const*)";
+}
+
+def StdcLoad8AlignedBeU : LibBuiltin<"stdbit.h", "C2Y_LANG">, Load8UnsignedTemplate {
+  let Spellings = ["stdc_load8_aligned_beu"];
+  let Attributes = [NoThrow, Pure, Constexpr, NonNull<NonOptimizing, [0]>];
+  let Prototype = "T(unsigned char const*)";
+}
+
+def StdcLoad8LeS : LibBuiltin<"stdbit.h", "C2Y_LANG">, Load8SignedTemplate {
+  let Spellings = ["stdc_load8_les"];
+  let Attributes = [NoThrow, Pure, Constexpr, NonNull<NonOptimizing, [0]>];
+  let Prototype = "T(unsigned char const*)";
+}
+
+def StdcLoad8BeS : LibBuiltin<"stdbit.h", "C2Y_LANG">, Load8SignedTemplate {
+  let Spellings = ["stdc_load8_bes"];
+  let Attributes = [NoThrow, Pure, Constexpr, NonNull<NonOptimizing, [0]>];
+  let Prototype = "T(unsigned char const*)";
+}
+
+def StdcLoad8AlignedLeS : LibBuiltin<"stdbit.h", "C2Y_LANG">, Load8SignedTemplate {
+  let Spellings = ["stdc_load8_aligned_les"];
+  let Attributes = [NoThrow, Pure, Constexpr, NonNull<NonOptimizing, [0]>];
+  let Prototype = "T(unsigned char const*)";
+}
+
+def StdcLoad8AlignedBeS : LibBuiltin<"stdbit.h", "C2Y_LANG">, Load8SignedTemplate {
+  let Spellings = ["stdc_load8_aligned_bes"];
+  let Attributes = [NoThrow, Pure, Constexpr, NonNull<NonOptimizing, [0]>];
+  let Prototype = "T(unsigned char const*)";
+}
+
 
 // Random GCC builtins
 // FIXME: The builtins marked FunctionWithBuiltinPrefix below should be
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index b76f13833da14..64b5b7b5dac78 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2058,6 +2058,73 @@ static bool isOneByteCharacterType(QualType T) {
   return T->isCharType() || T->isChar8Type();
 }
 
+static bool interp__builtin_load8(InterpState &S, CodePtr OpPC,
+                                  const InterpFrame *Frame,
+                                  const CallExpr *Call, bool IsBigEndian) {
+  Pointer Ptr = S.Stk.pop<Pointer>();
+
+  if (Ptr.isZero()) {
+    S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_access_null)
+        << AK_Read;
+    return false;
+  }
+
+  if (!isReadable(Ptr) && !Ptr.isOnePastEnd())
+    return false;
+
+  const Descriptor *Desc = Ptr.getFieldDesc();
+  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();
+
+  size_t BaseIdx = Ptr.getIndex();
+  size_t ArraySize = Ptr.getNumElems();
+  size_t RemainingElems = ArraySize - BaseIdx;
+
+  unsigned ByteWidth = S.getASTContext().getTypeSize(Call->getType()) / 8;
+  if (ByteWidth > RemainingElems) {
+    if (IsArray)
+      S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
+          << (uint64_t)(BaseIdx + ByteWidth - 1) << /*array*/ 0
+          << (uint64_t)ArraySize;
+    else
+      S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
+          << (uint64_t)(BaseIdx + ByteWidth - 1) << /*non-array*/ 1;
+    return false;
+  }
+
+  PrimType ElemT = *S.getContext().classify(ElemTy);
+  unsigned BitWidth = ByteWidth * 8;
+  APInt Result = APInt::getZero(BitWidth);
+
+  // C2y §7.18.21: result = sum(b_index * 2^(8*index)) for index in [0, N/8)
+  // where b_index = ptr[index] (LE) or ptr[N/8 - index - 1] (BE).
+  for (unsigned I = 0; I < ByteWidth; ++I) {
+    size_t SrcIdx = IsBigEndian ? (ByteWidth - I - 1) : I;
+    Pointer BytePtr = Ptr.atIndex(BaseIdx + SrcIdx);
+    if (!CheckLoad(S, OpPC, BytePtr, AK_Read))
+      return false;
+    uint64_t B;
+    INT_TYPE_SWITCH_NO_BOOL(
+        ElemT, { B = static_cast<uint64_t>(BytePtr.deref<T>().toUnsigned()); });
+    Result |= APInt(BitWidth, B) << (8 * I);
+  }
+
+  bool IsSigned = Call->getType()->isSignedIntegerType();
+  pushInteger(S, APSInt(Result, !IsSigned), Call->getType());
+  return true;
+}
+
 static bool interp__builtin_memcmp(InterpState &S, CodePtr OpPC,
                                    const InterpFrame *Frame,
                                    const CallExpr *Call, unsigned ID) {
@@ -5085,6 +5152,42 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
   case Builtin::BIstdc_memreverse8u64:
     return interp__builtin_bswap(S, OpPC, Frame, Call);
 
+  case Builtin::BIstdc_load8_leu8:
+  case Builtin::BIstdc_load8_leu16:
+  case Builtin::BIstdc_load8_leu32:
+  case Builtin::BIstdc_load8_leu64:
+  case Builtin::BIstdc_load8_les8:
+  case Builtin::BIstdc_load8_les16:
+  case Builtin::BIstdc_load8_les32:
+  case Builtin::BIstdc_load8_les64:
+  case Builtin::BIstdc_load8_aligned_leu8:
+  case Builtin::BIstdc_load8_aligned_leu16:
+  case Builtin::BIstdc_load8_aligned_leu32:
+  case Builtin::BIstdc_load8_aligned_leu64:
+  case Builtin::BIstdc_load8_aligned_les8:
+  case Builtin::BIstdc_load8_aligned_les16:
+  case Builtin::BIstdc_load8_aligned_les32:
+  case Builtin::BIstdc_load8_aligned_les64:
+    return interp__builtin_load8(S, OpPC, Frame, Call, /*IsBigEndian=*/false);
+
+  case Builtin::BIstdc_load8_beu8:
+  case Builtin::BIstdc_load8_beu16:
+  case Builtin::BIstdc_load8_beu32:
+  case Builtin::BIstdc_load8_beu64:
+  case Builtin::BIstdc_load8_bes8:
+  case Builtin::BIstdc_load8_bes16:
+  case Builtin::BIstdc_load8_bes32:
+  case Builtin::BIstdc_load8_bes64:
+  case Builtin::BIstdc_load8_aligned_beu8:
+  case Builtin::BIstdc_load8_aligned_beu16:
+  case Builtin::BIstdc_load8_aligned_beu32:
+  case Builtin::BIstdc_load8_aligned_beu64:
+  case Builtin::BIstdc_load8_aligned_bes8:
+  case Builtin::BIstdc_load8_aligned_bes16:
+  case Builtin::BIstdc_load8_aligned_bes32:
+  case Builtin::BIstdc_load8_aligned_bes64:
+    return interp__builtin_load8(S, OpPC, Frame, Call, /*IsBigEndian=*/true);
+
   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..ea73a75f18459 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -16711,6 +16711,108 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
     return Success(Val.byteSwap(), E);
   }
 
+  case Builtin::BIstdc_load8_leu8:
+  case Builtin::BIstdc_load8_leu16:
+  case Builtin::BIstdc_load8_leu32:
+  case Builtin::BIstdc_load8_leu64:
+  case Builtin::BIstdc_load8_les8:
+  case Builtin::BIstdc_load8_les16:
+  case Builtin::BIstdc_load8_les32:
+  case Builtin::BIstdc_load8_les64:
+  case Builtin::BIstdc_load8_aligned_leu8:
+  case Builtin::BIstdc_load8_aligned_leu16:
+  case Builtin::BIstdc_load8_aligned_leu32:
+  case Builtin::BIstdc_load8_aligned_leu64:
+  case Builtin::BIstdc_load8_aligned_les8:
+  case Builtin::BIstdc_load8_aligned_les16:
+  case Builtin::BIstdc_load8_aligned_les32:
+  case Builtin::BIstdc_load8_aligned_les64:
+  case Builtin::BIstdc_load8_beu8:
+  case Builtin::BIstdc_load8_beu16:
+  case Builtin::BIstdc_load8_beu32:
+  case Builtin::BIstdc_load8_beu64:
+  case Builtin::BIstdc_load8_bes8:
+  case Builtin::BIstdc_load8_bes16:
+  case Builtin::BIstdc_load8_bes32:
+  case Builtin::BIstdc_load8_bes64:
+  case Builtin::BIstdc_load8_aligned_beu8:
+  case Builtin::BIstdc_load8_aligned_beu16:
+  case Builtin::BIstdc_load8_aligned_beu32:
+  case Builtin::BIstdc_load8_aligned_beu64:
+  case Builtin::BIstdc_load8_aligned_bes8:
+  case Builtin::BIstdc_load8_aligned_bes16:
+  case Builtin::BIstdc_load8_aligned_bes32:
+  case Builtin::BIstdc_load8_aligned_bes64: {
+    bool IsBE = BuiltinOp == Builtin::BIstdc_load8_beu8 ||
+                BuiltinOp == Builtin::BIstdc_load8_beu16 ||
+                BuiltinOp == Builtin::BIstdc_load8_beu32 ||
+                BuiltinOp == Builtin::BIstdc_load8_beu64 ||
+                BuiltinOp == Builtin::BIstdc_load8_bes8 ||
+                BuiltinOp == Builtin::BIstdc_load8_bes16 ||
+                BuiltinOp == Builtin::BIstdc_load8_bes32 ||
+                BuiltinOp == Builtin::BIstdc_load8_bes64 ||
+                BuiltinOp == Builtin::BIstdc_load8_aligned_beu8 ||
+                BuiltinOp == Builtin::BIstdc_load8_aligned_beu16 ||
+                BuiltinOp == Builtin::BIstdc_load8_aligned_beu32 ||
+                BuiltinOp == Builtin::BIstdc_load8_aligned_beu64 ||
+                BuiltinOp == Builtin::BIstdc_load8_aligned_bes8 ||
+                BuiltinOp == Builtin::BIstdc_load8_aligned_bes16 ||
+                BuiltinOp == Builtin::BIstdc_load8_aligned_bes32 ||
+                BuiltinOp == Builtin::BIstdc_load8_aligned_bes64;
+
+    LValue Ptr;
+    if (!EvaluatePointer(E->getArg(0), Ptr, Info))
+      return false;
+
+    if (!Ptr.checkNullPointerForFoldAccess(Info, E, AK_Read) ||
+        Ptr.Designator.Invalid)
+      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;
+    }
+
+    unsigned ByteWidth = Info.Ctx.getTypeSize(E->getType()) / 8;
+    uint64_t RemainingElems = Ptr.Designator.validIndexAdjustments().second;
+    if (ByteWidth > RemainingElems) {
+      uint64_t ArrayIndex =
+          Ptr.Designator.MostDerivedIsArrayElement
+              ? Ptr.Designator.Entries.back().getAsArrayIndex()
+              : (uint64_t)Ptr.Designator.IsOnePastTheEnd;
+      APSInt Index = APSInt::get(ArrayIndex + ByteWidth - 1);
+      Ptr.Designator.diagnosePointerArithmetic(Info, E, Index);
+      return false;
+    }
+
+    // Load bytes sequentially, then assemble per C2y §7.18.21:
+    // result = sum(b_index * 2^(8*index)) where b_index = ptr[index] (LE)
+    // or ptr[N/8 - index - 1] (BE).
+    SmallVector<uint64_t, 8> Bytes(ByteWidth);
+    LValue BytePtr = Ptr;
+    for (unsigned I = 0; I < ByteWidth; ++I) {
+      APValue ByteVal;
+      if (!handleLValueToRValueConversion(Info, E, CharTy, BytePtr, ByteVal))
+        return false;
+      Bytes[I] = ByteVal.getInt().getZExtValue();
+      if (I + 1 < ByteWidth)
+        if (!HandleLValueArrayAdjustment(Info, E, BytePtr, CharTy, 1))
+          return false;
+    }
+
+    APInt Result = APInt::getZero(ByteWidth * 8);
+    for (unsigned I = 0; I < ByteWidth; ++I) {
+      unsigned SrcIdx = IsBE ? (ByteWidth - I - 1) : I;
+      Result |= APInt(ByteWidth * 8, Bytes[SrcIdx]) << (8 * I);
+    }
+
+    bool IsSigned = E->getType()->isSignedIntegerType();
+    return Success(APSInt(Result, !IsSigned), E);
+  }
+
   case Builtin::BI__builtin_classify_type:
     return Success((int)EvaluateBuiltinClassifyType(E, Info.getLangOpts()), E);
 
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 475bfec6199fc..6cb8ea325d232 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -4215,6 +4215,92 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
     break;
   }
 
+  // stdc_load8_*: load N bytes from a const unsigned char* and assemble
+  // into an integer using little-endian or big-endian byte order.
+  case Builtin::BIstdc_load8_leu8:
+  case Builtin::BIstdc_load8_les8:
+  case Builtin::BIstdc_load8_beu8:
+  case Builtin::BIstdc_load8_bes8:
+  case Builtin::BIstdc_load8_aligned_leu8:
+  case Builtin::BIstdc_load8_aligned_les8:
+  case Builtin::BIstdc_load8_aligned_beu8:
+  case Builtin::BIstdc_load8_aligned_bes8:
+  case Builtin::BIstdc_load8_leu16:
+  case Builtin::BIstdc_load8_les16:
+  case Builtin::BIstdc_load8_beu16:
+  case Builtin::BIstdc_load8_bes16:
+  case Builtin::BIstdc_load8_aligned_leu16:
+  case Builtin::BIstdc_load8_aligned_les16:
+  case Builtin::BIstdc_load8_aligned_beu16:
+  case Builtin::BIstdc_load8_aligned_bes16:
+  case Builtin::BIstdc_load8_leu32:
+  case Builtin::BIstdc_load8_les32:
+  case Builtin::BIstdc_load8_beu32:
+  case Builtin::BIstdc_load8_bes32:
+  case Builtin::BIstdc_load8_aligned_leu32:
+  case Builtin::BIstdc_load8_aligned_les32:
+  case Builtin::BIstdc_load8_aligned_beu32:
+  case Builtin::BIstdc_load8_aligned_bes32:
+  case Builtin::BIstdc_load8_leu64:
+  case Builtin::BIstdc_load8_les64:
+  case Builtin::BIstdc_load8_beu64:
+  case Builtin::BIstdc_load8_bes64:
+  case Builtin::BIstdc_load8_aligned_leu64:
+  case Builtin::BIstdc_load8_aligned_les64:
+  case Builtin::BIstdc_load8_aligned_beu64:
+  case Builtin::BIstdc_load8_aligned_bes64: {
+    bool IsBE = BuiltinID == Builtin::BIstdc_load8_beu8 ||
+                BuiltinID == Builtin::BIstdc_load8_bes8 ||
+                BuiltinID == Builtin::BIstdc_load8_beu16 ||
+                BuiltinID == Builtin::BIstdc_load8_bes16 ||
+                BuiltinID == Builtin::BIstdc_load8_beu32 ||
+                BuiltinID == Builtin::BIstdc_load8_bes32 ||
+                BuiltinID == Builtin::BIstdc_load8_beu64 ||
+                BuiltinID == Builtin::BIstdc_load8_bes64 ||
+                BuiltinID == Builtin::BIstdc_load8_aligned_beu8 ||
+                BuiltinID == Builtin::BIstdc_load8_aligned_bes8 ||
+                BuiltinID == Builtin::BIstdc_load8_aligned_beu16 ||
+                BuiltinID == Builtin::BIstdc_load8_aligned_bes16 ||
+                BuiltinID == Builtin::BIstdc_load8_aligned_beu32 ||
+                BuiltinID == Builtin::BIstdc_load8_aligned_bes32 ||
+                BuiltinID == Builtin::BIstdc_load8_aligned_beu64 ||
+                BuiltinID == Builtin::BIstdc_load8_aligned_bes64;
+    bool IsAligned = BuiltinID == Builtin::BIstdc_load8_aligned_leu8 ||
+                     BuiltinID == Builtin::BIstdc_load8_aligned_les8 ||
+                     BuiltinID == Builtin::BIstdc_load8_aligned_beu8 ||
+                     BuiltinID == Builtin::BIstdc_load8_aligned_bes8 ||
+                     BuiltinID == Builtin::BIstdc_load8_aligned_leu16 ||
+                     BuiltinID == Builtin::BIstdc_load8_aligned_les16 ||
+                     BuiltinID == Builtin::BIstdc_load8_aligned_beu16 ||
+                     BuiltinID == Builtin::BIstdc_load8_aligned_bes16 ||
+                     BuiltinID == Builtin::BIstdc_load8_aligned_leu32 ||
+                     BuiltinID == Builtin::BIstdc_load8_aligned_les32 ||
+                     BuiltinID == Builtin::BIstdc_load8_aligned_beu32 ||
+                     BuiltinID == Builtin::BIstdc_load8_aligned_bes32 ||
+                     BuiltinID == Builtin::BIstdc_load8_aligned_leu64 ||
+                     BuiltinID == Builtin::BIstdc_load8_aligned_les64 ||
+                     BuiltinID == Builtin::BIstdc_load8_aligned_beu64 ||
+                     BuiltinID == Builtin::BIstdc_load8_aligned_bes64;
+
+    llvm::Type *IntTy = ConvertType(E->getType());
+    unsigned BitWidth = IntTy->getIntegerBitWidth();
+    assert(
+        (BitWidth == 8 || BitWidth == 16 || BitWidth == 32 || BitWidth == 64) &&
+        "unexpected bit width for stdc_load8_*");
+    CharUnits Alignment = IsAligned
+                              ? getContext().getTypeAlignInChars(E->getType())
+                              : CharUnits::One();
+    Address Addr =
+        EmitPointerWithAlignment(E->getArg(0)).withAlignment(Alignment);
+    Addr = Addr.withElementType(IntTy);
+    Value *Val = Builder.CreateLoad(Addr);
+    if ((BitWidth == 16 || BitWidth == 32 || BitWidth == 64) &&
+        IsBE != getTarget().isBigEndian())
+      Val = Builder.CreateCall(CGM.getIntrinsic(Intrinsic::bswap, IntTy), Val);
+
+    return RValue::get(Val);
+  }
+
   case Builtin::BI__builtin_constant_p: {
     llvm::Type *ResultType = ConvertType(E->getType());
 
diff --git a/clang/test/CodeGen/builtin-stdc-load8.c b/clang/test/CodeGen/builtin-stdc-load8.c
new file mode 100644
index 0000000000000..88424c7bbda01
--- /dev/null
+++ b/clang/test/CodeGen/builtin-stdc-load8.c
@@ -0,0 +1,97 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c2y -isystem %S/../Sema/Inputs -emit-llvm -o - %s | FileCheck %s --check-prefix=LE
+// RUN: %clang_cc1 -triple s390x-unknown-unknown  -std=c2y -isystem %S/../Sema/Inputs -emit-llvm -o - %s | FileCheck %s --check-prefix=BE
+
+#include <stdbit.h>
+
+// 8-bit: single byte load, no bswap on either target.
+// LE-LABEL: test_leu8
+// LE: load i8, ptr {{.*}}, align 1
+// LE-NOT: bswap
+// BE-LABEL: test_leu8
+// BE: load i8, ptr {{.*}}, align 1
+// BE-NOT: bswap
+__UINT8_TYPE__ test_leu8(const unsigned char *p) { return stdc_load8_leu8(p); }
+
+// LE load on LE target: no bswap. On BE target: bswap needed.
+// LE-LABEL: test_leu16
+// LE: load i16, ptr {{.*}}, align 1
+// LE-NOT: bswap
+// BE-LABEL: test_leu16
+// BE: load i16, ptr {{.*}}, align 1
+// BE: call i16 @llvm.bswap.i16
+__UINT16_TYPE__ test_leu16(const unsigned char *p) { return stdc_load8_leu16(p); }
+
+// LE-LABEL: test_leu32
+// LE: load i32, ptr {{.*}}, align 1
+// LE-NOT: bswap
+// BE-LABEL: test_leu32
+// BE: load i32, ptr {{.*}}, align 1
+// BE: call i32 @llvm.bswap.i32
+__UINT32_TYPE__ test_leu32(const unsigned char *p) { return stdc_load8_leu32(p); }
+
+// LE-LABEL: test_leu64
+// LE: load i64, ptr {{.*}}, align 1
+// LE-NOT: bswap
+// BE-LABEL: test_leu64
+// BE: load i64, ptr {{.*}}, align 1
+// BE: call i64 @llvm.bswap.i64
+__UINT64_TYPE__ test_leu64(const unsigned char *p) { return stdc_load8_leu64(p); }
+
+// BE load on LE target: bswap needed. On BE target: no bswap.
+// LE-LABEL: test_beu16
+// LE: load i16, ptr {{.*}}, align 1
+// LE: call i16 @llvm.bswap.i16
+// BE-LABEL: test_beu16
+// BE: load i16, ptr {{.*}}, align 1
+// BE-NOT: bswap
+__UINT16_TYPE__ test_beu16(const unsigned char *p) { return stdc_load8_beu16(p); }
+
+// LE-LABEL: test_beu32
+// LE: load i32, ptr {{.*}}, align 1
+// LE: call i32 @llvm.bswap.i32
+// BE-LABEL: test_beu32
+// BE: load i32, ptr {{.*}}, align 1
+// BE-NOT: bswap
+__UINT32_TYPE__ test_beu32(const unsigned char *p) { return stdc_load8_beu32(p); }
+
+// LE-LABEL: test_beu64
+// LE: load i64, ptr {{.*}}, align 1
+// LE: call i64 @llvm.bswap.i64
+// BE-LABEL: test_beu64
+// BE: load i64, ptr {{.*}}, align 1
+// BE-NOT: bswap
+__UINT64_TYPE__ test_beu64(const unsigned char *p) { return stdc_load8_beu64(p); }
+
+// Aligned variants use natural alignment instead of align 1.
+// LE-LABEL: test_aligned_leu32
+// LE: load i32, ptr {{.*}}, align 4
+// LE-NOT: bswap
+// BE-LABEL: test_aligned_leu32
+// BE: load i32, ptr {{.*}}, align 4
+// BE: call i32 @llvm.bswap.i32
+__UINT32_TYPE__ test_aligned_leu32(const unsigned char *p) { return stdc_load8_aligned_leu32(p); }
+
+// LE-LABEL: test_aligned_beu32
+// LE: load i32, ptr {{.*}}, align 4
+// LE: call i32 @llvm.bswap.i32
+// BE-LABEL: test_aligned_beu32
+// BE: load i32, ptr {{.*}}, align 4
+// BE-NOT: bswap
+__UINT32_TYPE__ test_aligned_beu32(const unsigned char *p) { return stdc_load8_aligned_beu32(p); }
+
+// Signed variants: same load+bswap logic, result type is signed.
+// LE-LABEL: test_les32
+// LE: load i32, ptr {{.*}}, align 1
+// LE-NOT: bswap
+// BE-LABEL: test_les32
+// BE: load i32, ptr {{.*}}, align 1
+// BE: call i32 @llvm.bswap.i32
+__INT32_TYPE__ test_les32(const unsigned char *p) { return stdc_load8_les32(p); }
+
+// LE-LABEL: test_bes32
+// LE: load i32, ptr {{.*}}, align 1
+// LE: call i32 @llvm.bswap.i32
+// BE-LABEL: test_bes32
+// BE: load i32, ptr {{.*}}, align 1
+// BE-NOT: bswap
+__INT32_TYPE__ test_bes32(const unsigned char *p) { return stdc_load8_bes32(p); }
diff --git a/clang/test/Sema/Inputs/stdbit.h b/clang/test/Sema/Inputs/stdbit.h
index 170d3fdb382c3..8200a9e7f3a90 100644
--- a/clang/test/Sema/Inputs/stdbit.h
+++ b/clang/test/Sema/Inputs/stdbit.h
@@ -118,4 +118,44 @@ __UINT16_TYPE__ stdc_memreverse8u16(__UINT16_TYPE__);
 __UINT32_TYPE__ stdc_memreverse8u32(__UINT32_TYPE__);
 __UINT64_TYPE__ stdc_memreverse8u64(__UINT64_TYPE__);
 
+__UINT8_TYPE__ stdc_load8_leu8(const unsigned char *);
+__UINT16_TYPE__ stdc_load8_leu16(const unsigned char *);
+__UINT32_TYPE__ stdc_load8_leu32(const unsigned char *);
+__UINT64_TYPE__ stdc_load8_leu64(const unsigned char *);
+
+__UINT8_TYPE__ stdc_load8_beu8(const unsigned char *);
+__UINT16_TYPE__ stdc_load8_beu16(const unsigned char *);
+__UINT32_TYPE__ stdc_load8_beu32(const unsigned char *);
+__UINT64_TYPE__ stdc_load8_beu64(const unsigned char *);
+
+__UINT8_TYPE__ stdc_load8_aligned_leu8(const unsigned char *);
+__UINT16_TYPE__ stdc_load8_aligned_leu16(const unsigned char *);
+__UINT32_TYPE__ stdc_load8_aligned_leu32(const unsigned char *);
+__UINT64_TYPE__ stdc_load8_aligned_leu64(const unsigned char *);
+
+__UINT8_TYPE__ stdc_load8_aligned_beu8(const unsigned char *);
+__UINT16_TYPE__ stdc_load8_aligned_beu16(const unsigned char *);
+__UINT32_TYPE__ stdc_load8_aligned_beu32(const unsigned char *);
+__UINT64_TYPE__ stdc_load8_aligned_beu64(const unsigned char *);
+
+__INT8_TYPE__ stdc_load8_les8(const unsigned char *);
+__INT16_TYPE__ stdc_load8_les16(const unsigned char *);
+__INT32_TYPE__ stdc_load8_les32(const unsigned char *);
+__INT64_TYPE__ stdc_load8_les64(const unsigned char *);
+
+__INT8_TYPE__ stdc_load8_bes8(const unsigned char *);
+__INT16_TYPE__ stdc_load8_bes16(const unsigned char *);
+__INT32_TYPE__ stdc_load8_bes32(const unsigned char *);
+__INT64_TYPE__ stdc_load8_bes64(const unsigned char *);
+
+__INT8_TYPE__ stdc_load8_aligned_les8(const unsigned char *);
+__INT16_TYPE__ stdc_load8_aligned_les16(const unsigned char *);
+__INT32_TYPE__ stdc_load8_aligned_les32(const unsigned char *);
+__INT64_TYPE__ stdc_load8_aligned_les64(const unsigned char *);
+
+__INT8_TYPE__ stdc_load8_aligned_bes8(const unsigned char *);
+__INT16_TYPE__ stdc_load8_aligned_bes16(const unsigned char *);
+__INT32_TYPE__ stdc_load8_aligned_bes32(const unsigned char *);
+__INT64_TYPE__ stdc_load8_aligned_bes64(const unsigned char *);
+
 #endif
diff --git a/clang/test/Sema/constexpr-builtin-stdc-load8.c b/clang/test/Sema/constexpr-builtin-stdc-load8.c
new file mode 100644
index 0000000000000..2661089b8c901
--- /dev/null
+++ b/clang/test/Sema/constexpr-builtin-stdc-load8.c
@@ -0,0 +1,110 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c2y -isystem %S/Inputs -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c2y -isystem %S/Inputs -fsyntax-only -verify %s -fexperimental-new-constant-interpreter
+#include <stdbit.h>
+
+// LE unsigned: bytes ordered LSB-first (index 0 = byte 0 of the value)
+static const unsigned char le8_u[]  = {0xAB};
+static const unsigned char le16_u[] = {0x34, 0x12};
+static const unsigned char le32_u[] = {0x78, 0x56, 0x34, 0x12};
+static const unsigned char le64_u[] = {0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12};
+
+_Static_assert(stdc_load8_leu8(le8_u)   == (__UINT8_TYPE__)0xAB,               "");
+_Static_assert(stdc_load8_leu16(le16_u) == (__UINT16_TYPE__)0x1234,            "");
+_Static_assert(stdc_load8_leu32(le32_u) == (__UINT32_TYPE__)0x12345678U,       "");
+_Static_assert(stdc_load8_leu64(le64_u) == (__UINT64_TYPE__)0x123456789ABCDEF0ULL, "");
+
+// BE unsigned: bytes ordered MSB-first (index 0 = highest byte)
+static const unsigned char be8_u[]  = {0xAB};
+static const unsigned char be16_u[] = {0x12, 0x34};
+static const unsigned char be32_u[] = {0x12, 0x34, 0x56, 0x78};
+static const unsigned char be64_u[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0};
+
+_Static_assert(stdc_load8_beu8(be8_u)   == (__UINT8_TYPE__)0xAB,               "");
+_Static_assert(stdc_load8_beu16(be16_u) == (__UINT16_TYPE__)0x1234,            "");
+_Static_assert(stdc_load8_beu32(be32_u) == (__UINT32_TYPE__)0x12345678U,       "");
+_Static_assert(stdc_load8_beu64(be64_u) == (__UINT64_TYPE__)0x123456789ABCDEF0ULL, "");
+
+// Aligned LE unsigned
+_Static_assert(stdc_load8_aligned_leu8(le8_u)   == (__UINT8_TYPE__)0xAB,               "");
+_Static_assert(stdc_load8_aligned_leu16(le16_u) == (__UINT16_TYPE__)0x1234,            "");
+_Static_assert(stdc_load8_aligned_leu32(le32_u) == (__UINT32_TYPE__)0x12345678U,       "");
+_Static_assert(stdc_load8_aligned_leu64(le64_u) == (__UINT64_TYPE__)0x123456789ABCDEF0ULL, "");
+
+// Aligned BE unsigned
+_Static_assert(stdc_load8_aligned_beu8(be8_u)   == (__UINT8_TYPE__)0xAB,               "");
+_Static_assert(stdc_load8_aligned_beu16(be16_u) == (__UINT16_TYPE__)0x1234,            "");
+_Static_assert(stdc_load8_aligned_beu32(be32_u) == (__UINT32_TYPE__)0x12345678U,       "");
+_Static_assert(stdc_load8_aligned_beu64(be64_u) == (__UINT64_TYPE__)0x123456789ABCDEF0ULL, "");
+
+// LE signed: 0x80 in u8 = -128 as s8; {0x80, 0xFF} as u16 = -128 as s16
+static const unsigned char le8_s[]  = {0x80};
+static const unsigned char le16_s[] = {0x80, 0xFF};
+static const unsigned char le32_s[] = {0x80, 0xFF, 0xFF, 0xFF};
+static const unsigned char le64_s[] = {0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+
+_Static_assert(stdc_load8_les8(le8_s)   == (__INT8_TYPE__)-128,  "");
+_Static_assert(stdc_load8_les16(le16_s) == (__INT16_TYPE__)-128, "");
+_Static_assert(stdc_load8_les32(le32_s) == (__INT32_TYPE__)-128, "");
+_Static_assert(stdc_load8_les64(le64_s) == (__INT64_TYPE__)-128, "");
+
+// BE signed
+static const unsigned char be8_s[]  = {0x80};
+static const unsigned char be16_s[] = {0xFF, 0x80};
+static const unsigned char be32_s[] = {0xFF, 0xFF, 0xFF, 0x80};
+static const unsigned char be64_s[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80};
+
+_Static_assert(stdc_load8_bes8(be8_s)   == (__INT8_TYPE__)-128,  "");
+_Static_assert(stdc_load8_bes16(be16_s) == (__INT16_TYPE__)-128, "");
+_Static_assert(stdc_load8_bes32(be32_s) == (__INT32_TYPE__)-128, "");
+_Static_assert(stdc_load8_bes64(be64_s) == (__INT64_TYPE__)-128, "");
+
+// Aligned LE signed
+_Static_assert(stdc_load8_aligned_les8(le8_s)   == (__INT8_TYPE__)-128,  "");
+_Static_assert(stdc_load8_aligned_les16(le16_s) == (__INT16_TYPE__)-128, "");
+_Static_assert(stdc_load8_aligned_les32(le32_s) == (__INT32_TYPE__)-128, "");
+_Static_assert(stdc_load8_aligned_les64(le64_s) == (__INT64_TYPE__)-128, "");
+
+// Aligned BE signed
+_Static_assert(stdc_load8_aligned_bes8(be8_s)   == (__INT8_TYPE__)-128,  "");
+_Static_assert(stdc_load8_aligned_bes16(be16_s) == (__INT16_TYPE__)-128, "");
+_Static_assert(stdc_load8_aligned_bes32(be32_s) == (__INT32_TYPE__)-128, "");
+_Static_assert(stdc_load8_aligned_bes64(be64_s) == (__INT64_TYPE__)-128, "");
+
+// Positive signed round-trip
+static const unsigned char le16_pos[] = {0x01, 0x00};
+static const unsigned char be16_pos[] = {0x00, 0x01};
+_Static_assert(stdc_load8_les16(le16_pos) == 1, "");
+_Static_assert(stdc_load8_bes16(be16_pos) == 1, "");
+
+// constexpr variable declarations require constexpr arrays as the source
+constexpr unsigned char cx_le32[] = {0x78, 0x56, 0x34, 0x12};
+constexpr unsigned char cx_be32[] = {0x12, 0x34, 0x56, 0x78};
+constexpr unsigned char cx_le16_s[] = {0x80, 0xFF};
+constexpr unsigned char cx_be16_s[] = {0xFF, 0x80};
+
+constexpr __UINT32_TYPE__ u32_le = stdc_load8_leu32(cx_le32);
+_Static_assert(u32_le == 0x12345678U, "");
+
+constexpr __UINT32_TYPE__ u32_be = stdc_load8_beu32(cx_be32);
+_Static_assert(u32_be == 0x12345678U, "");
+
+constexpr __INT16_TYPE__ s16_le = stdc_load8_les16(cx_le16_s);
+_Static_assert(s16_le == -128, "");
+
+constexpr __INT16_TYPE__ s16_be = stdc_load8_bes16(cx_be16_s);
+_Static_assert(s16_be == -128, "");
+
+// Null pointer is rejected (NonNull attribute)
+void test_null(void) {
+  __UINT8_TYPE__ x = stdc_load8_leu8(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+}
+
+// Negative: out-of-bounds, scalar, and null.
+constexpr unsigned char small[] = {0x01, 0x02};
+constexpr __UINT32_TYPE__ oob_load = stdc_load8_leu32(small); // expected-error{{must be initialized by a constant expression}} expected-note{{cannot refer to element 3 of array of 2 elements in a constant expression}}
+constexpr __UINT32_TYPE__ oob_mid  = stdc_load8_leu32(small + 1); // expected-error{{must be initialized by a constant expression}} expected-note{{cannot refer to element 4 of array of 2 elements in a constant expression}}
+
+constexpr unsigned char scalar_byte = 0x42;
+constexpr __UINT32_TYPE__ oob_scalar = stdc_load8_leu32(&scalar_byte); // expected-error{{must be initialized by a constant expression}} expected-note{{cannot refer to element 3 of non-array object in a constant expression}}
+
+constexpr __UINT32_TYPE__ null_ce = stdc_load8_leu32((const unsigned char *)0); // expected-error{{must be initialized by a constant expression}} expected-note{{read of dereferenced null pointer is not allowed in a constant expression}}

>From 7c256ae6ac95499a0a62826561465b53e3d0a129 Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Thu, 25 Jun 2026 10:43:58 -0700
Subject: [PATCH 2/6] Use uint_least*_t/int_least*_t for stdc_load8_* return
 types

- C2y standard specifies stdc_load8_* functions to return
{uint,int}_least*_t types instead of {uint,int}_t types
---
 clang/include/clang/Basic/Builtins.td         |   8 +-
 clang/test/CodeGen/builtin-stdc-load8.c       | 130 +++++++++---------
 clang/test/Sema/Inputs/stdbit.h               |  78 +++++------
 .../test/Sema/constexpr-builtin-stdc-load8.c  |  82 +++++------
 clang/utils/TableGen/ClangBuiltinsEmitter.cpp |   8 ++
 5 files changed, 157 insertions(+), 149 deletions(-)

diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 56a707f5cb19b..91fca1aed11b0 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -39,12 +39,12 @@ class MemReverse8Template : Template<
     ["u8",      "u16",      "u32",      "u64"]>;
 
 class Load8UnsignedTemplate : Template<
-    ["uint8_t", "uint16_t", "uint32_t", "uint64_t"],
-    ["8",       "16",       "32",       "64"]>;
+    ["uint_least8_t", "uint_least16_t", "uint_least32_t", "uint_least64_t"],
+    ["8",             "16",             "32",             "64"]>;
 
 class Load8SignedTemplate : Template<
-    ["int8_t", "int16_t", "int32_t", "int64_t"],
-    ["8",      "16",      "32",      "64"]>;
+    ["int_least8_t", "int_least16_t", "int_least32_t", "int_least64_t"],
+    ["8",            "16",            "32",            "64"]>;
 
 class MSInt8_16_32Template : Template<["char", "short", "msint32_t"],
                                       ["8",    "16",    ""]>;
diff --git a/clang/test/CodeGen/builtin-stdc-load8.c b/clang/test/CodeGen/builtin-stdc-load8.c
index 88424c7bbda01..6fb1dcc8cc771 100644
--- a/clang/test/CodeGen/builtin-stdc-load8.c
+++ b/clang/test/CodeGen/builtin-stdc-load8.c
@@ -4,94 +4,94 @@
 #include <stdbit.h>
 
 // 8-bit: single byte load, no bswap on either target.
-// LE-LABEL: test_leu8
-// LE: load i8, ptr {{.*}}, align 1
+// LE-LABEL: @test_leu8(
+// LE: load i8, ptr {{.+}}, align 1
 // LE-NOT: bswap
-// BE-LABEL: test_leu8
-// BE: load i8, ptr {{.*}}, align 1
+// BE-LABEL: @test_leu8(
+// BE: load i8, ptr {{.+}}, align 1
 // BE-NOT: bswap
-__UINT8_TYPE__ test_leu8(const unsigned char *p) { return stdc_load8_leu8(p); }
+__UINT_LEAST8_TYPE__ test_leu8(const unsigned char *p) { return stdc_load8_leu8(p); }
 
 // LE load on LE target: no bswap. On BE target: bswap needed.
-// LE-LABEL: test_leu16
-// LE: load i16, ptr {{.*}}, align 1
+// LE-LABEL: @test_leu16(
+// LE: load i16, ptr {{.+}}, align 1
 // LE-NOT: bswap
-// BE-LABEL: test_leu16
-// BE: load i16, ptr {{.*}}, align 1
-// BE: call i16 @llvm.bswap.i16
-__UINT16_TYPE__ test_leu16(const unsigned char *p) { return stdc_load8_leu16(p); }
+// BE-LABEL: @test_leu16(
+// BE: load i16, ptr {{.+}}, align 1
+// BE: call i16 @llvm.bswap.i16(
+__UINT_LEAST16_TYPE__ test_leu16(const unsigned char *p) { return stdc_load8_leu16(p); }
 
-// LE-LABEL: test_leu32
-// LE: load i32, ptr {{.*}}, align 1
+// LE-LABEL: @test_leu32(
+// LE: load i32, ptr {{.+}}, align 1
 // LE-NOT: bswap
-// BE-LABEL: test_leu32
-// BE: load i32, ptr {{.*}}, align 1
-// BE: call i32 @llvm.bswap.i32
-__UINT32_TYPE__ test_leu32(const unsigned char *p) { return stdc_load8_leu32(p); }
+// BE-LABEL: @test_leu32(
+// BE: load i32, ptr {{.+}}, align 1
+// BE: call i32 @llvm.bswap.i32(
+__UINT_LEAST32_TYPE__ test_leu32(const unsigned char *p) { return stdc_load8_leu32(p); }
 
-// LE-LABEL: test_leu64
-// LE: load i64, ptr {{.*}}, align 1
+// LE-LABEL: @test_leu64(
+// LE: load i64, ptr {{.+}}, align 1
 // LE-NOT: bswap
-// BE-LABEL: test_leu64
-// BE: load i64, ptr {{.*}}, align 1
-// BE: call i64 @llvm.bswap.i64
-__UINT64_TYPE__ test_leu64(const unsigned char *p) { return stdc_load8_leu64(p); }
+// BE-LABEL: @test_leu64(
+// BE: load i64, ptr {{.+}}, align 1
+// BE: call i64 @llvm.bswap.i64(
+__UINT_LEAST64_TYPE__ test_leu64(const unsigned char *p) { return stdc_load8_leu64(p); }
 
 // BE load on LE target: bswap needed. On BE target: no bswap.
-// LE-LABEL: test_beu16
-// LE: load i16, ptr {{.*}}, align 1
-// LE: call i16 @llvm.bswap.i16
-// BE-LABEL: test_beu16
-// BE: load i16, ptr {{.*}}, align 1
+// LE-LABEL: @test_beu16(
+// LE: load i16, ptr {{.+}}, align 1
+// LE: call i16 @llvm.bswap.i16(
+// BE-LABEL: @test_beu16(
+// BE: load i16, ptr {{.+}}, align 1
 // BE-NOT: bswap
-__UINT16_TYPE__ test_beu16(const unsigned char *p) { return stdc_load8_beu16(p); }
+__UINT_LEAST16_TYPE__ test_beu16(const unsigned char *p) { return stdc_load8_beu16(p); }
 
-// LE-LABEL: test_beu32
-// LE: load i32, ptr {{.*}}, align 1
-// LE: call i32 @llvm.bswap.i32
-// BE-LABEL: test_beu32
-// BE: load i32, ptr {{.*}}, align 1
+// LE-LABEL: @test_beu32(
+// LE: load i32, ptr {{.+}}, align 1
+// LE: call i32 @llvm.bswap.i32(
+// BE-LABEL: @test_beu32(
+// BE: load i32, ptr {{.+}}, align 1
 // BE-NOT: bswap
-__UINT32_TYPE__ test_beu32(const unsigned char *p) { return stdc_load8_beu32(p); }
+__UINT_LEAST32_TYPE__ test_beu32(const unsigned char *p) { return stdc_load8_beu32(p); }
 
-// LE-LABEL: test_beu64
-// LE: load i64, ptr {{.*}}, align 1
-// LE: call i64 @llvm.bswap.i64
-// BE-LABEL: test_beu64
-// BE: load i64, ptr {{.*}}, align 1
+// LE-LABEL: @test_beu64(
+// LE: load i64, ptr {{.+}}, align 1
+// LE: call i64 @llvm.bswap.i64(
+// BE-LABEL: @test_beu64(
+// BE: load i64, ptr {{.+}}, align 1
 // BE-NOT: bswap
-__UINT64_TYPE__ test_beu64(const unsigned char *p) { return stdc_load8_beu64(p); }
+__UINT_LEAST64_TYPE__ test_beu64(const unsigned char *p) { return stdc_load8_beu64(p); }
 
 // Aligned variants use natural alignment instead of align 1.
-// LE-LABEL: test_aligned_leu32
-// LE: load i32, ptr {{.*}}, align 4
+// LE-LABEL: @test_aligned_leu32(
+// LE: load i32, ptr {{.+}}, align 4
 // LE-NOT: bswap
-// BE-LABEL: test_aligned_leu32
-// BE: load i32, ptr {{.*}}, align 4
-// BE: call i32 @llvm.bswap.i32
-__UINT32_TYPE__ test_aligned_leu32(const unsigned char *p) { return stdc_load8_aligned_leu32(p); }
+// BE-LABEL: @test_aligned_leu32(
+// BE: load i32, ptr {{.+}}, align 4
+// BE: call i32 @llvm.bswap.i32(
+__UINT_LEAST32_TYPE__ test_aligned_leu32(const unsigned char *p) { return stdc_load8_aligned_leu32(p); }
 
-// LE-LABEL: test_aligned_beu32
-// LE: load i32, ptr {{.*}}, align 4
-// LE: call i32 @llvm.bswap.i32
-// BE-LABEL: test_aligned_beu32
-// BE: load i32, ptr {{.*}}, align 4
+// LE-LABEL: @test_aligned_beu32(
+// LE: load i32, ptr {{.+}}, align 4
+// LE: call i32 @llvm.bswap.i32(
+// BE-LABEL: @test_aligned_beu32(
+// BE: load i32, ptr {{.+}}, align 4
 // BE-NOT: bswap
-__UINT32_TYPE__ test_aligned_beu32(const unsigned char *p) { return stdc_load8_aligned_beu32(p); }
+__UINT_LEAST32_TYPE__ test_aligned_beu32(const unsigned char *p) { return stdc_load8_aligned_beu32(p); }
 
 // Signed variants: same load+bswap logic, result type is signed.
-// LE-LABEL: test_les32
-// LE: load i32, ptr {{.*}}, align 1
+// LE-LABEL: @test_les32(
+// LE: load i32, ptr {{.+}}, align 1
 // LE-NOT: bswap
-// BE-LABEL: test_les32
-// BE: load i32, ptr {{.*}}, align 1
-// BE: call i32 @llvm.bswap.i32
-__INT32_TYPE__ test_les32(const unsigned char *p) { return stdc_load8_les32(p); }
+// BE-LABEL: @test_les32(
+// BE: load i32, ptr {{.+}}, align 1
+// BE: call i32 @llvm.bswap.i32(
+__INT_LEAST32_TYPE__ test_les32(const unsigned char *p) { return stdc_load8_les32(p); }
 
-// LE-LABEL: test_bes32
-// LE: load i32, ptr {{.*}}, align 1
-// LE: call i32 @llvm.bswap.i32
-// BE-LABEL: test_bes32
-// BE: load i32, ptr {{.*}}, align 1
+// LE-LABEL: @test_bes32(
+// LE: load i32, ptr {{.+}}, align 1
+// LE: call i32 @llvm.bswap.i32(
+// BE-LABEL: @test_bes32(
+// BE: load i32, ptr {{.+}}, align 1
 // BE-NOT: bswap
-__INT32_TYPE__ test_bes32(const unsigned char *p) { return stdc_load8_bes32(p); }
+__INT_LEAST32_TYPE__ test_bes32(const unsigned char *p) { return stdc_load8_bes32(p); }
diff --git a/clang/test/Sema/Inputs/stdbit.h b/clang/test/Sema/Inputs/stdbit.h
index 8200a9e7f3a90..9fa5ed7a655c2 100644
--- a/clang/test/Sema/Inputs/stdbit.h
+++ b/clang/test/Sema/Inputs/stdbit.h
@@ -118,44 +118,44 @@ __UINT16_TYPE__ stdc_memreverse8u16(__UINT16_TYPE__);
 __UINT32_TYPE__ stdc_memreverse8u32(__UINT32_TYPE__);
 __UINT64_TYPE__ stdc_memreverse8u64(__UINT64_TYPE__);
 
-__UINT8_TYPE__ stdc_load8_leu8(const unsigned char *);
-__UINT16_TYPE__ stdc_load8_leu16(const unsigned char *);
-__UINT32_TYPE__ stdc_load8_leu32(const unsigned char *);
-__UINT64_TYPE__ stdc_load8_leu64(const unsigned char *);
-
-__UINT8_TYPE__ stdc_load8_beu8(const unsigned char *);
-__UINT16_TYPE__ stdc_load8_beu16(const unsigned char *);
-__UINT32_TYPE__ stdc_load8_beu32(const unsigned char *);
-__UINT64_TYPE__ stdc_load8_beu64(const unsigned char *);
-
-__UINT8_TYPE__ stdc_load8_aligned_leu8(const unsigned char *);
-__UINT16_TYPE__ stdc_load8_aligned_leu16(const unsigned char *);
-__UINT32_TYPE__ stdc_load8_aligned_leu32(const unsigned char *);
-__UINT64_TYPE__ stdc_load8_aligned_leu64(const unsigned char *);
-
-__UINT8_TYPE__ stdc_load8_aligned_beu8(const unsigned char *);
-__UINT16_TYPE__ stdc_load8_aligned_beu16(const unsigned char *);
-__UINT32_TYPE__ stdc_load8_aligned_beu32(const unsigned char *);
-__UINT64_TYPE__ stdc_load8_aligned_beu64(const unsigned char *);
-
-__INT8_TYPE__ stdc_load8_les8(const unsigned char *);
-__INT16_TYPE__ stdc_load8_les16(const unsigned char *);
-__INT32_TYPE__ stdc_load8_les32(const unsigned char *);
-__INT64_TYPE__ stdc_load8_les64(const unsigned char *);
-
-__INT8_TYPE__ stdc_load8_bes8(const unsigned char *);
-__INT16_TYPE__ stdc_load8_bes16(const unsigned char *);
-__INT32_TYPE__ stdc_load8_bes32(const unsigned char *);
-__INT64_TYPE__ stdc_load8_bes64(const unsigned char *);
-
-__INT8_TYPE__ stdc_load8_aligned_les8(const unsigned char *);
-__INT16_TYPE__ stdc_load8_aligned_les16(const unsigned char *);
-__INT32_TYPE__ stdc_load8_aligned_les32(const unsigned char *);
-__INT64_TYPE__ stdc_load8_aligned_les64(const unsigned char *);
-
-__INT8_TYPE__ stdc_load8_aligned_bes8(const unsigned char *);
-__INT16_TYPE__ stdc_load8_aligned_bes16(const unsigned char *);
-__INT32_TYPE__ stdc_load8_aligned_bes32(const unsigned char *);
-__INT64_TYPE__ stdc_load8_aligned_bes64(const unsigned char *);
+__UINT_LEAST8_TYPE__ stdc_load8_leu8(const unsigned char *);
+__UINT_LEAST16_TYPE__ stdc_load8_leu16(const unsigned char *);
+__UINT_LEAST32_TYPE__ stdc_load8_leu32(const unsigned char *);
+__UINT_LEAST64_TYPE__ stdc_load8_leu64(const unsigned char *);
+
+__UINT_LEAST8_TYPE__ stdc_load8_beu8(const unsigned char *);
+__UINT_LEAST16_TYPE__ stdc_load8_beu16(const unsigned char *);
+__UINT_LEAST32_TYPE__ stdc_load8_beu32(const unsigned char *);
+__UINT_LEAST64_TYPE__ stdc_load8_beu64(const unsigned char *);
+
+__UINT_LEAST8_TYPE__ stdc_load8_aligned_leu8(const unsigned char *);
+__UINT_LEAST16_TYPE__ stdc_load8_aligned_leu16(const unsigned char *);
+__UINT_LEAST32_TYPE__ stdc_load8_aligned_leu32(const unsigned char *);
+__UINT_LEAST64_TYPE__ stdc_load8_aligned_leu64(const unsigned char *);
+
+__UINT_LEAST8_TYPE__ stdc_load8_aligned_beu8(const unsigned char *);
+__UINT_LEAST16_TYPE__ stdc_load8_aligned_beu16(const unsigned char *);
+__UINT_LEAST32_TYPE__ stdc_load8_aligned_beu32(const unsigned char *);
+__UINT_LEAST64_TYPE__ stdc_load8_aligned_beu64(const unsigned char *);
+
+__INT_LEAST8_TYPE__ stdc_load8_les8(const unsigned char *);
+__INT_LEAST16_TYPE__ stdc_load8_les16(const unsigned char *);
+__INT_LEAST32_TYPE__ stdc_load8_les32(const unsigned char *);
+__INT_LEAST64_TYPE__ stdc_load8_les64(const unsigned char *);
+
+__INT_LEAST8_TYPE__ stdc_load8_bes8(const unsigned char *);
+__INT_LEAST16_TYPE__ stdc_load8_bes16(const unsigned char *);
+__INT_LEAST32_TYPE__ stdc_load8_bes32(const unsigned char *);
+__INT_LEAST64_TYPE__ stdc_load8_bes64(const unsigned char *);
+
+__INT_LEAST8_TYPE__ stdc_load8_aligned_les8(const unsigned char *);
+__INT_LEAST16_TYPE__ stdc_load8_aligned_les16(const unsigned char *);
+__INT_LEAST32_TYPE__ stdc_load8_aligned_les32(const unsigned char *);
+__INT_LEAST64_TYPE__ stdc_load8_aligned_les64(const unsigned char *);
+
+__INT_LEAST8_TYPE__ stdc_load8_aligned_bes8(const unsigned char *);
+__INT_LEAST16_TYPE__ stdc_load8_aligned_bes16(const unsigned char *);
+__INT_LEAST32_TYPE__ stdc_load8_aligned_bes32(const unsigned char *);
+__INT_LEAST64_TYPE__ stdc_load8_aligned_bes64(const unsigned char *);
 
 #endif
diff --git a/clang/test/Sema/constexpr-builtin-stdc-load8.c b/clang/test/Sema/constexpr-builtin-stdc-load8.c
index 2661089b8c901..beef4f6a31f8e 100644
--- a/clang/test/Sema/constexpr-builtin-stdc-load8.c
+++ b/clang/test/Sema/constexpr-builtin-stdc-load8.c
@@ -8,10 +8,10 @@ static const unsigned char le16_u[] = {0x34, 0x12};
 static const unsigned char le32_u[] = {0x78, 0x56, 0x34, 0x12};
 static const unsigned char le64_u[] = {0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12};
 
-_Static_assert(stdc_load8_leu8(le8_u)   == (__UINT8_TYPE__)0xAB,               "");
-_Static_assert(stdc_load8_leu16(le16_u) == (__UINT16_TYPE__)0x1234,            "");
-_Static_assert(stdc_load8_leu32(le32_u) == (__UINT32_TYPE__)0x12345678U,       "");
-_Static_assert(stdc_load8_leu64(le64_u) == (__UINT64_TYPE__)0x123456789ABCDEF0ULL, "");
+_Static_assert(stdc_load8_leu8(le8_u)   == (__UINT_LEAST8_TYPE__)0xAB,               "");
+_Static_assert(stdc_load8_leu16(le16_u) == (__UINT_LEAST16_TYPE__)0x1234,            "");
+_Static_assert(stdc_load8_leu32(le32_u) == (__UINT_LEAST32_TYPE__)0x12345678U,       "");
+_Static_assert(stdc_load8_leu64(le64_u) == (__UINT_LEAST64_TYPE__)0x123456789ABCDEF0ULL, "");
 
 // BE unsigned: bytes ordered MSB-first (index 0 = highest byte)
 static const unsigned char be8_u[]  = {0xAB};
@@ -19,22 +19,22 @@ static const unsigned char be16_u[] = {0x12, 0x34};
 static const unsigned char be32_u[] = {0x12, 0x34, 0x56, 0x78};
 static const unsigned char be64_u[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0};
 
-_Static_assert(stdc_load8_beu8(be8_u)   == (__UINT8_TYPE__)0xAB,               "");
-_Static_assert(stdc_load8_beu16(be16_u) == (__UINT16_TYPE__)0x1234,            "");
-_Static_assert(stdc_load8_beu32(be32_u) == (__UINT32_TYPE__)0x12345678U,       "");
-_Static_assert(stdc_load8_beu64(be64_u) == (__UINT64_TYPE__)0x123456789ABCDEF0ULL, "");
+_Static_assert(stdc_load8_beu8(be8_u)   == (__UINT_LEAST8_TYPE__)0xAB,               "");
+_Static_assert(stdc_load8_beu16(be16_u) == (__UINT_LEAST16_TYPE__)0x1234,            "");
+_Static_assert(stdc_load8_beu32(be32_u) == (__UINT_LEAST32_TYPE__)0x12345678U,       "");
+_Static_assert(stdc_load8_beu64(be64_u) == (__UINT_LEAST64_TYPE__)0x123456789ABCDEF0ULL, "");
 
 // Aligned LE unsigned
-_Static_assert(stdc_load8_aligned_leu8(le8_u)   == (__UINT8_TYPE__)0xAB,               "");
-_Static_assert(stdc_load8_aligned_leu16(le16_u) == (__UINT16_TYPE__)0x1234,            "");
-_Static_assert(stdc_load8_aligned_leu32(le32_u) == (__UINT32_TYPE__)0x12345678U,       "");
-_Static_assert(stdc_load8_aligned_leu64(le64_u) == (__UINT64_TYPE__)0x123456789ABCDEF0ULL, "");
+_Static_assert(stdc_load8_aligned_leu8(le8_u)   == (__UINT_LEAST8_TYPE__)0xAB,               "");
+_Static_assert(stdc_load8_aligned_leu16(le16_u) == (__UINT_LEAST16_TYPE__)0x1234,            "");
+_Static_assert(stdc_load8_aligned_leu32(le32_u) == (__UINT_LEAST32_TYPE__)0x12345678U,       "");
+_Static_assert(stdc_load8_aligned_leu64(le64_u) == (__UINT_LEAST64_TYPE__)0x123456789ABCDEF0ULL, "");
 
 // Aligned BE unsigned
-_Static_assert(stdc_load8_aligned_beu8(be8_u)   == (__UINT8_TYPE__)0xAB,               "");
-_Static_assert(stdc_load8_aligned_beu16(be16_u) == (__UINT16_TYPE__)0x1234,            "");
-_Static_assert(stdc_load8_aligned_beu32(be32_u) == (__UINT32_TYPE__)0x12345678U,       "");
-_Static_assert(stdc_load8_aligned_beu64(be64_u) == (__UINT64_TYPE__)0x123456789ABCDEF0ULL, "");
+_Static_assert(stdc_load8_aligned_beu8(be8_u)   == (__UINT_LEAST8_TYPE__)0xAB,               "");
+_Static_assert(stdc_load8_aligned_beu16(be16_u) == (__UINT_LEAST16_TYPE__)0x1234,            "");
+_Static_assert(stdc_load8_aligned_beu32(be32_u) == (__UINT_LEAST32_TYPE__)0x12345678U,       "");
+_Static_assert(stdc_load8_aligned_beu64(be64_u) == (__UINT_LEAST64_TYPE__)0x123456789ABCDEF0ULL, "");
 
 // LE signed: 0x80 in u8 = -128 as s8; {0x80, 0xFF} as u16 = -128 as s16
 static const unsigned char le8_s[]  = {0x80};
@@ -42,10 +42,10 @@ static const unsigned char le16_s[] = {0x80, 0xFF};
 static const unsigned char le32_s[] = {0x80, 0xFF, 0xFF, 0xFF};
 static const unsigned char le64_s[] = {0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
 
-_Static_assert(stdc_load8_les8(le8_s)   == (__INT8_TYPE__)-128,  "");
-_Static_assert(stdc_load8_les16(le16_s) == (__INT16_TYPE__)-128, "");
-_Static_assert(stdc_load8_les32(le32_s) == (__INT32_TYPE__)-128, "");
-_Static_assert(stdc_load8_les64(le64_s) == (__INT64_TYPE__)-128, "");
+_Static_assert(stdc_load8_les8(le8_s)   == (__INT_LEAST8_TYPE__)-128,  "");
+_Static_assert(stdc_load8_les16(le16_s) == (__INT_LEAST16_TYPE__)-128, "");
+_Static_assert(stdc_load8_les32(le32_s) == (__INT_LEAST32_TYPE__)-128, "");
+_Static_assert(stdc_load8_les64(le64_s) == (__INT_LEAST64_TYPE__)-128, "");
 
 // BE signed
 static const unsigned char be8_s[]  = {0x80};
@@ -53,22 +53,22 @@ static const unsigned char be16_s[] = {0xFF, 0x80};
 static const unsigned char be32_s[] = {0xFF, 0xFF, 0xFF, 0x80};
 static const unsigned char be64_s[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80};
 
-_Static_assert(stdc_load8_bes8(be8_s)   == (__INT8_TYPE__)-128,  "");
-_Static_assert(stdc_load8_bes16(be16_s) == (__INT16_TYPE__)-128, "");
-_Static_assert(stdc_load8_bes32(be32_s) == (__INT32_TYPE__)-128, "");
-_Static_assert(stdc_load8_bes64(be64_s) == (__INT64_TYPE__)-128, "");
+_Static_assert(stdc_load8_bes8(be8_s)   == (__INT_LEAST8_TYPE__)-128,  "");
+_Static_assert(stdc_load8_bes16(be16_s) == (__INT_LEAST16_TYPE__)-128, "");
+_Static_assert(stdc_load8_bes32(be32_s) == (__INT_LEAST32_TYPE__)-128, "");
+_Static_assert(stdc_load8_bes64(be64_s) == (__INT_LEAST64_TYPE__)-128, "");
 
 // Aligned LE signed
-_Static_assert(stdc_load8_aligned_les8(le8_s)   == (__INT8_TYPE__)-128,  "");
-_Static_assert(stdc_load8_aligned_les16(le16_s) == (__INT16_TYPE__)-128, "");
-_Static_assert(stdc_load8_aligned_les32(le32_s) == (__INT32_TYPE__)-128, "");
-_Static_assert(stdc_load8_aligned_les64(le64_s) == (__INT64_TYPE__)-128, "");
+_Static_assert(stdc_load8_aligned_les8(le8_s)   == (__INT_LEAST8_TYPE__)-128,  "");
+_Static_assert(stdc_load8_aligned_les16(le16_s) == (__INT_LEAST16_TYPE__)-128, "");
+_Static_assert(stdc_load8_aligned_les32(le32_s) == (__INT_LEAST32_TYPE__)-128, "");
+_Static_assert(stdc_load8_aligned_les64(le64_s) == (__INT_LEAST64_TYPE__)-128, "");
 
 // Aligned BE signed
-_Static_assert(stdc_load8_aligned_bes8(be8_s)   == (__INT8_TYPE__)-128,  "");
-_Static_assert(stdc_load8_aligned_bes16(be16_s) == (__INT16_TYPE__)-128, "");
-_Static_assert(stdc_load8_aligned_bes32(be32_s) == (__INT32_TYPE__)-128, "");
-_Static_assert(stdc_load8_aligned_bes64(be64_s) == (__INT64_TYPE__)-128, "");
+_Static_assert(stdc_load8_aligned_bes8(be8_s)   == (__INT_LEAST8_TYPE__)-128,  "");
+_Static_assert(stdc_load8_aligned_bes16(be16_s) == (__INT_LEAST16_TYPE__)-128, "");
+_Static_assert(stdc_load8_aligned_bes32(be32_s) == (__INT_LEAST32_TYPE__)-128, "");
+_Static_assert(stdc_load8_aligned_bes64(be64_s) == (__INT_LEAST64_TYPE__)-128, "");
 
 // Positive signed round-trip
 static const unsigned char le16_pos[] = {0x01, 0x00};
@@ -82,29 +82,29 @@ constexpr unsigned char cx_be32[] = {0x12, 0x34, 0x56, 0x78};
 constexpr unsigned char cx_le16_s[] = {0x80, 0xFF};
 constexpr unsigned char cx_be16_s[] = {0xFF, 0x80};
 
-constexpr __UINT32_TYPE__ u32_le = stdc_load8_leu32(cx_le32);
+constexpr __UINT_LEAST32_TYPE__ u32_le = stdc_load8_leu32(cx_le32);
 _Static_assert(u32_le == 0x12345678U, "");
 
-constexpr __UINT32_TYPE__ u32_be = stdc_load8_beu32(cx_be32);
+constexpr __UINT_LEAST32_TYPE__ u32_be = stdc_load8_beu32(cx_be32);
 _Static_assert(u32_be == 0x12345678U, "");
 
-constexpr __INT16_TYPE__ s16_le = stdc_load8_les16(cx_le16_s);
+constexpr __INT_LEAST16_TYPE__ s16_le = stdc_load8_les16(cx_le16_s);
 _Static_assert(s16_le == -128, "");
 
-constexpr __INT16_TYPE__ s16_be = stdc_load8_bes16(cx_be16_s);
+constexpr __INT_LEAST16_TYPE__ s16_be = stdc_load8_bes16(cx_be16_s);
 _Static_assert(s16_be == -128, "");
 
 // Null pointer is rejected (NonNull attribute)
 void test_null(void) {
-  __UINT8_TYPE__ x = stdc_load8_leu8(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+  __UINT_LEAST8_TYPE__ x = stdc_load8_leu8(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
 }
 
 // Negative: out-of-bounds, scalar, and null.
 constexpr unsigned char small[] = {0x01, 0x02};
-constexpr __UINT32_TYPE__ oob_load = stdc_load8_leu32(small); // expected-error{{must be initialized by a constant expression}} expected-note{{cannot refer to element 3 of array of 2 elements in a constant expression}}
-constexpr __UINT32_TYPE__ oob_mid  = stdc_load8_leu32(small + 1); // expected-error{{must be initialized by a constant expression}} expected-note{{cannot refer to element 4 of array of 2 elements in a constant expression}}
+constexpr __UINT_LEAST32_TYPE__ oob_load = stdc_load8_leu32(small); // expected-error{{must be initialized by a constant expression}} expected-note{{cannot refer to element 3 of array of 2 elements in a constant expression}}
+constexpr __UINT_LEAST32_TYPE__ oob_mid  = stdc_load8_leu32(small + 1); // expected-error{{must be initialized by a constant expression}} expected-note{{cannot refer to element 4 of array of 2 elements in a constant expression}}
 
 constexpr unsigned char scalar_byte = 0x42;
-constexpr __UINT32_TYPE__ oob_scalar = stdc_load8_leu32(&scalar_byte); // expected-error{{must be initialized by a constant expression}} expected-note{{cannot refer to element 3 of non-array object in a constant expression}}
+constexpr __UINT_LEAST32_TYPE__ oob_scalar = stdc_load8_leu32(&scalar_byte); // expected-error{{must be initialized by a constant expression}} expected-note{{cannot refer to element 3 of non-array object in a constant expression}}
 
-constexpr __UINT32_TYPE__ null_ce = stdc_load8_leu32((const unsigned char *)0); // expected-error{{must be initialized by a constant expression}} expected-note{{read of dereferenced null pointer is not allowed in a constant expression}}
+constexpr __UINT_LEAST32_TYPE__ null_ce = stdc_load8_leu32((const unsigned char *)0); // expected-error{{must be initialized by a constant expression}} expected-note{{read of dereferenced null pointer is not allowed in a constant expression}}
diff --git a/clang/utils/TableGen/ClangBuiltinsEmitter.cpp b/clang/utils/TableGen/ClangBuiltinsEmitter.cpp
index 4f7f437bcf73c..87cfe30a4224e 100644
--- a/clang/utils/TableGen/ClangBuiltinsEmitter.cpp
+++ b/clang/utils/TableGen/ClangBuiltinsEmitter.cpp
@@ -378,6 +378,14 @@ class PrototypeParser {
                                .Case("uint16_t", "UTi")
                                .Case("uint32_t", "UZi")
                                .Case("uint64_t", "UWi")
+                               .Case("uint_least8_t", "UBi")
+                               .Case("uint_least16_t", "UTi")
+                               .Case("uint_least32_t", "UZi")
+                               .Case("uint_least64_t", "UWi")
+                               .Case("int_least8_t", "Bi")
+                               .Case("int_least16_t", "Ti")
+                               .Case("int_least32_t", "Zi")
+                               .Case("int_least64_t", "Wi")
                                .Case("void", "v")
                                .Case("wchar_t", "w")
                                .Case("...", ".")

>From a3baf036a8e03104d66c7b2449e4861f4f55eb1b Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Thu, 25 Jun 2026 17:00:29 -0700
Subject: [PATCH 3/6] Address review feedback for stdc_load8_* implementation

- Fix "signed counterparts" to "unsigned counterparts" in ReleaseNotes
- Remove redundant isOneByteCharacterType check; the declared const
  unsigned char * parameter already enforces this at the call site
- Add assert for CHAR_BIT == 8 in both constexpr evaluators
---
 clang/docs/ReleaseNotes.rst                    |  2 +-
 clang/lib/AST/ByteCode/InterpBuiltin.cpp       | 10 ++--------
 clang/lib/AST/ExprConstant.cpp                 |  9 ++-------
 clang/test/Sema/constexpr-builtin-stdc-load8.c |  3 +++
 4 files changed, 8 insertions(+), 16 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3eb25e3fb25de..2f7026fef200b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -242,7 +242,7 @@ C2y Feature Support
 
 - Implemented the C2y ``<stdbit.h>`` endian-aware 8-bit load functions
   (``stdc_load8_le``, ``stdc_load8_be``, ``stdc_load8_aligned_le``,
-  ``stdc_load8_aligned_be``, and their signed counterparts) with constexpr
+  ``stdc_load8_aligned_be``, and their unsigned counterparts) with constexpr
   evaluation support. These functions read N bytes from a
   ``const unsigned char *`` and assemble them into an integer using
   little-endian or big-endian byte order.
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 64b5b7b5dac78..726aad2537632 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2076,14 +2076,6 @@ static bool interp__builtin_load8(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();
 
@@ -2091,6 +2083,8 @@ static bool interp__builtin_load8(InterpState &S, CodePtr OpPC,
   size_t ArraySize = Ptr.getNumElems();
   size_t RemainingElems = ArraySize - BaseIdx;
 
+  assert(S.getASTContext().getTargetInfo().getCharWidth() == 8 &&
+         "stdc_load8_* requires CHAR_BIT == 8");
   unsigned ByteWidth = S.getASTContext().getTypeSize(Call->getType()) / 8;
   if (ByteWidth > RemainingElems) {
     if (IsArray)
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index ea73a75f18459..980a5735894c2 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -16769,13 +16769,8 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
       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;
-    }
-
+    assert(Info.Ctx.getTargetInfo().getCharWidth() == 8 &&
+           "stdc_load8_* requires CHAR_BIT == 8");
     unsigned ByteWidth = Info.Ctx.getTypeSize(E->getType()) / 8;
     uint64_t RemainingElems = Ptr.Designator.validIndexAdjustments().second;
     if (ByteWidth > RemainingElems) {
diff --git a/clang/test/Sema/constexpr-builtin-stdc-load8.c b/clang/test/Sema/constexpr-builtin-stdc-load8.c
index beef4f6a31f8e..45cd1a8ab0595 100644
--- a/clang/test/Sema/constexpr-builtin-stdc-load8.c
+++ b/clang/test/Sema/constexpr-builtin-stdc-load8.c
@@ -108,3 +108,6 @@ constexpr unsigned char scalar_byte = 0x42;
 constexpr __UINT_LEAST32_TYPE__ oob_scalar = stdc_load8_leu32(&scalar_byte); // expected-error{{must be initialized by a constant expression}} expected-note{{cannot refer to element 3 of non-array object in a constant expression}}
 
 constexpr __UINT_LEAST32_TYPE__ null_ce = stdc_load8_leu32((const unsigned char *)0); // expected-error{{must be initialized by a constant expression}} expected-note{{read of dereferenced null pointer is not allowed in a constant expression}}
+
+constexpr unsigned char one[] = {0x42};
+constexpr __UINT_LEAST8_TYPE__ oob_past_end = stdc_load8_leu8(one + 1); // expected-error{{must be initialized by a constant expression}} expected-note{{cannot refer to element 1 of array of 1 element in a constant expression}}

>From 3df3fcfe79e39a6b66cc9d808d205f2c4c09deee Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Thu, 25 Jun 2026 19:36:22 -0700
Subject: [PATCH 4/6] Add tests for invalid pointer type argument

---
 clang/test/Sema/constexpr-builtin-stdc-load8.c | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/clang/test/Sema/constexpr-builtin-stdc-load8.c b/clang/test/Sema/constexpr-builtin-stdc-load8.c
index 45cd1a8ab0595..b4ad6ea1f1d5d 100644
--- a/clang/test/Sema/constexpr-builtin-stdc-load8.c
+++ b/clang/test/Sema/constexpr-builtin-stdc-load8.c
@@ -99,6 +99,17 @@ void test_null(void) {
   __UINT_LEAST8_TYPE__ x = stdc_load8_leu8(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
 }
 
+// Wrong pointer types are rejected by the type system at the call site.
+void test_wrong_types(void) {
+  const int int_arr[] = {0};
+  const unsigned int uint_arr[] = {0};
+  const char char_arr[] = "A";
+
+  (void)stdc_load8_leu32(int_arr);  // expected-error{{incompatible pointer types}} expected-note at Inputs/stdbit.h:*{{passing argument to parameter here}}
+  (void)stdc_load8_leu32(uint_arr); // expected-error{{incompatible pointer types}} expected-note at Inputs/stdbit.h:*{{passing argument to parameter here}}
+  (void)stdc_load8_leu16(char_arr); // expected-warning{{converts between pointers to integer types}} expected-note at Inputs/stdbit.h:*{{passing argument to parameter here}}
+}
+
 // Negative: out-of-bounds, scalar, and null.
 constexpr unsigned char small[] = {0x01, 0x02};
 constexpr __UINT_LEAST32_TYPE__ oob_load = stdc_load8_leu32(small); // expected-error{{must be initialized by a constant expression}} expected-note{{cannot refer to element 3 of array of 2 elements in a constant expression}}

>From e2018e28f8ac533391f9d1b078fc9d6ecc3678d2 Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Thu, 25 Jun 2026 20:19:39 -0700
Subject: [PATCH 5/6] Remove CHAR_BIT == 8 asserts from both interpreters, use
 the existing check in Sema for CHAR_BIT to be 8.

---
 clang/lib/AST/ByteCode/InterpBuiltin.cpp |  2 --
 clang/lib/AST/ExprConstant.cpp           |  2 --
 clang/lib/Sema/SemaChecking.cpp          | 32 ++++++++++++++++++++++++
 3 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 726aad2537632..e16b85571fbb5 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2083,8 +2083,6 @@ static bool interp__builtin_load8(InterpState &S, CodePtr OpPC,
   size_t ArraySize = Ptr.getNumElems();
   size_t RemainingElems = ArraySize - BaseIdx;
 
-  assert(S.getASTContext().getTargetInfo().getCharWidth() == 8 &&
-         "stdc_load8_* requires CHAR_BIT == 8");
   unsigned ByteWidth = S.getASTContext().getTypeSize(Call->getType()) / 8;
   if (ByteWidth > RemainingElems) {
     if (IsArray)
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 980a5735894c2..ae1315f94e299 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -16769,8 +16769,6 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
       return false;
 
     QualType CharTy = Ptr.Designator.getType(Info.Ctx);
-    assert(Info.Ctx.getTargetInfo().getCharWidth() == 8 &&
-           "stdc_load8_* requires CHAR_BIT == 8");
     unsigned ByteWidth = Info.Ctx.getTypeSize(E->getType()) / 8;
     uint64_t RemainingElems = Ptr.Designator.validIndexAdjustments().second;
     if (ByteWidth > RemainingElems) {
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index c3ca45ee55786..bedb7628ed8ec 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -4038,6 +4038,38 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
   case Builtin::BIstdc_memreverse8u16:
   case Builtin::BIstdc_memreverse8u32:
   case Builtin::BIstdc_memreverse8u64:
+  case Builtin::BIstdc_load8_leu8:
+  case Builtin::BIstdc_load8_leu16:
+  case Builtin::BIstdc_load8_leu32:
+  case Builtin::BIstdc_load8_leu64:
+  case Builtin::BIstdc_load8_les8:
+  case Builtin::BIstdc_load8_les16:
+  case Builtin::BIstdc_load8_les32:
+  case Builtin::BIstdc_load8_les64:
+  case Builtin::BIstdc_load8_beu8:
+  case Builtin::BIstdc_load8_beu16:
+  case Builtin::BIstdc_load8_beu32:
+  case Builtin::BIstdc_load8_beu64:
+  case Builtin::BIstdc_load8_bes8:
+  case Builtin::BIstdc_load8_bes16:
+  case Builtin::BIstdc_load8_bes32:
+  case Builtin::BIstdc_load8_bes64:
+  case Builtin::BIstdc_load8_aligned_leu8:
+  case Builtin::BIstdc_load8_aligned_leu16:
+  case Builtin::BIstdc_load8_aligned_leu32:
+  case Builtin::BIstdc_load8_aligned_leu64:
+  case Builtin::BIstdc_load8_aligned_les8:
+  case Builtin::BIstdc_load8_aligned_les16:
+  case Builtin::BIstdc_load8_aligned_les32:
+  case Builtin::BIstdc_load8_aligned_les64:
+  case Builtin::BIstdc_load8_aligned_beu8:
+  case Builtin::BIstdc_load8_aligned_beu16:
+  case Builtin::BIstdc_load8_aligned_beu32:
+  case Builtin::BIstdc_load8_aligned_beu64:
+  case Builtin::BIstdc_load8_aligned_bes8:
+  case Builtin::BIstdc_load8_aligned_bes16:
+  case Builtin::BIstdc_load8_aligned_bes32:
+  case Builtin::BIstdc_load8_aligned_bes64:
     if (Context.getTargetInfo().getCharWidth() != 8) {
       Diag(TheCall->getBeginLoc(), diag::err_builtin_requires_char_bit_8)
           << TheCall->getDirectCallee()->getName();

>From 5f0a38e5e8ddfdae9bc2ed5364cb006b6a96f912 Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Thu, 25 Jun 2026 20:43:06 -0700
Subject: [PATCH 6/6] Add test for null pointer type

---
 clang/test/Sema/constexpr-builtin-stdc-load8.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/clang/test/Sema/constexpr-builtin-stdc-load8.c b/clang/test/Sema/constexpr-builtin-stdc-load8.c
index b4ad6ea1f1d5d..4df9097b88b88 100644
--- a/clang/test/Sema/constexpr-builtin-stdc-load8.c
+++ b/clang/test/Sema/constexpr-builtin-stdc-load8.c
@@ -96,7 +96,8 @@ _Static_assert(s16_be == -128, "");
 
 // Null pointer is rejected (NonNull attribute)
 void test_null(void) {
-  __UINT_LEAST8_TYPE__ x = stdc_load8_leu8(0); // expected-warning{{null passed to a callee that requires a non-null argument}}
+  __UINT_LEAST8_TYPE__ x = stdc_load8_leu8(0);       // expected-warning{{null passed to a callee that requires a non-null argument}}
+  __UINT_LEAST8_TYPE__ y = stdc_load8_leu8(nullptr);  // expected-warning{{null passed to a callee that requires a non-null argument}}
 }
 
 // Wrong pointer types are rejected by the type system at the call site.
@@ -119,6 +120,7 @@ constexpr unsigned char scalar_byte = 0x42;
 constexpr __UINT_LEAST32_TYPE__ oob_scalar = stdc_load8_leu32(&scalar_byte); // expected-error{{must be initialized by a constant expression}} expected-note{{cannot refer to element 3 of non-array object in a constant expression}}
 
 constexpr __UINT_LEAST32_TYPE__ null_ce = stdc_load8_leu32((const unsigned char *)0); // expected-error{{must be initialized by a constant expression}} expected-note{{read of dereferenced null pointer is not allowed in a constant expression}}
+constexpr __UINT_LEAST32_TYPE__ null_ce_nullptr = stdc_load8_leu32(nullptr); // expected-error{{must be initialized by a constant expression}} expected-note{{read of dereferenced null pointer is not allowed in a constant expression}}
 
 constexpr unsigned char one[] = {0x42};
 constexpr __UINT_LEAST8_TYPE__ oob_past_end = stdc_load8_leu8(one + 1); // expected-error{{must be initialized by a constant expression}} expected-note{{cannot refer to element 1 of array of 1 element in a constant expression}}



More information about the cfe-commits mailing list