Skip to content
Merged
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ All notable changes to seqproc are documented here. This project follows

## [Unreleased]

- Restore the declared Rust 1.88 compatibility of the x86-64 CPU-floor
diagnostic by isolating CPUID calls behind audited wrappers that compile
with both the older unsafe and newer safe intrinsic signatures.

## [0.1.0] - 2026-08-22

Initial public release. It includes the preprint functionality plus the
Expand Down
31 changes: 31 additions & 0 deletions planning/FIRST_RELEASE_REVIEW_REPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -1337,3 +1337,34 @@ precisely the class of defect the deferred plan-level differential harness
would catch, so a minimal backend-vs-reference differential matrix (even a
few hundred randomized cases per backend) is the highest-value test to add
with the fixes rather than waiting for the full Milestone 9 program.

---

## Post-publication tag validation (2026-08-22)

The release cascade published ANTISEQUENCE 0.1.0 first, changed seqproc to the
checksummed crates.io dependency, merged the reviewed seqproc `dev` branch at
`ec8639bfbd0b50e8ca3dd1de204ffa6a75aa364d`, and then published and tagged
seqproc 0.1.0. Two tag-validation findings require an explicit record:

1. ANTISEQUENCE's first `macos-15` job failed before compilation because its
workflow combined Cargo's mutually exclusive `--lib` and `--doc` target
selectors. PR #6 split these into separate library and rustdoc invocations
and was merged at `1b9990066d068db6b7832d99285b7f10e34d4101`.
The crate source and published package were unaffected, so this does not
require an ANTISEQUENCE patch release.
2. Seqproc's Rust 1.88 job exposed a compiler-signature compatibility issue in
the new CPU-floor diagnostic: Rust 1.88 declares `__cpuid` and
`__cpuid_count` unsafe, while current stable declares them safe. The
implementation now routes those calls through two narrowly scoped wrappers
whose safety argument is that CPUID is guaranteed on x86-64 and optional
leaves/subleaves are interpreted only after maximum-leaf checks. The
wrappers retain `unsafe` for Rust 1.88 and locally allow only the newer
compiler's `unused_unsafe` warning.

The correction passes `cargo +1.88.0 check --locked --all-targets`, current
stable warning-denied Clippy for library/binaries/tests, and the build
provenance tests. Because crates.io packages are immutable, the false MSRV
claim in seqproc 0.1.0 cannot be repaired in place; the source correction is a
candidate for seqproc 0.1.1. Publication of that patch is intentionally held
for the maintainer's explicit version decision.
40 changes: 31 additions & 9 deletions src/build_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,33 @@ pub fn ensure_runtime_cpu_compatible() -> Result<(), CpuCompatibilityError> {
Ok(())
}

#[cfg(target_arch = "x86_64")]
#[inline]
// These intrinsics are unsafe on the declared Rust 1.88 MSRV and safe on
// newer compilers. Keep the compatibility block without warning on either.
#[allow(unused_unsafe)]
fn cpuid(leaf: u32) -> std::arch::x86_64::CpuidResult {
// SAFETY: every x86-64 processor supports the CPUID instruction. The
// intrinsic accepts unsupported leaves and reports zero/vendor-defined
// values; callers below bound optional-leaf interpretation with the
// corresponding maximum-leaf query.
unsafe { std::arch::x86_64::__cpuid(leaf) }
}

#[cfg(target_arch = "x86_64")]
#[inline]
// See `cpuid`: the intrinsic's safety signature changed after our MSRV.
#[allow(unused_unsafe)]
fn cpuid_count(leaf: u32, subleaf: u32) -> std::arch::x86_64::CpuidResult {
// SAFETY: CPUID is guaranteed on x86-64, and ECX is an ordinary subleaf
// selector. Callers below query only architecturally defined subleaves
// after checking the associated maximum-leaf/subleaf value.
unsafe { std::arch::x86_64::__cpuid_count(leaf, subleaf) }
}

#[cfg(target_arch = "x86_64")]
#[inline(never)]
fn missing_required_x86_features() -> Vec<&'static str> {
use std::arch::x86_64::{__cpuid, __cpuid_count};

let target_features = env!("SEQPROC_TARGET_FEATURES");
let x86_64_v3_feature = |feature: &str| {
matches!(
Expand Down Expand Up @@ -125,8 +147,8 @@ fn missing_required_x86_features() -> Vec<&'static str> {
target_features.split(',').any(|item| item == feature)
|| (x86_64_v3_feature(feature) && cfg!(feature = "release-simd"))
};
let leaf0 = __cpuid(0);
let leaf1 = __cpuid(1);
let leaf0 = cpuid(0);
let leaf1 = cpuid(1);
let ecx = leaf1.ecx;
let edx = leaf1.edx;
let mut missing = Vec::new();
Expand Down Expand Up @@ -191,7 +213,7 @@ fn missing_required_x86_features() -> Vec<&'static str> {
}
}
} else {
let leaf7 = __cpuid_count(7, 0);
let leaf7 = cpuid_count(7, 0);
for (bit, feature, name) in [
(3, "bmi1", "bmi1"),
(5, "avx2", "avx2"),
Expand Down Expand Up @@ -234,7 +256,7 @@ fn missing_required_x86_features() -> Vec<&'static str> {
}
}
} else {
let leaf7_1 = __cpuid_count(7, 1);
let leaf7_1 = cpuid_count(7, 1);
for (bit, feature, name) in [(4, "avxvnni", "avxvnni"), (5, "avx512bf16", "avx512bf16")]
{
if required(feature) && leaf7_1.eax & (1 << bit) == 0 {
Expand All @@ -244,15 +266,15 @@ fn missing_required_x86_features() -> Vec<&'static str> {
}
}

let extended_max = __cpuid(0x8000_0000).eax;
let extended_max = cpuid(0x8000_0000).eax;
if extended_max < 0x8000_0001 {
for feature in ["lahfsahf", "lzcnt", "sse4a", "prfchw"] {
if required(feature) {
missing.push(feature);
}
}
} else {
let extended = __cpuid(0x8000_0001);
let extended = cpuid(0x8000_0001);
for (bit, feature, name) in [
(0, "lahfsahf", "lahf/sahf"),
(5, "lzcnt", "lzcnt"),
Expand Down Expand Up @@ -311,7 +333,7 @@ fn missing_required_x86_features() -> Vec<&'static str> {
}
}
} else {
let leaf_d_1 = __cpuid_count(0xD, 1);
let leaf_d_1 = cpuid_count(0xD, 1);
for (bit, feature, name) in [
(0, "xsaveopt", "xsaveopt"),
(1, "xsavec", "xsavec"),
Expand Down