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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ jobs:
strategy:
matrix:
include:
- rust: 1.41.1 # MSRV
- rust: 1.65.0 # MSRV
experimental: false
os: ubuntu-latest
target: x86_64-unknown-linux-gnu
features: cgemm
features: threading cgemm
- rust: stable
experimental: false
os: ubuntu-latest
Expand All @@ -40,7 +40,7 @@ jobs:
os: ubuntu-latest
target: x86_64-unknown-linux-gnu
features: threading cgemm
mmtest_feature: fma
mmtest_feature: avx2,fma
experimental: false
- rust: nightly
os: ubuntu-latest
Expand Down Expand Up @@ -101,7 +101,7 @@ jobs:
strategy:
matrix:
include:
- rust: 1.41.1 # MSRV
- rust: 1.65.0 # MSRV
experimental: false
target: thumbv6m-none-eabi
- rust: stable
Expand Down
16 changes: 7 additions & 9 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
fn main() {
println!("cargo:rerun-if-changed=build.rs");
if std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or(String::new()) == "aarch64" {
match autocfg::AutoCfg::new() {
// From 1.61 aarch64 intrinsics and #[target_feature]
Ok(ac) => if ac.probe_rustc_version(1, 61) {
println!("cargo:rustc-cfg=has_aarch64_simd");
}
Err(err) => println!("cargo:warning={}", err),
}
}
let _ = match autocfg::AutoCfg::new() {
Err(err) => {
println!("cargo:warning={}", err);
return;
},
Ok(ac) => ac,
};
}
4 changes: 2 additions & 2 deletions src/aarch64/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ macro_rules! is_aarch64_feature_detected_ {
// For testing purposes, we can make sure only one specific feature
// is enabled by setting MMTEST_FEATURE=featurename (all others
// disabled). This does not force it to be detected, it must also be.
compile_env_matches_or_is_empty!("MMTEST_FEATURE", $name) && std::arch::is_aarch64_feature_detected!($name)
crate::allow_feature($name) && std::arch::is_aarch64_feature_detected!($name)
}
#[cfg(not(feature="std"))]
{
Expand All @@ -15,7 +15,7 @@ macro_rules! is_aarch64_feature_detected_ {
// be. In the `no_std` case, the `is_86_feature_detected` macro is
// not available, so we have to fall back to checking whether the
// feature is enabled at compile-time.
compile_env_matches_or_is_empty!("MMTEST_FEATURE", $name) && cfg!(target_feature=$name)
crate::allow_feature($name) && cfg!(target_feature=$name)
}
}};
}
11 changes: 0 additions & 11 deletions src/archmacros.rs

This file was deleted.

7 changes: 0 additions & 7 deletions src/cgemm_kernel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ struct KernelAvx2;
struct KernelFma;

#[cfg(target_arch = "aarch64")]
#[cfg(has_aarch64_simd)]
struct KernelNeon;

struct KernelFallback;
Expand All @@ -45,7 +44,6 @@ pub(crate) fn detect<G>(selector: G) where G: GemmSelect<T> {
}
}
#[cfg(target_arch = "aarch64")]
#[cfg(has_aarch64_simd)]
{
if is_aarch64_feature_detected_!("neon") {
return selector.select(KernelNeon);
Expand Down Expand Up @@ -123,7 +121,6 @@ impl GemmKernel for KernelFma {
}

#[cfg(target_arch = "aarch64")]
#[cfg(has_aarch64_simd)]
impl GemmKernel for KernelNeon {
type Elem = T;

Expand Down Expand Up @@ -220,14 +217,11 @@ kernel_fallback_impl_complex! {
// Kernel neon

#[cfg(target_arch = "aarch64")]
#[cfg(has_aarch64_simd)]
macro_rules! loop_m { ($i:ident, $e:expr) => { loop4!($i, $e) }; }
#[cfg(target_arch = "aarch64")]
#[cfg(has_aarch64_simd)]
macro_rules! loop_n { ($j:ident, $e:expr) => { loop2!($j, $e) }; }

#[cfg(target_arch = "aarch64")]
#[cfg(has_aarch64_simd)]
kernel_fallback_impl_complex! {
[inline target_feature(enable="neon")] [fma_yes]
kernel_target_neon, T, TReal, KernelNeon::MR, KernelNeon::NR, 1
Expand Down Expand Up @@ -259,7 +253,6 @@ mod tests {
}

#[cfg(target_arch = "aarch64")]
#[cfg(has_aarch64_simd)]
mod test_kernel_aarch64 {
use super::test_complex_packed_kernel;
use super::super::*;
Expand Down
57 changes: 57 additions & 0 deletions src/constfind.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//! Copyright 2026 Ulrik Sverdrup "bluss"

pub(crate) const fn slice_eq(a: &[u8], b: &[u8]) -> bool {
if a.len() != b.len() { return false; }
let mut i = 0;
while i < a.len() {
if a[i] != b[i] { return false; }
i += 1;
}
true
}

const fn find_byte(text: &[u8], byte: u8) -> Option<usize> {
let mut j = 0;
while j < text.len() && text[j] != byte {
j += 1;
}
if j == text.len() { None } else { Some(j) }
}

/// Search for exact word match in string of comma separated words
///
/// Example "avx2,fma", "avx2" => true; "avx2,fma", "avx" => false
pub(crate) const fn comma_separated_contains(text: &str, word: &str) -> bool {
let mut text = text.as_bytes();
loop {
let next_comma = find_byte(text, b',');
let word_end = match next_comma { Some(x) => x, None => text.len() };
let (this_word, _) = text.split_at(word_end);
if slice_eq(this_word, word.as_bytes()) {
return true;
}

// take next segment
if let None = next_comma {
return false;
}
let (_, tail) = text.split_at(word_end + 1);
text = tail;
}
}

#[test]
fn test_find_byte() {
assert_eq!(find_byte(b"abc", b'z'), None);
assert_eq!(find_byte(b"abc", b'b'), Some(1));
}

#[test]
fn test_comma_separated_contains() {
assert!(comma_separated_contains("abc,xyz", "abc"));
assert!(comma_separated_contains("abc,xyz", "xyz"));
assert!(!comma_separated_contains("abc,xyz", "abc,"));
assert!(!comma_separated_contains("avx2,fma", "avx"));
}


5 changes: 0 additions & 5 deletions src/dgemm_kernel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ struct KernelFma;
struct KernelSse2;

#[cfg(target_arch="aarch64")]
#[cfg(has_aarch64_simd)]
struct KernelNeon;

struct KernelFallback;
Expand Down Expand Up @@ -62,7 +61,6 @@ pub(crate) fn detect<G>(selector: G) where G: GemmSelect<T> {
}

#[cfg(target_arch="aarch64")]
#[cfg(has_aarch64_simd)]
{
if is_aarch64_feature_detected_!("neon") {
return selector.select(KernelNeon);
Expand Down Expand Up @@ -236,7 +234,6 @@ impl GemmKernel for KernelSse2 {
}

#[cfg(target_arch="aarch64")]
#[cfg(has_aarch64_simd)]
impl GemmKernel for KernelNeon {
type Elem = T;

Expand Down Expand Up @@ -884,7 +881,6 @@ unsafe fn kernel_x86_avx<MA>(k: usize, alpha: T, a: *const T, b: *const T,
}

#[cfg(target_arch="aarch64")]
#[cfg(has_aarch64_simd)]
#[target_feature(enable="neon")]
unsafe fn kernel_target_neon(k: usize, alpha: T, a: *const T, b: *const T,
beta: T, c: *mut T, rsc: isize, csc: isize)
Expand Down Expand Up @@ -1069,7 +1065,6 @@ mod tests {
}

#[cfg(any(target_arch="aarch64"))]
#[cfg(has_aarch64_simd)]
mod test_kernel_aarch64 {
use super::test_a_kernel;
use super::super::*;
Expand Down
11 changes: 6 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,12 @@
//! The functions in this crate are thread safe, as long as the destination
//! matrix is distinct.
//!
//! ## Rust Version
//! ## Rust Version (MSRV)
//!
//! This version requires Rust 1.41.1 or later; the crate follows a carefully
//! This version requires Rust 1.65 or later; the crate follows a carefully
//! considered upgrade policy, where updating the minimum Rust version is not a breaking
//! change.
//!
//! Some features are enabled with later versions: from Rust 1.61 AArch64 NEON support.

#![doc(html_root_url = "https://docs.rs/matrixmultiply/0.3/")]
#![cfg_attr(not(feature = "std"), no_std)]
Expand All @@ -145,6 +144,7 @@ mod loopmacros;

mod archparam_defaults;

mod constfind;
#[cfg(feature = "constconf")]
mod archparam;
#[cfg(feature = "constconf")]
Expand All @@ -162,8 +162,7 @@ mod threading;
mod aligned_alloc;
mod util;

#[macro_use]
mod archmacros;
mod target_features;
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
#[macro_use]
mod x86;
Expand Down Expand Up @@ -191,3 +190,5 @@ pub use crate::gemm::cgemm;
pub use crate::gemm::zgemm;
#[cfg(feature = "cgemm")]
pub use crate::gemm::CGemmOption;

pub(crate) use crate::target_features::allow_feature;
27 changes: 13 additions & 14 deletions src/sgemm_kernel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ struct KernelFma;
struct KernelSse2;

#[cfg(target_arch="aarch64")]
#[cfg(has_aarch64_simd)]
struct KernelNeon;
#[cfg(all(target_arch="wasm32", target_feature="simd128"))]
struct KernelWasmSimd;
Expand Down Expand Up @@ -61,7 +60,6 @@ pub(crate) fn detect<G>(selector: G) where G: GemmSelect<T> {
}
}
#[cfg(target_arch="aarch64")]
#[cfg(has_aarch64_simd)]
{
if is_aarch64_feature_detected_!("neon") {
return selector.select(KernelNeon);
Expand Down Expand Up @@ -226,7 +224,6 @@ impl GemmKernel for KernelSse2 {


#[cfg(target_arch="aarch64")]
#[cfg(has_aarch64_simd)]
impl GemmKernel for KernelNeon {
type Elem = T;

Expand Down Expand Up @@ -541,7 +538,6 @@ unsafe fn kernel_x86_avx<MA>(k: usize, alpha: T, a: *const T, b: *const T,
}

#[cfg(target_arch="aarch64")]
#[cfg(has_aarch64_simd)]
#[target_feature(enable="neon")]
unsafe fn kernel_target_neon(k: usize, alpha: T, a: *const T, b: *const T,
beta: T, c: *mut T, rsc: isize, csc: isize)
Expand Down Expand Up @@ -879,7 +875,6 @@ mod tests {
}

#[cfg(any(target_arch="aarch64"))]
#[cfg(has_aarch64_simd)]
mod test_kernel_aarch64 {
use super::test_a_kernel;
use super::super::*;
Expand Down Expand Up @@ -957,16 +952,20 @@ mod tests {
// skip
return;
}
let feature_name = option_env!("MMTEST_FEATURE")
let feature_names = option_env!("MMTEST_FEATURE")
.expect("No MMTEST_FEATURE configured!");
let detected = match feature_name {
"avx" => is_x86_feature_detected_!("avx"),
"fma" => is_x86_feature_detected_!("fma"),
"sse2" => is_x86_feature_detected_!("sse2"),
_ => false,
};
assert!(detected, "Feature {:?} was not detected, so it could not be tested",
feature_name);
for feature_name in feature_names.split(",") {
let detected = match feature_name {
"sse2" => is_x86_feature_detected_!("sse2"),
"avx" => is_x86_feature_detected_!("avx"),
"fma" => is_x86_feature_detected_!("fma"),
"avx2" => is_x86_feature_detected_!("avx2"),
"avx512f" => is_x86_feature_detected_!("avx512f"),
_ => panic!("Unknown feature {:?}", feature_name),
};
assert!(detected, "Feature {:?} was not detected, so it could not be tested",
feature_name);
}
}
}
}
30 changes: 30 additions & 0 deletions src/target_features.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@

/// Is the target feature allowed currently?
///
/// The environment variable MMTEST_FEATURE is read at compile-time.
/// Used for testing only - if the environment variable is non-empty,
/// **only** features listed, comma-separated, are allowed to be detected,
/// all other are disabled.
///
/// This is internal only, not stable.
pub(crate) const fn allow_feature(feature: &str) -> bool {
match option_env!("MMTEST_FEATURE") {
None => true,
Some(s) if s.is_empty() => true,
Some(value) => crate::constfind::comma_separated_contains(value, feature),
}
}



#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch="aarch64"))]
#[test]
fn test_features() {
// This is just a test you can run to see the effect
// of environment variable parsing.
let features = &["sse2", "avx", "avx2", "fma", "avx512f", "neon"];
for feat in features {
println!(r#"feature= {:12} allowed= {}"#, feat, allow_feature(feat));

}
}
4 changes: 2 additions & 2 deletions src/x86/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ macro_rules! is_x86_feature_detected_ {
// For testing purposes, we can make sure only one specific feature
// is enabled by setting MMTEST_FEATURE=featurename (all others
// disabled). This does not force it to be detected, it must also be.
compile_env_matches_or_is_empty!("MMTEST_FEATURE", $name) && is_x86_feature_detected!($name)
crate::allow_feature($name) && is_x86_feature_detected!($name)
}
#[cfg(not(feature="std"))]
{
Expand All @@ -15,7 +15,7 @@ macro_rules! is_x86_feature_detected_ {
// be. In the `no_std` case, the `is_86_feature_detected` macro is
// not available, so we have to fall back to checking whether the
// feature is enabled at compile-time.
compile_env_matches_or_is_empty!("MMTEST_FEATURE", $name) && cfg!(target_feature=$name)
crate::allow_feature($name) && cfg!(target_feature=$name)
}
}};
}
Loading
Loading