Constexpr X86 Vector Element Operations in Clang

This post documents LLVM PR #161302, where I enabled constexpr evaluation for the x86 element extract/insert builtins that back Intel's MMX, SSE, and AVX2 intrinsics. The work continues the previous patch and completes the basic SIMD element operations needed for compile-time table generation.

Goals

  • Bring parity between runtime and constexpr behavior for __builtin_ia32_vec_ext_* and __builtin_ia32_vec_set_*.
  • Preserve architectural quirks such as index masking (index & (NumElts - 1)) so constexpr execution matches hardware results.
  • Keep the AST evaluator and bytecode interpreter in sync, preventing divergence between the two constant-evaluation engines.

Design and Implementation

The targeted builtins span 64-bit MMX vectors through 256-bit AVX2 vectors, covering signed/unsigned integers and floats. Each builtin follows the same pattern:

  1. Normalize operands (vector value, element value, immediate index).
  2. Mask the index to the vector width.
  3. Either read or write the lane and return a new APValue.

Clang's dual evaluator required two sets of changes:

  • ExprConstant.cpp: teach VectorExprEvaluator to interpret the insert/extract builtins directly from the AST using APValue helpers.
  • InterpBuiltin.cpp: add bytecode handlers that operate on the constexpr VM's register model, with explicit type switching for integer and floating-point elements.

While prototyping I attempted to share logic via the TYPE_SWITCH macro, but the macro expands to pointer types that lack toAPSInt(). The final implementation keeps the float case separate and uses INT_TYPE_SWITCH_NO_BOOL for the integer matrix. I filed issue #161685 to track a cleaner abstraction.

Testing

Support landed alongside updates to the existing x86 builtin tests:

  • clang/test/CodeGen/X86/mmx-builtins.c
  • clang/test/CodeGen/X86/sse{2,41}-builtins.c
  • clang/test/CodeGen/X86/avx2-builtins.c

Each file now exercises the relevant extract/set intrinsics under TEST_CONSTEXPR, covering lane masking, type conversions, and boundary conditions. Example:

TEST_CONSTEXPR(_mm_extract_epi16(vec16i, 5) == vec16i[5 & 0x3]);
TEST_CONSTEXPR(_mm256_insert_epi32(vec8i, value, 18) ==
               replace_lane(vec8i, value, 18 & 0x7));

The tests ensure both evaluators agree and that regressions are caught by the standard Clang test suite.

Outcome

The patch resolves issue #159753 and unlocks constexpr usage for the remaining x86 element operations. Together with PR #158778, users can now construct and deconstruct SIMD data entirely at compile time, making it easier to build lookup tables and perform metaprogramming in header-only libraries.