Author: Julian Pokrovsky Date: 2026-01-21T16:34:30Z New Revision: d836261ca3c9ceffd550cc40c22bab5afa69e612
URL: https://github.com/llvm/llvm-project/commit/d836261ca3c9ceffd550cc40c22bab5afa69e612 DIFF: https://github.com/llvm/llvm-project/commit/d836261ca3c9ceffd550cc40c22bab5afa69e612.diff LOG: [X86][Clang] allow CRC32 intrinsics to be used in constexp (#173908) Mostly inspired by https://github.com/llvm/llvm-project/pull/152971 CRC32 implementation using reversed polynomial that does not match an Intel manual, can be changed to canonical implementation if required (if there is a canonical implementation we should use, please attach a link) Closes #168881 Part of #30794 Added: Modified: clang/include/clang/Basic/BuiltinsX86.td clang/include/clang/Basic/BuiltinsX86_64.td clang/lib/AST/ByteCode/InterpBuiltin.cpp clang/lib/AST/ExprConstant.cpp clang/lib/Headers/crc32intrin.h clang/test/CodeGen/X86/sse42-builtins.c Removed: ################################################################################ diff --git a/clang/include/clang/Basic/BuiltinsX86.td b/clang/include/clang/Basic/BuiltinsX86.td index 0776426c95d63..23eac47eb5e4c 100644 --- a/clang/include/clang/Basic/BuiltinsX86.td +++ b/clang/include/clang/Basic/BuiltinsX86.td @@ -363,7 +363,7 @@ let Features = "sse4.2", Attributes = [NoThrow, Const, RequiredVectorWidth<128>] def pcmpestriz128 : X86Builtin<"int(_Vector<16, char>, int, _Vector<16, char>, int, _Constant char)">; } -let Features = "crc32", Attributes = [NoThrow, Const] in { +let Features = "crc32", Attributes = [NoThrow, Const, Constexpr] in { def crc32qi : X86Builtin<"unsigned int(unsigned int, unsigned char)">; def crc32hi : X86Builtin<"unsigned int(unsigned int, unsigned short)">; def crc32si : X86Builtin<"unsigned int(unsigned int, unsigned int)">; diff --git a/clang/include/clang/Basic/BuiltinsX86_64.td b/clang/include/clang/Basic/BuiltinsX86_64.td index 2bd62bd5e2663..baba21d47f93f 100644 --- a/clang/include/clang/Basic/BuiltinsX86_64.td +++ b/clang/include/clang/Basic/BuiltinsX86_64.td @@ -60,7 +60,7 @@ let Features = "sse4.1", Attributes = [NoThrow, Const, Constexpr, RequiredVector def vec_set_v2di : X86Builtin<"_Vector<2, long long int>(_Vector<2, long long int>, long long int, _Constant int)">; } -let Features = "crc32", Attributes = [NoThrow, Const] in { +let Features = "crc32", Attributes = [NoThrow, Const, Constexpr] in { def crc32di : X86Builtin<"unsigned long long int(unsigned long long int, unsigned long long int)">; } diff --git a/clang/lib/AST/ByteCode/InterpBuiltin.cpp b/clang/lib/AST/ByteCode/InterpBuiltin.cpp index f52b5a454a8a5..d668fa118d3b9 100644 --- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp +++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp @@ -731,6 +731,30 @@ static bool interp__builtin_popcount(InterpState &S, CodePtr OpPC, return true; } +static bool interp__builtin_ia32_crc32(InterpState &S, CodePtr OpPC, + const InterpFrame *Frame, + const CallExpr *Call, + unsigned DataBytes) { + uint64_t DataVal = popToUInt64(S, Call->getArg(1)); + uint64_t CRCVal = popToUInt64(S, Call->getArg(0)); + + // CRC32C polynomial (iSCSI polynomial, bit-reversed) + static const uint32_t CRC32C_POLY = 0x82F63B78; + + // Process each byte + uint32_t Result = static_cast<uint32_t>(CRCVal); + for (unsigned I = 0; I != DataBytes; ++I) { + uint8_t Byte = static_cast<uint8_t>((DataVal >> (I * 8)) & 0xFF); + Result ^= Byte; + for (int J = 0; J != 8; ++J) { + Result = (Result >> 1) ^ ((Result & 1) ? CRC32C_POLY : 0); + } + } + + pushInteger(S, Result, Call->getType()); + return true; +} + static bool interp__builtin_classify_type(InterpState &S, CodePtr OpPC, const InterpFrame *Frame, const CallExpr *Call) { @@ -4408,6 +4432,15 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const CallExpr *Call, case Builtin::BI__builtin_assume_aligned: return interp__builtin_assume_aligned(S, OpPC, Frame, Call); + case clang::X86::BI__builtin_ia32_crc32qi: + return interp__builtin_ia32_crc32(S, OpPC, Frame, Call, 1); + case clang::X86::BI__builtin_ia32_crc32hi: + return interp__builtin_ia32_crc32(S, OpPC, Frame, Call, 2); + case clang::X86::BI__builtin_ia32_crc32si: + return interp__builtin_ia32_crc32(S, OpPC, Frame, Call, 4); + case clang::X86::BI__builtin_ia32_crc32di: + return interp__builtin_ia32_crc32(S, OpPC, Frame, Call, 8); + case clang::X86::BI__builtin_ia32_bextr_u32: case clang::X86::BI__builtin_ia32_bextr_u64: case clang::X86::BI__builtin_ia32_bextri_u32: diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 51e08e738f675..857688ed8039d 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -16018,10 +16018,44 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const CallExpr *E, return Success(APValue(ResultInt), E); }; + auto HandleCRC32 = [&](unsigned DataBytes) -> bool { + APSInt CRC, Data; + if (!EvaluateInteger(E->getArg(0), CRC, Info) || + !EvaluateInteger(E->getArg(1), Data, Info)) + return false; + + uint64_t CRCVal = CRC.getZExtValue(); + uint64_t DataVal = Data.getZExtValue(); + + // CRC32C polynomial (iSCSI polynomial, bit-reversed) + static const uint32_t CRC32C_POLY = 0x82F63B78; + + // Process each byte + uint32_t Result = static_cast<uint32_t>(CRCVal); + for (unsigned I = 0; I != DataBytes; ++I) { + uint8_t Byte = static_cast<uint8_t>((DataVal >> (I * 8)) & 0xFF); + Result ^= Byte; + for (int J = 0; J != 8; ++J) { + Result = (Result >> 1) ^ ((Result & 1) ? CRC32C_POLY : 0); + } + } + + return Success(Result, E); + }; + switch (BuiltinOp) { default: return false; + case X86::BI__builtin_ia32_crc32qi: + return HandleCRC32(1); + case X86::BI__builtin_ia32_crc32hi: + return HandleCRC32(2); + case X86::BI__builtin_ia32_crc32si: + return HandleCRC32(4); + case X86::BI__builtin_ia32_crc32di: + return HandleCRC32(8); + case Builtin::BI__builtin_dynamic_object_size: case Builtin::BI__builtin_object_size: { // The type was checked when we built the expression. diff --git a/clang/lib/Headers/crc32intrin.h b/clang/lib/Headers/crc32intrin.h index a0bd99d1b5725..9bf8b2edb6e9f 100644 --- a/clang/lib/Headers/crc32intrin.h +++ b/clang/lib/Headers/crc32intrin.h @@ -10,8 +10,14 @@ #ifndef __CRC32INTRIN_H #define __CRC32INTRIN_H +/// We only declare crc32 as a constexpr if we are compiling C++ code +#if defined(__cplusplus) && (__cplusplus >= 201103L) +#define __DEFAULT_FN_ATTRS \ + __attribute__((__always_inline__, __nodebug__, __target__("crc32"))) constexpr +#else #define __DEFAULT_FN_ATTRS \ __attribute__((__always_inline__, __nodebug__, __target__("crc32"))) +#endif /// Adds the unsigned integer operand to the CRC-32C checksum of the /// unsigned char operand. @@ -28,8 +34,7 @@ /// \returns The result of adding operand \a __C to the CRC-32C checksum of /// operand \a __D. static __inline__ unsigned int __DEFAULT_FN_ATTRS -_mm_crc32_u8(unsigned int __C, unsigned char __D) -{ +_mm_crc32_u8(unsigned int __C, unsigned char __D) { return __builtin_ia32_crc32qi(__C, __D); } @@ -48,8 +53,7 @@ _mm_crc32_u8(unsigned int __C, unsigned char __D) /// \returns The result of adding operand \a __C to the CRC-32C checksum of /// operand \a __D. static __inline__ unsigned int __DEFAULT_FN_ATTRS -_mm_crc32_u16(unsigned int __C, unsigned short __D) -{ +_mm_crc32_u16(unsigned int __C, unsigned short __D) { return __builtin_ia32_crc32hi(__C, __D); } @@ -68,8 +72,7 @@ _mm_crc32_u16(unsigned int __C, unsigned short __D) /// \returns The result of adding operand \a __C to the CRC-32C checksum of /// operand \a __D. static __inline__ unsigned int __DEFAULT_FN_ATTRS -_mm_crc32_u32(unsigned int __C, unsigned int __D) -{ +_mm_crc32_u32(unsigned int __C, unsigned int __D) { return __builtin_ia32_crc32si(__C, __D); } @@ -89,8 +92,7 @@ _mm_crc32_u32(unsigned int __C, unsigned int __D) /// \returns The result of adding operand \a __C to the CRC-32C checksum of /// operand \a __D. static __inline__ unsigned long long __DEFAULT_FN_ATTRS -_mm_crc32_u64(unsigned long long __C, unsigned long long __D) -{ +_mm_crc32_u64(unsigned long long __C, unsigned long long __D) { return __builtin_ia32_crc32di(__C, __D); } #endif /* __x86_64__ */ diff --git a/clang/test/CodeGen/X86/sse42-builtins.c b/clang/test/CodeGen/X86/sse42-builtins.c index 3a1e8fc793031..b54e1e77dd5b4 100644 --- a/clang/test/CodeGen/X86/sse42-builtins.c +++ b/clang/test/CodeGen/X86/sse42-builtins.c @@ -118,18 +118,30 @@ unsigned int test_mm_crc32_u8(unsigned int CRC, unsigned char V) { // CHECK: call {{.*}}i32 @llvm.x86.sse42.crc32.32.8(i32 %{{.*}}, i8 %{{.*}}) return _mm_crc32_u8(CRC, V); } +TEST_CONSTEXPR(_mm_crc32_u8(0x02a24bf8, 0xba) == 0xa042cf00); +TEST_CONSTEXPR(_mm_crc32_u8(0x0cd5e10f, 0xe7) == 0x69e52534); +TEST_CONSTEXPR(_mm_crc32_u8(0x50e739b8, 0x2e) == 0xb459fcc6); +TEST_CONSTEXPR(_mm_crc32_u8(0x3c2db116, 0x3d) == 0xb9080854); unsigned int test_mm_crc32_u16(unsigned int CRC, unsigned short V) { // CHECK-LABEL: test_mm_crc32_u16 // CHECK: call {{.*}}i32 @llvm.x86.sse42.crc32.32.16(i32 %{{.*}}, i16 %{{.*}}) return _mm_crc32_u16(CRC, V); } +TEST_CONSTEXPR(_mm_crc32_u16(0x02a24bf8, 0xd4ba) == 0x14e9347b); +TEST_CONSTEXPR(_mm_crc32_u16(0x0cd5e10f, 0x42e7) == 0x575056c0); +TEST_CONSTEXPR(_mm_crc32_u16(0x50e739b8, 0x382e) == 0x5fa289ae); +TEST_CONSTEXPR(_mm_crc32_u16(0x3c2db116, 0x7f3d) == 0xb98d2ded); unsigned int test_mm_crc32_u32(unsigned int CRC, unsigned int V) { // CHECK-LABEL: test_mm_crc32_u32 // CHECK: call {{.*}}i32 @llvm.x86.sse42.crc32.32.32(i32 %{{.*}}, i32 %{{.*}}) return _mm_crc32_u32(CRC, V); } +TEST_CONSTEXPR(_mm_crc32_u32(0x02a24bf8, 0xf37dd4ba) == 0x7e8807f4); +TEST_CONSTEXPR(_mm_crc32_u32(0x0cd5e10f, 0x832b42e7) == 0x348e1639); +TEST_CONSTEXPR(_mm_crc32_u32(0x50e739b8, 0xcefb382e) == 0x084be8db); +TEST_CONSTEXPR(_mm_crc32_u32(0x3c2db116, 0xd3947f3d) == 0x6ef9e697); #ifdef __x86_64__ unsigned long long test_mm_crc32_u64(unsigned long long CRC, unsigned long long V) { @@ -137,4 +149,8 @@ unsigned long long test_mm_crc32_u64(unsigned long long CRC, unsigned long long // X64: call {{.*}}i64 @llvm.x86.sse42.crc32.64.64(i64 %{{.*}}, i64 %{{.*}}) return _mm_crc32_u64(CRC, V); } +TEST_CONSTEXPR(_mm_crc32_u64(0x9f65239602a24bf8, 0x894a58bff37dd4ba) == 0x00000000c093154a); +TEST_CONSTEXPR(_mm_crc32_u64(0x06ef97970cd5e10f, 0x24334e2e832b42e7) == 0x00000000141ddf67); +TEST_CONSTEXPR(_mm_crc32_u64(0x5024a45450e739b8, 0x289ee1b7cefb382e) == 0x000000002a710fcb); +TEST_CONSTEXPR(_mm_crc32_u64(0xcbc89e1c3c2db116, 0xa89143dad3947f3d) == 0x0000000052dd3aeb); #endif _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
