Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ python/doc/

.vscode
.idea/
.icodemate/
.pytest_cache/
pkgs
docker_cache
Expand Down
7 changes: 7 additions & 0 deletions cpp/cmake_modules/DefineOptions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ takes precedence over ccache if a storage backend is configured" ON)
"SVE128" # fixed size SVE
"SVE256" # "
"SVE512" # "
"RVV" # size agnostic RVV
"RVV128" # fixed size RVV
"RVV256" # "
"RVV512" # "
"DEFAULT")

define_option_string(ARROW_RUNTIME_SIMD_LEVEL
Expand All @@ -183,6 +187,9 @@ takes precedence over ccache if a storage backend is configured" ON)
"SVE128" # fixed size SVE
"SVE256" # "
"SVE512" # "
"RVV128" # fixed size RVV
"RVV256" # "
"RVV512" # "
"MAX")

define_option(ARROW_ALTIVEC "Build with Altivec if compiler has support" ON)
Expand Down
44 changes: 44 additions & 0 deletions cpp/cmake_modules/SetupCxxFlags.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,24 @@ 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 "^(RVV128|RVV256|RVV512|MAX)$")
set(ARROW_HAVE_RUNTIME_RVV128 ON)
add_definitions(-DARROW_HAVE_RUNTIME_RVV128)
endif()
if(CXX_SUPPORTS_RVV AND ARROW_RUNTIME_SIMD_LEVEL MATCHES "^(RVV256|RVV512|MAX)$")
set(ARROW_HAVE_RUNTIME_RVV256 ON)
add_definitions(-DARROW_HAVE_RUNTIME_RVV256)
endif()
if(CXX_SUPPORTS_RVV AND ARROW_RUNTIME_SIMD_LEVEL MATCHES "^(RVV512|MAX)$")
set(ARROW_HAVE_RUNTIME_RVV512 ON)
add_definitions(-DARROW_HAVE_RUNTIME_RVV512)
endif()
if(ARROW_SIMD_LEVEL STREQUAL "DEFAULT")
set(ARROW_SIMD_LEVEL "NONE")
endif()
endif()

# Support C11
Expand Down Expand Up @@ -548,6 +566,32 @@ if(ARROW_CPU_FLAG STREQUAL "aarch64")
endif()
endif()

if(ARROW_CPU_FLAG STREQUAL "riscv64")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: add line break as above.

Suggested change
if(ARROW_CPU_FLAG STREQUAL "riscv64")
if(ARROW_CPU_FLAG STREQUAL "riscv64")

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: add line break as above.

Added the missing blank line before if(ARROW_CPU_FLAG STREQUAL "riscv64"), as in the SVE/Arm block above.

if(ARROW_SIMD_LEVEL MATCHES "RVV")
if(NOT CXX_SUPPORTS_RVV)
message(FATAL_ERROR "RVV required but compiler doesn't support it.")
endif()
string(REGEX MATCH "[0-9]+" RVV_VECTOR_BITS ${ARROW_SIMD_LEVEL})
if(RVV_VECTOR_BITS)
set(ARROW_RVV_FIXED_FLAGS
"-march=${ARROW_RVV_MARCH}_zvl${RVV_VECTOR_BITS}b" "-mrvv-vector-bits=zvl")
check_cxx_compiler_flag("${ARROW_RVV_FIXED_FLAGS}" CXX_SUPPORTS_RVV_FIXED)
if(NOT CXX_SUPPORTS_RVV_FIXED)
message(FATAL_ERROR "Fixed-width RVV (zvl${RVV_VECTOR_BITS}b) required but compiler doesn't support it.")
endif()
set(CXX_COMMON_FLAGS "${CXX_COMMON_FLAGS} ${ARROW_RVV_FIXED_FLAGS}")
set(ARROW_HAVE_RVV${RVV_VECTOR_BITS} ON)
add_definitions(-DARROW_HAVE_RVV${RVV_VECTOR_BITS})

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we also enable a compiler option for the given vector size, as in SVE above?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GCC documentation (https://gcc.gnu.org/onlinedocs/gcc/RISC-V-Options.html) shows -mrvv-vector-bits only accepts scalable/zvl (and pairs with a zvlXXXb march extension), unlike SVE's direct -msve-vector-bits=N.
Fixing VLEN at compile time is also contrary to RVV's design: RVV is a variable-length architecture and the vector length is selected at runtime via vsetvl (xsimd's RVV backend is VLA as well). So the ARROW_HAVE_RVV${bits} macro is a runtime dispatch tag rather than a compile-time vector width.

@pitrou pitrou Oct 29, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it looks like xsimd itself uses something like -march=rv64gcv_zvl${vector_bits} -mrvv-vector-bits=zvl.

I'm not sure how another approach would work. We need vectors to have a well-known length at compile-time.

@zhanchangbao-sanechips zhanchangbao-sanechips Oct 31, 2026 •

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it looks like xsimd itself uses something like -march=rv64gcv_zvl${vector_bits} -mrvv-vector-bits=zvl.

I'm not sure how another approach would work. We need vectors to have a well-known length at compile-time.

You're right — for a static fixed-width build we should fix the vector length at compile time, matching xsimd. Updated the RVV${bits} branch to add -march=${ARROW_RVV_MARCH}_zvl${bits}b -mrvv-vector-bits=zvl (guarded by check_cxx_compiler_flag since GCC ≤ 13 doesn't support -mrvv-vector-bits), while the sizeless RVV build keeps -march=rv64gcv.
Additionally, to align with SVE's ARROW_USER_SIMD_LEVEL support, I added a RISC-V branch in cpu_info.cc so that ARROW_USER_SIMD_LEVEL=RVV/RVV128/RVV256/RVV512 takes effect.

else()
set(CXX_COMMON_FLAGS "${CXX_COMMON_FLAGS} -march=${ARROW_RVV_MARCH}")
set(ARROW_HAVE_RVV_SIZELESS ON)
add_definitions(-DARROW_HAVE_RVV_SIZELESS)
endif()
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)
find_program(LD_MOLD ld.mold)
Expand Down
51 changes: 51 additions & 0 deletions cpp/src/arrow/util/cpu_info.cc
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@ 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;

// RVV and length
const bool rvv = cpu.rvv();
const auto rvv_size = cpu.rvv_size_bytes();
*hardware_flags |= rvv ? CpuInfo::RVV : 0;
*hardware_flags |= (rvv && rvv_size == 16) ? CpuInfo::RVV128 : 0;
*hardware_flags |= (rvv && rvv_size == 32) ? CpuInfo::RVV256 : 0;
*hardware_flags |= (rvv && rvv_size == 64) ? CpuInfo::RVV512 : 0;

// x86 only
switch (cpu.known_manufacturer()) {
case (xsimd::x86_manufacturer::intel):
Expand Down Expand Up @@ -363,6 +371,49 @@ void ArchVerifyCpuRequirements(const CpuInfo* ci) {
}
}

#elif XSIMD_TARGET_RISCV
//------------------------------ RISCV64 ------------------------------//
bool ArchParseUserSimdLevel(const std::string& simd_level, int64_t* hardware_flags) {
enum {
USER_SIMD_NONE,
USER_SIMD_RVV,
USER_SIMD_RVV128,
USER_SIMD_RVV256,
USER_SIMD_RVV512,
USER_SIMD_MAX,
};

int level = USER_SIMD_MAX;
if (simd_level == "RVV") {
level = USER_SIMD_RVV;
} else if (simd_level == "RVV128") {
level = USER_SIMD_RVV128;
} else if (simd_level == "RVV256") {
level = USER_SIMD_RVV256;
} else if (simd_level == "RVV512") {
level = USER_SIMD_RVV512;
} else if (simd_level == "NONE") {
level = USER_SIMD_NONE;
} else {
return false;
}

if (level < USER_SIMD_RVV512) *hardware_flags &= ~CpuInfo::RVV512;
if (level < USER_SIMD_RVV256) *hardware_flags &= ~CpuInfo::RVV256;
if (level < USER_SIMD_RVV128) *hardware_flags &= ~CpuInfo::RVV128;
if (level < USER_SIMD_RVV) *hardware_flags &= ~CpuInfo::RVV;
return true;
}

void ArchVerifyCpuRequirements(const CpuInfo* ci) {
# if defined(ARROW_HAVE_RVV128) || defined(ARROW_HAVE_RVV256) || \
defined(ARROW_HAVE_RVV512) || defined(ARROW_HAVE_RVV_SIZELESS)
if (!ci->IsDetected(CpuInfo::RVV)) {
DCHECK(false) << "CPU does not support the RISC-V Vector instruction set";
}
# endif
}

#else
//------------------------------ PPC, ... ------------------------------//
bool ArchParseUserSimdLevel(const std::string& simd_level, int64_t* hardware_flags) {
Expand Down
6 changes: 6 additions & 0 deletions cpp/src/arrow/util/cpu_info.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 };

Expand Down
39 changes: 39 additions & 0 deletions cpp/src/arrow/util/dispatch_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ enum class DispatchLevel : int {
SVE128,
SVE256,
SVE512,
RVV128,
RVV256,
RVV512,
MAX
};

Expand Down Expand Up @@ -221,6 +224,36 @@ constexpr DynamicDispatchTarget<Func> BestDispatchTarget(
# define ARROW_DISPATCH_TARGET_SVE512(func)
#endif

#if defined(ARROW_HAVE_RVV128) || defined(ARROW_HAVE_RUNTIME_RVV128)
# define ARROW_DISPATCH_TARGET_RVV128(func) \
::arrow::internal::DynamicDispatchTarget{ \
::arrow::internal::DispatchLevel::RVV128, \
(func), \
},
#else
# define ARROW_DISPATCH_TARGET_RVV128(func)
#endif

#if defined(ARROW_HAVE_RVV256) || defined(ARROW_HAVE_RUNTIME_RVV256)
# define ARROW_DISPATCH_TARGET_RVV256(func) \
::arrow::internal::DynamicDispatchTarget{ \
::arrow::internal::DispatchLevel::RVV256, \
(func), \
},
#else
# define ARROW_DISPATCH_TARGET_RVV256(func)
#endif

#if defined(ARROW_HAVE_RVV512) || defined(ARROW_HAVE_RUNTIME_RVV512)
# define ARROW_DISPATCH_TARGET_RVV512(func) \
::arrow::internal::DynamicDispatchTarget{ \
::arrow::internal::DispatchLevel::RVV512, \
(func), \
},
#else
# define ARROW_DISPATCH_TARGET_RVV512(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
Expand Down Expand Up @@ -352,6 +385,12 @@ class DynamicDispatch {
return cpu_info->IsSupported(CpuInfo::SVE256);
case DispatchLevel::SVE512:
return cpu_info->IsSupported(CpuInfo::SVE512);
case DispatchLevel::RVV128:
return cpu_info->IsSupported(CpuInfo::RVV128);
case DispatchLevel::RVV256:
return cpu_info->IsSupported(CpuInfo::RVV256);
case DispatchLevel::RVV512:
return cpu_info->IsSupported(CpuInfo::RVV512);
default:
return false;
}
Expand Down
8 changes: 8 additions & 0 deletions cpp/src/arrow/util/io_util_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1109,6 +1109,14 @@ 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) {
Expand Down
Loading