https://github.com/raventid created 
https://github.com/llvm/llvm-project/pull/173908

Mostly inspired by https://github.com/llvm/llvm-project/pull/152971

Tests are ad-hoc, any suggestions for improving the test cases are welcomed!

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 https://github.com/llvm/llvm-project/issues/168881 
Part of https://github.com/llvm/llvm-project/issues/30794

>From 1db8a499076f7fff8f33c2652645aa28239ad93c Mon Sep 17 00:00:00 2001
From: raventid <[email protected]>
Date: Tue, 30 Dec 2025 02:29:32 +0800
Subject: [PATCH] [X86][Clang] allow CRC32 intrinsics to be used in constexp

Mostly inspired by https://github.com/llvm/llvm-project/pull/152971

Tests are ad-hoc, any suggestions for improving the test cases are welcomed!

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 https://github.com/llvm/llvm-project/issues/168881
Part of https://github.com/llvm/llvm-project/issues/30794
---
 clang/include/clang/Basic/BuiltinsX86.td    |  2 +-
 clang/include/clang/Basic/BuiltinsX86_64.td |  2 +-
 clang/lib/AST/ByteCode/InterpBuiltin.cpp    | 52 +++++++++++++++++++++
 clang/lib/AST/ExprConstant.cpp              | 39 ++++++++++++++++
 clang/lib/Headers/crc32intrin.h             | 30 ++++++------
 clang/test/CodeGen/X86/sse42-builtins.c     | 10 ++++
 6 files changed, 119 insertions(+), 16 deletions(-)

diff --git a/clang/include/clang/Basic/BuiltinsX86.td 
b/clang/include/clang/Basic/BuiltinsX86.td
index 24db7a6fa334c..a8ccc4c9761c3 100644
--- a/clang/include/clang/Basic/BuiltinsX86.td
+++ b/clang/include/clang/Basic/BuiltinsX86.td
@@ -349,7 +349,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 062060e6afbbe..6f37c46654949 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 6170da63fbcaf..ae7a4b5cb0481 100644
--- a/clang/lib/AST/ByteCode/InterpBuiltin.cpp
+++ b/clang/lib/AST/ByteCode/InterpBuiltin.cpp
@@ -717,6 +717,52 @@ 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 BuiltinID) {
+  APSInt Data = popToAPSInt(S, Call->getArg(1));
+  APSInt CRC = popToAPSInt(S, Call->getArg(0));
+
+  uint64_t CRCVal = CRC.getZExtValue();
+  uint64_t DataVal = Data.getZExtValue();
+
+  // Determine the data width based on the builtin
+  unsigned DataBytes;
+  switch (BuiltinID) {
+  case clang::X86::BI__builtin_ia32_crc32qi:
+    DataBytes = 1;
+    break;
+  case clang::X86::BI__builtin_ia32_crc32hi:
+    DataBytes = 2;
+    break;
+  case clang::X86::BI__builtin_ia32_crc32si:
+    DataBytes = 4;
+    break;
+  case clang::X86::BI__builtin_ia32_crc32di:
+    DataBytes = 8;
+    break;
+  default:
+    llvm_unreachable("Unknown CRC32 builtin");
+  }
+
+  // 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) {
@@ -4303,6 +4349,12 @@ 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:
+  case clang::X86::BI__builtin_ia32_crc32hi:
+  case clang::X86::BI__builtin_ia32_crc32si:
+  case clang::X86::BI__builtin_ia32_crc32di:
+    return interp__builtin_ia32_crc32(S, OpPC, Frame, Call, BuiltinID);
+
   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 4a04743f7c03e..052270e283d3d 100644
--- a/clang/lib/AST/ExprConstant.cpp
+++ b/clang/lib/AST/ExprConstant.cpp
@@ -15896,6 +15896,45 @@ bool IntExprEvaluator::VisitBuiltinCallExpr(const 
CallExpr *E,
   default:
     return false;
 
+  case X86::BI__builtin_ia32_crc32qi:
+  case X86::BI__builtin_ia32_crc32hi:
+  case X86::BI__builtin_ia32_crc32si:
+  case X86::BI__builtin_ia32_crc32di: {
+    APSInt CRC, Data;
+    if (!EvaluateInteger(E->getArg(0), CRC, Info) ||
+        !EvaluateInteger(E->getArg(1), Data, Info))
+      return false;
+
+    // Get the input values
+    uint64_t CRCVal = CRC.getZExtValue();
+    uint64_t DataVal = Data.getZExtValue();
+
+    // Determine the data width based on the builtin
+    unsigned DataBytes;
+    switch (BuiltinOp) {
+    case X86::BI__builtin_ia32_crc32qi: DataBytes = 1; break;
+    case X86::BI__builtin_ia32_crc32hi: DataBytes = 2; break;
+    case X86::BI__builtin_ia32_crc32si: DataBytes = 4; break;
+    case X86::BI__builtin_ia32_crc32di: DataBytes = 8; break;
+    default: llvm_unreachable("Unknown CRC32 builtin");
+    }
+
+    // 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);
+  }
+
   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..926cf56967c04 100644
--- a/clang/lib/Headers/crc32intrin.h
+++ b/clang/lib/Headers/crc32intrin.h
@@ -10,8 +10,14 @@
 #ifndef __CRC32INTRIN_H
 #define __CRC32INTRIN_H
 
-#define __DEFAULT_FN_ATTRS                                                     
\
+/// We only declare crc32 as a constexpr if we are compiling C++ code
+#if defined(__cplusplus) && (__cplusplus >= 201103L)
+#define __DEFAULT_FN_ATTRS_CONSTEXPR                                           
\
+  __attribute__((__always_inline__, __nodebug__, __target__("crc32"))) 
constexpr
+#else
+#define __DEFAULT_FN_ATTRS_CONSTEXPR                                           
\
   __attribute__((__always_inline__, __nodebug__, __target__("crc32")))
+#endif
 
 /// Adds the unsigned integer operand to the CRC-32C checksum of the
 ///    unsigned char operand.
@@ -27,9 +33,8 @@
 ///    An unsigned 8-bit integer operand used to compute the CRC-32C checksum.
 /// \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)
-{
+static __inline__ unsigned int __DEFAULT_FN_ATTRS_CONSTEXPR
+_mm_crc32_u8(unsigned int __C, unsigned char __D) {
   return __builtin_ia32_crc32qi(__C, __D);
 }
 
@@ -47,9 +52,8 @@ _mm_crc32_u8(unsigned int __C, unsigned char __D)
 ///    An unsigned 16-bit integer operand used to compute the CRC-32C checksum.
 /// \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)
-{
+static __inline__ unsigned int __DEFAULT_FN_ATTRS_CONSTEXPR
+_mm_crc32_u16(unsigned int __C, unsigned short __D) {
   return __builtin_ia32_crc32hi(__C, __D);
 }
 
@@ -67,9 +71,8 @@ _mm_crc32_u16(unsigned int __C, unsigned short __D)
 ///    An unsigned 32-bit integer operand used to compute the CRC-32C checksum.
 /// \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)
-{
+static __inline__ unsigned int __DEFAULT_FN_ATTRS_CONSTEXPR
+_mm_crc32_u32(unsigned int __C, unsigned int __D) {
   return __builtin_ia32_crc32si(__C, __D);
 }
 
@@ -88,13 +91,12 @@ _mm_crc32_u32(unsigned int __C, unsigned int __D)
 ///    An unsigned 64-bit integer operand used to compute the CRC-32C checksum.
 /// \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)
-{
+static __inline__ unsigned long long __DEFAULT_FN_ATTRS_CONSTEXPR
+_mm_crc32_u64(unsigned long long __C, unsigned long long __D) {
   return __builtin_ia32_crc32di(__C, __D);
 }
 #endif /* __x86_64__ */
 
-#undef __DEFAULT_FN_ATTRS
+#undef __DEFAULT_FN_ATTRS_CONSTEXPR
 
 #endif /* __CRC32INTRIN_H */
diff --git a/clang/test/CodeGen/X86/sse42-builtins.c 
b/clang/test/CodeGen/X86/sse42-builtins.c
index 3a1e8fc793031..d6e40545d3e11 100644
--- a/clang/test/CodeGen/X86/sse42-builtins.c
+++ b/clang/test/CodeGen/X86/sse42-builtins.c
@@ -138,3 +138,13 @@ unsigned long long test_mm_crc32_u64(unsigned long long 
CRC, unsigned long long
   return _mm_crc32_u64(CRC, V);
 }
 #endif
+
+#ifdef __cplusplus
+TEST_CONSTEXPR(_mm_crc32_u8(0, 42) == 0x4b5fa6e6);
+TEST_CONSTEXPR(_mm_crc32_u8(0x12345678, 42) == 0xb00c9e72);
+TEST_CONSTEXPR(_mm_crc32_u16(0, 42) == 0xc5da1054);
+TEST_CONSTEXPR(_mm_crc32_u32(0, 42) == 0x9e0654ec);
+#ifdef __x86_64__
+TEST_CONSTEXPR(_mm_crc32_u64(0, 42) == 0xdd439b0d);
+#endif
+#endif

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to