From 6918490b22a389ddc197a011ee8164683713dafd Mon Sep 17 00:00:00 2001 From: Dominique Belhachemi Date: Sun, 19 Jul 2026 16:07:41 +0000 Subject: [PATCH 1/4] GH-50542: [C++] Fix ARROW_SIMD_LEVEL=NONE build by compiling the SSE4.2 kernels Building with ARROW_SIMD_LEVEL=NONE and ARROW_RUNTIME_SIMD_LEVEL=MAX failed with: bpacking.cc:39:49: error: 'unpack_sse4_2' is not a member of 'arrow::internal::bpacking' The SSE4.2 dispatch table entries are emitted when either the compile-time or the runtime macro is set, but the kernels themselves were only compiled when the compile-time macro was set. So I moved the byte_stream_split SSE4.2 instantiations into a dedicated translation unit, keeping the dispatcher's own unit at the baseline ISA. --- cpp/src/arrow/CMakeLists.txt | 15 +++++++ cpp/src/arrow/util/bpacking_simd_128.cc | 2 +- cpp/src/arrow/util/bpacking_simd_internal.h | 2 +- cpp/src/arrow/util/bpacking_test.cc | 8 +++- .../arrow/util/byte_stream_split_internal.h | 20 +++++++++ .../util/byte_stream_split_internal_sse4_2.cc | 43 +++++++++++++++++++ 6 files changed, 86 insertions(+), 4 deletions(-) create mode 100644 cpp/src/arrow/util/byte_stream_split_internal_sse4_2.cc diff --git a/cpp/src/arrow/CMakeLists.txt b/cpp/src/arrow/CMakeLists.txt index 8750598f6c3b..4365a92b702d 100644 --- a/cpp/src/arrow/CMakeLists.txt +++ b/cpp/src/arrow/CMakeLists.txt @@ -319,6 +319,13 @@ function(ADD_ARROW_BENCHMARK REL_TEST_NAME) ${ARG_UNPARSED_ARGUMENTS}) endfunction() +macro(append_runtime_sse4_2_src SRCS SRC) + if(ARROW_HAVE_RUNTIME_SSE4_2 AND ARROW_SIMD_LEVEL STREQUAL "NONE") + list(APPEND ${SRCS} ${SRC}) + set_source_files_properties(${SRC} PROPERTIES COMPILE_OPTIONS "${ARROW_SSE4_2_FLAG}") + endif() +endmacro() + macro(append_runtime_avx2_src SRCS SRC) if(ARROW_HAVE_RUNTIME_AVX2) list(APPEND ${SRCS} ${SRC}) @@ -585,9 +592,17 @@ set(ARROW_UTIL_SRCS append_runtime_avx2_src(ARROW_UTIL_SRCS util/byte_stream_split_internal_avx2.cc) +append_runtime_sse4_2_src(ARROW_UTIL_SRCS util/byte_stream_split_internal_sse4_2.cc) + append_runtime_avx2_src(ARROW_UTIL_SRCS util/bpacking_simd_256.cc) append_runtime_avx512_src(ARROW_UTIL_SRCS util/bpacking_simd_avx512.cc) +# also provides the NEON kernels on aarch64, so it stays in ARROW_UTIL_SRCS +if(ARROW_CPU_FLAG STREQUAL "x86" AND ARROW_HAVE_RUNTIME_SSE4_2) + set_source_files_properties(util/bpacking_simd_128.cc PROPERTIES COMPILE_OPTIONS + "${ARROW_SSE4_2_FLAG}") +endif() + append_runtime_sve128_src(ARROW_UTIL_SRCS util/bpacking_simd_128_alt.cc) append_runtime_sve256_src(ARROW_UTIL_SRCS util/bpacking_simd_256.cc) diff --git a/cpp/src/arrow/util/bpacking_simd_128.cc b/cpp/src/arrow/util/bpacking_simd_128.cc index 1bc756b2aa7d..9f35667d2adb 100644 --- a/cpp/src/arrow/util/bpacking_simd_128.cc +++ b/cpp/src/arrow/util/bpacking_simd_128.cc @@ -18,7 +18,7 @@ #if defined(ARROW_HAVE_NEON) # define UNPACK_PLATFORM unpack_neon # define KERNEL_PLATFORM KernelNeon -#elif defined(ARROW_HAVE_SSE4_2) +#elif defined(ARROW_HAVE_SSE4_2) || defined(ARROW_HAVE_RUNTIME_SSE4_2) # define UNPACK_PLATFORM unpack_sse4_2 # define KERNEL_PLATFORM KernelSse42 #endif diff --git a/cpp/src/arrow/util/bpacking_simd_internal.h b/cpp/src/arrow/util/bpacking_simd_internal.h index d5a81baaec09..fc778cfa1d53 100644 --- a/cpp/src/arrow/util/bpacking_simd_internal.h +++ b/cpp/src/arrow/util/bpacking_simd_internal.h @@ -26,7 +26,7 @@ namespace arrow::internal::bpacking { #if defined(ARROW_HAVE_NEON) # define UNPACK_ARCH128 unpack_neon -#elif defined(ARROW_HAVE_SSE4_2) +#elif defined(ARROW_HAVE_SSE4_2) || defined(ARROW_HAVE_RUNTIME_SSE4_2) # define UNPACK_ARCH128 unpack_sse4_2 #endif diff --git a/cpp/src/arrow/util/bpacking_test.cc b/cpp/src/arrow/util/bpacking_test.cc index d4d588228e7e..b6e8b44656ea 100644 --- a/cpp/src/arrow/util/bpacking_test.cc +++ b/cpp/src/arrow/util/bpacking_test.cc @@ -27,7 +27,8 @@ #include "arrow/util/bpacking_scalar_internal.h" #include "arrow/util/bpacking_simd_internal.h" -#if defined(ARROW_HAVE_RUNTIME_AVX2) || defined(ARROW_HAVE_RUNTIME_AVX512) || \ +#if defined(ARROW_HAVE_SSE4_2) || defined(ARROW_HAVE_RUNTIME_SSE4_2) || \ + defined(ARROW_HAVE_RUNTIME_AVX2) || defined(ARROW_HAVE_RUNTIME_AVX512) || \ defined(ARROW_HAVE_RUNTIME_SVE128) || defined(ARROW_HAVE_RUNTIME_SVE256) # include "arrow/util/cpu_info.h" #endif @@ -273,8 +274,11 @@ TYPED_TEST(TestUnpack, UnpackScalar) { this->TestAll(&bpacking::unpack_scalar); } -#if defined(ARROW_HAVE_SSE4_2) +#if defined(ARROW_HAVE_SSE4_2) || defined(ARROW_HAVE_RUNTIME_SSE4_2) TYPED_TEST(TestUnpack, UnpackSse4_2) { + if (!CpuInfo::GetInstance()->IsSupported(CpuInfo::SSE4_2)) { + GTEST_SKIP() << "Test requires SSE4.2"; + } this->TestAll(&bpacking::unpack_sse4_2); } #endif diff --git a/cpp/src/arrow/util/byte_stream_split_internal.h b/cpp/src/arrow/util/byte_stream_split_internal.h index 2e713dd42f2a..82d23ff7dfa8 100644 --- a/cpp/src/arrow/util/byte_stream_split_internal.h +++ b/cpp/src/arrow/util/byte_stream_split_internal.h @@ -286,6 +286,26 @@ void ByteStreamSplitEncodeSimd(const uint8_t* raw_values, int width, } } +# if defined(ARROW_HAVE_RUNTIME_SSE4_2) && !defined(ARROW_HAVE_SSE4_2) + +// instantiated in byte_stream_split_internal_sse4_2.cc + +extern template ARROW_TEMPLATE_EXPORT void ByteStreamSplitDecodeSimd( + const uint8_t*, int, int64_t, int64_t, uint8_t*); +extern template ARROW_TEMPLATE_EXPORT void ByteStreamSplitDecodeSimd( + const uint8_t*, int, int64_t, int64_t, uint8_t*); +extern template ARROW_TEMPLATE_EXPORT void ByteStreamSplitDecodeSimd( + const uint8_t*, int, int64_t, int64_t, uint8_t*); + +extern template ARROW_TEMPLATE_EXPORT void ByteStreamSplitEncodeSimd( + const uint8_t*, int, const int64_t, uint8_t*); +extern template ARROW_TEMPLATE_EXPORT void ByteStreamSplitEncodeSimd( + const uint8_t*, int, const int64_t, uint8_t*); +extern template ARROW_TEMPLATE_EXPORT void ByteStreamSplitEncodeSimd( + const uint8_t*, int, const int64_t, uint8_t*); + +# endif + # if defined(ARROW_HAVE_RUNTIME_AVX2) // The extern template declaration are used internally and need export diff --git a/cpp/src/arrow/util/byte_stream_split_internal_sse4_2.cc b/cpp/src/arrow/util/byte_stream_split_internal_sse4_2.cc new file mode 100644 index 000000000000..e96a24d67ffe --- /dev/null +++ b/cpp/src/arrow/util/byte_stream_split_internal_sse4_2.cc @@ -0,0 +1,43 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +#include "arrow/util/byte_stream_split_internal.h" +#include "arrow/util/math_internal.h" +#include "arrow/util/simd.h" + +#include +#include + +#include + +namespace arrow::util::internal { + +template void ByteStreamSplitDecodeSimd(const uint8_t*, int, int64_t, + int64_t, uint8_t*); +template void ByteStreamSplitDecodeSimd(const uint8_t*, int, int64_t, + int64_t, uint8_t*); +template void ByteStreamSplitDecodeSimd(const uint8_t*, int, int64_t, + int64_t, uint8_t*); + +template void ByteStreamSplitEncodeSimd(const uint8_t*, int, + const int64_t, uint8_t*); +template void ByteStreamSplitEncodeSimd(const uint8_t*, int, + const int64_t, uint8_t*); +template void ByteStreamSplitEncodeSimd(const uint8_t*, int, + const int64_t, uint8_t*); + +} // namespace arrow::util::internal From 477114533be8bc879df940b5216a3bdc4bcc6635 Mon Sep 17 00:00:00 2001 From: Dominique Belhachemi Date: Tue, 21 Jul 2026 05:07:40 +0000 Subject: [PATCH 2/4] GH-50542: [CI][C++] Add a Debian ARROW_SIMD_LEVEL=NONE job Add a cpp_extra.yml matrix entry building the debian-cpp image with ARROW_SIMD_LEVEL=NONE so the x86-64-v1 baseline configuration is covered. --- .github/workflows/cpp_extra.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/cpp_extra.yml b/.github/workflows/cpp_extra.yml index 8b072ed90afa..c4fd738f4cb4 100644 --- a/.github/workflows/cpp_extra.yml +++ b/.github/workflows/cpp_extra.yml @@ -122,6 +122,13 @@ jobs: -e CMAKE_CXX_STANDARD=23 runs-on: ubuntu-latest title: AMD64 Debian C++23 + - envs: + - DEBIAN=13 + image: debian-cpp + run-options: >- + -e ARROW_SIMD_LEVEL=NONE + runs-on: ubuntu-latest + title: AMD64 Debian SIMD Level NONE env: ARCHERY_DEBUG: 1 ARROW_ENABLE_TIMING_TESTS: OFF From 7b0cd789596a4ba355e8b4ba9d271632a78f7a26 Mon Sep 17 00:00:00 2001 From: Dominique Belhachemi Date: Tue, 21 Jul 2026 14:24:02 +0000 Subject: [PATCH 3/4] Add CXXFLAGS=-march=x86-64 to the ARROW_SIMD_LEVEL=NONE matrix entry --- .github/workflows/cpp_extra.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/cpp_extra.yml b/.github/workflows/cpp_extra.yml index c4fd738f4cb4..3b5f41f1b7eb 100644 --- a/.github/workflows/cpp_extra.yml +++ b/.github/workflows/cpp_extra.yml @@ -127,6 +127,7 @@ jobs: image: debian-cpp run-options: >- -e ARROW_SIMD_LEVEL=NONE + -e CXXFLAGS=-march=x86-64 runs-on: ubuntu-latest title: AMD64 Debian SIMD Level NONE env: From e4b76e39d01f0c557e4cf4c7528f15d686992827 Mon Sep 17 00:00:00 2001 From: Dominique Belhachemi Date: Tue, 21 Jul 2026 14:34:04 +0000 Subject: [PATCH 4/4] Move the extern template export comment above the SSE4.2 block --- cpp/src/arrow/util/byte_stream_split_internal.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/src/arrow/util/byte_stream_split_internal.h b/cpp/src/arrow/util/byte_stream_split_internal.h index 82d23ff7dfa8..79543e1cf88e 100644 --- a/cpp/src/arrow/util/byte_stream_split_internal.h +++ b/cpp/src/arrow/util/byte_stream_split_internal.h @@ -286,6 +286,9 @@ void ByteStreamSplitEncodeSimd(const uint8_t* raw_values, int width, } } +// The extern template declaration are used internally and need export +// to be used in tests and benchmarks. + # if defined(ARROW_HAVE_RUNTIME_SSE4_2) && !defined(ARROW_HAVE_SSE4_2) // instantiated in byte_stream_split_internal_sse4_2.cc @@ -308,9 +311,6 @@ extern template ARROW_TEMPLATE_EXPORT void ByteStreamSplitEncodeSimd( const uint8_t*, int, int64_t, int64_t, uint8_t*); extern template ARROW_TEMPLATE_EXPORT void ByteStreamSplitDecodeSimd(