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

Reply via email to