https://github.com/chaitanyav updated https://github.com/llvm/llvm-project/pull/197358
>From 914d7e605714adf369f6470cc59e0845b80aac16 Mon Sep 17 00:00:00 2001 From: NagaChaitanya Vellanki <[email protected]> Date: Tue, 12 May 2026 16:18:35 -0700 Subject: [PATCH 1/7] [clang] Implement C2y stdc_memreverse8 and stdc_memreverse8u{8,16,32,64} builtins Implements the C2y <stdbit.h> memory reversal functions stdc_memreverse8 and stdc_memreverse8u{8,16,32,64}. The typed variants lower to llvm.bswap and support constexpr evaluation. --- clang/docs/ReleaseNotes.rst | 5 + clang/include/clang/Basic/Builtins.td | 16 ++++ .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/lib/AST/ByteCode/InterpBuiltin.cpp | 4 + clang/lib/AST/ExprConstant.cpp | 6 +- clang/lib/CodeGen/CGBuiltin.cpp | 58 +++++++++++ clang/lib/Sema/SemaChecking.cpp | 12 +++ clang/test/CodeGen/Inputs/stdbit.h | 6 ++ clang/test/CodeGen/builtin-stdc-memreverse8.c | 96 +++++++++++++++++++ clang/test/Sema/Inputs/stdbit.h | 6 ++ clang/test/Sema/builtin-stdc-memreverse8.c | 84 ++++++++++++++++ 11 files changed, 294 insertions(+), 1 deletion(-) create mode 100644 clang/test/CodeGen/builtin-stdc-memreverse8.c create mode 100644 clang/test/Sema/builtin-stdc-memreverse8.c diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 812603f7ffe1b..52cd539717130 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -232,6 +232,11 @@ C2y Feature Support ``stdc_rotate_left_{uc,us,ui,ul,ull}`` and ``stdc_rotate_right_{uc,us,ui,ul,ull}``. +- Implemented C2y ``<stdbit.h>`` memory reversal functions: + ``__builtin_stdc_memreverse8`` / ``stdc_memreverse8`` (in-place byte + reversal of a byte array) and ``stdc_memreverse8u{8,16,32,64}`` (byte-swap + of an exact-width unsigned integer value, usable in constant expressions). + C23 Feature Support ^^^^^^^^^^^^^^^^^^^ - Clang now allows C23 ``constexpr`` struct member access through the dot operator in constant expressions. (#GH178349) diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 8d2a824ef5610..f5e3bdf00d935 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -34,6 +34,10 @@ class IntBitUtilTemplate : Template< "unsigned long int", "unsigned long long int"], ["_uc", "_us", "_ui", "_ul", "_ull"]>; +class MemReverse8Template : Template< + ["unsigned char", "unsigned short", "uint32_t", "uint64_t"], + ["u8", "u16", "u32", "u64"]>; + class MSInt8_16_32Template : Template<["char", "short", "msint32_t"], ["8", "16", ""]>; @@ -986,6 +990,18 @@ def StdcRotateRightTyped : LibBuiltin<"stdbit.h", "C2Y_LANG">, IntBitUtilTemplat let Prototype = "T(T, unsigned int)"; } +def StdcMemReverse8: Builtin { + let Spellings = ["__builtin_stdc_memreverse8"]; + let Attributes = [NoThrow, NonNull<NonOptimizing, [1]>]; + let Prototype = "void(size_t, unsigned char*)"; +} + +def StdcMemReverse8Typed : LibBuiltin<"stdbit.h", "C2Y_LANG">, MemReverse8Template { + let Spellings = ["stdc_memreverse8"]; + let Attributes = [NoThrow, Const, Constexpr]; + let Prototype = "T(T)"; +} + // Random GCC builtins // FIXME: The builtins marked FunctionWithBuiltinPrefix below should be diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 4e3585b7b8191..7bc12da459669 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11530,6 +11530,8 @@ def err_builtin_setjmp_unsupported : Error< def err_builtin_longjmp_invalid_val : Error< "argument to __builtin_longjmp must be a constant 1">; def err_builtin_requires_language : Error<"'%0' is only available in %1">; +def err_builtin_requires_char_bit_8 : Error< + "'%0' is only available on targets where CHAR_BIT == 8">; def err_constant_integer_arg_type : Error< "argument to %0 must be a constant integer">; diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index ee3813a9287be..96900187e6575 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -4954,6 +4954,10 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call, case Builtin::BI__builtin_bswap16: case Builtin::BI__builtin_bswap32: case Builtin::BI__builtin_bswap64: + case Builtin::BIstdc_memreverse8u8: + case Builtin::BIstdc_memreverse8u16: + case Builtin::BIstdc_memreverse8u32: + case Builtin::BIstdc_memreverse8u64: return interp__builtin_bswap(S, OpPC, Frame, Call); case Builtin::BI__atomic_always_lock_free: diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 86c162cc040f9..283626523b559 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -16552,7 +16552,11 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, case Builtin::BI__builtin_bswapg: case Builtin::BI__builtin_bswap16: case Builtin::BI__builtin_bswap32: - case Builtin::BI__builtin_bswap64: { + case Builtin::BI__builtin_bswap64: + case Builtin::BIstdc_memreverse8u8: + case Builtin::BIstdc_memreverse8u16: + case Builtin::BIstdc_memreverse8u32: + case Builtin::BIstdc_memreverse8u64: { APSInt Val; if (!EvaluateInteger(E->getArg(0), Val, Info)) return false; diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 7ce38c5d1922c..ac1cf03525a35 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -4230,6 +4230,64 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, return RValue::get(Phi); } + // stdc_memreverse8u8 is a no-op (single byte, nothing to swap). + case Builtin::BIstdc_memreverse8u8: + return RValue::get(EmitScalarExpr(E->getArg(0))); + + case Builtin::BIstdc_memreverse8u16: + case Builtin::BIstdc_memreverse8u32: + case Builtin::BIstdc_memreverse8u64: + return RValue::get( + emitBuiltinWithOneOverloadedType<1>(*this, E, Intrinsic::bswap)); + + case Builtin::BI__builtin_stdc_memreverse8: { + Value *N = EmitScalarExpr(E->getArg(0)); + Value *Ptr = EmitScalarExpr(E->getArg(1)); + + if (auto *CI = dyn_cast<ConstantInt>(N)) { + uint64_t Size = CI->getZExtValue(); + if (Size == 2 || Size == 4 || Size == 8) { + llvm::Type *IntTy = Builder.getIntNTy(Size * 8); + CharUnits Align = CharUnits::One(); + Address Addr(Ptr, IntTy, Align); + Value *Val = Builder.CreateLoad(Addr); + Function *F = CGM.getIntrinsic(Intrinsic::bswap, IntTy); + Value *Swapped = Builder.CreateCall(F, Val); + Builder.CreateStore(Swapped, Addr); + return RValue::get(nullptr); + } + } + + // General case: emit a loop swapping ptr[i] and ptr[n-i-1]. + BasicBlock *EntryBB = Builder.GetInsertBlock(); + BasicBlock *LoopBB = createBasicBlock("memreverse8.loop", CurFn); + BasicBlock *AfterBB = createBasicBlock("memreverse8.after", CurFn); + Value *Half = Builder.CreateLShr(N, ConstantInt::get(N->getType(), 1)); + Value *IsEmpty = + Builder.CreateICmpEQ(Half, ConstantInt::get(Half->getType(), 0)); + Builder.CreateCondBr(IsEmpty, AfterBB, LoopBB); + + Builder.SetInsertPoint(LoopBB); + PHINode *Idx = Builder.CreatePHI(Half->getType(), 2, "i"); + Idx->addIncoming(ConstantInt::get(Half->getType(), 0), EntryBB); + Value *J = Builder.CreateSub(Builder.CreateSub(N, Idx), + ConstantInt::get(N->getType(), 1)); + Address AddrI(Builder.CreateGEP(Int8Ty, Ptr, Idx), Int8Ty, + CharUnits::One()); + Address AddrJ(Builder.CreateGEP(Int8Ty, Ptr, J), Int8Ty, CharUnits::One()); + Value *XI = Builder.CreateLoad(AddrI); + Value *XJ = Builder.CreateLoad(AddrJ); + Builder.CreateStore(XJ, AddrI); + Builder.CreateStore(XI, AddrJ); + Value *Next = Builder.CreateAdd(Idx, ConstantInt::get(Half->getType(), 1)); + Idx->addIncoming(Next, LoopBB); + Value *Done = Builder.CreateICmpEQ(Next, Half); + Builder.CreateCondBr(Done, AfterBB, LoopBB); + + Builder.SetInsertPoint(AfterBB); + return RValue::get(nullptr); + } + case Builtin::BI__builtin_constant_p: { llvm::Type *ResultType = ConvertType(E->getType()); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 8a8c9cc9d2c23..265f04e279e2a 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -3933,6 +3933,18 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, return ExprError(); break; + case Builtin::BI__builtin_stdc_memreverse8: + case Builtin::BIstdc_memreverse8u8: + case Builtin::BIstdc_memreverse8u16: + case Builtin::BIstdc_memreverse8u32: + case Builtin::BIstdc_memreverse8u64: + if (Context.getTargetInfo().getCharWidth() != 8) { + Diag(TheCall->getBeginLoc(), diag::err_builtin_requires_char_bit_8) + << TheCall->getDirectCallee()->getName(); + return ExprError(); + } + break; + case Builtin::BI__builtin_stdc_bit_floor: case Builtin::BI__builtin_stdc_bit_ceil: if (BuiltinStdCBuiltin(*this, TheCall, QualType())) diff --git a/clang/test/CodeGen/Inputs/stdbit.h b/clang/test/CodeGen/Inputs/stdbit.h index bdabb9ec8ee7d..ca8b87deb67e3 100644 --- a/clang/test/CodeGen/Inputs/stdbit.h +++ b/clang/test/CodeGen/Inputs/stdbit.h @@ -112,4 +112,10 @@ unsigned int stdc_rotate_right_ui(unsigned int, unsigned int); unsigned long stdc_rotate_right_ul(unsigned long, unsigned int); unsigned long long stdc_rotate_right_ull(unsigned long long, unsigned int); +void stdc_memreverse8(__SIZE_TYPE__, unsigned char *); +unsigned char stdc_memreverse8u8(unsigned char); +unsigned short stdc_memreverse8u16(unsigned short); +__UINT32_TYPE__ stdc_memreverse8u32(__UINT32_TYPE__); +__UINT64_TYPE__ stdc_memreverse8u64(__UINT64_TYPE__); + #endif diff --git a/clang/test/CodeGen/builtin-stdc-memreverse8.c b/clang/test/CodeGen/builtin-stdc-memreverse8.c new file mode 100644 index 0000000000000..730f29bb9664a --- /dev/null +++ b/clang/test/CodeGen/builtin-stdc-memreverse8.c @@ -0,0 +1,96 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c2y -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c2y -isystem %S/Inputs -DTEST_C2Y_LIB_SPELLINGS %s -emit-llvm -o - | FileCheck %s --check-prefix=C2Y + +#ifndef TEST_C2Y_LIB_SPELLINGS + +// N=0 and N=1: IsEmpty folds to true, branch to after without entering loop. +// CHECK-LABEL: test_memreverse8_const0 +// CHECK: br i1 true, +void test_memreverse8_const0(unsigned char *p) { + __builtin_stdc_memreverse8(0, p); +} + +// CHECK-LABEL: test_memreverse8_const1 +// CHECK: br i1 true, +void test_memreverse8_const1(unsigned char *p) { + __builtin_stdc_memreverse8(1, p); +} + +// N=2, 4, 8: lowered to a single bswap with unaligned load/store. +// CHECK-LABEL: test_memreverse8_const2 +// CHECK: load i16, ptr {{.*}}, align 1 +// CHECK: call i16 @llvm.bswap.i16 +// CHECK: store i16 {{.*}}, ptr {{.*}}, align 1 +void test_memreverse8_const2(unsigned char *p) { + __builtin_stdc_memreverse8(2, p); +} + +// CHECK-LABEL: test_memreverse8_const4 +// CHECK: load i32, ptr {{.*}}, align 1 +// CHECK: call i32 @llvm.bswap.i32 +// CHECK: store i32 {{.*}}, ptr {{.*}}, align 1 +void test_memreverse8_const4(unsigned char *p) { + __builtin_stdc_memreverse8(4, p); +} + +// CHECK-LABEL: test_memreverse8_const8 +// CHECK: load i64, ptr {{.*}}, align 1 +// CHECK: call i64 @llvm.bswap.i64 +// CHECK: store i64 {{.*}}, ptr {{.*}}, align 1 +void test_memreverse8_const8(unsigned char *p) { + __builtin_stdc_memreverse8(8, p); +} + +// Constant N=3: not bswap-optimized, falls back to the loop. +// CHECK-LABEL: test_memreverse8_const3 +// CHECK: br i1 false, +// CHECK-NOT: @llvm.bswap +void test_memreverse8_const3(unsigned char *p) { + __builtin_stdc_memreverse8(3, p); +} + +// CHECK-LABEL: test_memreverse8_runtime +// CHECK: lshr +// CHECK: memreverse8.loop: +// CHECK: getelementptr +// CHECK: getelementptr +// CHECK: load i8 +// CHECK: load i8 +// CHECK: store i8 +// CHECK: store i8 +// CHECK: memreverse8.after: +void test_memreverse8_runtime(__SIZE_TYPE__ n, unsigned char *p) { + __builtin_stdc_memreverse8(n, p); +} + +#endif // !TEST_C2Y_LIB_SPELLINGS + +#ifdef TEST_C2Y_LIB_SPELLINGS +#include <stdbit.h> + +// u8 is a no-op: single byte, nothing to swap. +// C2Y-LABEL: test_typed_memreverse8u8 +// C2Y-NOT: @llvm.bswap +unsigned char test_typed_memreverse8u8(unsigned char x) { + return stdc_memreverse8u8(x); +} + +// C2Y-LABEL: test_typed_memreverse8u16 +// C2Y: call i16 @llvm.bswap.i16 +unsigned short test_typed_memreverse8u16(unsigned short x) { + return stdc_memreverse8u16(x); +} + +// C2Y-LABEL: test_typed_memreverse8u32 +// C2Y: call i32 @llvm.bswap.i32 +unsigned int test_typed_memreverse8u32(unsigned int x) { + return stdc_memreverse8u32(x); +} + +// C2Y-LABEL: test_typed_memreverse8u64 +// C2Y: call i64 @llvm.bswap.i64 +__UINT64_TYPE__ test_typed_memreverse8u64(__UINT64_TYPE__ x) { + return stdc_memreverse8u64(x); +} + +#endif // TEST_C2Y_LIB_SPELLINGS diff --git a/clang/test/Sema/Inputs/stdbit.h b/clang/test/Sema/Inputs/stdbit.h index bdabb9ec8ee7d..ca8b87deb67e3 100644 --- a/clang/test/Sema/Inputs/stdbit.h +++ b/clang/test/Sema/Inputs/stdbit.h @@ -112,4 +112,10 @@ unsigned int stdc_rotate_right_ui(unsigned int, unsigned int); unsigned long stdc_rotate_right_ul(unsigned long, unsigned int); unsigned long long stdc_rotate_right_ull(unsigned long long, unsigned int); +void stdc_memreverse8(__SIZE_TYPE__, unsigned char *); +unsigned char stdc_memreverse8u8(unsigned char); +unsigned short stdc_memreverse8u16(unsigned short); +__UINT32_TYPE__ stdc_memreverse8u32(__UINT32_TYPE__); +__UINT64_TYPE__ stdc_memreverse8u64(__UINT64_TYPE__); + #endif diff --git a/clang/test/Sema/builtin-stdc-memreverse8.c b/clang/test/Sema/builtin-stdc-memreverse8.c new file mode 100644 index 0000000000000..6b0017ab7540c --- /dev/null +++ b/clang/test/Sema/builtin-stdc-memreverse8.c @@ -0,0 +1,84 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c2y -fsyntax-only -verify %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -std=c2y -isystem %S/Inputs -fsyntax-only -verify %s + +void test_errors(void) { + unsigned char buf[4]; + + __builtin_stdc_memreverse8(4); // expected-error {{too few arguments to function call}} + __builtin_stdc_memreverse8(4, buf, buf); // expected-error {{too many arguments to function call}} +} + +void test_null_pointer(void) { + __builtin_stdc_memreverse8(4, 0); // expected-warning {{null passed to a callee that requires a non-null argument}} + __builtin_stdc_memreverse8(4, (unsigned char *)0); // expected-warning {{null passed to a callee that requires a non-null argument}} +} + +void test_uintN_pointer_types(void) { + __UINT8_TYPE__ *p8 = 0; + __UINT16_TYPE__ *p16 = 0; + __UINT32_TYPE__ *p32 = 0; + __UINT64_TYPE__ *p64 = 0; + + __builtin_stdc_memreverse8(4, p8); // uint8_t* == unsigned char*, no diagnostic + __builtin_stdc_memreverse8(4, p16); // expected-error {{incompatible pointer types}} + __builtin_stdc_memreverse8(4, p32); // expected-error {{incompatible pointer types}} + __builtin_stdc_memreverse8(4, p64); // expected-error {{incompatible pointer types}} +} + +void test_pointer_types(void) { + char *cp = 0; + int *ip = 0; + void *vp = 0; + const unsigned char *ccp = 0; + signed char *scp = 0; + unsigned char **ucpp = 0; + + __builtin_stdc_memreverse8(4, cp); // expected-warning {{converts between pointers to integer types}} + __builtin_stdc_memreverse8(4, ip); // expected-error {{incompatible pointer types}} + __builtin_stdc_memreverse8(4, vp); // void* implicitly converts, no diagnostic + __builtin_stdc_memreverse8(4, ccp); // expected-warning {{discards qualifiers}} + __builtin_stdc_memreverse8(4, scp); // expected-warning {{converts between pointers to integer types}} + __builtin_stdc_memreverse8(4, ucpp); // expected-error {{incompatible pointer types}} +} + +#ifdef __has_include +#if __has_include(<stdbit.h>) +#include <stdbit.h> + +_Static_assert(stdc_memreverse8u8(0xAB) == 0xAB, ""); +_Static_assert(stdc_memreverse8u16(0x1234) == 0x3412, ""); +_Static_assert(stdc_memreverse8u32(0x12345678U) == 0x78563412U, ""); +_Static_assert(stdc_memreverse8u64(0x123456789ABCDEF0ULL) == 0xF0DEBC9A78563412ULL, ""); + +_Static_assert(stdc_memreverse8u16(0) == 0, ""); +_Static_assert(stdc_memreverse8u32(0) == 0, ""); +_Static_assert(stdc_memreverse8u64(0) == 0, ""); +_Static_assert(stdc_memreverse8u16(0xFFFF) == 0xFFFF, ""); +_Static_assert(stdc_memreverse8u32(0xFFFFFFFFU) == 0xFFFFFFFFU, ""); +_Static_assert(stdc_memreverse8u64(0xFFFFFFFFFFFFFFFFULL) == 0xFFFFFFFFFFFFFFFFULL, ""); + +_Static_assert(stdc_memreverse8u16(stdc_memreverse8u16(0x1234)) == 0x1234, ""); +_Static_assert(stdc_memreverse8u32(stdc_memreverse8u32(0x12345678U)) == 0x12345678U, ""); +_Static_assert(stdc_memreverse8u64(stdc_memreverse8u64(0x123456789ABCDEF0ULL)) == 0x123456789ABCDEF0ULL, ""); +_Static_assert(stdc_memreverse8u8(0xAB) == 0xAB, ""); +_Static_assert(stdc_memreverse8u16(0xDEAD) == 0xADDE, ""); +_Static_assert(stdc_memreverse8u32(0xDEADBEEFU) == 0xEFBEADDEU, ""); +_Static_assert(stdc_memreverse8u64(0x0102030405060708ULL) == 0x0807060504030201ULL, ""); + +_Static_assert(stdc_memreverse8u8(0xAA) == 0xAA, ""); +_Static_assert(stdc_memreverse8u16(0xABAB) == 0xABAB, ""); +_Static_assert(stdc_memreverse8u32(0xABCDCDABU) == 0xABCDCDABU, ""); +_Static_assert(stdc_memreverse8u64(0x0102030404030201ULL) == 0x0102030404030201ULL, ""); + +void test_typed_variant_errors(void) { + stdc_memreverse8u8(); // expected-error {{too few arguments to function call}} + stdc_memreverse8u8(1, 2); // expected-error {{too many arguments to function call}} + stdc_memreverse8u16(); // expected-error {{too few arguments to function call}} + stdc_memreverse8u16(1, 2); // expected-error {{too many arguments to function call}} + stdc_memreverse8u32(); // expected-error {{too few arguments to function call}} + stdc_memreverse8u32(1, 2); // expected-error {{too many arguments to function call}} + stdc_memreverse8u64(); // expected-error {{too few arguments to function call}} + stdc_memreverse8u64(1, 2); // expected-error {{too many arguments to function call}} +} +#endif +#endif >From 33667ed942cb11d6e411d0a05f647f1d88485968 Mon Sep 17 00:00:00 2001 From: NagaChaitanya Vellanki <[email protected]> Date: Fri, 15 May 2026 09:25:56 -0700 Subject: [PATCH 2/7] Quote 'CHAR_BIT == 8' in the diagnostic --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 7bc12da459669..423482fd08b27 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11531,7 +11531,7 @@ def err_builtin_longjmp_invalid_val : Error< "argument to __builtin_longjmp must be a constant 1">; def err_builtin_requires_language : Error<"'%0' is only available in %1">; def err_builtin_requires_char_bit_8 : Error< - "'%0' is only available on targets where CHAR_BIT == 8">; + "'%0' is only available on targets where 'CHAR_BIT == 8'">; def err_constant_integer_arg_type : Error< "argument to %0 must be a constant integer">; >From ebcc64be9f0bb21280131c22d359e538e10132f2 Mon Sep 17 00:00:00 2001 From: NagaChaitanya Vellanki <[email protected]> Date: Fri, 15 May 2026 17:12:09 -0700 Subject: [PATCH 3/7] Add Pure to StdcMemReverse8Typed attributes alongside Const --- clang/include/clang/Basic/Builtins.td | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index f5e3bdf00d935..7f8a44de98c4d 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -998,7 +998,7 @@ def StdcMemReverse8: Builtin { def StdcMemReverse8Typed : LibBuiltin<"stdbit.h", "C2Y_LANG">, MemReverse8Template { let Spellings = ["stdc_memreverse8"]; - let Attributes = [NoThrow, Const, Constexpr]; + let Attributes = [NoThrow, Pure, Const, Constexpr]; let Prototype = "T(T)"; } >From f785776ccc912d729d12ced6b0e94faec3a8b67f Mon Sep 17 00:00:00 2001 From: NagaChaitanya Vellanki <[email protected]> Date: Thu, 28 May 2026 08:56:28 -0700 Subject: [PATCH 4/7] Add uint8_t, uint16_t, int8_t, int16_t support to builtin prototype parser Introduces B (byte/8-bit) and T (short/16-bit) prefix modifiers in the builtin type encoding system --- clang/include/clang/Basic/Builtins.def | 2 ++ clang/include/clang/Basic/Builtins.td | 4 +-- clang/lib/AST/ASTContext.cpp | 27 +++++++++++++++++-- clang/test/CodeGen/Inputs/stdbit.h | 4 +-- clang/test/Sema/Inputs/stdbit.h | 4 +-- .../target-builtins-prototype-parser.td | 12 +++++++++ clang/utils/TableGen/ClangBuiltinsEmitter.cpp | 4 +++ 7 files changed, 49 insertions(+), 8 deletions(-) diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def index 0e94b0bba4e32..77fca0a7ad8c1 100644 --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -50,6 +50,8 @@ // L -> long (e.g. Li for 'long int', Ld for 'long double') // LL -> long long (e.g. LLi for 'long long int', LLd for __float128) // LLL -> __int128_t (e.g. LLLi) +// B -> int8_t (byte-width integer, e.g. Bi for 'int8_t', UBi for 'uint8_t') +// T -> int16_t (short-width integer, e.g. Ti for 'int16_t', UTi for 'uint16_t') // Z -> int32_t (require a native 32-bit integer type on the target) // W -> int64_t (require a native 64-bit integer type on the target) // N -> 'int' size if target is LP64, 'L' otherwise. diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 7f8a44de98c4d..060d0cd00f2d0 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -35,8 +35,8 @@ class IntBitUtilTemplate : Template< ["_uc", "_us", "_ui", "_ul", "_ull"]>; class MemReverse8Template : Template< - ["unsigned char", "unsigned short", "uint32_t", "uint64_t"], - ["u8", "u16", "u32", "u64"]>; + ["uint8_t", "uint16_t", "uint32_t", "uint64_t"], + ["u8", "u16", "u32", "u64"]>; class MSInt8_16_32Template : Template<["char", "short", "msint32_t"], ["8", "16", ""]>; diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 1e35aba7b2b19..b4c40da299452 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -12586,6 +12586,7 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context, // Modifiers. int HowLong = 0; bool Signed = false, Unsigned = false; + bool IsChar = false, IsShort = false; RequiresICE = false; // Read the prefixed modifiers first. @@ -12609,8 +12610,26 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context, assert(!Unsigned && "Can't use 'U' modifier multiple times!"); Unsigned = true; break; + case 'B': + // This modifier represents int8 type (byte-width). + assert(!IsSpecial && "Can't use two 'N', 'W', 'Z', 'O', 'B', or 'T' modifiers!"); + assert(HowLong == 0 && "Can't use both 'L' and 'B' modifiers!"); + #ifndef NDEBUG + IsSpecial = true; + #endif + IsChar = true; + break; + case 'T': + // This modifier represents int16 type (short-width). + assert(!IsSpecial && "Can't use two 'N', 'W', 'Z', 'O', 'B', or 'T' modifiers!"); + assert(HowLong == 0 && "Can't use both 'L' and 'T' modifiers!"); + #ifndef NDEBUG + IsSpecial = true; + #endif + IsShort = true; + break; case 'L': - assert(!IsSpecial && "Can't use 'L' with 'W', 'N', 'Z' or 'O' modifiers"); + assert(!IsSpecial && "Can't use 'L' with 'W', 'N', 'Z', 'O', 'B', or 'T' modifiers"); assert(HowLong <= 2 && "Can't have LLLL modifier"); ++HowLong; break; @@ -12726,7 +12745,11 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context, Type = Context.ShortTy; break; case 'i': - if (HowLong == 3) + if (IsChar) + Type = Unsigned ? Context.UnsignedCharTy : Context.SignedCharTy; + else if (IsShort) + Type = Unsigned ? Context.UnsignedShortTy : Context.ShortTy; + else if (HowLong == 3) Type = Unsigned ? Context.UnsignedInt128Ty : Context.Int128Ty; else if (HowLong == 2) Type = Unsigned ? Context.UnsignedLongLongTy : Context.LongLongTy; diff --git a/clang/test/CodeGen/Inputs/stdbit.h b/clang/test/CodeGen/Inputs/stdbit.h index ca8b87deb67e3..170d3fdb382c3 100644 --- a/clang/test/CodeGen/Inputs/stdbit.h +++ b/clang/test/CodeGen/Inputs/stdbit.h @@ -113,8 +113,8 @@ unsigned long stdc_rotate_right_ul(unsigned long, unsigned int); unsigned long long stdc_rotate_right_ull(unsigned long long, unsigned int); void stdc_memreverse8(__SIZE_TYPE__, unsigned char *); -unsigned char stdc_memreverse8u8(unsigned char); -unsigned short stdc_memreverse8u16(unsigned short); +__UINT8_TYPE__ stdc_memreverse8u8(__UINT8_TYPE__); +__UINT16_TYPE__ stdc_memreverse8u16(__UINT16_TYPE__); __UINT32_TYPE__ stdc_memreverse8u32(__UINT32_TYPE__); __UINT64_TYPE__ stdc_memreverse8u64(__UINT64_TYPE__); diff --git a/clang/test/Sema/Inputs/stdbit.h b/clang/test/Sema/Inputs/stdbit.h index ca8b87deb67e3..170d3fdb382c3 100644 --- a/clang/test/Sema/Inputs/stdbit.h +++ b/clang/test/Sema/Inputs/stdbit.h @@ -113,8 +113,8 @@ unsigned long stdc_rotate_right_ul(unsigned long, unsigned int); unsigned long long stdc_rotate_right_ull(unsigned long long, unsigned int); void stdc_memreverse8(__SIZE_TYPE__, unsigned char *); -unsigned char stdc_memreverse8u8(unsigned char); -unsigned short stdc_memreverse8u16(unsigned short); +__UINT8_TYPE__ stdc_memreverse8u8(__UINT8_TYPE__); +__UINT16_TYPE__ stdc_memreverse8u16(__UINT16_TYPE__); __UINT32_TYPE__ stdc_memreverse8u32(__UINT32_TYPE__); __UINT64_TYPE__ stdc_memreverse8u64(__UINT64_TYPE__); diff --git a/clang/test/TableGen/target-builtins-prototype-parser.td b/clang/test/TableGen/target-builtins-prototype-parser.td index ea22b8ed626eb..0fa1246a01008 100644 --- a/clang/test/TableGen/target-builtins-prototype-parser.td +++ b/clang/test/TableGen/target-builtins-prototype-parser.td @@ -75,6 +75,18 @@ def : Builtin { let Spellings = ["__builtin_11"]; } +def : Builtin { +// CHECK: Builtin::Info{{.*}} __builtin_12 {{.*}} /* UBiUTiUZiUWi */ + let Prototype = "uint8_t(uint16_t, uint32_t, uint64_t)"; + let Spellings = ["__builtin_12"]; +} + +def : Builtin { +// CHECK: Builtin::Info{{.*}} __builtin_13 {{.*}} /* BiTiZiWi */ + let Prototype = "int8_t(int16_t, int32_t, int64_t)"; + let Spellings = ["__builtin_13"]; +} + #ifdef ERROR_EXPECTED_LANES def : Builtin { // ERROR_EXPECTED_LANES: :[[# @LINE + 1]]:7: error: Expected number of lanes after '_ExtVector<' diff --git a/clang/utils/TableGen/ClangBuiltinsEmitter.cpp b/clang/utils/TableGen/ClangBuiltinsEmitter.cpp index c2e38c0d6aeb8..4f7f437bcf73c 100644 --- a/clang/utils/TableGen/ClangBuiltinsEmitter.cpp +++ b/clang/utils/TableGen/ClangBuiltinsEmitter.cpp @@ -359,6 +359,8 @@ class PrototypeParser { .Case("float", "f") .Case("id", "G") .Case("int", "i") + .Case("int8_t", "Bi") + .Case("int16_t", "Ti") .Case("int32_t", "Zi") .Case("int64_t", "Wi") .Case("jmp_buf", "J") @@ -372,6 +374,8 @@ class PrototypeParser { .Case("sigjmp_buf", "SJ") .Case("size_t", "z") .Case("ucontext_t", "K") + .Case("uint8_t", "UBi") + .Case("uint16_t", "UTi") .Case("uint32_t", "UZi") .Case("uint64_t", "UWi") .Case("void", "v") >From a8062893bc6ebd82f3085de784b41423863ca002 Mon Sep 17 00:00:00 2001 From: NagaChaitanya Vellanki <[email protected]> Date: Fri, 29 May 2026 12:51:03 -0700 Subject: [PATCH 5/7] Use EmitPointerWithAlignment for stdc_memreverse8 --- clang/lib/CodeGen/CGBuiltin.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index ac1cf03525a35..68d0060e6573c 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -4242,14 +4242,13 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, case Builtin::BI__builtin_stdc_memreverse8: { Value *N = EmitScalarExpr(E->getArg(0)); - Value *Ptr = EmitScalarExpr(E->getArg(1)); + Address PtrAddr = EmitPointerWithAlignment(E->getArg(1)); if (auto *CI = dyn_cast<ConstantInt>(N)) { uint64_t Size = CI->getZExtValue(); if (Size == 2 || Size == 4 || Size == 8) { llvm::Type *IntTy = Builder.getIntNTy(Size * 8); - CharUnits Align = CharUnits::One(); - Address Addr(Ptr, IntTy, Align); + Address Addr = PtrAddr.withElementType(IntTy); Value *Val = Builder.CreateLoad(Addr); Function *F = CGM.getIntrinsic(Intrinsic::bswap, IntTy); Value *Swapped = Builder.CreateCall(F, Val); @@ -4259,6 +4258,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, } // General case: emit a loop swapping ptr[i] and ptr[n-i-1]. + Value *Ptr = PtrAddr.emitRawPointer(*this); BasicBlock *EntryBB = Builder.GetInsertBlock(); BasicBlock *LoopBB = createBasicBlock("memreverse8.loop", CurFn); BasicBlock *AfterBB = createBasicBlock("memreverse8.after", CurFn); >From 1f339b61b6721d0aa0d6b375cdd596c3874149e8 Mon Sep 17 00:00:00 2001 From: NagaChaitanya Vellanki <[email protected]> Date: Fri, 29 May 2026 13:23:34 -0700 Subject: [PATCH 6/7] Fix formatting using clang-format --- clang/lib/AST/ASTContext.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index b4c40da299452..e4e6be78dea97 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -12612,24 +12612,27 @@ static QualType DecodeTypeFromStr(const char *&Str, const ASTContext &Context, break; case 'B': // This modifier represents int8 type (byte-width). - assert(!IsSpecial && "Can't use two 'N', 'W', 'Z', 'O', 'B', or 'T' modifiers!"); + assert(!IsSpecial && + "Can't use two 'N', 'W', 'Z', 'O', 'B', or 'T' modifiers!"); assert(HowLong == 0 && "Can't use both 'L' and 'B' modifiers!"); - #ifndef NDEBUG +#ifndef NDEBUG IsSpecial = true; - #endif +#endif IsChar = true; break; case 'T': // This modifier represents int16 type (short-width). - assert(!IsSpecial && "Can't use two 'N', 'W', 'Z', 'O', 'B', or 'T' modifiers!"); + assert(!IsSpecial && + "Can't use two 'N', 'W', 'Z', 'O', 'B', or 'T' modifiers!"); assert(HowLong == 0 && "Can't use both 'L' and 'T' modifiers!"); - #ifndef NDEBUG +#ifndef NDEBUG IsSpecial = true; - #endif +#endif IsShort = true; break; case 'L': - assert(!IsSpecial && "Can't use 'L' with 'W', 'N', 'Z', 'O', 'B', or 'T' modifiers"); + assert(!IsSpecial && + "Can't use 'L' with 'W', 'N', 'Z', 'O', 'B', or 'T' modifiers"); assert(HowLong <= 2 && "Can't have LLLL modifier"); ++HowLong; break; >From 029c30d88f3a3943dae995eec140e8e3d0c452fa Mon Sep 17 00:00:00 2001 From: NagaChaitanya Vellanki <[email protected]> Date: Thu, 4 Jun 2026 09:16:03 -0700 Subject: [PATCH 7/7] Use library fallback for stdc_memreverse8 general case, optimize N=2,4,8 with bswap --- clang/include/clang/Basic/Builtins.td | 5 +-- clang/lib/CodeGen/CGBuiltin.cpp | 32 ++----------------- clang/lib/Sema/SemaChecking.cpp | 1 + clang/test/CodeGen/builtin-stdc-memreverse8.c | 21 ++++-------- 4 files changed, 14 insertions(+), 45 deletions(-) diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 060d0cd00f2d0..e7fb43be8794e 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -990,10 +990,11 @@ def StdcRotateRightTyped : LibBuiltin<"stdbit.h", "C2Y_LANG">, IntBitUtilTemplat let Prototype = "T(T, unsigned int)"; } -def StdcMemReverse8: Builtin { - let Spellings = ["__builtin_stdc_memreverse8"]; +def StdcMemReverse8: LibBuiltin<"stdbit.h", "C2Y_LANG"> { + let Spellings = ["stdc_memreverse8"]; let Attributes = [NoThrow, NonNull<NonOptimizing, [1]>]; let Prototype = "void(size_t, unsigned char*)"; + let AddBuiltinPrefixedAlias = 1; } def StdcMemReverse8Typed : LibBuiltin<"stdbit.h", "C2Y_LANG">, MemReverse8Template { diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 68d0060e6573c..aa7f9033ae0e1 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -4240,6 +4240,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, return RValue::get( emitBuiltinWithOneOverloadedType<1>(*this, E, Intrinsic::bswap)); + case Builtin::BIstdc_memreverse8: case Builtin::BI__builtin_stdc_memreverse8: { Value *N = EmitScalarExpr(E->getArg(0)); Address PtrAddr = EmitPointerWithAlignment(E->getArg(1)); @@ -4257,35 +4258,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, } } - // General case: emit a loop swapping ptr[i] and ptr[n-i-1]. - Value *Ptr = PtrAddr.emitRawPointer(*this); - BasicBlock *EntryBB = Builder.GetInsertBlock(); - BasicBlock *LoopBB = createBasicBlock("memreverse8.loop", CurFn); - BasicBlock *AfterBB = createBasicBlock("memreverse8.after", CurFn); - Value *Half = Builder.CreateLShr(N, ConstantInt::get(N->getType(), 1)); - Value *IsEmpty = - Builder.CreateICmpEQ(Half, ConstantInt::get(Half->getType(), 0)); - Builder.CreateCondBr(IsEmpty, AfterBB, LoopBB); - - Builder.SetInsertPoint(LoopBB); - PHINode *Idx = Builder.CreatePHI(Half->getType(), 2, "i"); - Idx->addIncoming(ConstantInt::get(Half->getType(), 0), EntryBB); - Value *J = Builder.CreateSub(Builder.CreateSub(N, Idx), - ConstantInt::get(N->getType(), 1)); - Address AddrI(Builder.CreateGEP(Int8Ty, Ptr, Idx), Int8Ty, - CharUnits::One()); - Address AddrJ(Builder.CreateGEP(Int8Ty, Ptr, J), Int8Ty, CharUnits::One()); - Value *XI = Builder.CreateLoad(AddrI); - Value *XJ = Builder.CreateLoad(AddrJ); - Builder.CreateStore(XJ, AddrI); - Builder.CreateStore(XI, AddrJ); - Value *Next = Builder.CreateAdd(Idx, ConstantInt::get(Half->getType(), 1)); - Idx->addIncoming(Next, LoopBB); - Value *Done = Builder.CreateICmpEQ(Next, Half); - Builder.CreateCondBr(Done, AfterBB, LoopBB); - - Builder.SetInsertPoint(AfterBB); - return RValue::get(nullptr); + // General case: fall back to the library function stdc_memreverse8. + break; } case Builtin::BI__builtin_constant_p: { diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index 265f04e279e2a..c88648336151a 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -3934,6 +3934,7 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID, break; case Builtin::BI__builtin_stdc_memreverse8: + case Builtin::BIstdc_memreverse8: case Builtin::BIstdc_memreverse8u8: case Builtin::BIstdc_memreverse8u16: case Builtin::BIstdc_memreverse8u32: diff --git a/clang/test/CodeGen/builtin-stdc-memreverse8.c b/clang/test/CodeGen/builtin-stdc-memreverse8.c index 730f29bb9664a..2622b261a0270 100644 --- a/clang/test/CodeGen/builtin-stdc-memreverse8.c +++ b/clang/test/CodeGen/builtin-stdc-memreverse8.c @@ -3,15 +3,15 @@ #ifndef TEST_C2Y_LIB_SPELLINGS -// N=0 and N=1: IsEmpty folds to true, branch to after without entering loop. +// N=0 and N=1: fall back to library call. // CHECK-LABEL: test_memreverse8_const0 -// CHECK: br i1 true, +// CHECK: call void @stdc_memreverse8( void test_memreverse8_const0(unsigned char *p) { __builtin_stdc_memreverse8(0, p); } // CHECK-LABEL: test_memreverse8_const1 -// CHECK: br i1 true, +// CHECK: call void @stdc_memreverse8( void test_memreverse8_const1(unsigned char *p) { __builtin_stdc_memreverse8(1, p); } @@ -41,24 +41,17 @@ void test_memreverse8_const8(unsigned char *p) { __builtin_stdc_memreverse8(8, p); } -// Constant N=3: not bswap-optimized, falls back to the loop. +// Constant N=3: not bswap-optimized, falls back to library call. // CHECK-LABEL: test_memreverse8_const3 -// CHECK: br i1 false, +// CHECK: call void @stdc_memreverse8( // CHECK-NOT: @llvm.bswap void test_memreverse8_const3(unsigned char *p) { __builtin_stdc_memreverse8(3, p); } +// Runtime N: falls back to library call. // CHECK-LABEL: test_memreverse8_runtime -// CHECK: lshr -// CHECK: memreverse8.loop: -// CHECK: getelementptr -// CHECK: getelementptr -// CHECK: load i8 -// CHECK: load i8 -// CHECK: store i8 -// CHECK: store i8 -// CHECK: memreverse8.after: +// CHECK: call void @stdc_memreverse8( void test_memreverse8_runtime(__SIZE_TYPE__ n, unsigned char *p) { __builtin_stdc_memreverse8(n, p); } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
