From 74efff7a40ba205a824128acdb1b6848de3f03b4 Mon Sep 17 00:00:00 2001 From: changbao zhan Date: Tue, 15 Sep 2026 11:12:33 +0800 Subject: [PATCH] GH-51334: [C++] Add RISC-V Vector (RVV) support and dynamic dispatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds support for the RISC‑V Vector (RVV) extension in the C++ runtime SIMD dispatch machinery, analogous to the existing NEON/SVE support for AArch64, and paves the way for upcoming RVV‑related optimizations. --- .gitignore | 1 + cpp/cmake_modules/DefineOptions.cmake | 2 ++ cpp/cmake_modules/SetupCxxFlags.cmake | 21 +++++++++++++++++++++ cpp/src/arrow/util/cpu_info.cc | 19 +++++++++++++++++++ cpp/src/arrow/util/cpu_info.h | 6 ++++++ cpp/src/arrow/util/dispatch_internal.h | 13 +++++++++++++ cpp/src/arrow/util/io_util_test.cc | 9 +++++++++ cpp/src/arrow/util/simd.h | 4 ++++ 8 files changed, 75 insertions(+) diff --git a/.gitignore b/.gitignore index 83458ab2057b..14ddc1ff1e71 100644 --- a/.gitignore +++ b/.gitignore @@ -59,6 +59,7 @@ python/doc/ .vscode .idea/ +.icodemate/ .pytest_cache/ pkgs docker_cache diff --git a/cpp/cmake_modules/DefineOptions.cmake b/cpp/cmake_modules/DefineOptions.cmake index 1d12edd0613e..6582f64be3f4 100644 --- a/cpp/cmake_modules/DefineOptions.cmake +++ b/cpp/cmake_modules/DefineOptions.cmake @@ -171,6 +171,7 @@ takes precedence over ccache if a storage backend is configured" ON) "SVE128" # fixed size SVE "SVE256" # " "SVE512" # " + "RVV" "DEFAULT") define_option_string(ARROW_RUNTIME_SIMD_LEVEL @@ -183,6 +184,7 @@ takes precedence over ccache if a storage backend is configured" ON) "SVE128" # fixed size SVE "SVE256" # " "SVE512" # " + "RVV" "MAX") define_option(ARROW_ALTIVEC "Build with Altivec if compiler has support" ON) diff --git a/cpp/cmake_modules/SetupCxxFlags.cmake b/cpp/cmake_modules/SetupCxxFlags.cmake index f6590ddce687..2bc6a9fd0063 100644 --- a/cpp/cmake_modules/SetupCxxFlags.cmake +++ b/cpp/cmake_modules/SetupCxxFlags.cmake @@ -162,6 +162,16 @@ elseif(ARROW_CPU_FLAG STREQUAL "aarch64") if(ARROW_SIMD_LEVEL STREQUAL "DEFAULT") set(ARROW_SIMD_LEVEL "NEON") endif() +elseif(ARROW_CPU_FLAG STREQUAL "riscv64") + set(ARROW_RVV_MARCH "rv64gcv") + check_cxx_compiler_flag("-march=${ARROW_RVV_MARCH}" CXX_SUPPORTS_RVV) + if(CXX_SUPPORTS_RVV AND ARROW_RUNTIME_SIMD_LEVEL MATCHES "^(RVV|MAX)$") + set(ARROW_HAVE_RUNTIME_RVV ON) + add_definitions(-DARROW_HAVE_RUNTIME_RVV) + endif() + if(ARROW_SIMD_LEVEL STREQUAL "DEFAULT") + set(ARROW_SIMD_LEVEL "NONE") + endif() endif() # Support C11 @@ -552,6 +562,17 @@ if(ARROW_CPU_FLAG STREQUAL "aarch64") message(WARNING "ARROW_SIMD_LEVEL=${ARROW_SIMD_LEVEL} not supported by Arm.") endif() endif() +if(ARROW_CPU_FLAG STREQUAL "riscv64") + if(ARROW_SIMD_LEVEL STREQUAL "RVV") + if(NOT CXX_SUPPORTS_RVV) + message(FATAL_ERROR "RVV required but compiler doesn't support it.") + endif() + set(CXX_COMMON_FLAGS "${CXX_COMMON_FLAGS} -march=${ARROW_RVV_MARCH}") + add_definitions(-DARROW_HAVE_RVV) + elseif(NOT ARROW_SIMD_LEVEL STREQUAL "NONE") + message(WARNING "ARROW_SIMD_LEVEL=${ARROW_SIMD_LEVEL} not supported by riscv64.") + endif() +endif() if(NOT WIN32 AND NOT APPLE) if(ARROW_USE_MOLD) diff --git a/cpp/src/arrow/util/cpu_info.cc b/cpp/src/arrow/util/cpu_info.cc index 2f7cd2408df6..19573e351356 100644 --- a/cpp/src/arrow/util/cpu_info.cc +++ b/cpp/src/arrow/util/cpu_info.cc @@ -36,6 +36,7 @@ #ifdef __linux__ # include +# include #endif #ifdef __APPLE__ @@ -98,6 +99,24 @@ void OsRetrieveCpuInfo(int64_t* hardware_flags, CpuInfo::Vendor* vendor) { *hardware_flags |= (sve && sve_size == 32) ? CpuInfo::SVE256 : 0; *hardware_flags |= (sve && sve_size == 64) ? CpuInfo::SVE512 : 0; +#if defined(__riscv) && __riscv_xlen == 64 +# ifndef COMPAT_HWCAP_ISA_V +# define COMPAT_HWCAP_ISA_V (1UL << ('V' - 'A')) +# endif + bool rvv = false; +#ifdef __linux__ + rvv = (getauxval(AT_HWCAP) & COMPAT_HWCAP_ISA_V) != 0; +#endif + *hardware_flags |= rvv ? CpuInfo::RVV : 0; + if (rvv) { + unsigned long vlenb = 0; + __asm__ volatile("csrr %0, vlenb" : "=r"(vlenb)); + *hardware_flags |= (vlenb == 16) ? CpuInfo::RVV128 : 0; + *hardware_flags |= (vlenb == 32) ? CpuInfo::RVV256 : 0; + *hardware_flags |= (vlenb == 64) ? CpuInfo::RVV512 : 0; + } +#endif // __riscv && __riscv_xlen == 64 + // x86 only switch (cpu.known_manufacturer()) { case (xsimd::x86_manufacturer::intel): diff --git a/cpp/src/arrow/util/cpu_info.h b/cpp/src/arrow/util/cpu_info.h index 7e4ca4c7f14d..6f451787688b 100644 --- a/cpp/src/arrow/util/cpu_info.h +++ b/cpp/src/arrow/util/cpu_info.h @@ -57,6 +57,12 @@ class ARROW_EXPORT CpuInfo { static constexpr int64_t SVE256 = (1LL << 34); static constexpr int64_t SVE512 = (1LL << 35); + /// RISC-V features + static constexpr int64_t RVV = (1LL << 37); + static constexpr int64_t RVV128 = (1LL << 38); + static constexpr int64_t RVV256 = (1LL << 39); + static constexpr int64_t RVV512 = (1LL << 40); + /// Cache enums for L1 (data), L2 and L3 enum class CacheLevel { L1 = 0, L2, L3, Last = L3 }; diff --git a/cpp/src/arrow/util/dispatch_internal.h b/cpp/src/arrow/util/dispatch_internal.h index 36e5152ee650..20cd828a0489 100644 --- a/cpp/src/arrow/util/dispatch_internal.h +++ b/cpp/src/arrow/util/dispatch_internal.h @@ -38,6 +38,7 @@ enum class DispatchLevel : int { SVE128, SVE256, SVE512, + RVV, MAX }; @@ -221,6 +222,16 @@ constexpr DynamicDispatchTarget BestDispatchTarget( # define ARROW_DISPATCH_TARGET_SVE512(func) #endif +#if defined(ARROW_HAVE_RVV) || defined(ARROW_HAVE_RUNTIME_RVV) +# define ARROW_DISPATCH_TARGET_RVV(func) \ + ::arrow::internal::DynamicDispatchTarget{ \ + ::arrow::internal::DispatchLevel::RVV, \ + (func), \ + }, +#else +# define ARROW_DISPATCH_TARGET_RVV(func) +#endif + /// A concept to specify how dynamic dispatch should be handled. /// /// A requirement is that the list of available targets must be compile time @@ -352,6 +363,8 @@ class DynamicDispatch { return cpu_info->IsSupported(CpuInfo::SVE256); case DispatchLevel::SVE512: return cpu_info->IsSupported(CpuInfo::SVE512); + case DispatchLevel::RVV: + return cpu_info->IsSupported(CpuInfo::RVV); default: return false; } diff --git a/cpp/src/arrow/util/io_util_test.cc b/cpp/src/arrow/util/io_util_test.cc index 44188b3f2ee9..d1feb63198f9 100644 --- a/cpp/src/arrow/util/io_util_test.cc +++ b/cpp/src/arrow/util/io_util_test.cc @@ -1109,6 +1109,15 @@ TEST(CpuInfo, Basic) { ASSERT_EQ(ci->hardware_flags(), 0); ci_rw->EnableFeature(original_hardware_flags, true); ASSERT_EQ(ci->hardware_flags(), original_hardware_flags); + +#if defined(__riscv) && __riscv_xlen == 64 + if (ci->IsDetected(CpuInfo::RVV)) { + EXPECT_NE(ci->hardware_flags() & CpuInfo::RVV, 0); + EXPECT_NE(ci->hardware_flags() & + (CpuInfo::RVV128 | CpuInfo::RVV256 | CpuInfo::RVV512), + 0); + } +#endif } TEST(Memory, TotalMemory) { diff --git a/cpp/src/arrow/util/simd.h b/cpp/src/arrow/util/simd.h index cc1a7d6cc807..9a3e53a53a71 100644 --- a/cpp/src/arrow/util/simd.h +++ b/cpp/src/arrow/util/simd.h @@ -42,6 +42,10 @@ # include # endif +# if defined(ARROW_HAVE_RVV) || defined(ARROW_HAVE_RUNTIME_RVV) +# include +# endif + // GH-44098: Workaround for missing _mm256_set_m128i in older versions of GCC. # if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8 # define _mm256_set_m128i(hi, lo) \