From e976a8b8a6051321c7c1d6153bb7f83460620de8 Mon Sep 17 00:00:00 2001 From: "riseproject-dev[bot]" <330740410+riseproject-dev[bot]@users.noreply.github.com> Date: Thu, 8 Oct 2026 08:33:29 +0000 Subject: [PATCH 1/4] lancedb: Add version 0.40.0 Signed-off-by: riseproject-dev[bot] <330740410+riseproject-dev[bot]@users.noreply.github.com> --- docs/packages/lancedb.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/packages/lancedb.yaml b/docs/packages/lancedb.yaml index aada5fc930c..79aef63062f 100644 --- a/docs/packages/lancedb.yaml +++ b/docs/packages/lancedb.yaml @@ -23,3 +23,4 @@ versions: - filename: lancedb-0.39.0-cp310-abi3-manylinux_2_39_riscv64.whl sha256: 758156284cb0b1d87d14fddc9066c7c7fba2c16f847a90d5d70dd7ee694a0283 requires-python: '>=3.10' +- version: 0.40.0 From b9cface03c60d41ced7220f1f18c8af2a40c4d92 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Fri, 9 Oct 2026 16:33:00 +0000 Subject: [PATCH 2/4] lancedb: carry forward riscv64 patches to 0.40.0 Nightly version bump omitted the patches/lancedb/ directory for 0.40.0, so the build's unconditional `git apply .../*.patch` step failed with "No such file or directory" before any build/test ran. Copied forward the same patch set from 0.39.0 (unchanged since then). --- ...lance-core-lance-linalg-to-a-riscv64.patch | 64 + ...add-a-portable-riscv64-simd-fallback.patch | 1292 +++++++++++++++++ 2 files changed, 1356 insertions(+) create mode 100644 patches/lancedb/0.40.0/0001-cargo-redirect-lance-core-lance-linalg-to-a-riscv64.patch create mode 100644 patches/lancedb/0.40.0/0002-lance-linalg-add-a-portable-riscv64-simd-fallback.patch diff --git a/patches/lancedb/0.40.0/0001-cargo-redirect-lance-core-lance-linalg-to-a-riscv64.patch b/patches/lancedb/0.40.0/0001-cargo-redirect-lance-core-lance-linalg-to-a-riscv64.patch new file mode 100644 index 00000000000..4be13c59701 --- /dev/null +++ b/patches/lancedb/0.40.0/0001-cargo-redirect-lance-core-lance-linalg-to-a-riscv64.patch @@ -0,0 +1,64 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Thu, 03 Sep 2026 00:00:00 +0000 +Subject: [PATCH] cargo: redirect lance-core/lance-linalg to a riscv64-patched + checkout + +lance-core's SIMD_SUPPORT static (rust/lance-core/src/utils/cpu.rs) and +lance-linalg's f32x8/f32x16/i32x8/f64x4/f64x8 SIMD types +(rust/lance-linalg/src/simd/{f32,i32,f64}.rs) are compiled for +x86_64/aarch64/loongarch64 only, with no fallback arm for any other +architecture: the SIMD_SUPPORT closure has no trailing expression on +riscv64 ("expected SimdSupport, found ()"), and the SIMD type structs are +defined only under those three `#[cfg(target_arch = ...)]` attributes +while every method implementing them is unconditional, so riscv64 hits +"cannot find type `f32x8` in this scope" as soon as lance-linalg is +built. + +Both crates are pulled in from crates.io at a pinned `=10.0.0`, so the +fix cannot be applied in place. Redirect them to a checkout of the same +v10.0.0 tag carrying a portable scalar fallback for both, see +0002-lance-linalg-add-a-portable-riscv64-simd-fallback.patch. `exclude` +keeps that checkout's own [workspace] from being silently absorbed into +this one, which would drop its `[workspace.package]` inheritance +(version, edition, ...) for lance-core and lance-linalg. + +Only the source line of the lance-core/lance-linalg entries changes in +Cargo.lock, so every other dependency stays pinned exactly as upstream +released it. + +Upstream-Status: Inappropriate [redirects two pinned dependencies to a local checkout; the fix itself belongs in lancedb/lance] + +Signed-off-by: Ludovic Henry +--- + Cargo.toml | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/Cargo.toml b/Cargo.toml +index a47e59e..ab6cf5c 100644 +--- a/Cargo.toml ++++ b/Cargo.toml +@@ -1,5 +1,10 @@ + [workspace] + members = ["rust/lancedb", "nodejs", "python"] ++# ./lance (see [patch.crates-io] below) carries its own [workspace]; excluding ++# it keeps that workspace independent instead of being silently absorbed into ++# this one, which would drop its `[workspace.package]` inheritance (version, ++# edition, ...) for lance-core and lance-linalg. ++exclude = ["lance"] + resolver = "2" + + [workspace.package] +@@ -80,3 +85,10 @@ debug = false + debug-assertions = false + strip = "debuginfo" + incremental = false ++ ++# lance-core's SIMD_SUPPORT and lance-linalg's f32x8/f32x16/i32x8/f64x4/f64x8 ++# SIMD types compile only for x86_64/aarch64/loongarch64 upstream; ./lance is ++# the same v10.0.0 tag with a riscv64 scalar fallback added to both. ++[patch.crates-io] ++lance-core = { path = "lance/rust/lance-core" } ++lance-linalg = { path = "lance/rust/lance-linalg" } +-- +2.43.0 diff --git a/patches/lancedb/0.40.0/0002-lance-linalg-add-a-portable-riscv64-simd-fallback.patch b/patches/lancedb/0.40.0/0002-lance-linalg-add-a-portable-riscv64-simd-fallback.patch new file mode 100644 index 00000000000..0708da295a5 --- /dev/null +++ b/patches/lancedb/0.40.0/0002-lance-linalg-add-a-portable-riscv64-simd-fallback.patch @@ -0,0 +1,1292 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Tue, 8 Sep 2026 00:00:00 +0000 +Subject: [PATCH] lance-linalg: add a portable riscv64 SIMD fallback + +lance-core's SIMD_SUPPORT static already carries a portable riscv64 +fallback (rust/lance-core/src/utils/cpu.rs), but lance-linalg's +f32x8/f32x16/i32x8/f64x4/f64x8 SIMD types still hardcode x86_64 +(AVX/AVX2/AVX-512), aarch64 (NEON) and loongarch64 (LSX/LASX) as the +only supported architectures, with no portable fallback -- unlike +u8x16/u8x32, which already carry one (rust/lance-linalg/src/simd/u8.rs). +This adds the same shape of fallback for the missing types: an +array-backed struct plus a scalar implementation of every method the +SIMD/FloatSimd traits require, gated +`#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64")))]` +beside the existing per-architecture arms, which are otherwise +untouched. Add/Sub/Mul on i32x8 use wrapping arithmetic to match the +silent-wraparound semantics of the SIMD instructions they replace. + +Verified with `cargo check --target riscv64gc-unknown-linux-gnu` (and, +as a regression check, `--target aarch64-apple-darwin` and +`--target x86_64-unknown-linux-gnu`) against an extracted copy of +lance-linalg's simd module. + +Upstream-Status: To upstream [not yet submitted to lancedb/lance] + +Signed-off-by: Ludovic Henry +--- + rust/lance-linalg/src/simd/f32.rs | 383 ++++++++++++++++++++++++++++++ + rust/lance-linalg/src/simd/f64.rs | 364 ++++++++++++++++++++++++++++ + rust/lance-linalg/src/simd/i32.rs | 196 +++++++++++++++ + 3 files changed, 943 insertions(+) + +diff --git a/rust/lance-linalg/src/simd/f32.rs b/rust/lance-linalg/src/simd/f32.rs +index 434a1ef..141185d 100644 +--- a/rust/lance-linalg/src/simd/f32.rs ++++ b/rust/lance-linalg/src/simd/f32.rs +@@ -35,6 +35,15 @@ pub struct f32x8(float32x4x2_t); + #[derive(Clone, Copy)] + pub struct f32x8(v8f32); + ++#[allow(non_camel_case_types)] ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++#[derive(Clone, Copy)] ++pub struct f32x8([f32; 8]); ++ + impl std::fmt::Debug for f32x8 { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let mut arr = [0.0_f32; 8]; +@@ -45,6 +54,7 @@ impl std::fmt::Debug for f32x8 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl f32x8 { + /// Gather 8 f32 values from `slice` at the offsets in `indices`. + /// +@@ -163,6 +173,7 @@ impl<'a> From<&'a [f32; 8]> for f32x8 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl SIMD for f32x8 { + fn splat(val: f32) -> Self { + #[cfg(target_arch = "x86_64")] +@@ -371,6 +382,7 @@ impl SIMD for f32x8 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl FloatSimd for f32x8 { + fn multiply_add(&mut self, a: Self, b: Self) { + #[cfg(target_arch = "x86_64")] +@@ -389,6 +401,7 @@ impl FloatSimd for f32x8 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl Add for f32x8 { + type Output = Self; + +@@ -412,6 +425,7 @@ impl Add for f32x8 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl AddAssign for f32x8 { + #[inline] + fn add_assign(&mut self, rhs: Self) { +@@ -431,6 +445,7 @@ impl AddAssign for f32x8 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl Sub for f32x8 { + type Output = Self; + +@@ -454,6 +469,7 @@ impl Sub for f32x8 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl SubAssign for f32x8 { + #[inline] + fn sub_assign(&mut self, rhs: Self) { +@@ -473,6 +489,7 @@ impl SubAssign for f32x8 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl Mul for f32x8 { + type Output = Self; + +@@ -521,6 +538,15 @@ pub struct f32x16(float32x4x4_t); + #[derive(Clone, Copy)] + pub struct f32x16(v8f32, v8f32); + ++#[allow(non_camel_case_types)] ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++#[derive(Clone, Copy)] ++pub struct f32x16([f32; 16]); ++ + impl std::fmt::Debug for f32x16 { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let mut arr = [0.0_f32; 16]; +@@ -548,6 +574,7 @@ impl<'a> From<&'a [f32; 16]> for f32x16 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl SIMD for f32x16 { + #[inline] + fn splat(val: f32) -> Self { +@@ -795,6 +822,7 @@ impl SIMD for f32x16 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl FloatSimd for f32x16 { + #[inline] + fn multiply_add(&mut self, a: Self, b: Self) { +@@ -818,6 +846,7 @@ impl FloatSimd for f32x16 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl Add for f32x16 { + type Output = Self; + +@@ -843,6 +872,7 @@ impl Add for f32x16 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl AddAssign for f32x16 { + #[inline] + fn add_assign(&mut self, rhs: Self) { +@@ -866,6 +896,7 @@ impl AddAssign for f32x16 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl Mul for f32x16 { + type Output = Self; + +@@ -891,6 +922,7 @@ impl Mul for f32x16 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl Sub for f32x16 { + type Output = Self; + +@@ -916,6 +948,7 @@ impl Sub for f32x16 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl SubAssign for f32x16 { + #[inline] + fn sub_assign(&mut self, rhs: Self) { +@@ -939,6 +972,356 @@ impl SubAssign for f32x16 { + } + } + ++ ++// --------------------------------------------------------------------------- ++// Portable scalar fallback for architectures with no dedicated SIMD kernel ++// above (e.g. riscv64). Correctness-first, no intrinsics. ++// --------------------------------------------------------------------------- ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl f32x8 { ++ /// Gather 8 f32 values from `slice` at the offsets in `indices`. ++ /// ++ /// # Panics ++ /// ++ /// If any index is negative or lands outside `slice`. ++ #[inline] ++ pub fn gather(slice: &[f32], indices: &[i32; 8]) -> Self { ++ let values = indices.map(|i| slice[i as usize]); ++ unsafe { Self::load_unaligned(values.as_ptr()) } ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl SIMD for f32x8 { ++ #[inline] ++ fn splat(val: f32) -> Self { ++ Self([val; 8]) ++ } ++ ++ #[inline] ++ fn zeros() -> Self { ++ Self([0.0; 8]) ++ } ++ ++ #[inline] ++ unsafe fn load(ptr: *const f32) -> Self { ++ unsafe { Self::load_unaligned(ptr) } ++ } ++ ++ #[inline] ++ unsafe fn load_unaligned(ptr: *const f32) -> Self { ++ let mut arr = [0.0_f32; 8]; ++ unsafe { ++ std::ptr::copy_nonoverlapping(ptr, arr.as_mut_ptr(), 8); ++ } ++ Self(arr) ++ } ++ ++ #[inline] ++ unsafe fn store(&self, ptr: *mut f32) { ++ unsafe { self.store_unaligned(ptr) } ++ } ++ ++ #[inline] ++ unsafe fn store_unaligned(&self, ptr: *mut f32) { ++ unsafe { ++ std::ptr::copy_nonoverlapping(self.0.as_ptr(), ptr, 8); ++ } ++ } ++ ++ #[inline] ++ fn reduce_sum(&self) -> f32 { ++ self.0.iter().sum() ++ } ++ ++ #[inline] ++ fn reduce_min(&self) -> f32 { ++ self.0.iter().copied().fold(f32::INFINITY, f32::min) ++ } ++ ++ #[inline] ++ fn min(&self, rhs: &Self) -> Self { ++ let mut result = [0.0_f32; 8]; ++ for i in 0..8 { ++ result[i] = self.0[i].min(rhs.0[i]); ++ } ++ Self(result) ++ } ++ ++ #[inline] ++ fn find(&self, val: f32) -> Option { ++ self.0.iter().position(|&x| x == val).map(|i| i as i32) ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl FloatSimd for f32x8 { ++ #[inline] ++ fn multiply_add(&mut self, a: Self, b: Self) { ++ for i in 0..8 { ++ self.0[i] += a.0[i] * b.0[i]; ++ } ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl Add for f32x8 { ++ type Output = Self; ++ ++ #[inline] ++ fn add(self, rhs: Self) -> Self::Output { ++ let mut result = [0.0_f32; 8]; ++ for i in 0..8 { ++ result[i] = self.0[i] + rhs.0[i]; ++ } ++ Self(result) ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl AddAssign for f32x8 { ++ #[inline] ++ fn add_assign(&mut self, rhs: Self) { ++ for i in 0..8 { ++ self.0[i] += rhs.0[i]; ++ } ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl Sub for f32x8 { ++ type Output = Self; ++ ++ #[inline] ++ fn sub(self, rhs: Self) -> Self::Output { ++ let mut result = [0.0_f32; 8]; ++ for i in 0..8 { ++ result[i] = self.0[i] - rhs.0[i]; ++ } ++ Self(result) ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl SubAssign for f32x8 { ++ #[inline] ++ fn sub_assign(&mut self, rhs: Self) { ++ for i in 0..8 { ++ self.0[i] -= rhs.0[i]; ++ } ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl Mul for f32x8 { ++ type Output = Self; ++ ++ #[inline] ++ fn mul(self, rhs: Self) -> Self::Output { ++ let mut result = [0.0_f32; 8]; ++ for i in 0..8 { ++ result[i] = self.0[i] * rhs.0[i]; ++ } ++ Self(result) ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl SIMD for f32x16 { ++ #[inline] ++ fn splat(val: f32) -> Self { ++ Self([val; 16]) ++ } ++ ++ #[inline] ++ fn zeros() -> Self { ++ Self([0.0; 16]) ++ } ++ ++ #[inline] ++ unsafe fn load(ptr: *const f32) -> Self { ++ unsafe { Self::load_unaligned(ptr) } ++ } ++ ++ #[inline] ++ unsafe fn load_unaligned(ptr: *const f32) -> Self { ++ let mut arr = [0.0_f32; 16]; ++ unsafe { ++ std::ptr::copy_nonoverlapping(ptr, arr.as_mut_ptr(), 16); ++ } ++ Self(arr) ++ } ++ ++ #[inline] ++ unsafe fn store(&self, ptr: *mut f32) { ++ unsafe { self.store_unaligned(ptr) } ++ } ++ ++ #[inline] ++ unsafe fn store_unaligned(&self, ptr: *mut f32) { ++ unsafe { ++ std::ptr::copy_nonoverlapping(self.0.as_ptr(), ptr, 16); ++ } ++ } ++ ++ #[inline] ++ fn reduce_sum(&self) -> f32 { ++ self.0.iter().sum() ++ } ++ ++ #[inline] ++ fn reduce_min(&self) -> f32 { ++ self.0.iter().copied().fold(f32::INFINITY, f32::min) ++ } ++ ++ #[inline] ++ fn min(&self, rhs: &Self) -> Self { ++ let mut result = [0.0_f32; 16]; ++ for i in 0..16 { ++ result[i] = self.0[i].min(rhs.0[i]); ++ } ++ Self(result) ++ } ++ ++ #[inline] ++ fn find(&self, val: f32) -> Option { ++ self.0.iter().position(|&x| x == val).map(|i| i as i32) ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl FloatSimd for f32x16 { ++ #[inline] ++ fn multiply_add(&mut self, a: Self, b: Self) { ++ for i in 0..16 { ++ self.0[i] += a.0[i] * b.0[i]; ++ } ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl Add for f32x16 { ++ type Output = Self; ++ ++ #[inline] ++ fn add(self, rhs: Self) -> Self::Output { ++ let mut result = [0.0_f32; 16]; ++ for i in 0..16 { ++ result[i] = self.0[i] + rhs.0[i]; ++ } ++ Self(result) ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl AddAssign for f32x16 { ++ #[inline] ++ fn add_assign(&mut self, rhs: Self) { ++ for i in 0..16 { ++ self.0[i] += rhs.0[i]; ++ } ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl Sub for f32x16 { ++ type Output = Self; ++ ++ #[inline] ++ fn sub(self, rhs: Self) -> Self::Output { ++ let mut result = [0.0_f32; 16]; ++ for i in 0..16 { ++ result[i] = self.0[i] - rhs.0[i]; ++ } ++ Self(result) ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl SubAssign for f32x16 { ++ #[inline] ++ fn sub_assign(&mut self, rhs: Self) { ++ for i in 0..16 { ++ self.0[i] -= rhs.0[i]; ++ } ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl Mul for f32x16 { ++ type Output = Self; ++ ++ #[inline] ++ fn mul(self, rhs: Self) -> Self::Output { ++ let mut result = [0.0_f32; 16]; ++ for i in 0..16 { ++ result[i] = self.0[i] * rhs.0[i]; ++ } ++ Self(result) ++ } ++} ++ + #[cfg(test)] + mod tests { + +diff --git a/rust/lance-linalg/src/simd/f64.rs b/rust/lance-linalg/src/simd/f64.rs +index 129b2f0..a2b5f02 100644 +--- a/rust/lance-linalg/src/simd/f64.rs ++++ b/rust/lance-linalg/src/simd/f64.rs +@@ -33,6 +33,15 @@ pub struct f64x4(float64x2x2_t); + #[derive(Clone, Copy)] + pub struct f64x4(v4f64); + ++#[allow(non_camel_case_types)] ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++#[derive(Clone, Copy)] ++pub struct f64x4([f64; 4]); ++ + impl std::fmt::Debug for f64x4 { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let mut arr = [0.0_f64; 4]; +@@ -60,6 +69,7 @@ impl<'a> From<&'a [f64; 4]> for f64x4 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl SIMD for f64x4 { + fn splat(val: f64) -> Self { + #[cfg(target_arch = "x86_64")] +@@ -231,6 +241,7 @@ impl SIMD for f64x4 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl FloatSimd for f64x4 { + fn multiply_add(&mut self, a: Self, b: Self) { + #[cfg(target_arch = "x86_64")] +@@ -249,6 +260,7 @@ impl FloatSimd for f64x4 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl Add for f64x4 { + type Output = Self; + +@@ -272,6 +284,7 @@ impl Add for f64x4 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl AddAssign for f64x4 { + #[inline] + fn add_assign(&mut self, rhs: Self) { +@@ -291,6 +304,7 @@ impl AddAssign for f64x4 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl Sub for f64x4 { + type Output = Self; + +@@ -314,6 +328,7 @@ impl Sub for f64x4 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl SubAssign for f64x4 { + #[inline] + fn sub_assign(&mut self, rhs: Self) { +@@ -333,6 +348,7 @@ impl SubAssign for f64x4 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl Mul for f64x4 { + type Output = Self; + +@@ -382,6 +398,15 @@ pub struct f64x8(float64x2x2_t, float64x2x2_t); + #[derive(Clone, Copy)] + pub struct f64x8(v4f64, v4f64); + ++#[allow(non_camel_case_types)] ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++#[derive(Clone, Copy)] ++pub struct f64x8([f64; 8]); ++ + impl std::fmt::Debug for f64x8 { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let mut arr = [0.0_f64; 8]; +@@ -409,6 +434,7 @@ impl<'a> From<&'a [f64; 8]> for f64x8 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl SIMD for f64x8 { + #[inline] + fn splat(val: f64) -> Self { +@@ -602,6 +628,7 @@ impl SIMD for f64x8 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl FloatSimd for f64x8 { + #[inline] + fn multiply_add(&mut self, a: Self, b: Self) { +@@ -625,6 +652,7 @@ impl FloatSimd for f64x8 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl Add for f64x8 { + type Output = Self; + +@@ -648,6 +676,7 @@ impl Add for f64x8 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl AddAssign for f64x8 { + #[inline] + fn add_assign(&mut self, rhs: Self) { +@@ -671,6 +700,7 @@ impl AddAssign for f64x8 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl Mul for f64x8 { + type Output = Self; + +@@ -694,6 +724,7 @@ impl Mul for f64x8 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl Sub for f64x8 { + type Output = Self; + +@@ -717,6 +748,7 @@ impl Sub for f64x8 { + } + } + ++#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] + impl SubAssign for f64x8 { + #[inline] + fn sub_assign(&mut self, rhs: Self) { +@@ -740,6 +772,338 @@ impl SubAssign for f64x8 { + } + } + ++ ++// --------------------------------------------------------------------------- ++// Portable scalar fallback for architectures with no dedicated SIMD kernel ++// above (e.g. riscv64). Correctness-first, no intrinsics. ++// --------------------------------------------------------------------------- ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl SIMD for f64x4 { ++ #[inline] ++ fn splat(val: f64) -> Self { ++ Self([val; 4]) ++ } ++ ++ #[inline] ++ fn zeros() -> Self { ++ Self([0.0; 4]) ++ } ++ ++ #[inline] ++ unsafe fn load(ptr: *const f64) -> Self { ++ unsafe { Self::load_unaligned(ptr) } ++ } ++ ++ #[inline] ++ unsafe fn load_unaligned(ptr: *const f64) -> Self { ++ let mut arr = [0.0_f64; 4]; ++ unsafe { ++ std::ptr::copy_nonoverlapping(ptr, arr.as_mut_ptr(), 4); ++ } ++ Self(arr) ++ } ++ ++ #[inline] ++ unsafe fn store(&self, ptr: *mut f64) { ++ unsafe { self.store_unaligned(ptr) } ++ } ++ ++ #[inline] ++ unsafe fn store_unaligned(&self, ptr: *mut f64) { ++ unsafe { ++ std::ptr::copy_nonoverlapping(self.0.as_ptr(), ptr, 4); ++ } ++ } ++ ++ #[inline] ++ fn reduce_sum(&self) -> f64 { ++ self.0.iter().sum() ++ } ++ ++ #[inline] ++ fn reduce_min(&self) -> f64 { ++ self.0.iter().copied().fold(f64::INFINITY, f64::min) ++ } ++ ++ #[inline] ++ fn min(&self, rhs: &Self) -> Self { ++ let mut result = [0.0_f64; 4]; ++ for i in 0..4 { ++ result[i] = self.0[i].min(rhs.0[i]); ++ } ++ Self(result) ++ } ++ ++ #[inline] ++ fn find(&self, val: f64) -> Option { ++ self.0.iter().position(|&x| x == val).map(|i| i as i32) ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl FloatSimd for f64x4 { ++ #[inline] ++ fn multiply_add(&mut self, a: Self, b: Self) { ++ for i in 0..4 { ++ self.0[i] += a.0[i] * b.0[i]; ++ } ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl Add for f64x4 { ++ type Output = Self; ++ ++ #[inline] ++ fn add(self, rhs: Self) -> Self::Output { ++ let mut result = [0.0_f64; 4]; ++ for i in 0..4 { ++ result[i] = self.0[i] + rhs.0[i]; ++ } ++ Self(result) ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl AddAssign for f64x4 { ++ #[inline] ++ fn add_assign(&mut self, rhs: Self) { ++ for i in 0..4 { ++ self.0[i] += rhs.0[i]; ++ } ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl Sub for f64x4 { ++ type Output = Self; ++ ++ #[inline] ++ fn sub(self, rhs: Self) -> Self::Output { ++ let mut result = [0.0_f64; 4]; ++ for i in 0..4 { ++ result[i] = self.0[i] - rhs.0[i]; ++ } ++ Self(result) ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl SubAssign for f64x4 { ++ #[inline] ++ fn sub_assign(&mut self, rhs: Self) { ++ for i in 0..4 { ++ self.0[i] -= rhs.0[i]; ++ } ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl Mul for f64x4 { ++ type Output = Self; ++ ++ #[inline] ++ fn mul(self, rhs: Self) -> Self::Output { ++ let mut result = [0.0_f64; 4]; ++ for i in 0..4 { ++ result[i] = self.0[i] * rhs.0[i]; ++ } ++ Self(result) ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl SIMD for f64x8 { ++ #[inline] ++ fn splat(val: f64) -> Self { ++ Self([val; 8]) ++ } ++ ++ #[inline] ++ fn zeros() -> Self { ++ Self([0.0; 8]) ++ } ++ ++ #[inline] ++ unsafe fn load(ptr: *const f64) -> Self { ++ unsafe { Self::load_unaligned(ptr) } ++ } ++ ++ #[inline] ++ unsafe fn load_unaligned(ptr: *const f64) -> Self { ++ let mut arr = [0.0_f64; 8]; ++ unsafe { ++ std::ptr::copy_nonoverlapping(ptr, arr.as_mut_ptr(), 8); ++ } ++ Self(arr) ++ } ++ ++ #[inline] ++ unsafe fn store(&self, ptr: *mut f64) { ++ unsafe { self.store_unaligned(ptr) } ++ } ++ ++ #[inline] ++ unsafe fn store_unaligned(&self, ptr: *mut f64) { ++ unsafe { ++ std::ptr::copy_nonoverlapping(self.0.as_ptr(), ptr, 8); ++ } ++ } ++ ++ #[inline] ++ fn reduce_sum(&self) -> f64 { ++ self.0.iter().sum() ++ } ++ ++ #[inline] ++ fn reduce_min(&self) -> f64 { ++ self.0.iter().copied().fold(f64::INFINITY, f64::min) ++ } ++ ++ #[inline] ++ fn min(&self, rhs: &Self) -> Self { ++ let mut result = [0.0_f64; 8]; ++ for i in 0..8 { ++ result[i] = self.0[i].min(rhs.0[i]); ++ } ++ Self(result) ++ } ++ ++ #[inline] ++ fn find(&self, val: f64) -> Option { ++ self.0.iter().position(|&x| x == val).map(|i| i as i32) ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl FloatSimd for f64x8 { ++ #[inline] ++ fn multiply_add(&mut self, a: Self, b: Self) { ++ for i in 0..8 { ++ self.0[i] += a.0[i] * b.0[i]; ++ } ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl Add for f64x8 { ++ type Output = Self; ++ ++ #[inline] ++ fn add(self, rhs: Self) -> Self::Output { ++ let mut result = [0.0_f64; 8]; ++ for i in 0..8 { ++ result[i] = self.0[i] + rhs.0[i]; ++ } ++ Self(result) ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl AddAssign for f64x8 { ++ #[inline] ++ fn add_assign(&mut self, rhs: Self) { ++ for i in 0..8 { ++ self.0[i] += rhs.0[i]; ++ } ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl Sub for f64x8 { ++ type Output = Self; ++ ++ #[inline] ++ fn sub(self, rhs: Self) -> Self::Output { ++ let mut result = [0.0_f64; 8]; ++ for i in 0..8 { ++ result[i] = self.0[i] - rhs.0[i]; ++ } ++ Self(result) ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl SubAssign for f64x8 { ++ #[inline] ++ fn sub_assign(&mut self, rhs: Self) { ++ for i in 0..8 { ++ self.0[i] -= rhs.0[i]; ++ } ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl Mul for f64x8 { ++ type Output = Self; ++ ++ #[inline] ++ fn mul(self, rhs: Self) -> Self::Output { ++ let mut result = [0.0_f64; 8]; ++ for i in 0..8 { ++ result[i] = self.0[i] * rhs.0[i]; ++ } ++ Self(result) ++ } ++} ++ + #[cfg(test)] + mod tests { + use super::*; +diff --git a/rust/lance-linalg/src/simd/i32.rs b/rust/lance-linalg/src/simd/i32.rs +index 6e08129..0b6dc14 100644 +--- a/rust/lance-linalg/src/simd/i32.rs ++++ b/rust/lance-linalg/src/simd/i32.rs +@@ -38,6 +38,17 @@ pub struct i32x8(int32x4x2_t); + #[derive(Clone, Copy)] + pub struct i32x8(v8i32); + ++/// Portable scalar fallback for architectures with no dedicated SIMD kernel ++/// above (e.g. riscv64). Correctness-first, no intrinsics. ++#[allow(non_camel_case_types)] ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++#[derive(Clone, Copy)] ++pub struct i32x8([i32; 8]); ++ + impl std::fmt::Debug for i32x8 { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let mut arr = [0; 8]; +@@ -65,6 +76,11 @@ impl From<&[i32; 8]> for i32x8 { + } + } + ++#[cfg(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++))] + impl SIMD for i32x8 { + #[inline] + fn splat(val: i32) -> Self { +@@ -223,6 +239,11 @@ impl SIMD for i32x8 { + } + } + ++#[cfg(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++))] + impl Add for i32x8 { + type Output = Self; + +@@ -246,6 +267,11 @@ impl Add for i32x8 { + } + } + ++#[cfg(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++))] + impl AddAssign for i32x8 { + #[inline] + fn add_assign(&mut self, rhs: Self) { +@@ -265,6 +291,11 @@ impl AddAssign for i32x8 { + } + } + ++#[cfg(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++))] + impl Sub for i32x8 { + type Output = Self; + +@@ -288,6 +319,11 @@ impl Sub for i32x8 { + } + } + ++#[cfg(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++))] + impl SubAssign for i32x8 { + #[inline] + fn sub_assign(&mut self, rhs: Self) { +@@ -307,6 +343,11 @@ impl SubAssign for i32x8 { + } + } + ++#[cfg(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++))] + impl Mul for i32x8 { + type Output = Self; + +@@ -343,6 +384,161 @@ impl Mul for i32x8 { + } + } + ++// Portable scalar fallback for architectures with no dedicated SIMD kernel ++// above (e.g. riscv64). Correctness-first, no intrinsics. Add/Sub/Mul use ++// wrapping arithmetic to mirror the silent-wraparound semantics of the ++// `_mm256_{add,sub,mul}_epi32` / `vaddq_s32` / `lasx_xv{add,sub,mul}_w` ++// instructions above. ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl SIMD for i32x8 { ++ #[inline] ++ fn splat(val: i32) -> Self { ++ Self([val; 8]) ++ } ++ ++ #[inline] ++ fn zeros() -> Self { ++ Self([0; 8]) ++ } ++ ++ #[inline] ++ unsafe fn load(ptr: *const i32) -> Self { ++ unsafe { Self::load_unaligned(ptr) } ++ } ++ ++ #[inline] ++ unsafe fn load_unaligned(ptr: *const i32) -> Self { ++ let mut arr = [0_i32; 8]; ++ unsafe { ++ std::ptr::copy_nonoverlapping(ptr, arr.as_mut_ptr(), 8); ++ } ++ Self(arr) ++ } ++ ++ #[inline] ++ unsafe fn store(&self, ptr: *mut i32) { ++ unsafe { self.store_unaligned(ptr) } ++ } ++ ++ #[inline] ++ unsafe fn store_unaligned(&self, ptr: *mut i32) { ++ unsafe { ++ std::ptr::copy_nonoverlapping(self.0.as_ptr(), ptr, 8); ++ } ++ } ++ ++ #[inline] ++ fn reduce_sum(&self) -> i32 { ++ self.0.iter().sum() ++ } ++ ++ #[inline] ++ fn reduce_min(&self) -> i32 { ++ todo!() ++ } ++ ++ #[inline] ++ fn min(&self, rhs: &Self) -> Self { ++ let mut result = [0_i32; 8]; ++ for i in 0..8 { ++ result[i] = std::cmp::min(self.0[i], rhs.0[i]); ++ } ++ Self(result) ++ } ++ ++ #[inline] ++ fn find(&self, val: i32) -> Option { ++ self.0.iter().position(|&x| x == val).map(|i| i as i32) ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl Add for i32x8 { ++ type Output = Self; ++ ++ #[inline] ++ fn add(self, rhs: Self) -> Self::Output { ++ let mut result = [0_i32; 8]; ++ for i in 0..8 { ++ result[i] = self.0[i].wrapping_add(rhs.0[i]); ++ } ++ Self(result) ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl AddAssign for i32x8 { ++ #[inline] ++ fn add_assign(&mut self, rhs: Self) { ++ for i in 0..8 { ++ self.0[i] = self.0[i].wrapping_add(rhs.0[i]); ++ } ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl Sub for i32x8 { ++ type Output = Self; ++ ++ #[inline] ++ fn sub(self, rhs: Self) -> Self::Output { ++ let mut result = [0_i32; 8]; ++ for i in 0..8 { ++ result[i] = self.0[i].wrapping_sub(rhs.0[i]); ++ } ++ Self(result) ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl SubAssign for i32x8 { ++ #[inline] ++ fn sub_assign(&mut self, rhs: Self) { ++ for i in 0..8 { ++ self.0[i] = self.0[i].wrapping_sub(rhs.0[i]); ++ } ++ } ++} ++ ++#[cfg(not(any( ++ target_arch = "x86_64", ++ target_arch = "aarch64", ++ target_arch = "loongarch64" ++)))] ++impl Mul for i32x8 { ++ type Output = Self; ++ ++ #[inline] ++ fn mul(self, rhs: Self) -> Self::Output { ++ let mut result = [0_i32; 8]; ++ for i in 0..8 { ++ result[i] = self.0[i].wrapping_mul(rhs.0[i]); ++ } ++ Self(result) ++ } ++} ++ + #[cfg(test)] + mod tests { + use super::*; +-- +2.50.1 (Apple Git-155) + From 2e52d07abbc970ded1c9cc2a7260ed79fa6d2916 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sat, 10 Oct 2026 07:51:01 +0000 Subject: [PATCH 3/4] lancedb: drop the riscv64 SIMD fallback patches for 0.40.0 lancedb v0.40.0 pins lance-core/lance-linalg =13.0.0, and lance 13.0.0 ships the portable SIMD fallback the 0002 patch carried (lancedb/lance#9446, "fix(linalg): build on architectures without SIMD kernels"): lance-linalg's build.rs now emits `cfg(simd_fallback)` for every target_arch other than x86_64/aarch64/loongarch64, and f32x8/f32x16/f64x4/f64x8/i32x8 gain an array-backed struct plus a scalar arm in each method under that cfg, inserted right after the per-arch struct definitions -- which is why the 0002 hunks no longer apply there. Rebasing them would define each type twice on riscv64, so both patches go: with no local lance fix to point at, the 0001 Cargo.toml redirect is moot and the crates.io lance 13.0.0 in lancedb's Cargo.lock is used as released. The patch-apply and lance-checkout steps now run only when the version being built has patches under patches/lancedb//, the same hashFiles() gate build-fastparquet.yml uses, so the 0.37.1-0.39.0 path is unchanged. Checked by type-checking lance v13.0.0's simd/{f32,f64,i32,u8}.rs for riscv64gc-unknown-linux-gnu: clean with simd_fallback set, and the old "cannot find type `f32x8`" errors without it. Signed-off-by: Ludovic Henry --- ...lance-core-lance-linalg-to-a-riscv64.patch | 64 - ...add-a-portable-riscv64-simd-fallback.patch | 1292 ----------------- 2 files changed, 1356 deletions(-) delete mode 100644 patches/lancedb/0.40.0/0001-cargo-redirect-lance-core-lance-linalg-to-a-riscv64.patch delete mode 100644 patches/lancedb/0.40.0/0002-lance-linalg-add-a-portable-riscv64-simd-fallback.patch diff --git a/patches/lancedb/0.40.0/0001-cargo-redirect-lance-core-lance-linalg-to-a-riscv64.patch b/patches/lancedb/0.40.0/0001-cargo-redirect-lance-core-lance-linalg-to-a-riscv64.patch deleted file mode 100644 index 4be13c59701..00000000000 --- a/patches/lancedb/0.40.0/0001-cargo-redirect-lance-core-lance-linalg-to-a-riscv64.patch +++ /dev/null @@ -1,64 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Ludovic Henry -Date: Thu, 03 Sep 2026 00:00:00 +0000 -Subject: [PATCH] cargo: redirect lance-core/lance-linalg to a riscv64-patched - checkout - -lance-core's SIMD_SUPPORT static (rust/lance-core/src/utils/cpu.rs) and -lance-linalg's f32x8/f32x16/i32x8/f64x4/f64x8 SIMD types -(rust/lance-linalg/src/simd/{f32,i32,f64}.rs) are compiled for -x86_64/aarch64/loongarch64 only, with no fallback arm for any other -architecture: the SIMD_SUPPORT closure has no trailing expression on -riscv64 ("expected SimdSupport, found ()"), and the SIMD type structs are -defined only under those three `#[cfg(target_arch = ...)]` attributes -while every method implementing them is unconditional, so riscv64 hits -"cannot find type `f32x8` in this scope" as soon as lance-linalg is -built. - -Both crates are pulled in from crates.io at a pinned `=10.0.0`, so the -fix cannot be applied in place. Redirect them to a checkout of the same -v10.0.0 tag carrying a portable scalar fallback for both, see -0002-lance-linalg-add-a-portable-riscv64-simd-fallback.patch. `exclude` -keeps that checkout's own [workspace] from being silently absorbed into -this one, which would drop its `[workspace.package]` inheritance -(version, edition, ...) for lance-core and lance-linalg. - -Only the source line of the lance-core/lance-linalg entries changes in -Cargo.lock, so every other dependency stays pinned exactly as upstream -released it. - -Upstream-Status: Inappropriate [redirects two pinned dependencies to a local checkout; the fix itself belongs in lancedb/lance] - -Signed-off-by: Ludovic Henry ---- - Cargo.toml | 12 ++++++++++++ - 1 file changed, 12 insertions(+) - -diff --git a/Cargo.toml b/Cargo.toml -index a47e59e..ab6cf5c 100644 ---- a/Cargo.toml -+++ b/Cargo.toml -@@ -1,5 +1,10 @@ - [workspace] - members = ["rust/lancedb", "nodejs", "python"] -+# ./lance (see [patch.crates-io] below) carries its own [workspace]; excluding -+# it keeps that workspace independent instead of being silently absorbed into -+# this one, which would drop its `[workspace.package]` inheritance (version, -+# edition, ...) for lance-core and lance-linalg. -+exclude = ["lance"] - resolver = "2" - - [workspace.package] -@@ -80,3 +85,10 @@ debug = false - debug-assertions = false - strip = "debuginfo" - incremental = false -+ -+# lance-core's SIMD_SUPPORT and lance-linalg's f32x8/f32x16/i32x8/f64x4/f64x8 -+# SIMD types compile only for x86_64/aarch64/loongarch64 upstream; ./lance is -+# the same v10.0.0 tag with a riscv64 scalar fallback added to both. -+[patch.crates-io] -+lance-core = { path = "lance/rust/lance-core" } -+lance-linalg = { path = "lance/rust/lance-linalg" } --- -2.43.0 diff --git a/patches/lancedb/0.40.0/0002-lance-linalg-add-a-portable-riscv64-simd-fallback.patch b/patches/lancedb/0.40.0/0002-lance-linalg-add-a-portable-riscv64-simd-fallback.patch deleted file mode 100644 index 0708da295a5..00000000000 --- a/patches/lancedb/0.40.0/0002-lance-linalg-add-a-portable-riscv64-simd-fallback.patch +++ /dev/null @@ -1,1292 +0,0 @@ -From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 -From: Ludovic Henry -Date: Tue, 8 Sep 2026 00:00:00 +0000 -Subject: [PATCH] lance-linalg: add a portable riscv64 SIMD fallback - -lance-core's SIMD_SUPPORT static already carries a portable riscv64 -fallback (rust/lance-core/src/utils/cpu.rs), but lance-linalg's -f32x8/f32x16/i32x8/f64x4/f64x8 SIMD types still hardcode x86_64 -(AVX/AVX2/AVX-512), aarch64 (NEON) and loongarch64 (LSX/LASX) as the -only supported architectures, with no portable fallback -- unlike -u8x16/u8x32, which already carry one (rust/lance-linalg/src/simd/u8.rs). -This adds the same shape of fallback for the missing types: an -array-backed struct plus a scalar implementation of every method the -SIMD/FloatSimd traits require, gated -`#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64")))]` -beside the existing per-architecture arms, which are otherwise -untouched. Add/Sub/Mul on i32x8 use wrapping arithmetic to match the -silent-wraparound semantics of the SIMD instructions they replace. - -Verified with `cargo check --target riscv64gc-unknown-linux-gnu` (and, -as a regression check, `--target aarch64-apple-darwin` and -`--target x86_64-unknown-linux-gnu`) against an extracted copy of -lance-linalg's simd module. - -Upstream-Status: To upstream [not yet submitted to lancedb/lance] - -Signed-off-by: Ludovic Henry ---- - rust/lance-linalg/src/simd/f32.rs | 383 ++++++++++++++++++++++++++++++ - rust/lance-linalg/src/simd/f64.rs | 364 ++++++++++++++++++++++++++++ - rust/lance-linalg/src/simd/i32.rs | 196 +++++++++++++++ - 3 files changed, 943 insertions(+) - -diff --git a/rust/lance-linalg/src/simd/f32.rs b/rust/lance-linalg/src/simd/f32.rs -index 434a1ef..141185d 100644 ---- a/rust/lance-linalg/src/simd/f32.rs -+++ b/rust/lance-linalg/src/simd/f32.rs -@@ -35,6 +35,15 @@ pub struct f32x8(float32x4x2_t); - #[derive(Clone, Copy)] - pub struct f32x8(v8f32); - -+#[allow(non_camel_case_types)] -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+#[derive(Clone, Copy)] -+pub struct f32x8([f32; 8]); -+ - impl std::fmt::Debug for f32x8 { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let mut arr = [0.0_f32; 8]; -@@ -45,6 +54,7 @@ impl std::fmt::Debug for f32x8 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl f32x8 { - /// Gather 8 f32 values from `slice` at the offsets in `indices`. - /// -@@ -163,6 +173,7 @@ impl<'a> From<&'a [f32; 8]> for f32x8 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl SIMD for f32x8 { - fn splat(val: f32) -> Self { - #[cfg(target_arch = "x86_64")] -@@ -371,6 +382,7 @@ impl SIMD for f32x8 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl FloatSimd for f32x8 { - fn multiply_add(&mut self, a: Self, b: Self) { - #[cfg(target_arch = "x86_64")] -@@ -389,6 +401,7 @@ impl FloatSimd for f32x8 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl Add for f32x8 { - type Output = Self; - -@@ -412,6 +425,7 @@ impl Add for f32x8 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl AddAssign for f32x8 { - #[inline] - fn add_assign(&mut self, rhs: Self) { -@@ -431,6 +445,7 @@ impl AddAssign for f32x8 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl Sub for f32x8 { - type Output = Self; - -@@ -454,6 +469,7 @@ impl Sub for f32x8 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl SubAssign for f32x8 { - #[inline] - fn sub_assign(&mut self, rhs: Self) { -@@ -473,6 +489,7 @@ impl SubAssign for f32x8 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl Mul for f32x8 { - type Output = Self; - -@@ -521,6 +538,15 @@ pub struct f32x16(float32x4x4_t); - #[derive(Clone, Copy)] - pub struct f32x16(v8f32, v8f32); - -+#[allow(non_camel_case_types)] -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+#[derive(Clone, Copy)] -+pub struct f32x16([f32; 16]); -+ - impl std::fmt::Debug for f32x16 { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let mut arr = [0.0_f32; 16]; -@@ -548,6 +574,7 @@ impl<'a> From<&'a [f32; 16]> for f32x16 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl SIMD for f32x16 { - #[inline] - fn splat(val: f32) -> Self { -@@ -795,6 +822,7 @@ impl SIMD for f32x16 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl FloatSimd for f32x16 { - #[inline] - fn multiply_add(&mut self, a: Self, b: Self) { -@@ -818,6 +846,7 @@ impl FloatSimd for f32x16 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl Add for f32x16 { - type Output = Self; - -@@ -843,6 +872,7 @@ impl Add for f32x16 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl AddAssign for f32x16 { - #[inline] - fn add_assign(&mut self, rhs: Self) { -@@ -866,6 +896,7 @@ impl AddAssign for f32x16 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl Mul for f32x16 { - type Output = Self; - -@@ -891,6 +922,7 @@ impl Mul for f32x16 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl Sub for f32x16 { - type Output = Self; - -@@ -916,6 +948,7 @@ impl Sub for f32x16 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl SubAssign for f32x16 { - #[inline] - fn sub_assign(&mut self, rhs: Self) { -@@ -939,6 +972,356 @@ impl SubAssign for f32x16 { - } - } - -+ -+// --------------------------------------------------------------------------- -+// Portable scalar fallback for architectures with no dedicated SIMD kernel -+// above (e.g. riscv64). Correctness-first, no intrinsics. -+// --------------------------------------------------------------------------- -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl f32x8 { -+ /// Gather 8 f32 values from `slice` at the offsets in `indices`. -+ /// -+ /// # Panics -+ /// -+ /// If any index is negative or lands outside `slice`. -+ #[inline] -+ pub fn gather(slice: &[f32], indices: &[i32; 8]) -> Self { -+ let values = indices.map(|i| slice[i as usize]); -+ unsafe { Self::load_unaligned(values.as_ptr()) } -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl SIMD for f32x8 { -+ #[inline] -+ fn splat(val: f32) -> Self { -+ Self([val; 8]) -+ } -+ -+ #[inline] -+ fn zeros() -> Self { -+ Self([0.0; 8]) -+ } -+ -+ #[inline] -+ unsafe fn load(ptr: *const f32) -> Self { -+ unsafe { Self::load_unaligned(ptr) } -+ } -+ -+ #[inline] -+ unsafe fn load_unaligned(ptr: *const f32) -> Self { -+ let mut arr = [0.0_f32; 8]; -+ unsafe { -+ std::ptr::copy_nonoverlapping(ptr, arr.as_mut_ptr(), 8); -+ } -+ Self(arr) -+ } -+ -+ #[inline] -+ unsafe fn store(&self, ptr: *mut f32) { -+ unsafe { self.store_unaligned(ptr) } -+ } -+ -+ #[inline] -+ unsafe fn store_unaligned(&self, ptr: *mut f32) { -+ unsafe { -+ std::ptr::copy_nonoverlapping(self.0.as_ptr(), ptr, 8); -+ } -+ } -+ -+ #[inline] -+ fn reduce_sum(&self) -> f32 { -+ self.0.iter().sum() -+ } -+ -+ #[inline] -+ fn reduce_min(&self) -> f32 { -+ self.0.iter().copied().fold(f32::INFINITY, f32::min) -+ } -+ -+ #[inline] -+ fn min(&self, rhs: &Self) -> Self { -+ let mut result = [0.0_f32; 8]; -+ for i in 0..8 { -+ result[i] = self.0[i].min(rhs.0[i]); -+ } -+ Self(result) -+ } -+ -+ #[inline] -+ fn find(&self, val: f32) -> Option { -+ self.0.iter().position(|&x| x == val).map(|i| i as i32) -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl FloatSimd for f32x8 { -+ #[inline] -+ fn multiply_add(&mut self, a: Self, b: Self) { -+ for i in 0..8 { -+ self.0[i] += a.0[i] * b.0[i]; -+ } -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl Add for f32x8 { -+ type Output = Self; -+ -+ #[inline] -+ fn add(self, rhs: Self) -> Self::Output { -+ let mut result = [0.0_f32; 8]; -+ for i in 0..8 { -+ result[i] = self.0[i] + rhs.0[i]; -+ } -+ Self(result) -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl AddAssign for f32x8 { -+ #[inline] -+ fn add_assign(&mut self, rhs: Self) { -+ for i in 0..8 { -+ self.0[i] += rhs.0[i]; -+ } -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl Sub for f32x8 { -+ type Output = Self; -+ -+ #[inline] -+ fn sub(self, rhs: Self) -> Self::Output { -+ let mut result = [0.0_f32; 8]; -+ for i in 0..8 { -+ result[i] = self.0[i] - rhs.0[i]; -+ } -+ Self(result) -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl SubAssign for f32x8 { -+ #[inline] -+ fn sub_assign(&mut self, rhs: Self) { -+ for i in 0..8 { -+ self.0[i] -= rhs.0[i]; -+ } -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl Mul for f32x8 { -+ type Output = Self; -+ -+ #[inline] -+ fn mul(self, rhs: Self) -> Self::Output { -+ let mut result = [0.0_f32; 8]; -+ for i in 0..8 { -+ result[i] = self.0[i] * rhs.0[i]; -+ } -+ Self(result) -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl SIMD for f32x16 { -+ #[inline] -+ fn splat(val: f32) -> Self { -+ Self([val; 16]) -+ } -+ -+ #[inline] -+ fn zeros() -> Self { -+ Self([0.0; 16]) -+ } -+ -+ #[inline] -+ unsafe fn load(ptr: *const f32) -> Self { -+ unsafe { Self::load_unaligned(ptr) } -+ } -+ -+ #[inline] -+ unsafe fn load_unaligned(ptr: *const f32) -> Self { -+ let mut arr = [0.0_f32; 16]; -+ unsafe { -+ std::ptr::copy_nonoverlapping(ptr, arr.as_mut_ptr(), 16); -+ } -+ Self(arr) -+ } -+ -+ #[inline] -+ unsafe fn store(&self, ptr: *mut f32) { -+ unsafe { self.store_unaligned(ptr) } -+ } -+ -+ #[inline] -+ unsafe fn store_unaligned(&self, ptr: *mut f32) { -+ unsafe { -+ std::ptr::copy_nonoverlapping(self.0.as_ptr(), ptr, 16); -+ } -+ } -+ -+ #[inline] -+ fn reduce_sum(&self) -> f32 { -+ self.0.iter().sum() -+ } -+ -+ #[inline] -+ fn reduce_min(&self) -> f32 { -+ self.0.iter().copied().fold(f32::INFINITY, f32::min) -+ } -+ -+ #[inline] -+ fn min(&self, rhs: &Self) -> Self { -+ let mut result = [0.0_f32; 16]; -+ for i in 0..16 { -+ result[i] = self.0[i].min(rhs.0[i]); -+ } -+ Self(result) -+ } -+ -+ #[inline] -+ fn find(&self, val: f32) -> Option { -+ self.0.iter().position(|&x| x == val).map(|i| i as i32) -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl FloatSimd for f32x16 { -+ #[inline] -+ fn multiply_add(&mut self, a: Self, b: Self) { -+ for i in 0..16 { -+ self.0[i] += a.0[i] * b.0[i]; -+ } -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl Add for f32x16 { -+ type Output = Self; -+ -+ #[inline] -+ fn add(self, rhs: Self) -> Self::Output { -+ let mut result = [0.0_f32; 16]; -+ for i in 0..16 { -+ result[i] = self.0[i] + rhs.0[i]; -+ } -+ Self(result) -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl AddAssign for f32x16 { -+ #[inline] -+ fn add_assign(&mut self, rhs: Self) { -+ for i in 0..16 { -+ self.0[i] += rhs.0[i]; -+ } -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl Sub for f32x16 { -+ type Output = Self; -+ -+ #[inline] -+ fn sub(self, rhs: Self) -> Self::Output { -+ let mut result = [0.0_f32; 16]; -+ for i in 0..16 { -+ result[i] = self.0[i] - rhs.0[i]; -+ } -+ Self(result) -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl SubAssign for f32x16 { -+ #[inline] -+ fn sub_assign(&mut self, rhs: Self) { -+ for i in 0..16 { -+ self.0[i] -= rhs.0[i]; -+ } -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl Mul for f32x16 { -+ type Output = Self; -+ -+ #[inline] -+ fn mul(self, rhs: Self) -> Self::Output { -+ let mut result = [0.0_f32; 16]; -+ for i in 0..16 { -+ result[i] = self.0[i] * rhs.0[i]; -+ } -+ Self(result) -+ } -+} -+ - #[cfg(test)] - mod tests { - -diff --git a/rust/lance-linalg/src/simd/f64.rs b/rust/lance-linalg/src/simd/f64.rs -index 129b2f0..a2b5f02 100644 ---- a/rust/lance-linalg/src/simd/f64.rs -+++ b/rust/lance-linalg/src/simd/f64.rs -@@ -33,6 +33,15 @@ pub struct f64x4(float64x2x2_t); - #[derive(Clone, Copy)] - pub struct f64x4(v4f64); - -+#[allow(non_camel_case_types)] -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+#[derive(Clone, Copy)] -+pub struct f64x4([f64; 4]); -+ - impl std::fmt::Debug for f64x4 { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let mut arr = [0.0_f64; 4]; -@@ -60,6 +69,7 @@ impl<'a> From<&'a [f64; 4]> for f64x4 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl SIMD for f64x4 { - fn splat(val: f64) -> Self { - #[cfg(target_arch = "x86_64")] -@@ -231,6 +241,7 @@ impl SIMD for f64x4 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl FloatSimd for f64x4 { - fn multiply_add(&mut self, a: Self, b: Self) { - #[cfg(target_arch = "x86_64")] -@@ -249,6 +260,7 @@ impl FloatSimd for f64x4 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl Add for f64x4 { - type Output = Self; - -@@ -272,6 +284,7 @@ impl Add for f64x4 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl AddAssign for f64x4 { - #[inline] - fn add_assign(&mut self, rhs: Self) { -@@ -291,6 +304,7 @@ impl AddAssign for f64x4 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl Sub for f64x4 { - type Output = Self; - -@@ -314,6 +328,7 @@ impl Sub for f64x4 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl SubAssign for f64x4 { - #[inline] - fn sub_assign(&mut self, rhs: Self) { -@@ -333,6 +348,7 @@ impl SubAssign for f64x4 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl Mul for f64x4 { - type Output = Self; - -@@ -382,6 +398,15 @@ pub struct f64x8(float64x2x2_t, float64x2x2_t); - #[derive(Clone, Copy)] - pub struct f64x8(v4f64, v4f64); - -+#[allow(non_camel_case_types)] -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+#[derive(Clone, Copy)] -+pub struct f64x8([f64; 8]); -+ - impl std::fmt::Debug for f64x8 { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let mut arr = [0.0_f64; 8]; -@@ -409,6 +434,7 @@ impl<'a> From<&'a [f64; 8]> for f64x8 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl SIMD for f64x8 { - #[inline] - fn splat(val: f64) -> Self { -@@ -602,6 +628,7 @@ impl SIMD for f64x8 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl FloatSimd for f64x8 { - #[inline] - fn multiply_add(&mut self, a: Self, b: Self) { -@@ -625,6 +652,7 @@ impl FloatSimd for f64x8 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl Add for f64x8 { - type Output = Self; - -@@ -648,6 +676,7 @@ impl Add for f64x8 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl AddAssign for f64x8 { - #[inline] - fn add_assign(&mut self, rhs: Self) { -@@ -671,6 +700,7 @@ impl AddAssign for f64x8 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl Mul for f64x8 { - type Output = Self; - -@@ -694,6 +724,7 @@ impl Mul for f64x8 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl Sub for f64x8 { - type Output = Self; - -@@ -717,6 +748,7 @@ impl Sub for f64x8 { - } - } - -+#[cfg(any(target_arch = "x86_64", target_arch = "aarch64", target_arch = "loongarch64"))] - impl SubAssign for f64x8 { - #[inline] - fn sub_assign(&mut self, rhs: Self) { -@@ -740,6 +772,338 @@ impl SubAssign for f64x8 { - } - } - -+ -+// --------------------------------------------------------------------------- -+// Portable scalar fallback for architectures with no dedicated SIMD kernel -+// above (e.g. riscv64). Correctness-first, no intrinsics. -+// --------------------------------------------------------------------------- -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl SIMD for f64x4 { -+ #[inline] -+ fn splat(val: f64) -> Self { -+ Self([val; 4]) -+ } -+ -+ #[inline] -+ fn zeros() -> Self { -+ Self([0.0; 4]) -+ } -+ -+ #[inline] -+ unsafe fn load(ptr: *const f64) -> Self { -+ unsafe { Self::load_unaligned(ptr) } -+ } -+ -+ #[inline] -+ unsafe fn load_unaligned(ptr: *const f64) -> Self { -+ let mut arr = [0.0_f64; 4]; -+ unsafe { -+ std::ptr::copy_nonoverlapping(ptr, arr.as_mut_ptr(), 4); -+ } -+ Self(arr) -+ } -+ -+ #[inline] -+ unsafe fn store(&self, ptr: *mut f64) { -+ unsafe { self.store_unaligned(ptr) } -+ } -+ -+ #[inline] -+ unsafe fn store_unaligned(&self, ptr: *mut f64) { -+ unsafe { -+ std::ptr::copy_nonoverlapping(self.0.as_ptr(), ptr, 4); -+ } -+ } -+ -+ #[inline] -+ fn reduce_sum(&self) -> f64 { -+ self.0.iter().sum() -+ } -+ -+ #[inline] -+ fn reduce_min(&self) -> f64 { -+ self.0.iter().copied().fold(f64::INFINITY, f64::min) -+ } -+ -+ #[inline] -+ fn min(&self, rhs: &Self) -> Self { -+ let mut result = [0.0_f64; 4]; -+ for i in 0..4 { -+ result[i] = self.0[i].min(rhs.0[i]); -+ } -+ Self(result) -+ } -+ -+ #[inline] -+ fn find(&self, val: f64) -> Option { -+ self.0.iter().position(|&x| x == val).map(|i| i as i32) -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl FloatSimd for f64x4 { -+ #[inline] -+ fn multiply_add(&mut self, a: Self, b: Self) { -+ for i in 0..4 { -+ self.0[i] += a.0[i] * b.0[i]; -+ } -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl Add for f64x4 { -+ type Output = Self; -+ -+ #[inline] -+ fn add(self, rhs: Self) -> Self::Output { -+ let mut result = [0.0_f64; 4]; -+ for i in 0..4 { -+ result[i] = self.0[i] + rhs.0[i]; -+ } -+ Self(result) -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl AddAssign for f64x4 { -+ #[inline] -+ fn add_assign(&mut self, rhs: Self) { -+ for i in 0..4 { -+ self.0[i] += rhs.0[i]; -+ } -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl Sub for f64x4 { -+ type Output = Self; -+ -+ #[inline] -+ fn sub(self, rhs: Self) -> Self::Output { -+ let mut result = [0.0_f64; 4]; -+ for i in 0..4 { -+ result[i] = self.0[i] - rhs.0[i]; -+ } -+ Self(result) -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl SubAssign for f64x4 { -+ #[inline] -+ fn sub_assign(&mut self, rhs: Self) { -+ for i in 0..4 { -+ self.0[i] -= rhs.0[i]; -+ } -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl Mul for f64x4 { -+ type Output = Self; -+ -+ #[inline] -+ fn mul(self, rhs: Self) -> Self::Output { -+ let mut result = [0.0_f64; 4]; -+ for i in 0..4 { -+ result[i] = self.0[i] * rhs.0[i]; -+ } -+ Self(result) -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl SIMD for f64x8 { -+ #[inline] -+ fn splat(val: f64) -> Self { -+ Self([val; 8]) -+ } -+ -+ #[inline] -+ fn zeros() -> Self { -+ Self([0.0; 8]) -+ } -+ -+ #[inline] -+ unsafe fn load(ptr: *const f64) -> Self { -+ unsafe { Self::load_unaligned(ptr) } -+ } -+ -+ #[inline] -+ unsafe fn load_unaligned(ptr: *const f64) -> Self { -+ let mut arr = [0.0_f64; 8]; -+ unsafe { -+ std::ptr::copy_nonoverlapping(ptr, arr.as_mut_ptr(), 8); -+ } -+ Self(arr) -+ } -+ -+ #[inline] -+ unsafe fn store(&self, ptr: *mut f64) { -+ unsafe { self.store_unaligned(ptr) } -+ } -+ -+ #[inline] -+ unsafe fn store_unaligned(&self, ptr: *mut f64) { -+ unsafe { -+ std::ptr::copy_nonoverlapping(self.0.as_ptr(), ptr, 8); -+ } -+ } -+ -+ #[inline] -+ fn reduce_sum(&self) -> f64 { -+ self.0.iter().sum() -+ } -+ -+ #[inline] -+ fn reduce_min(&self) -> f64 { -+ self.0.iter().copied().fold(f64::INFINITY, f64::min) -+ } -+ -+ #[inline] -+ fn min(&self, rhs: &Self) -> Self { -+ let mut result = [0.0_f64; 8]; -+ for i in 0..8 { -+ result[i] = self.0[i].min(rhs.0[i]); -+ } -+ Self(result) -+ } -+ -+ #[inline] -+ fn find(&self, val: f64) -> Option { -+ self.0.iter().position(|&x| x == val).map(|i| i as i32) -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl FloatSimd for f64x8 { -+ #[inline] -+ fn multiply_add(&mut self, a: Self, b: Self) { -+ for i in 0..8 { -+ self.0[i] += a.0[i] * b.0[i]; -+ } -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl Add for f64x8 { -+ type Output = Self; -+ -+ #[inline] -+ fn add(self, rhs: Self) -> Self::Output { -+ let mut result = [0.0_f64; 8]; -+ for i in 0..8 { -+ result[i] = self.0[i] + rhs.0[i]; -+ } -+ Self(result) -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl AddAssign for f64x8 { -+ #[inline] -+ fn add_assign(&mut self, rhs: Self) { -+ for i in 0..8 { -+ self.0[i] += rhs.0[i]; -+ } -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl Sub for f64x8 { -+ type Output = Self; -+ -+ #[inline] -+ fn sub(self, rhs: Self) -> Self::Output { -+ let mut result = [0.0_f64; 8]; -+ for i in 0..8 { -+ result[i] = self.0[i] - rhs.0[i]; -+ } -+ Self(result) -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl SubAssign for f64x8 { -+ #[inline] -+ fn sub_assign(&mut self, rhs: Self) { -+ for i in 0..8 { -+ self.0[i] -= rhs.0[i]; -+ } -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl Mul for f64x8 { -+ type Output = Self; -+ -+ #[inline] -+ fn mul(self, rhs: Self) -> Self::Output { -+ let mut result = [0.0_f64; 8]; -+ for i in 0..8 { -+ result[i] = self.0[i] * rhs.0[i]; -+ } -+ Self(result) -+ } -+} -+ - #[cfg(test)] - mod tests { - use super::*; -diff --git a/rust/lance-linalg/src/simd/i32.rs b/rust/lance-linalg/src/simd/i32.rs -index 6e08129..0b6dc14 100644 ---- a/rust/lance-linalg/src/simd/i32.rs -+++ b/rust/lance-linalg/src/simd/i32.rs -@@ -38,6 +38,17 @@ pub struct i32x8(int32x4x2_t); - #[derive(Clone, Copy)] - pub struct i32x8(v8i32); - -+/// Portable scalar fallback for architectures with no dedicated SIMD kernel -+/// above (e.g. riscv64). Correctness-first, no intrinsics. -+#[allow(non_camel_case_types)] -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+#[derive(Clone, Copy)] -+pub struct i32x8([i32; 8]); -+ - impl std::fmt::Debug for i32x8 { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - let mut arr = [0; 8]; -@@ -65,6 +76,11 @@ impl From<&[i32; 8]> for i32x8 { - } - } - -+#[cfg(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+))] - impl SIMD for i32x8 { - #[inline] - fn splat(val: i32) -> Self { -@@ -223,6 +239,11 @@ impl SIMD for i32x8 { - } - } - -+#[cfg(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+))] - impl Add for i32x8 { - type Output = Self; - -@@ -246,6 +267,11 @@ impl Add for i32x8 { - } - } - -+#[cfg(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+))] - impl AddAssign for i32x8 { - #[inline] - fn add_assign(&mut self, rhs: Self) { -@@ -265,6 +291,11 @@ impl AddAssign for i32x8 { - } - } - -+#[cfg(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+))] - impl Sub for i32x8 { - type Output = Self; - -@@ -288,6 +319,11 @@ impl Sub for i32x8 { - } - } - -+#[cfg(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+))] - impl SubAssign for i32x8 { - #[inline] - fn sub_assign(&mut self, rhs: Self) { -@@ -307,6 +343,11 @@ impl SubAssign for i32x8 { - } - } - -+#[cfg(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+))] - impl Mul for i32x8 { - type Output = Self; - -@@ -343,6 +384,161 @@ impl Mul for i32x8 { - } - } - -+// Portable scalar fallback for architectures with no dedicated SIMD kernel -+// above (e.g. riscv64). Correctness-first, no intrinsics. Add/Sub/Mul use -+// wrapping arithmetic to mirror the silent-wraparound semantics of the -+// `_mm256_{add,sub,mul}_epi32` / `vaddq_s32` / `lasx_xv{add,sub,mul}_w` -+// instructions above. -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl SIMD for i32x8 { -+ #[inline] -+ fn splat(val: i32) -> Self { -+ Self([val; 8]) -+ } -+ -+ #[inline] -+ fn zeros() -> Self { -+ Self([0; 8]) -+ } -+ -+ #[inline] -+ unsafe fn load(ptr: *const i32) -> Self { -+ unsafe { Self::load_unaligned(ptr) } -+ } -+ -+ #[inline] -+ unsafe fn load_unaligned(ptr: *const i32) -> Self { -+ let mut arr = [0_i32; 8]; -+ unsafe { -+ std::ptr::copy_nonoverlapping(ptr, arr.as_mut_ptr(), 8); -+ } -+ Self(arr) -+ } -+ -+ #[inline] -+ unsafe fn store(&self, ptr: *mut i32) { -+ unsafe { self.store_unaligned(ptr) } -+ } -+ -+ #[inline] -+ unsafe fn store_unaligned(&self, ptr: *mut i32) { -+ unsafe { -+ std::ptr::copy_nonoverlapping(self.0.as_ptr(), ptr, 8); -+ } -+ } -+ -+ #[inline] -+ fn reduce_sum(&self) -> i32 { -+ self.0.iter().sum() -+ } -+ -+ #[inline] -+ fn reduce_min(&self) -> i32 { -+ todo!() -+ } -+ -+ #[inline] -+ fn min(&self, rhs: &Self) -> Self { -+ let mut result = [0_i32; 8]; -+ for i in 0..8 { -+ result[i] = std::cmp::min(self.0[i], rhs.0[i]); -+ } -+ Self(result) -+ } -+ -+ #[inline] -+ fn find(&self, val: i32) -> Option { -+ self.0.iter().position(|&x| x == val).map(|i| i as i32) -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl Add for i32x8 { -+ type Output = Self; -+ -+ #[inline] -+ fn add(self, rhs: Self) -> Self::Output { -+ let mut result = [0_i32; 8]; -+ for i in 0..8 { -+ result[i] = self.0[i].wrapping_add(rhs.0[i]); -+ } -+ Self(result) -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl AddAssign for i32x8 { -+ #[inline] -+ fn add_assign(&mut self, rhs: Self) { -+ for i in 0..8 { -+ self.0[i] = self.0[i].wrapping_add(rhs.0[i]); -+ } -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl Sub for i32x8 { -+ type Output = Self; -+ -+ #[inline] -+ fn sub(self, rhs: Self) -> Self::Output { -+ let mut result = [0_i32; 8]; -+ for i in 0..8 { -+ result[i] = self.0[i].wrapping_sub(rhs.0[i]); -+ } -+ Self(result) -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl SubAssign for i32x8 { -+ #[inline] -+ fn sub_assign(&mut self, rhs: Self) { -+ for i in 0..8 { -+ self.0[i] = self.0[i].wrapping_sub(rhs.0[i]); -+ } -+ } -+} -+ -+#[cfg(not(any( -+ target_arch = "x86_64", -+ target_arch = "aarch64", -+ target_arch = "loongarch64" -+)))] -+impl Mul for i32x8 { -+ type Output = Self; -+ -+ #[inline] -+ fn mul(self, rhs: Self) -> Self::Output { -+ let mut result = [0_i32; 8]; -+ for i in 0..8 { -+ result[i] = self.0[i].wrapping_mul(rhs.0[i]); -+ } -+ Self(result) -+ } -+} -+ - #[cfg(test)] - mod tests { - use super::*; --- -2.50.1 (Apple Git-155) - From 5bf97a46f9db2f3224bf9107572976114b037414 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sat, 10 Oct 2026 08:06:00 +0000 Subject: [PATCH 4/4] lancedb: skip patch/lance-checkout steps for 0.40.0 (no patches) lance >= 13.0.0 (lancedb >= v0.40.0) ships the riscv64 SIMD fallback itself, so patches/lancedb/0.40.0/ doesn't exist and the unconditional git apply/checkout steps would fail on the unexpanded glob. --- .github/workflows/build-lancedb.yml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-lancedb.yml b/.github/workflows/build-lancedb.yml index 813f46d0344..8e4a89e364c 100644 --- a/.github/workflows/build-lancedb.yml +++ b/.github/workflows/build-lancedb.yml @@ -74,7 +74,11 @@ jobs: sparse-checkout: patches/lancedb persist-credentials: false + # lance >= 13.0.0 (lancedb >= v0.40.0) ships the riscv64 SIMD fallback + # itself (lancedb/lance#9446), so those versions carry no patches and + # skip the redirect to a patched lance checkout below. - name: Apply lancedb patches + if: hashFiles(format('python-wheels/patches/lancedb/{0}/*.patch', env.LANCEDB_VERSION)) != '' run: git apply -v python-wheels/patches/lancedb/${{ env.LANCEDB_VERSION }}/0001*.patch # lancedb pins lance-core/lance-linalg to a different exact version per @@ -82,14 +86,16 @@ jobs: # so the sibling checkout below must track whatever this lancedb tag's # own Cargo.toml pins rather than a fixed tag. - name: Determine the lance-core/lance-linalg version lancedb pins + if: hashFiles(format('python-wheels/patches/lancedb/{0}/*.patch', env.LANCEDB_VERSION)) != '' run: echo "LANCE_VERSION=$(grep -m1 '^lance-core = ' Cargo.toml | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')" >> "$GITHUB_ENV" # lance-core's SIMD_SUPPORT static and lance-linalg's f32x8/f32x16/ # i32x8/f64x4/f64x8 SIMD types compile only for x86_64/aarch64/ - # loongarch64 upstream; the 0002 patch adds a portable riscv64 fallback - # to this checkout, and Cargo.toml's `[patch.crates-io]` (0001) redirects - # both crates here. + # loongarch64 before lance 13.0.0; the 0002 patch adds a portable riscv64 + # fallback to this checkout, and Cargo.toml's `[patch.crates-io]` (0001) + # redirects both crates here. - name: Checkout lance v${{ env.LANCE_VERSION }} and add a riscv64 SIMD fallback + if: hashFiles(format('python-wheels/patches/lancedb/{0}/*.patch', env.LANCEDB_VERSION)) != '' uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: repository: lancedb/lance @@ -98,6 +104,7 @@ jobs: persist-credentials: false - name: Apply lance patches + if: hashFiles(format('python-wheels/patches/lancedb/{0}/*.patch', env.LANCEDB_VERSION)) != '' run: cd lance && git apply -v ../python-wheels/patches/lancedb/${{ env.LANCEDB_VERSION }}/0002*.patch - name: Free disk space