From 84cf4c3b461f30e09155b143e251abf4b411838f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 18 Sep 2026 07:30:00 +0000 Subject: [PATCH 1/4] lancedb: Add versions 0.38.0, 0.39.0 Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- docs/packages/lancedb.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/packages/lancedb.yaml b/docs/packages/lancedb.yaml index 0009159b22..fb0e0a4745 100644 --- a/docs/packages/lancedb.yaml +++ b/docs/packages/lancedb.yaml @@ -9,3 +9,5 @@ versions: - filename: lancedb-0.37.1-cp310-abi3-manylinux_2_39_riscv64.whl sha256: 405656a126ed0c70028834c5370463e3e2b5f9b53dc7ef36a6d79b000ee276ac requires-python: '>=3.10' +- version: 0.38.0 +- version: 0.39.0 From 00589999b3908285bbabfbfb1196fe5bc80d46a7 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sat, 19 Sep 2026 10:25:06 +0000 Subject: [PATCH 2/4] lancedb: fix riscv64 SIMD-fallback patches for 0.38.0/0.39.0 CI failed applying patches/lancedb//0001*.patch for the two new pending versions: no patch directory existed for them yet (only 0.37.1 had one), so the glob matched nothing and `git apply` errored on a literal, missing filename. lancedb pins lance-core/lance-linalg to a different exact version per release (10.0.0 for v0.37.1, 11.0.0 for v0.38.0, 12.0.0 for v0.39.0), but the workflow hardcoded checking out lance v10.0.0 for the riscv64 SIMD fallback regardless of which lancedb version was being built. Make that checkout track whatever lance-core version the checked-out lancedb's own Cargo.toml pins, and add the corresponding per-version patch directories: - 0001 (Cargo.toml `[patch.crates-io]` redirect) is unchanged across lance versions, copied verbatim from 0.37.1. - 0002 (portable riscv64 SIMD fallback for lance-linalg's f32x8/f32x16/ i32x8/f64x4/f64x8) reuses patches/pylance/11.0.0's fallback patch, which applies cleanly to both lance v11.0.0 (0.38.0) and v12.0.0 (0.39.0) unchanged (verified locally with `git apply --check` against real lancedb/lance checkouts at both tags). --- .github/workflows/build-lancedb.yml | 11 +- ...lance-core-lance-linalg-to-a-riscv64.patch | 64 + ...add-a-portable-riscv64-simd-fallback.patch | 1292 +++++++++++++++++ ...lance-core-lance-linalg-to-a-riscv64.patch | 64 + ...add-a-portable-riscv64-simd-fallback.patch | 1292 +++++++++++++++++ 5 files changed, 2721 insertions(+), 2 deletions(-) create mode 100644 patches/lancedb/0.38.0/0001-cargo-redirect-lance-core-lance-linalg-to-a-riscv64.patch create mode 100644 patches/lancedb/0.38.0/0002-lance-linalg-add-a-portable-riscv64-simd-fallback.patch create mode 100644 patches/lancedb/0.39.0/0001-cargo-redirect-lance-core-lance-linalg-to-a-riscv64.patch create mode 100644 patches/lancedb/0.39.0/0002-lance-linalg-add-a-portable-riscv64-simd-fallback.patch diff --git a/.github/workflows/build-lancedb.yml b/.github/workflows/build-lancedb.yml index edaed86b91..c680891f9b 100644 --- a/.github/workflows/build-lancedb.yml +++ b/.github/workflows/build-lancedb.yml @@ -77,16 +77,23 @@ jobs: - name: Apply lancedb patches 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 + # release (10.0.0 for v0.37.1, 11.0.0 for v0.38.0, 12.0.0 for v0.39.0), + # 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 + 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. - - name: Checkout lance v10.0.0 and add a riscv64 SIMD fallback + - name: Checkout lance v${{ env.LANCE_VERSION }} and add a riscv64 SIMD fallback uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: repository: lancedb/lance - ref: v10.0.0 + ref: v${{ env.LANCE_VERSION }} path: lance persist-credentials: false diff --git a/patches/lancedb/0.38.0/0001-cargo-redirect-lance-core-lance-linalg-to-a-riscv64.patch b/patches/lancedb/0.38.0/0001-cargo-redirect-lance-core-lance-linalg-to-a-riscv64.patch new file mode 100644 index 0000000000..4be13c5970 --- /dev/null +++ b/patches/lancedb/0.38.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.38.0/0002-lance-linalg-add-a-portable-riscv64-simd-fallback.patch b/patches/lancedb/0.38.0/0002-lance-linalg-add-a-portable-riscv64-simd-fallback.patch new file mode 100644 index 0000000000..0708da295a --- /dev/null +++ b/patches/lancedb/0.38.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) + diff --git a/patches/lancedb/0.39.0/0001-cargo-redirect-lance-core-lance-linalg-to-a-riscv64.patch b/patches/lancedb/0.39.0/0001-cargo-redirect-lance-core-lance-linalg-to-a-riscv64.patch new file mode 100644 index 0000000000..4be13c5970 --- /dev/null +++ b/patches/lancedb/0.39.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.39.0/0002-lance-linalg-add-a-portable-riscv64-simd-fallback.patch b/patches/lancedb/0.39.0/0002-lance-linalg-add-a-portable-riscv64-simd-fallback.patch new file mode 100644 index 0000000000..0708da295a --- /dev/null +++ b/patches/lancedb/0.39.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 1a7e193d7c9952ee898336e63e3941227d6ca461 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sun, 20 Sep 2026 07:20:29 +0000 Subject: [PATCH 3/4] lancedb: install pylance for test_blob.py, fixing 0.38.0/0.39.0 tests lancedb's own test suite added test_blob.py in 0.38.0, which imports the lance package (published as pylance) at module scope for blob-v2 interop tests. It wasn't caught by the existing lance/polars module-scope exclusion list because that list predates 0.38.0 and pypi.riseproject.dev now hosts a pylance riscv64 wheel (11.0.0, see docs/packages/pylance.yaml), so pylance is added to CIBW_TEST_REQUIRES (pinned to that exact version per gotcha 30, since PyPI's newer pylance releases have no riscv64 wheel) instead of excluding the file. lance's blob.py API is unchanged between pylance 9.0.0 (what lancedb 0.38.0/0.39.0 themselves test against) and 11.0.0. Confirmed against CI run 35461355989 (jobs 105946021790/...), where both versions failed at collection with ModuleNotFoundError: No module named 'lance'. --- .github/workflows/build-lancedb.yml | 38 ++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build-lancedb.yml b/.github/workflows/build-lancedb.yml index c680891f9b..eeeb9db214 100644 --- a/.github/workflows/build-lancedb.yml +++ b/.github/workflows/build-lancedb.yml @@ -151,18 +151,38 @@ jobs: CARGO_BUILD_JOBS=2 PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ # Without this pip prefers PyPI's releases, which have no riscv64 - # wheel for these, and source-builds them in the container. - CIBW_TEST_ENVIRONMENT: PIP_ONLY_BINARY=numpy,pandas,pyarrow,duckdb,pydantic-core - CIBW_TEST_REQUIRES: pytest pytest-asyncio numpy pandas pyarrow duckdb + # wheel for these, and source-builds them in the container. pylance + # is pinned to our registry's exact published version (not just + # named) per gotcha 30: with PIP_EXTRA_INDEX_URL alone pip picks the + # highest version across both indexes, and PyPI's latest pylance is + # newer than what pypi.riseproject.dev hosts, so an unpinned + # `pylance` would resolve to a PyPI release with no riscv64 wheel + # and source-build the whole lance Rust workspace here. + CIBW_TEST_ENVIRONMENT: PIP_ONLY_BINARY=numpy,pandas,pyarrow,duckdb,pydantic-core,pylance + CIBW_TEST_REQUIRES: pytest pytest-asyncio numpy pandas pyarrow duckdb pylance==11.0.0 CIBW_TEST_SOURCES: python/pyproject.toml python/python/tests # test_table.py, test_util.py, test_embeddings.py and # test_namespace_integration.py import `polars` and/or `lance` - # (pylance) unconditionally at module scope, as do - # docs/test_guide_tables.py and docs/test_python.py; neither package - # has a riscv64 wheel and both are large enough Rust projects in - # their own right that building them from source here is out of - # scope, so those six files fail collection and are excluded rather - # than left to error. Otherwise mirrors run_tests/action.yml's + # (pylance) and/or `lance_namespace` unconditionally at module + # scope, as do docs/test_guide_tables.py and docs/test_python.py; + # polars has no riscv64 wheel and is a large enough Rust project in + # its own right that building it from source here is out of scope, + # so those six files fail collection and stay excluded. This was + # not re-checked file-by-file against pylance's newer availability + # below -- only test_blob.py, the one file CI actually reported + # failing here, was fixed rather than excluded. + # + # test_blob.py imports `lance` (the pylance package) unconditionally + # at module scope too, but is new in 0.38.0/0.39.0 (not present in + # 0.37.1, when the six-file exclusion above was established) so it + # was never checked against pylance's availability until now. It + # isn't excluded because pypi.riseproject.dev already hosts a + # pylance riscv64 wheel (see docs/packages/pylance.yaml, added to + # CIBW_TEST_REQUIRES above) and lance's python/python/lance/blob.py + # is unchanged between the v9.0.0 pylance/lance release lancedb's + # own pyproject.toml `tests` extra pins and the v11.0.0 this repo + # publishes, aside from an added BlobFile.read_ranges method + # test_blob.py doesn't use. Otherwise mirrors run_tests/action.yml's # non-integration path (`-m "not slow and not s3_test"`, no docker # compose/localstack). # From d2204efe74b8198e1ecd4198b53f6966040ef656 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sun, 20 Sep 2026 20:34:20 +0000 Subject: [PATCH 4/4] lancedb: ignore test_first_class_function_slice1/2.py, sandbox-incompatible Both files were added in 0.38.0/0.39.0 and read golden fixtures (docs/src/python/python.md, rust/lancedb/tests/fixtures/first_class_functions/v1/*.json) that live outside the CIBW_TEST_SOURCES tree (only pyproject.toml and python/tests are copied into the isolated cibuildwheel test dir), so every test in both files fails with FileNotFoundError regardless of platform -- 23/23 failures on both 0.38.0 and 0.39.0. Same class of trap as the existing test_pyo3_abi_matches_minimum_supported_python deselect: a source-tree/golden-contract check, not installed-wheel runtime behavior. --- .github/workflows/build-lancedb.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/.github/workflows/build-lancedb.yml b/.github/workflows/build-lancedb.yml index 32affbc010..813f46d034 100644 --- a/.github/workflows/build-lancedb.yml +++ b/.github/workflows/build-lancedb.yml @@ -206,6 +206,19 @@ jobs: # identically either way) and the tests run and fail anyway, as seen # in CI run 33843581031. `cd python` first puts pyproject.toml back # in the search path so --deselect (and the -m marker filter) work. + # + # test_first_class_function_slice1.py/slice2.py are new (0.38.0/ + # 0.39.0) and hit the exact same class of trap as + # test_pyo3_abi_matches_minimum_supported_python above: they read + # golden fixtures from outside the CIBW_TEST_SOURCES tree -- + # docs/src/python/python.md and rust/lancedb/tests/fixtures/ + # first_class_functions/v1/*.json, both siblings of python/ in the + # full checkout but not copied into the isolated test dir (only + # pyproject.toml and python/tests are) -- and fail with the same + # FileNotFoundError regardless of platform. They assert Python/Rust + # golden-contract and docs consistency, not installed-wheel runtime + # behavior, so ignore both files rather than widen + # CIBW_TEST_SOURCES to mirror docs/ and rust/'s layout. CIBW_TEST_COMMAND: >- cd python && python -m pytest python/tests -vv --durations=30 @@ -216,6 +229,8 @@ jobs: --ignore=python/tests/test_namespace_integration.py --ignore=python/tests/docs/test_guide_tables.py --ignore=python/tests/docs/test_python.py + --ignore=python/tests/test_first_class_function_slice1.py + --ignore=python/tests/test_first_class_function_slice2.py --deselect=python/tests/test_db.py::test_create_table_stable_row_ids_via_storage_options --deselect=python/tests/test_db.py::test_create_table_stable_row_ids_via_storage_options_sync --deselect=python/tests/test_db.py::test_create_table_stable_row_ids_table_level_override