[clang] d836261 - [X86][Clang] allow CRC32 intrinsics to be used in constexp (#173908)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jan 21 08:34:36 PST 2026
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
More information about the cfe-commits
mailing list