[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
Tue Sep 15 20:51:12 PDT 2026
https://github.com/chaitanyav updated https://github.com/llvm/llvm-project/pull/203666
>From 6c028a707ccb4d601889a48936290c18c59c451b 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 01/18] [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.md | 6 +
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, 600 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.md b/clang/docs/ReleaseNotes.md
index db710b64a6ae5..738fcbccd2db2 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -222,6 +222,12 @@ features cannot lower the translation-unit ABI level;
// ...
}
```
+- 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.
- Clang now diagnoses the use of the same identifier with both internal and
external linkage within a translation unit, as made ill-formed by
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 90340ad7f812c..98bcd514c32e9 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 42ee33541b41b..49de3822105ef 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2127,6 +2127,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) {
@@ -5146,6 +5213,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 1286c77ad1c69..7f5439da3371e 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -17152,6 +17152,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 e005632bfbb6c..8e907e425a134 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -4044,6 +4044,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 8b0d0fbbcaf2790f29eecef5ecd14bd59a6b9555 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 02/18] 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 98bcd514c32e9..d1fef5dd869c5 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 22c81522f9e41..b646db9b95834 100644
--- a/clang/utils/TableGen/ClangBuiltinsEmitter.cpp
+++ b/clang/utils/TableGen/ClangBuiltinsEmitter.cpp
@@ -381,6 +381,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 b223addad131e0340b80b40124818673472259e8 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 03/18] 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.md | 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.md b/clang/docs/ReleaseNotes.md
index 738fcbccd2db2..db8c1cc140b68 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -224,7 +224,7 @@ features cannot lower the translation-unit ABI level;
```
- 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 49de3822105ef..15b520d49bc87 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2145,14 +2145,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();
@@ -2160,6 +2152,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 7f5439da3371e..0b50e9231abe6 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -17210,13 +17210,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 7ab04d57bf78ecb401c81768600e8bf6c4d7ffe8 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 04/18] 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 6c3a97266dadfdb7518f7e73deb19db40f521c90 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 05/18] 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 15b520d49bc87..57591a1eb6c3f 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2152,8 +2152,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 0b50e9231abe6..bb9cc43e73108 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -17210,8 +17210,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 ac333d5b66662..67b4ddae6dd26 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -4118,6 +4118,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 621e126fd610fc975800c235f9a10a464ce8563d 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 06/18] 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}}
>From 690233d17fba785f7ba5f7ecaa9b5c55ab66e17a Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Fri, 26 Jun 2026 09:16:01 -0700
Subject: [PATCH 07/18] Reject unaligned pointers in stdc_load8_aligned_* at
constexpr
---
.../include/clang/Basic/DiagnosticASTKinds.td | 3 +
clang/lib/AST/ByteCode/InterpBuiltin.cpp | 42 ++++-
clang/lib/AST/ExprConstant.cpp | 152 ++++++++++--------
.../test/Sema/constexpr-builtin-stdc-load8.c | 56 +++++--
4 files changed, 171 insertions(+), 82 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticASTKinds.td b/clang/include/clang/Basic/DiagnosticASTKinds.td
index 0aca1f75428f8..cee6b0666f7b2 100644
--- a/clang/include/clang/Basic/DiagnosticASTKinds.td
+++ b/clang/include/clang/Basic/DiagnosticASTKinds.td
@@ -282,6 +282,9 @@ def note_constexpr_alignment_compute : Note<
"cannot constant evaluate whether run-time alignment is at least %0">;
def note_constexpr_alignment_adjust : Note<
"cannot constant evaluate the result of adjusting alignment to %0">;
+def note_constexpr_load8_unaligned : Note<
+ "%0 requires a pointer aligned to %1 %plural{1:byte|:bytes}1, but the "
+ "given pointer is only aligned to %2 %plural{1:byte|:bytes}2">;
def note_constexpr_destroy_out_of_lifetime : Note<
"destroying object '%0' whose lifetime has already ended">;
def note_constexpr_unsupported_destruction : Note<
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 57591a1eb6c3f..943273f08d4d4 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2129,7 +2129,8 @@ static bool isOneByteCharacterType(QualType T) {
static bool interp__builtin_load8(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
- const CallExpr *Call, bool IsBigEndian) {
+ const CallExpr *Call, bool IsBigEndian,
+ bool IsAligned) {
Pointer Ptr = S.Stk.pop<Pointer>();
if (Ptr.isZero()) {
@@ -2141,6 +2142,33 @@ static bool interp__builtin_load8(InterpState &S, CodePtr OpPC,
if (!isReadable(Ptr) && !Ptr.isOnePastEnd())
return false;
+ if (IsAligned) {
+ CharUnits RequiredAlign =
+ S.getASTContext().getTypeAlignInChars(Call->getType());
+ CharUnits BaseAlignment;
+ if (const auto *VD = Ptr.getDeclDesc()->asValueDecl())
+ BaseAlignment = S.getASTContext().getDeclAlign(VD);
+ else if (const auto *E = Ptr.getDeclDesc()->asExpr())
+ BaseAlignment = GetAlignOfExpr(S.getASTContext(), E, UETT_AlignOf);
+ else {
+ S.FFDiag(S.Current->getSource(OpPC),
+ diag::note_constexpr_alignment_compute)
+ << RequiredAlign.getQuantity();
+ return false;
+ }
+ unsigned PtrOffset =
+ Ptr.isElementPastEnd() ? Ptr.getNumElems() : Ptr.getIndex();
+ CharUnits PtrAlign =
+ BaseAlignment.alignmentAtOffset(CharUnits::fromQuantity(PtrOffset));
+ if (PtrAlign < RequiredAlign) {
+ S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_load8_unaligned)
+ << S.getASTContext().BuiltinInfo.getQuotedName(
+ Call->getBuiltinCallee())
+ << RequiredAlign.getQuantity() << PtrAlign.getQuantity();
+ return false;
+ }
+ }
+
const Descriptor *Desc = Ptr.getFieldDesc();
bool IsArray = Desc->isArray();
QualType ElemTy = IsArray ? Desc->getElemQualType() : Desc->getType();
@@ -5213,6 +5241,9 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
case Builtin::BIstdc_load8_les16:
case Builtin::BIstdc_load8_les32:
case Builtin::BIstdc_load8_les64:
+ return interp__builtin_load8(S, OpPC, Frame, Call, /*IsBigEndian=*/false,
+ /*IsAligned=*/false);
+
case Builtin::BIstdc_load8_aligned_leu8:
case Builtin::BIstdc_load8_aligned_leu16:
case Builtin::BIstdc_load8_aligned_leu32:
@@ -5221,7 +5252,8 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
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);
+ return interp__builtin_load8(S, OpPC, Frame, Call, /*IsBigEndian=*/false,
+ /*IsAligned=*/true);
case Builtin::BIstdc_load8_beu8:
case Builtin::BIstdc_load8_beu16:
@@ -5231,6 +5263,9 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
case Builtin::BIstdc_load8_bes16:
case Builtin::BIstdc_load8_bes32:
case Builtin::BIstdc_load8_bes64:
+ return interp__builtin_load8(S, OpPC, Frame, Call, /*IsBigEndian=*/true,
+ /*IsAligned=*/false);
+
case Builtin::BIstdc_load8_aligned_beu8:
case Builtin::BIstdc_load8_aligned_beu16:
case Builtin::BIstdc_load8_aligned_beu32:
@@ -5239,7 +5274,8 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call,
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);
+ return interp__builtin_load8(S, OpPC, Frame, Call, /*IsBigEndian=*/true,
+ /*IsAligned=*/true);
case Builtin::BI__atomic_always_lock_free:
case Builtin::BI__atomic_is_lock_free:
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index bb9cc43e73108..ee169040500ce 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -16938,6 +16938,70 @@ static bool getBuiltinAlignArguments(const CallExpr *E, EvalInfo &Info,
return true;
}
+// Implements the C2y §7.18.21 stdc_load8_* family: reads N bytes from
+// E->getArg(0) and assembles them per IsBE into a value of E's type. The
+// aligned_* variants additionally require the pointer to be aligned to the
+// natural alignment of E's type.
+static bool EvaluateStdcLoad8(EvalInfo &Info, const CallExpr *E, bool IsBE,
+ bool IsAligned, APSInt &Result) {
+ LValue Ptr;
+ if (!EvaluatePointer(E->getArg(0), Ptr, Info))
+ return false;
+
+ if (!Ptr.checkNullPointerForFoldAccess(Info, E, AK_Read) ||
+ Ptr.Designator.Invalid)
+ return false;
+
+ if (IsAligned) {
+ CharUnits RequiredAlign = Info.Ctx.getTypeAlignInChars(E->getType());
+ CharUnits BaseAlignment = getBaseAlignment(Info, Ptr);
+ CharUnits PtrAlign = BaseAlignment.alignmentAtOffset(Ptr.Offset);
+ if (PtrAlign < RequiredAlign) {
+ Info.FFDiag(E, diag::note_constexpr_load8_unaligned)
+ << Info.Ctx.BuiltinInfo.getQuotedName(E->getBuiltinCallee())
+ << RequiredAlign.getQuantity() << PtrAlign.getQuantity();
+ return false;
+ }
+ }
+
+ QualType CharTy = Ptr.Designator.getType(Info.Ctx);
+ 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 ResultBits = APInt::getZero(ByteWidth * 8);
+ for (unsigned I = 0; I < ByteWidth; ++I) {
+ unsigned SrcIdx = IsBE ? (ByteWidth - I - 1) : I;
+ ResultBits |= APInt(ByteWidth * 8, Bytes[SrcIdx]) << (8 * I);
+ }
+
+ bool IsSigned = E->getType()->isSignedIntegerType();
+ Result = APSInt(ResultBits, !IsSigned);
+ return true;
+}
+
bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
unsigned BuiltinOp) {
auto EvalTestOp = [&](llvm::function_ref<bool(const APInt &, const APInt &)>
@@ -17159,7 +17223,14 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
case Builtin::BIstdc_load8_les8:
case Builtin::BIstdc_load8_les16:
case Builtin::BIstdc_load8_les32:
- case Builtin::BIstdc_load8_les64:
+ case Builtin::BIstdc_load8_les64: {
+ APSInt Result;
+ if (!EvaluateStdcLoad8(Info, E, /*IsBE=*/false, /*IsAligned=*/false,
+ Result))
+ return false;
+ return Success(Result, E);
+ }
+
case Builtin::BIstdc_load8_aligned_leu8:
case Builtin::BIstdc_load8_aligned_leu16:
case Builtin::BIstdc_load8_aligned_leu32:
@@ -17167,7 +17238,13 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
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_les64: {
+ APSInt Result;
+ if (!EvaluateStdcLoad8(Info, E, /*IsBE=*/false, /*IsAligned=*/true, Result))
+ return false;
+ return Success(Result, E);
+ }
+
case Builtin::BIstdc_load8_beu8:
case Builtin::BIstdc_load8_beu16:
case Builtin::BIstdc_load8_beu32:
@@ -17175,7 +17252,13 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
case Builtin::BIstdc_load8_bes8:
case Builtin::BIstdc_load8_bes16:
case Builtin::BIstdc_load8_bes32:
- case Builtin::BIstdc_load8_bes64:
+ case Builtin::BIstdc_load8_bes64: {
+ APSInt Result;
+ if (!EvaluateStdcLoad8(Info, E, /*IsBE=*/true, /*IsAligned=*/false, Result))
+ return false;
+ return Success(Result, E);
+ }
+
case Builtin::BIstdc_load8_aligned_beu8:
case Builtin::BIstdc_load8_aligned_beu16:
case Builtin::BIstdc_load8_aligned_beu32:
@@ -17184,67 +17267,10 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
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);
- 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);
+ APSInt Result;
+ if (!EvaluateStdcLoad8(Info, E, /*IsBE=*/true, /*IsAligned=*/true, Result))
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);
+ return Success(Result, E);
}
case Builtin::BI__builtin_classify_type:
diff --git a/clang/test/Sema/constexpr-builtin-stdc-load8.c b/clang/test/Sema/constexpr-builtin-stdc-load8.c
index 4df9097b88b88..d012473d0bc5a 100644
--- a/clang/test/Sema/constexpr-builtin-stdc-load8.c
+++ b/clang/test/Sema/constexpr-builtin-stdc-load8.c
@@ -3,10 +3,11 @@
#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};
+// alignas(8) so these buffers also satisfy the stdc_load8_aligned_* variants.
+alignas(8) static const unsigned char le8_u[] = {0xAB};
+alignas(8) static const unsigned char le16_u[] = {0x34, 0x12};
+alignas(8) static const unsigned char le32_u[] = {0x78, 0x56, 0x34, 0x12};
+alignas(8) static const unsigned char le64_u[] = {0xF0, 0xDE, 0xBC, 0x9A, 0x78, 0x56, 0x34, 0x12};
_Static_assert(stdc_load8_leu8(le8_u) == (__UINT_LEAST8_TYPE__)0xAB, "");
_Static_assert(stdc_load8_leu16(le16_u) == (__UINT_LEAST16_TYPE__)0x1234, "");
@@ -14,10 +15,10 @@ _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};
-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};
+alignas(8) static const unsigned char be8_u[] = {0xAB};
+alignas(8) static const unsigned char be16_u[] = {0x12, 0x34};
+alignas(8) static const unsigned char be32_u[] = {0x12, 0x34, 0x56, 0x78};
+alignas(8) static const unsigned char be64_u[] = {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0};
_Static_assert(stdc_load8_beu8(be8_u) == (__UINT_LEAST8_TYPE__)0xAB, "");
_Static_assert(stdc_load8_beu16(be16_u) == (__UINT_LEAST16_TYPE__)0x1234, "");
@@ -37,10 +38,10 @@ _Static_assert(stdc_load8_aligned_beu32(be32_u) == (__UINT_LEAST32_TYPE__)0x1234
_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};
-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};
+alignas(8) static const unsigned char le8_s[] = {0x80};
+alignas(8) static const unsigned char le16_s[] = {0x80, 0xFF};
+alignas(8) static const unsigned char le32_s[] = {0x80, 0xFF, 0xFF, 0xFF};
+alignas(8) static const unsigned char le64_s[] = {0x80, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
_Static_assert(stdc_load8_les8(le8_s) == (__INT_LEAST8_TYPE__)-128, "");
_Static_assert(stdc_load8_les16(le16_s) == (__INT_LEAST16_TYPE__)-128, "");
@@ -48,10 +49,10 @@ _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};
-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};
+alignas(8) static const unsigned char be8_s[] = {0x80};
+alignas(8) static const unsigned char be16_s[] = {0xFF, 0x80};
+alignas(8) static const unsigned char be32_s[] = {0xFF, 0xFF, 0xFF, 0x80};
+alignas(8) static const unsigned char be64_s[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x80};
_Static_assert(stdc_load8_bes8(be8_s) == (__INT_LEAST8_TYPE__)-128, "");
_Static_assert(stdc_load8_bes16(be16_s) == (__INT_LEAST16_TYPE__)-128, "");
@@ -76,6 +77,13 @@ static const unsigned char be16_pos[] = {0x00, 0x01};
_Static_assert(stdc_load8_les16(le16_pos) == 1, "");
_Static_assert(stdc_load8_bes16(be16_pos) == 1, "");
+// N=64 signed boundary: result == 2^63 is not < 2^63, so it wraps to
+// result - 2^64 == INT64_MIN, not a positive value.
+static const unsigned char le64_min[] = {0, 0, 0, 0, 0, 0, 0, 0x80};
+static const unsigned char be64_min[] = {0x80, 0, 0, 0, 0, 0, 0, 0};
+_Static_assert(stdc_load8_les64(le64_min) == (__INT_LEAST64_TYPE__)(-9223372036854775807LL - 1), "");
+_Static_assert(stdc_load8_bes64(be64_min) == (__INT_LEAST64_TYPE__)(-9223372036854775807LL - 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};
@@ -124,3 +132,19 @@ constexpr __UINT_LEAST32_TYPE__ null_ce_nullptr = stdc_load8_leu32(nullptr); //
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}}
+
+// aligned_* variants require the pointer to be aligned to the result type.
+alignas(8) constexpr unsigned char align_buf[9] = {0, 0x78, 0x56, 0x34, 0x12, 0, 0, 0, 0};
+
+constexpr __UINT_LEAST16_TYPE__ misaligned16 = stdc_load8_aligned_leu16(align_buf + 1); // expected-error{{must be initialized by a constant expression}} expected-note{{'stdc_load8_aligned_leu16' requires a pointer aligned to 2 bytes, but the given pointer is only aligned to 1 byte}}
+constexpr __UINT_LEAST32_TYPE__ misaligned32 = stdc_load8_aligned_leu32(align_buf + 1); // expected-error{{must be initialized by a constant expression}} expected-note{{'stdc_load8_aligned_leu32' requires a pointer aligned to 4 bytes, but the given pointer is only aligned to 1 byte}}
+constexpr __UINT_LEAST64_TYPE__ misaligned64 = stdc_load8_aligned_leu64(align_buf + 1); // expected-error{{must be initialized by a constant expression}} expected-note{{'stdc_load8_aligned_leu64' requires a pointer aligned to 8 bytes, but the given pointer is only aligned to 1 byte}}
+
+// Offset 2 from an 8-byte aligned base is only 2-byte aligned, enough for a
+// 16-bit load but not a 32-bit one.
+constexpr __UINT_LEAST32_TYPE__ misaligned32_half = stdc_load8_aligned_leu32(align_buf + 2); // expected-error{{must be initialized by a constant expression}} expected-note{{'stdc_load8_aligned_leu32' requires a pointer aligned to 4 bytes, but the given pointer is only aligned to 2 bytes}}
+
+// Offset 4 is still 4-byte aligned, enough for a 16-bit (2-byte) load.
+// align_buf[4..5] == {0x12, 0x00}, so LE u16 == 0x0012.
+constexpr __UINT_LEAST16_TYPE__ partially_aligned16 = stdc_load8_aligned_leu16(align_buf + 4);
+static_assert(partially_aligned16 == 0x0012, "");
>From 7b3926792a497b671e4a21d722f01c68ec163ab7 Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Mon, 13 Jul 2026 09:24:40 -0700
Subject: [PATCH 08/18] Add tests for stdc_load8_aligned_* and widen CodeGen
coverage across widths/signedness
---
clang/test/CodeGen/builtin-stdc-load8.c | 50 +++++++++++++++++++
.../test/Sema/constexpr-builtin-stdc-load8.c | 46 +++++++++++++++++
2 files changed, 96 insertions(+)
diff --git a/clang/test/CodeGen/builtin-stdc-load8.c b/clang/test/CodeGen/builtin-stdc-load8.c
index 6fb1dcc8cc771..c239a4dfa06d6 100644
--- a/clang/test/CodeGen/builtin-stdc-load8.c
+++ b/clang/test/CodeGen/builtin-stdc-load8.c
@@ -95,3 +95,53 @@ __INT_LEAST32_TYPE__ test_les32(const unsigned char *p) { return stdc_load8_les3
// BE: load i32, ptr {{.+}}, align 1
// BE-NOT: bswap
__INT_LEAST32_TYPE__ test_bes32(const unsigned char *p) { return stdc_load8_bes32(p); }
+
+// Aligned load's alignment scales with the result type's width.
+// LE-LABEL: @test_aligned_leu16(
+// LE: load i16, ptr {{.+}}, align 2
+// LE-NOT: bswap
+// BE-LABEL: @test_aligned_leu16(
+// BE: load i16, ptr {{.+}}, align 2
+// BE: call i16 @llvm.bswap.i16(
+__UINT_LEAST16_TYPE__ test_aligned_leu16(const unsigned char *p) { return stdc_load8_aligned_leu16(p); }
+
+// LE-LABEL: @test_aligned_beu16(
+// LE: load i16, ptr {{.+}}, align 2
+// LE: call i16 @llvm.bswap.i16(
+// BE-LABEL: @test_aligned_beu16(
+// BE: load i16, ptr {{.+}}, align 2
+// BE-NOT: bswap
+__UINT_LEAST16_TYPE__ test_aligned_beu16(const unsigned char *p) { return stdc_load8_aligned_beu16(p); }
+
+// LE-LABEL: @test_aligned_leu64(
+// LE: load i64, ptr {{.+}}, align 8
+// LE-NOT: bswap
+// BE-LABEL: @test_aligned_leu64(
+// BE: load i64, ptr {{.+}}, align 8
+// BE: call i64 @llvm.bswap.i64(
+__UINT_LEAST64_TYPE__ test_aligned_leu64(const unsigned char *p) { return stdc_load8_aligned_leu64(p); }
+
+// LE-LABEL: @test_aligned_beu64(
+// LE: load i64, ptr {{.+}}, align 8
+// LE: call i64 @llvm.bswap.i64(
+// BE-LABEL: @test_aligned_beu64(
+// BE: load i64, ptr {{.+}}, align 8
+// BE-NOT: bswap
+__UINT_LEAST64_TYPE__ test_aligned_beu64(const unsigned char *p) { return stdc_load8_aligned_beu64(p); }
+
+// Signed aligned variants use the same alignment lowering as unsigned.
+// LE-LABEL: @test_aligned_les32(
+// LE: load i32, ptr {{.+}}, align 4
+// LE-NOT: bswap
+// BE-LABEL: @test_aligned_les32(
+// BE: load i32, ptr {{.+}}, align 4
+// BE: call i32 @llvm.bswap.i32(
+__INT_LEAST32_TYPE__ test_aligned_les32(const unsigned char *p) { return stdc_load8_aligned_les32(p); }
+
+// LE-LABEL: @test_aligned_bes32(
+// LE: load i32, ptr {{.+}}, align 4
+// LE: call i32 @llvm.bswap.i32(
+// BE-LABEL: @test_aligned_bes32(
+// BE: load i32, ptr {{.+}}, align 4
+// BE-NOT: bswap
+__INT_LEAST32_TYPE__ test_aligned_bes32(const unsigned char *p) { return stdc_load8_aligned_bes32(p); }
diff --git a/clang/test/Sema/constexpr-builtin-stdc-load8.c b/clang/test/Sema/constexpr-builtin-stdc-load8.c
index d012473d0bc5a..28893ebc9d2a7 100644
--- a/clang/test/Sema/constexpr-builtin-stdc-load8.c
+++ b/clang/test/Sema/constexpr-builtin-stdc-load8.c
@@ -148,3 +148,49 @@ constexpr __UINT_LEAST32_TYPE__ misaligned32_half = stdc_load8_aligned_leu32(ali
// align_buf[4..5] == {0x12, 0x00}, so LE u16 == 0x0012.
constexpr __UINT_LEAST16_TYPE__ partially_aligned16 = stdc_load8_aligned_leu16(align_buf + 4);
static_assert(partially_aligned16 == 0x0012, "");
+
+constexpr unsigned char noalign_buf[8] = {0x78, 0x56, 0x34, 0x12, 0, 0, 0, 0};
+constexpr __UINT_LEAST8_TYPE__ noalign_ok = stdc_load8_aligned_leu8(noalign_buf);
+static_assert(noalign_ok == 0x78, "");
+constexpr __UINT_LEAST16_TYPE__ noalign_fail16 = stdc_load8_aligned_leu16(noalign_buf); // expected-error{{must be initialized by a constant expression}} expected-note{{'stdc_load8_aligned_leu16' requires a pointer aligned to 2 bytes, but the given pointer is only aligned to 1 byte}}
+constexpr __UINT_LEAST32_TYPE__ noalign_fail32 = stdc_load8_aligned_leu32(noalign_buf); // expected-error{{must be initialized by a constant expression}} expected-note{{'stdc_load8_aligned_leu32' requires a pointer aligned to 4 bytes, but the given pointer is only aligned to 1 byte}}
+
+void test_block_scope_vardecl(void) {
+ alignas(8) constexpr unsigned char blk_buf[4] = {0x78, 0x56, 0x34, 0x12};
+ static_assert(stdc_load8_aligned_leu32(blk_buf) == 0x12345678U, "");
+
+ constexpr unsigned char blk_noalign[4] = {0x78, 0x56, 0x34, 0x12};
+ constexpr __UINT_LEAST32_TYPE__ blk_fail = stdc_load8_aligned_leu32(blk_noalign); // expected-error{{must be initialized by a constant expression}} expected-note{{'stdc_load8_aligned_leu32' requires a pointer aligned to 4 bytes, but the given pointer is only aligned to 1 byte}}
+}
+
+constexpr __UINT_LEAST8_TYPE__ strlit_ok = stdc_load8_aligned_leu8((const unsigned char *)u8"\xAB");
+static_assert(strlit_ok == (__UINT_LEAST8_TYPE__)0xAB, "");
+
+constexpr __UINT_LEAST16_TYPE__ strlit_fail = stdc_load8_aligned_leu16((const unsigned char *)u8"\x34\x12"); // expected-error{{must be initialized by a constant expression}} expected-note{{'stdc_load8_aligned_leu16' requires a pointer aligned to 2 bytes, but the given pointer is only aligned to 1 byte}}
+
+constexpr __UINT_LEAST32_TYPE__ complit_fail = stdc_load8_aligned_leu32((unsigned char[4]){0x78, 0x56, 0x34, 0x12}); // expected-error{{must be initialized by a constant expression}} expected-note{{'stdc_load8_aligned_leu32' requires a pointer aligned to 4 bytes, but the given pointer is only aligned to 1 byte}}
+
+typedef unsigned char AlignedBuf8[8] __attribute__((aligned(8)));
+
+constexpr __UINT_LEAST32_TYPE__ complit_offset_fail = stdc_load8_aligned_leu32((AlignedBuf8){0, 0, 0x78, 0x56, 0x34, 0x12} + 2); // expected-error{{must be initialized by a constant expression}} expected-note{{'stdc_load8_aligned_leu32' requires a pointer aligned to 4 bytes, but the given pointer is only aligned to 2 bytes}}
+
+alignas(8) constexpr unsigned char align_buf_be[9] = {0, 0x12, 0x34, 0x56, 0x78, 0, 0, 0, 0};
+
+constexpr __UINT_LEAST16_TYPE__ misaligned16_be = stdc_load8_aligned_beu16(align_buf_be + 1); // expected-error{{must be initialized by a constant expression}} expected-note{{'stdc_load8_aligned_beu16' requires a pointer aligned to 2 bytes, but the given pointer is only aligned to 1 byte}}
+constexpr __UINT_LEAST32_TYPE__ misaligned32_be = stdc_load8_aligned_beu32(align_buf_be + 1); // expected-error{{must be initialized by a constant expression}} expected-note{{'stdc_load8_aligned_beu32' requires a pointer aligned to 4 bytes, but the given pointer is only aligned to 1 byte}}
+constexpr __UINT_LEAST64_TYPE__ misaligned64_be = stdc_load8_aligned_beu64(align_buf_be + 1); // expected-error{{must be initialized by a constant expression}} expected-note{{'stdc_load8_aligned_beu64' requires a pointer aligned to 8 bytes, but the given pointer is only aligned to 1 byte}}
+
+constexpr __UINT_LEAST16_TYPE__ partially_aligned16_be = stdc_load8_aligned_beu16(align_buf_be + 4);
+static_assert(partially_aligned16_be == 0x7800, "");
+
+alignas(8) constexpr unsigned char align_buf_les[9] = {0, 0x80, 0xFF, 0xFF, 0xFF, 0, 0, 0, 0};
+constexpr __INT_LEAST32_TYPE__ misaligned32_les = stdc_load8_aligned_les32(align_buf_les + 1); // expected-error{{must be initialized by a constant expression}} expected-note{{'stdc_load8_aligned_les32' requires a pointer aligned to 4 bytes, but the given pointer is only aligned to 1 byte}}
+
+alignas(8) constexpr unsigned char align_buf_bes[9] = {0, 0xFF, 0xFF, 0xFF, 0x80, 0, 0, 0, 0};
+constexpr __INT_LEAST32_TYPE__ misaligned32_bes = stdc_load8_aligned_bes32(align_buf_bes + 1); // expected-error{{must be initialized by a constant expression}} expected-note{{'stdc_load8_aligned_bes32' requires a pointer aligned to 4 bytes, but the given pointer is only aligned to 1 byte}}
+
+alignas(8) constexpr unsigned char onepastend_buf[4] = {0x78, 0x56, 0x34, 0x12};
+constexpr __UINT_LEAST32_TYPE__ onepastend_aligned = stdc_load8_aligned_leu32(onepastend_buf + 4); // expected-error{{must be initialized by a constant expression}} expected-note{{cannot refer to element 7 of array of 4 elements in a constant expression}}
+
+alignas(8) constexpr unsigned char onepastend_misaligned_buf[5] = {0, 0x78, 0x56, 0x34, 0x12};
+constexpr __UINT_LEAST32_TYPE__ onepastend_misaligned = stdc_load8_aligned_leu32(onepastend_misaligned_buf + 5); // expected-error{{must be initialized by a constant expression}} expected-note{{'stdc_load8_aligned_leu32' requires a pointer aligned to 4 bytes, but the given pointer is only aligned to 1 byte}}
>From dfa188e03c5d511b1a697a6ff4387790d02b63fd Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Mon, 13 Jul 2026 11:27:24 -0700
Subject: [PATCH 09/18] Fix incorrect offset in stdc_load8_aligned_* alignment
check for struct fields
---
clang/lib/AST/ByteCode/InterpBuiltin.cpp | 6 ++----
clang/test/Sema/constexpr-builtin-stdc-load8.c | 12 ++++++++++++
2 files changed, 14 insertions(+), 4 deletions(-)
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 943273f08d4d4..aab7b7e0d93d7 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2156,10 +2156,8 @@ static bool interp__builtin_load8(InterpState &S, CodePtr OpPC,
<< RequiredAlign.getQuantity();
return false;
}
- unsigned PtrOffset =
- Ptr.isElementPastEnd() ? Ptr.getNumElems() : Ptr.getIndex();
- CharUnits PtrAlign =
- BaseAlignment.alignmentAtOffset(CharUnits::fromQuantity(PtrOffset));
+ CharUnits PtrOffset = Ptr.toAPValue(S.getASTContext()).getLValueOffset();
+ CharUnits PtrAlign = BaseAlignment.alignmentAtOffset(PtrOffset);
if (PtrAlign < RequiredAlign) {
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_load8_unaligned)
<< S.getASTContext().BuiltinInfo.getQuotedName(
diff --git a/clang/test/Sema/constexpr-builtin-stdc-load8.c b/clang/test/Sema/constexpr-builtin-stdc-load8.c
index 28893ebc9d2a7..3396b7cdbea2b 100644
--- a/clang/test/Sema/constexpr-builtin-stdc-load8.c
+++ b/clang/test/Sema/constexpr-builtin-stdc-load8.c
@@ -194,3 +194,15 @@ constexpr __UINT_LEAST32_TYPE__ onepastend_aligned = stdc_load8_aligned_leu32(on
alignas(8) constexpr unsigned char onepastend_misaligned_buf[5] = {0, 0x78, 0x56, 0x34, 0x12};
constexpr __UINT_LEAST32_TYPE__ onepastend_misaligned = stdc_load8_aligned_leu32(onepastend_misaligned_buf + 5); // expected-error{{must be initialized by a constant expression}} expected-note{{'stdc_load8_aligned_leu32' requires a pointer aligned to 4 bytes, but the given pointer is only aligned to 1 byte}}
+
+struct FieldOffset { unsigned char pad; unsigned char data[8]; };
+alignas(8) constexpr struct FieldOffset field_s = {0, {0x78, 0x56, 0x34, 0x12}};
+constexpr __UINT_LEAST32_TYPE__ field_fail = stdc_load8_aligned_leu32(field_s.data); // expected-error{{must be initialized by a constant expression}} expected-note{{'stdc_load8_aligned_leu32' requires a pointer aligned to 4 bytes, but the given pointer is only aligned to 1 byte}}
+
+alignas(8) constexpr struct FieldOffset field_arr[2] = {{0}, {0, {0x78, 0x56, 0x34, 0x12}}};
+constexpr __UINT_LEAST32_TYPE__ field_arr_fail = stdc_load8_aligned_leu32(field_arr[1].data); // expected-error{{must be initialized by a constant expression}} expected-note{{'stdc_load8_aligned_leu32' requires a pointer aligned to 4 bytes, but the given pointer is only aligned to 2 bytes}}
+
+union FieldUnion { unsigned char c; unsigned char data[8]; };
+alignas(8) constexpr union FieldUnion field_u = {.data = {0x78, 0x56, 0x34, 0x12}};
+constexpr __UINT_LEAST32_TYPE__ union_ok = stdc_load8_aligned_leu32(field_u.data);
+static_assert(union_ok == 0x12345678U, "");
>From 4868869bf55889d8c7255bbf3f98bfddd982bbfe Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Mon, 13 Jul 2026 12:13:52 -0700
Subject: [PATCH 10/18] Update ReleaseNotes with function suffixes
---
clang/docs/ReleaseNotes.md | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index db8c1cc140b68..5fd554504bd33 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -223,11 +223,11 @@ features cannot lower the translation-unit ABI level;
}
```
- 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 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.
+ `stdc_load8_{le,be}{u,s}{8,16,32,64}` and their aligned counterparts
+ `stdc_load8_aligned_{le,be}{u,s}{8,16,32,64}` 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.
- Clang now diagnoses the use of the same identifier with both internal and
external linkage within a translation unit, as made ill-formed by
>From 05ab8d162a179f5741b052e6158e21f2f6a5dfdd Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Mon, 13 Jul 2026 12:49:53 -0700
Subject: [PATCH 11/18] Avoid repeating the attributes and prototypes across
function spellings
---
clang/include/clang/Basic/Builtins.td | 35 +++++++++------------------
1 file changed, 12 insertions(+), 23 deletions(-)
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index d1fef5dd869c5..51db423069250 100644
--- a/clang/include/clang/Basic/Builtins.td
+++ b/clang/include/clang/Basic/Builtins.td
@@ -1011,52 +1011,41 @@ 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"];
+class Load8Common : LibBuiltin<"stdbit.h", "C2Y_LANG"> {
let Attributes = [NoThrow, Pure, Constexpr, NonNull<NonOptimizing, [0]>];
let Prototype = "T(unsigned char const*)";
}
-def StdcLoad8BeU : LibBuiltin<"stdbit.h", "C2Y_LANG">, Load8UnsignedTemplate {
+def StdcLoad8LeU : Load8Common, Load8UnsignedTemplate {
+ let Spellings = ["stdc_load8_leu"];
+}
+
+def StdcLoad8BeU : Load8Common, 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 {
+def StdcLoad8AlignedLeU : Load8Common, 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 {
+def StdcLoad8AlignedBeU : Load8Common, 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 {
+def StdcLoad8LeS : Load8Common, 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 {
+def StdcLoad8BeS : Load8Common, 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 {
+def StdcLoad8AlignedLeS : Load8Common, 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 {
+def StdcLoad8AlignedBeS : Load8Common, Load8SignedTemplate {
let Spellings = ["stdc_load8_aligned_bes"];
- let Attributes = [NoThrow, Pure, Constexpr, NonNull<NonOptimizing, [0]>];
- let Prototype = "T(unsigned char const*)";
}
>From 0f80b9049551884653083289ac85b49b920962c9 Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Mon, 13 Jul 2026 15:52:35 -0700
Subject: [PATCH 12/18] Avoid indexing into scalar pointer
---
clang/lib/AST/ByteCode/InterpBuiltin.cpp | 2 +-
clang/test/Sema/constexpr-builtin-stdc-load8.c | 12 ++++++++++++
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index aab7b7e0d93d7..4e95862f7bc2f 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2198,7 +2198,7 @@ static bool interp__builtin_load8(InterpState &S, CodePtr OpPC,
// 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);
+ Pointer BytePtr = IsArray ? Ptr.atIndex(BaseIdx + SrcIdx) : Ptr;
if (!CheckLoad(S, OpPC, BytePtr, AK_Read))
return false;
uint64_t B;
diff --git a/clang/test/Sema/constexpr-builtin-stdc-load8.c b/clang/test/Sema/constexpr-builtin-stdc-load8.c
index 3396b7cdbea2b..586afcee5de88 100644
--- a/clang/test/Sema/constexpr-builtin-stdc-load8.c
+++ b/clang/test/Sema/constexpr-builtin-stdc-load8.c
@@ -206,3 +206,15 @@ union FieldUnion { unsigned char c; unsigned char data[8]; };
alignas(8) constexpr union FieldUnion field_u = {.data = {0x78, 0x56, 0x34, 0x12}};
constexpr __UINT_LEAST32_TYPE__ union_ok = stdc_load8_aligned_leu32(field_u.data);
static_assert(union_ok == 0x12345678U, "");
+
+constexpr unsigned char scalar_ok = 0x42;
+constexpr __UINT_LEAST8_TYPE__ scalar_aligned_ok = stdc_load8_aligned_leu8(&scalar_ok);
+static_assert(scalar_aligned_ok == 0x42, "");
+
+alignas(4) constexpr unsigned char scalar_aligned_oob = 0x42;
+constexpr __UINT_LEAST32_TYPE__ oob_scalar_aligned = stdc_load8_aligned_leu32(&scalar_aligned_oob); // expected-error{{must be initialized by a constant expression}} expected-note{{cannot refer to element 3 of non-array object in a constant expression}}
+
+void test_block_scope_scalar(void) {
+ constexpr unsigned char local_scalar = 0x42;
+ static_assert(stdc_load8_aligned_leu8(&local_scalar) == 0x42, "");
+}
>From 054f62af7592e7a840c87701b70e850355a5bb84 Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Tue, 14 Jul 2026 13:45:01 -0700
Subject: [PATCH 13/18] Extract getBaseAlignment function into ExprConstShared
for sharing
Instead of duplicating the alignment checks in both interpreters, add
a check for DynamicAllocLValue to getBaseAlignment. Now both
interpreters check for TypeInfoLValue and DynamicAllocLValue.
---
clang/lib/AST/ByteCode/InterpBuiltin.cpp | 17 ++++-----------
clang/lib/AST/ExprConstShared.h | 5 +++++
clang/lib/AST/ExprConstant.cpp | 27 +++++++++++++-----------
3 files changed, 24 insertions(+), 25 deletions(-)
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 4e95862f7bc2f..9afb653f2118f 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2145,19 +2145,10 @@ static bool interp__builtin_load8(InterpState &S, CodePtr OpPC,
if (IsAligned) {
CharUnits RequiredAlign =
S.getASTContext().getTypeAlignInChars(Call->getType());
- CharUnits BaseAlignment;
- if (const auto *VD = Ptr.getDeclDesc()->asValueDecl())
- BaseAlignment = S.getASTContext().getDeclAlign(VD);
- else if (const auto *E = Ptr.getDeclDesc()->asExpr())
- BaseAlignment = GetAlignOfExpr(S.getASTContext(), E, UETT_AlignOf);
- else {
- S.FFDiag(S.Current->getSource(OpPC),
- diag::note_constexpr_alignment_compute)
- << RequiredAlign.getQuantity();
- return false;
- }
- CharUnits PtrOffset = Ptr.toAPValue(S.getASTContext()).getLValueOffset();
- CharUnits PtrAlign = BaseAlignment.alignmentAtOffset(PtrOffset);
+ APValue AV = Ptr.toAPValue(S.getASTContext());
+ CharUnits BaseAlignment =
+ GetBaseAlignment(S.getASTContext(), AV.getLValueBase());
+ CharUnits PtrAlign = BaseAlignment.alignmentAtOffset(AV.getLValueOffset());
if (PtrAlign < RequiredAlign) {
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_load8_unaligned)
<< S.getASTContext().BuiltinInfo.getQuotedName(
diff --git a/clang/lib/AST/ExprConstShared.h b/clang/lib/AST/ExprConstShared.h
index 0eee03dce57ab..f7d688be16feb 100644
--- a/clang/lib/AST/ExprConstShared.h
+++ b/clang/lib/AST/ExprConstShared.h
@@ -14,6 +14,7 @@
#ifndef LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H
#define LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H
+#include "clang/AST/APValue.h"
#include "clang/Basic/BuiltinTraits.h"
#include <cstdint>
#include <optional>
@@ -78,6 +79,10 @@ void HandleComplexComplexDiv(llvm::APFloat A, llvm::APFloat B, llvm::APFloat C,
CharUnits GetAlignOfExpr(const ASTContext &Ctx, const Expr *E,
UnaryExprOrTypeTrait ExprKind);
+CharUnits GetAlignOfType(const ASTContext &Ctx, QualType T,
+ UnaryExprOrTypeTrait ExprKind);
+CharUnits GetBaseAlignment(const ASTContext &Ctx,
+ const APValue::LValueBase &Base);
/// Convert a builtin ID to the canonical x86 builtin ID the constant evaluators
/// dispatch on in their x86 target-specific cases.
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index ee169040500ce..3105256f04b23 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -10468,8 +10468,8 @@ bool PointerExprEvaluator::VisitCastExpr(const CastExpr *E) {
return ExprEvaluatorBaseTy::VisitCastExpr(E);
}
-static CharUnits GetAlignOfType(const ASTContext &Ctx, QualType T,
- UnaryExprOrTypeTrait ExprKind) {
+CharUnits GetAlignOfType(const ASTContext &Ctx, QualType T,
+ UnaryExprOrTypeTrait ExprKind) {
// C++ [expr.alignof]p3:
// When alignof is applied to a reference type, the result is the
// alignment of the referenced type.
@@ -10564,12 +10564,15 @@ CharUnits GetAlignOfExpr(const ASTContext &Ctx, const Expr *E,
return GetAlignOfType(Ctx, E->getType(), ExprKind);
}
-static CharUnits getBaseAlignment(EvalInfo &Info, const LValue &Value) {
- if (const auto *VD = Value.Base.dyn_cast<const ValueDecl *>())
- return Info.Ctx.getDeclAlign(VD);
- if (const auto *E = Value.Base.dyn_cast<const Expr *>())
- return GetAlignOfExpr(Info.Ctx, E, UETT_AlignOf);
- return GetAlignOfType(Info.Ctx, Value.Base.getTypeInfoType(), UETT_AlignOf);
+CharUnits GetBaseAlignment(const ASTContext &Ctx,
+ const APValue::LValueBase &Base) {
+ if (const auto *VD = Base.dyn_cast<const ValueDecl *>())
+ return Ctx.getDeclAlign(VD);
+ if (const auto *E = Base.dyn_cast<const Expr *>())
+ return GetAlignOfExpr(Ctx, E, UETT_AlignOf);
+ if (Base.is<TypeInfoLValue>())
+ return GetAlignOfType(Ctx, Base.getTypeInfoType(), UETT_AlignOf);
+ return GetAlignOfType(Ctx, Base.getDynamicAllocType(), UETT_AlignOf);
}
/// Evaluate the value of the alignment argument to __builtin_align_{up,down},
@@ -10661,7 +10664,7 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
// If there is a base object, then it must have the correct alignment.
if (OffsetResult.Base) {
- CharUnits BaseAlignment = getBaseAlignment(Info, OffsetResult);
+ CharUnits BaseAlignment = GetBaseAlignment(Info.Ctx, OffsetResult.Base);
if (BaseAlignment < Align) {
Result.Designator.setInvalid();
@@ -10695,7 +10698,7 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
if (!getAlignmentArgument(E->getArg(1), E->getArg(0)->getType(), Info,
Alignment))
return false;
- CharUnits BaseAlignment = getBaseAlignment(Info, Result);
+ CharUnits BaseAlignment = GetBaseAlignment(Info.Ctx, Result.Base);
CharUnits PtrAlign = BaseAlignment.alignmentAtOffset(Result.Offset);
// For align_up/align_down, we can return the same value if the alignment
// is known to be greater or equal to the requested value.
@@ -16954,7 +16957,7 @@ static bool EvaluateStdcLoad8(EvalInfo &Info, const CallExpr *E, bool IsBE,
if (IsAligned) {
CharUnits RequiredAlign = Info.Ctx.getTypeAlignInChars(E->getType());
- CharUnits BaseAlignment = getBaseAlignment(Info, Ptr);
+ CharUnits BaseAlignment = GetBaseAlignment(Info.Ctx, Ptr.Base);
CharUnits PtrAlign = BaseAlignment.alignmentAtOffset(Ptr.Offset);
if (PtrAlign < RequiredAlign) {
Info.FFDiag(E, diag::note_constexpr_load8_unaligned)
@@ -17136,7 +17139,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
// If we evaluated a pointer, check the minimum known alignment.
LValue Ptr;
Ptr.setFrom(Info.Ctx, Src);
- CharUnits BaseAlignment = getBaseAlignment(Info, Ptr);
+ CharUnits BaseAlignment = GetBaseAlignment(Info.Ctx, Ptr.Base);
CharUnits PtrAlign = BaseAlignment.alignmentAtOffset(Ptr.Offset);
// We can return true if the known alignment at the computed offset is
// greater than the requested alignment.
>From 64535b731086addda4ef3796c2d7dc7f8c6305cc Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Fri, 28 Aug 2026 13:01:45 -0700
Subject: [PATCH 14/18] Fix crash in stdc_load8_* bytecode interpreter for
string literals
---
clang/lib/AST/ByteCode/InterpBuiltin.cpp | 33 +++++++++++++------
.../test/Sema/constexpr-builtin-stdc-load8.c | 9 +++++
2 files changed, 32 insertions(+), 10 deletions(-)
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 9afb653f2118f..6ff594c842664 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2158,26 +2158,39 @@ static bool interp__builtin_load8(InterpState &S, CodePtr OpPC,
}
}
- const Descriptor *Desc = Ptr.getFieldDesc();
- bool IsArray = Desc->isArray();
- QualType ElemTy = IsArray ? Desc->getElemQualType() : Desc->getType();
+ // A string pointer has no Descriptor; treat it as an array of its
+ // character type.
+ bool IsArray;
+ QualType ElemTy;
+ if (Ptr.isStringPointer()) {
+ IsArray = true;
+ ElemTy = Ptr.asStringPointer()
+ .getLiteral()
+ ->getType()
+ ->getAsArrayTypeUnsafe()
+ ->getElementType();
+ } else {
+ const Descriptor *Desc = Ptr.getFieldDesc();
+ IsArray = Desc->isArray();
+ 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;
+ uint64_t BaseIdx = Ptr.getIndex();
+ uint64_t ArraySize = Ptr.getNumElems();
+ uint64_t RemainingElems = ArraySize - BaseIdx;
unsigned ByteWidth = S.getASTContext().getTypeSize(Call->getType()) / 8;
if (ByteWidth > RemainingElems) {
+ uint64_t LastIndex = llvm::SaturatingAdd(BaseIdx, uint64_t(ByteWidth - 1));
if (IsArray)
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
- << (uint64_t)(BaseIdx + ByteWidth - 1) << /*array*/ 0
- << (uint64_t)ArraySize;
+ << LastIndex << /*array*/ 0 << ArraySize;
else
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
- << (uint64_t)(BaseIdx + ByteWidth - 1) << /*non-array*/ 1;
+ << LastIndex << /*non-array*/ 1;
return false;
}
@@ -2194,7 +2207,7 @@ static bool interp__builtin_load8(InterpState &S, CodePtr OpPC,
return false;
uint64_t B;
INT_TYPE_SWITCH_NO_BOOL(
- ElemT, { B = static_cast<uint64_t>(BytePtr.deref<T>().toUnsigned()); });
+ ElemT, { B = static_cast<uint64_t>(BytePtr.load<T>().toUnsigned()); });
Result |= APInt(BitWidth, B) << (8 * I);
}
diff --git a/clang/test/Sema/constexpr-builtin-stdc-load8.c b/clang/test/Sema/constexpr-builtin-stdc-load8.c
index 586afcee5de88..8cc607e6b910b 100644
--- a/clang/test/Sema/constexpr-builtin-stdc-load8.c
+++ b/clang/test/Sema/constexpr-builtin-stdc-load8.c
@@ -166,6 +166,15 @@ void test_block_scope_vardecl(void) {
constexpr __UINT_LEAST8_TYPE__ strlit_ok = stdc_load8_aligned_leu8((const unsigned char *)u8"\xAB");
static_assert(strlit_ok == (__UINT_LEAST8_TYPE__)0xAB, "");
+// Multi-byte loads from a string literal: string-literal pointers have no
+// Descriptor, so this exercises the byte-loop/indexing path for that
+// pointer kind, LE and BE.
+constexpr __UINT_LEAST32_TYPE__ strlit_plain_le = stdc_load8_leu32((const unsigned char *)u8"\x78\x56\x34\x12");
+static_assert(strlit_plain_le == 0x12345678U, "");
+
+constexpr __INT_LEAST16_TYPE__ strlit_plain_be = stdc_load8_bes16((const unsigned char *)u8"\xFF\x80");
+static_assert(strlit_plain_be == -128, "");
+
constexpr __UINT_LEAST16_TYPE__ strlit_fail = stdc_load8_aligned_leu16((const unsigned char *)u8"\x34\x12"); // expected-error{{must be initialized by a constant expression}} expected-note{{'stdc_load8_aligned_leu16' requires a pointer aligned to 2 bytes, but the given pointer is only aligned to 1 byte}}
constexpr __UINT_LEAST32_TYPE__ complit_fail = stdc_load8_aligned_leu32((unsigned char[4]){0x78, 0x56, 0x34, 0x12}); // expected-error{{must be initialized by a constant expression}} expected-note{{'stdc_load8_aligned_leu32' requires a pointer aligned to 4 bytes, but the given pointer is only aligned to 1 byte}}
>From 8f2bacb41301b8e7933a25e92ef171519f7b003d Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Fri, 28 Aug 2026 14:04:58 -0700
Subject: [PATCH 15/18] Use getElemType in stdc_load8_*
---
clang/lib/AST/ByteCode/InterpBuiltin.cpp | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 6ff594c842664..1e2aa0ac8d3ea 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2164,11 +2164,7 @@ static bool interp__builtin_load8(InterpState &S, CodePtr OpPC,
QualType ElemTy;
if (Ptr.isStringPointer()) {
IsArray = true;
- ElemTy = Ptr.asStringPointer()
- .getLiteral()
- ->getType()
- ->getAsArrayTypeUnsafe()
- ->getElementType();
+ ElemTy = getElemType(Ptr);
} else {
const Descriptor *Desc = Ptr.getFieldDesc();
IsArray = Desc->isArray();
>From bf5cfcf9a079579da032f1124d065c2f92e792f6 Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Tue, 8 Sep 2026 09:44:22 -0700
Subject: [PATCH 16/18] Address codereview comments
---
clang/lib/AST/ByteCode/InterpBuiltin.cpp | 40 +++++++++----------
clang/lib/AST/ExprConstShared.h | 2 +-
clang/lib/AST/ExprConstant.cpp | 10 ++---
.../test/Sema/constexpr-builtin-stdc-load8.c | 4 ++
4 files changed, 30 insertions(+), 26 deletions(-)
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 1e2aa0ac8d3ea..24634fc557606 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -1419,6 +1419,16 @@ static bool interp__builtin_is_aligned_up_down(InterpState &S, CodePtr OpPC,
return false;
}
+static APValue::LValueBase getLValueBase(const Pointer &Ptr) {
+ if (Ptr.isBlockPointer()) {
+ if (const auto *VD = Ptr.getDeclDesc()->asValueDecl())
+ return VD;
+ }
+ if (const auto *E = Ptr.getRootExpr())
+ return E;
+ return APValue::LValueBase();
+}
+
/// __builtin_assume_aligned(Ptr, Alignment[, ExtraOffset])
static bool interp__builtin_assume_aligned(InterpState &S, CodePtr OpPC,
const InterpFrame *Frame,
@@ -2145,10 +2155,10 @@ static bool interp__builtin_load8(InterpState &S, CodePtr OpPC,
if (IsAligned) {
CharUnits RequiredAlign =
S.getASTContext().getTypeAlignInChars(Call->getType());
- APValue AV = Ptr.toAPValue(S.getASTContext());
CharUnits BaseAlignment =
- GetBaseAlignment(S.getASTContext(), AV.getLValueBase());
- CharUnits PtrAlign = BaseAlignment.alignmentAtOffset(AV.getLValueOffset());
+ getBaseAlignment(S.getASTContext(), getLValueBase(Ptr));
+ CharUnits PtrOffset = Ptr.toAPValue(S.getASTContext()).getLValueOffset();
+ CharUnits PtrAlign = BaseAlignment.alignmentAtOffset(PtrOffset);
if (PtrAlign < RequiredAlign) {
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_load8_unaligned)
<< S.getASTContext().BuiltinInfo.getQuotedName(
@@ -2160,16 +2170,8 @@ static bool interp__builtin_load8(InterpState &S, CodePtr OpPC,
// A string pointer has no Descriptor; treat it as an array of its
// character type.
- bool IsArray;
- QualType ElemTy;
- if (Ptr.isStringPointer()) {
- IsArray = true;
- ElemTy = getElemType(Ptr);
- } else {
- const Descriptor *Desc = Ptr.getFieldDesc();
- IsArray = Desc->isArray();
- ElemTy = IsArray ? Desc->getElemQualType() : Desc->getType();
- }
+ bool IsArray = Ptr.isStringPointer() || Ptr.getFieldDesc()->isArray();
+ QualType ElemTy = getElemType(Ptr);
if (IsArray)
Ptr = Ptr.expand();
@@ -2181,12 +2183,8 @@ static bool interp__builtin_load8(InterpState &S, CodePtr OpPC,
unsigned ByteWidth = S.getASTContext().getTypeSize(Call->getType()) / 8;
if (ByteWidth > RemainingElems) {
uint64_t LastIndex = llvm::SaturatingAdd(BaseIdx, uint64_t(ByteWidth - 1));
- if (IsArray)
- S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
- << LastIndex << /*array*/ 0 << ArraySize;
- else
- S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
- << LastIndex << /*non-array*/ 1;
+ S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_array_index)
+ << LastIndex << /*array*/ !IsArray << ArraySize;
return false;
}
@@ -2196,8 +2194,10 @@ static bool interp__builtin_load8(InterpState &S, CodePtr OpPC,
// 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) {
+ for (unsigned I = 0; I != ByteWidth; ++I) {
size_t SrcIdx = IsBigEndian ? (ByteWidth - I - 1) : I;
+ // When Ptr is not an array, the RemainingElems check above already
+ // guarantees ByteWidth == 1, so this loop runs once and BytePtr == Ptr.
Pointer BytePtr = IsArray ? Ptr.atIndex(BaseIdx + SrcIdx) : Ptr;
if (!CheckLoad(S, OpPC, BytePtr, AK_Read))
return false;
diff --git a/clang/lib/AST/ExprConstShared.h b/clang/lib/AST/ExprConstShared.h
index f7d688be16feb..f16c9ed071af2 100644
--- a/clang/lib/AST/ExprConstShared.h
+++ b/clang/lib/AST/ExprConstShared.h
@@ -81,7 +81,7 @@ CharUnits GetAlignOfExpr(const ASTContext &Ctx, const Expr *E,
UnaryExprOrTypeTrait ExprKind);
CharUnits GetAlignOfType(const ASTContext &Ctx, QualType T,
UnaryExprOrTypeTrait ExprKind);
-CharUnits GetBaseAlignment(const ASTContext &Ctx,
+CharUnits getBaseAlignment(const ASTContext &Ctx,
const APValue::LValueBase &Base);
/// Convert a builtin ID to the canonical x86 builtin ID the constant evaluators
diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp
index 3105256f04b23..22da8ea99ff4d 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -10564,7 +10564,7 @@ CharUnits GetAlignOfExpr(const ASTContext &Ctx, const Expr *E,
return GetAlignOfType(Ctx, E->getType(), ExprKind);
}
-CharUnits GetBaseAlignment(const ASTContext &Ctx,
+CharUnits getBaseAlignment(const ASTContext &Ctx,
const APValue::LValueBase &Base) {
if (const auto *VD = Base.dyn_cast<const ValueDecl *>())
return Ctx.getDeclAlign(VD);
@@ -10664,7 +10664,7 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
// If there is a base object, then it must have the correct alignment.
if (OffsetResult.Base) {
- CharUnits BaseAlignment = GetBaseAlignment(Info.Ctx, OffsetResult.Base);
+ CharUnits BaseAlignment = getBaseAlignment(Info.Ctx, OffsetResult.Base);
if (BaseAlignment < Align) {
Result.Designator.setInvalid();
@@ -10698,7 +10698,7 @@ bool PointerExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
if (!getAlignmentArgument(E->getArg(1), E->getArg(0)->getType(), Info,
Alignment))
return false;
- CharUnits BaseAlignment = GetBaseAlignment(Info.Ctx, Result.Base);
+ CharUnits BaseAlignment = getBaseAlignment(Info.Ctx, Result.Base);
CharUnits PtrAlign = BaseAlignment.alignmentAtOffset(Result.Offset);
// For align_up/align_down, we can return the same value if the alignment
// is known to be greater or equal to the requested value.
@@ -16957,7 +16957,7 @@ static bool EvaluateStdcLoad8(EvalInfo &Info, const CallExpr *E, bool IsBE,
if (IsAligned) {
CharUnits RequiredAlign = Info.Ctx.getTypeAlignInChars(E->getType());
- CharUnits BaseAlignment = GetBaseAlignment(Info.Ctx, Ptr.Base);
+ CharUnits BaseAlignment = getBaseAlignment(Info.Ctx, Ptr.Base);
CharUnits PtrAlign = BaseAlignment.alignmentAtOffset(Ptr.Offset);
if (PtrAlign < RequiredAlign) {
Info.FFDiag(E, diag::note_constexpr_load8_unaligned)
@@ -17139,7 +17139,7 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E,
// If we evaluated a pointer, check the minimum known alignment.
LValue Ptr;
Ptr.setFrom(Info.Ctx, Src);
- CharUnits BaseAlignment = GetBaseAlignment(Info.Ctx, Ptr.Base);
+ CharUnits BaseAlignment = getBaseAlignment(Info.Ctx, Ptr.Base);
CharUnits PtrAlign = BaseAlignment.alignmentAtOffset(Ptr.Offset);
// We can return true if the known alignment at the computed offset is
// greater than the requested alignment.
diff --git a/clang/test/Sema/constexpr-builtin-stdc-load8.c b/clang/test/Sema/constexpr-builtin-stdc-load8.c
index 8cc607e6b910b..2f84c026b18e3 100644
--- a/clang/test/Sema/constexpr-builtin-stdc-load8.c
+++ b/clang/test/Sema/constexpr-builtin-stdc-load8.c
@@ -119,6 +119,10 @@ void test_wrong_types(void) {
(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}}
}
+struct NonChar { int x; };
+constexpr struct NonChar non_char_arr[2] = {{0x12345678}, {0}};
+constexpr __UINT_LEAST32_TYPE__ struct_arr_fail = stdc_load8_leu32((const unsigned char *)non_char_arr); // expected-error{{must be initialized by a constant expression}} expected-note{{this conversion is not allowed in a constant expression}}
+
// 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 6d5457fe584563a53fde4f25c9e3cd587d8f0168 Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Tue, 15 Sep 2026 18:40:39 -0700
Subject: [PATCH 17/18] Use Pointer::computeLayoutOffset() instead of
converting Ptr to APValue
---
clang/lib/AST/ByteCode/InterpBuiltin.cpp | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
index 24634fc557606..da681fa8ff2fc 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -2157,7 +2157,11 @@ static bool interp__builtin_load8(InterpState &S, CodePtr OpPC,
S.getASTContext().getTypeAlignInChars(Call->getType());
CharUnits BaseAlignment =
getBaseAlignment(S.getASTContext(), getLValueBase(Ptr));
- CharUnits PtrOffset = Ptr.toAPValue(S.getASTContext()).getLValueOffset();
+ std::optional<size_t> LayoutOffset =
+ Ptr.computeLayoutOffset(S.getASTContext());
+ if (!LayoutOffset)
+ return false;
+ CharUnits PtrOffset = CharUnits::fromQuantity(*LayoutOffset);
CharUnits PtrAlign = BaseAlignment.alignmentAtOffset(PtrOffset);
if (PtrAlign < RequiredAlign) {
S.FFDiag(S.Current->getSource(OpPC), diag::note_constexpr_load8_unaligned)
>From 803b5a0c45f1bd8c33efe63d2c0e507711bd5ded Mon Sep 17 00:00:00 2001
From: NagaChaitanya Vellanki <pnagato at protonmail.com>
Date: Tue, 15 Sep 2026 20:16:34 -0700
Subject: [PATCH 18/18] Preserve alignment instead of dropping down to 1-byte
alignment and if the original alignment is better use that instead of using
lower alignment
---
clang/lib/CodeGen/CGBuiltin.cpp | 10 +++++-----
clang/test/CodeGen/builtin-stdc-load8.c | 24 ++++++++++++++++++++++++
2 files changed, 29 insertions(+), 5 deletions(-)
diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp
index 8e907e425a134..3b6d879177a22 100644
--- a/clang/lib/CodeGen/CGBuiltin.cpp
+++ b/clang/lib/CodeGen/CGBuiltin.cpp
@@ -4116,11 +4116,11 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
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);
+ Address Addr = EmitPointerWithAlignment(E->getArg(0));
+ if (IsAligned) {
+ CharUnits TypeAlign = getContext().getTypeAlignInChars(E->getType());
+ Addr = Addr.withAlignment(std::max(Addr.getAlignment(), TypeAlign));
+ }
Addr = Addr.withElementType(IntTy);
Value *Val = Builder.CreateLoad(Addr);
if ((BitWidth == 16 || BitWidth == 32 || BitWidth == 64) &&
diff --git a/clang/test/CodeGen/builtin-stdc-load8.c b/clang/test/CodeGen/builtin-stdc-load8.c
index c239a4dfa06d6..47636aaea3b26 100644
--- a/clang/test/CodeGen/builtin-stdc-load8.c
+++ b/clang/test/CodeGen/builtin-stdc-load8.c
@@ -145,3 +145,27 @@ __INT_LEAST32_TYPE__ test_aligned_les32(const unsigned char *p) { return stdc_lo
// BE: load i32, ptr {{.+}}, align 4
// BE-NOT: bswap
__INT_LEAST32_TYPE__ test_aligned_bes32(const unsigned char *p) { return stdc_load8_aligned_bes32(p); }
+
+// The unaligned variants must not throw away alignment info that is already
+// known for the pointer expression: buf's declared alignment is 4, so the
+// load should use align 4, not be forced down to align 1.
+alignas(4) unsigned char buf4[4];
+// LE-LABEL: @test_leu32_known_alignment(
+// LE: load i32, ptr @buf4, align 4
+// LE-NOT: bswap
+// BE-LABEL: @test_leu32_known_alignment(
+// BE: load i32, ptr @buf4, align 4
+// BE: call i32 @llvm.bswap.i32(
+__UINT_LEAST32_TYPE__ test_leu32_known_alignment(void) { return stdc_load8_leu32(buf4); }
+
+// The aligned variants must keep a stronger alignment than the minimum they
+// require: buf16's declared alignment (16) exceeds the 4-byte alignment
+// stdc_load8_aligned_leu32 needs, so the load should use align 16.
+alignas(16) unsigned char buf16[4];
+// LE-LABEL: @test_aligned_leu32_stronger_known_alignment(
+// LE: load i32, ptr @buf16, align 16
+// LE-NOT: bswap
+// BE-LABEL: @test_aligned_leu32_stronger_known_alignment(
+// BE: load i32, ptr @buf16, align 16
+// BE: call i32 @llvm.bswap.i32(
+__UINT_LEAST32_TYPE__ test_aligned_leu32_stronger_known_alignment(void) { return stdc_load8_aligned_leu32(buf16); }
More information about the cfe-commits
mailing list