From f78622134977576d6c4ed4cc26b7ecec0c7520a1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 19 Aug 2026 22:49:14 +0200 Subject: [PATCH 1/4] assert_instr: add support for explicit exclusion of bad instructions --- crates/assert-instr-macro/src/lib.rs | 85 +++++++++++++++++----------- crates/core_arch/src/x86/sse2.rs | 4 +- crates/stdarch-test/src/lib.rs | 10 +++- 3 files changed, 63 insertions(+), 36 deletions(-) diff --git a/crates/assert-instr-macro/src/lib.rs b/crates/assert-instr-macro/src/lib.rs index 839aae67cb..224e8b6e62 100644 --- a/crates/assert-instr-macro/src/lib.rs +++ b/crates/assert-instr-macro/src/lib.rs @@ -35,6 +35,7 @@ pub fn assert_instr( }; let instr = &invoc.instr; + let not = invoc.not; let name = &func.sig.ident; let maybe_allow_deprecated = if func .attrs @@ -179,7 +180,7 @@ pub fn assert_instr( fn #assert_name() { #to_test - ::stdarch_test::assert(#shim_name as usize, stringify!(#shim_name), #instr); + ::stdarch_test::assert(#shim_name as usize, stringify!(#shim_name), #instr, &[#(#not),*]); } }; @@ -192,51 +193,71 @@ pub fn assert_instr( struct Invoc { instr: String, + not: Vec, args: Vec<(syn::Ident, syn::Expr)>, } +fn parse_instr(input: syn::parse::ParseStream<'_>) -> syn::Result { + use syn::{Token, ext::IdentExt}; + + let mut instr = String::new(); + while !input.is_empty() { + if let Ok(ident) = syn::Ident::parse_any(input) { + instr.push_str(&ident.to_string()); + continue; + } + if input.parse::().is_ok() { + instr.push('.'); + continue; + } + if let Ok(s) = input.parse::() { + instr.push_str(&s.value()); + continue; + } + // Some other token, must be start of the next thing. + break; + } + if instr.is_empty() { + return Err(input.error("expected an instruction")); + } + Ok(instr) +} + impl syn::parse::Parse for Invoc { fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result { - use syn::{Token, ext::IdentExt}; + use syn::{Token, parenthesized}; + + let instr = parse_instr(input)?; + let mut not = Vec::new(); + let mut args = Vec::new(); - let mut instr = String::new(); while !input.is_empty() { - if input.parse::().is_ok() { - break; - } - if let Ok(ident) = syn::Ident::parse_any(input) { - instr.push_str(&ident.to_string()); - continue; - } - if input.parse::().is_ok() { - instr.push('.'); - continue; + // Parse the comma after the previous thing + if input.parse::().is_err() { + return Err(input.error("extra tokens at end")); } - if let Ok(s) = input.parse::() { - instr.push_str(&s.value()); + + // This is either `not(instr)` or `arg = val`. + // We treat `not` as a magic identifier here. + + let name = input.parse::()?; + + if name == "not" { + let content; + parenthesized!(content in input); + let not_instr = parse_instr(&content)?; + if !content.is_empty() { + return Err(input.error("expected just an instruction in `not(...)`")); + } + not.push(not_instr); continue; } - println!("{:?}", input.cursor().token_stream()); - return Err(input.error("expected an instruction")); - } - if instr.is_empty() { - return Err(input.error("expected an instruction before comma")); - } - let mut args = Vec::new(); - while !input.is_empty() { - let name = input.parse::()?; + input.parse::()?; let expr = input.parse::()?; args.push((name, expr)); - - if input.parse::().is_err() { - if !input.is_empty() { - return Err(input.error("extra tokens at end")); - } - break; - } } - Ok(Self { instr, args }) + Ok(Self { instr, not, args }) } } diff --git a/crates/core_arch/src/x86/sse2.rs b/crates/core_arch/src/x86/sse2.rs index 50e56005c9..1ced0bc9a4 100644 --- a/crates/core_arch/src/x86/sse2.rs +++ b/crates/core_arch/src/x86/sse2.rs @@ -1496,7 +1496,7 @@ pub const fn _mm_move_epi64(a: __m128i) -> __m128i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packs_epi16) #[inline] #[target_feature(enable = "sse2")] -#[cfg_attr(test, assert_instr(packsswb))] +#[cfg_attr(test, assert_instr(packsswb, not(pminsw), not(pmaxsw)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm_packs_epi16(a: __m128i, b: __m128i) -> __m128i { unsafe { transmute(packsswb(a.as_i16x8(), b.as_i16x8())) } @@ -1520,7 +1520,7 @@ pub fn _mm_packs_epi32(a: __m128i, b: __m128i) -> __m128i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packus_epi16) #[inline] #[target_feature(enable = "sse2")] -#[cfg_attr(test, assert_instr(packuswb))] +#[cfg_attr(test, assert_instr(packuswb, not(pminsw), not(pmaxsw)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm_packus_epi16(a: __m128i, b: __m128i) -> __m128i { unsafe { transmute(packuswb(a.as_i16x8(), b.as_i16x8())) } diff --git a/crates/stdarch-test/src/lib.rs b/crates/stdarch-test/src/lib.rs index 9ef25a7957..35f6454aa4 100644 --- a/crates/stdarch-test/src/lib.rs +++ b/crates/stdarch-test/src/lib.rs @@ -54,7 +54,7 @@ impl hash::Hash for Function { /// /// This asserts that the function at `fnptr` contains the instruction /// `expected` provided. -pub fn assert(shim_addr: usize, fnname: &str, expected: &str) { +pub fn assert(shim_addr: usize, fnname: &str, expected: &str, not: &[&str]) { // Make sure that the shim is not removed black_box(shim_addr); @@ -100,6 +100,10 @@ pub fn assert(shim_addr: usize, fnname: &str, expected: &str) { // && !instruction[expected.len()..].starts_with(|c: char| c.is_ascii_alphanumeric()) }); + let found_bad = not + .iter() + .find(|not| instrs.iter().any(|instr| instr.starts_with(**not))); + // Look for subroutine call instructions in the disassembly to detect whether // inlining failed: all intrinsics are `#[inline(always)]`, so calling one // intrinsic from another should not generate subroutine call instructions. @@ -191,7 +195,7 @@ pub fn assert(shim_addr: usize, fnname: &str, expected: &str) { ); let probably_only_one_instruction = instrs.len() < instruction_limit; - if found && probably_only_one_instruction && !inlining_failed { + if found && found_bad.is_none() && probably_only_one_instruction && !inlining_failed { return; } @@ -204,6 +208,8 @@ pub fn assert(shim_addr: usize, fnname: &str, expected: &str) { if !found { panic!("failed to find instruction `{expected}` in the disassembly"); + } else if let Some(bad) = found_bad { + panic!("instruction found, but the disassembly also contains the bad instructions `{bad}`"); } else if !probably_only_one_instruction { panic!( "instruction found, but the disassembly contains too many \ From 373a0de16298c23478be57512d6dd29f2d1172eb Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 19 Aug 2026 23:06:24 +0200 Subject: [PATCH 2/4] make assert_instr total instruction limit configurable per-test --- crates/assert-instr-macro/src/lib.rs | 31 ++++++- crates/core_arch/src/x86/avx2.rs | 8 +- crates/core_arch/src/x86/sse2.rs | 6 +- crates/core_arch/src/x86/sse41.rs | 2 +- crates/stdarch-test/src/lib.rs | 123 +++++++++++++-------------- 5 files changed, 96 insertions(+), 74 deletions(-) diff --git a/crates/assert-instr-macro/src/lib.rs b/crates/assert-instr-macro/src/lib.rs index 224e8b6e62..47f58a8533 100644 --- a/crates/assert-instr-macro/src/lib.rs +++ b/crates/assert-instr-macro/src/lib.rs @@ -36,6 +36,11 @@ pub fn assert_instr( let instr = &invoc.instr; let not = invoc.not; + let limit = match invoc.limit { + None => quote! { None }, + Some(l) => quote! { Some(#l) }, + }; + let name = &func.sig.ident; let maybe_allow_deprecated = if func .attrs @@ -180,7 +185,7 @@ pub fn assert_instr( fn #assert_name() { #to_test - ::stdarch_test::assert(#shim_name as usize, stringify!(#shim_name), #instr, &[#(#not),*]); + ::stdarch_test::assert(#shim_name as usize, stringify!(#shim_name), #instr, &[#(#not),*], #limit); } }; @@ -194,6 +199,7 @@ pub fn assert_instr( struct Invoc { instr: String, not: Vec, + limit: Option, args: Vec<(syn::Ident, syn::Expr)>, } @@ -229,6 +235,7 @@ impl syn::parse::Parse for Invoc { let instr = parse_instr(input)?; let mut not = Vec::new(); + let mut limit = None; let mut args = Vec::new(); while !input.is_empty() { @@ -236,9 +243,13 @@ impl syn::parse::Parse for Invoc { if input.parse::().is_err() { return Err(input.error("extra tokens at end")); } + // Handle trailing comma. + if input.is_empty() { + break; + } - // This is either `not(instr)` or `arg = val`. - // We treat `not` as a magic identifier here. + // This is either `not(instr)` or `limit(n)` or `arg = val`. + // We treat `not` and `limit` as a magic identifier here. let name = input.parse::()?; @@ -252,12 +263,24 @@ impl syn::parse::Parse for Invoc { not.push(not_instr); continue; } + if name == "limit" { + let content; + parenthesized!(content in input); + let n = content.parse::()?; + limit = Some(n); + continue; + } input.parse::()?; let expr = input.parse::()?; args.push((name, expr)); } - Ok(Self { instr, not, args }) + Ok(Self { + instr, + not, + limit, + args, + }) } } diff --git a/crates/core_arch/src/x86/avx2.rs b/crates/core_arch/src/x86/avx2.rs index e2c3865810..249c09ebce 100644 --- a/crates/core_arch/src/x86/avx2.rs +++ b/crates/core_arch/src/x86/avx2.rs @@ -2321,7 +2321,7 @@ pub const fn _mm256_or_si256(a: __m256i, b: __m256i) -> __m256i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_packs_epi16) #[inline] #[target_feature(enable = "avx2")] -#[cfg_attr(test, assert_instr(vpacksswb))] +#[cfg_attr(test, assert_instr(vpacksswb, limit(2)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm256_packs_epi16(a: __m256i, b: __m256i) -> __m256i { unsafe { transmute(packsswb(a.as_i16x16(), b.as_i16x16())) } @@ -2333,7 +2333,7 @@ pub fn _mm256_packs_epi16(a: __m256i, b: __m256i) -> __m256i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_packs_epi32) #[inline] #[target_feature(enable = "avx2")] -#[cfg_attr(test, assert_instr(vpackssdw))] +#[cfg_attr(test, assert_instr(vpackssdw, limit(2)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm256_packs_epi32(a: __m256i, b: __m256i) -> __m256i { unsafe { transmute(packssdw(a.as_i32x8(), b.as_i32x8())) } @@ -2345,7 +2345,7 @@ pub fn _mm256_packs_epi32(a: __m256i, b: __m256i) -> __m256i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_packus_epi16) #[inline] #[target_feature(enable = "avx2")] -#[cfg_attr(test, assert_instr(vpackuswb))] +#[cfg_attr(test, assert_instr(vpackuswb, limit(2)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm256_packus_epi16(a: __m256i, b: __m256i) -> __m256i { unsafe { transmute(packuswb(a.as_i16x16(), b.as_i16x16())) } @@ -2357,7 +2357,7 @@ pub fn _mm256_packus_epi16(a: __m256i, b: __m256i) -> __m256i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_packus_epi32) #[inline] #[target_feature(enable = "avx2")] -#[cfg_attr(test, assert_instr(vpackusdw))] +#[cfg_attr(test, assert_instr(vpackusdw, limit(2)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm256_packus_epi32(a: __m256i, b: __m256i) -> __m256i { unsafe { transmute(packusdw(a.as_i32x8(), b.as_i32x8())) } diff --git a/crates/core_arch/src/x86/sse2.rs b/crates/core_arch/src/x86/sse2.rs index 1ced0bc9a4..2e62687eab 100644 --- a/crates/core_arch/src/x86/sse2.rs +++ b/crates/core_arch/src/x86/sse2.rs @@ -1496,7 +1496,7 @@ pub const fn _mm_move_epi64(a: __m128i) -> __m128i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packs_epi16) #[inline] #[target_feature(enable = "sse2")] -#[cfg_attr(test, assert_instr(packsswb, not(pminsw), not(pmaxsw)))] +#[cfg_attr(test, assert_instr(packsswb, limit(2), not(pminsw), not(pmaxsw)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm_packs_epi16(a: __m128i, b: __m128i) -> __m128i { unsafe { transmute(packsswb(a.as_i16x8(), b.as_i16x8())) } @@ -1508,7 +1508,7 @@ pub fn _mm_packs_epi16(a: __m128i, b: __m128i) -> __m128i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packs_epi32) #[inline] #[target_feature(enable = "sse2")] -#[cfg_attr(test, assert_instr(packssdw))] +#[cfg_attr(test, assert_instr(packssdw, limit(2)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm_packs_epi32(a: __m128i, b: __m128i) -> __m128i { unsafe { transmute(packssdw(a.as_i32x4(), b.as_i32x4())) } @@ -1520,7 +1520,7 @@ pub fn _mm_packs_epi32(a: __m128i, b: __m128i) -> __m128i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packus_epi16) #[inline] #[target_feature(enable = "sse2")] -#[cfg_attr(test, assert_instr(packuswb, not(pminsw), not(pmaxsw)))] +#[cfg_attr(test, assert_instr(packuswb, limit(2), not(pminsw), not(pmaxsw)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm_packus_epi16(a: __m128i, b: __m128i) -> __m128i { unsafe { transmute(packuswb(a.as_i16x8(), b.as_i16x8())) } diff --git a/crates/core_arch/src/x86/sse41.rs b/crates/core_arch/src/x86/sse41.rs index 063de0d592..2f42b34992 100644 --- a/crates/core_arch/src/x86/sse41.rs +++ b/crates/core_arch/src/x86/sse41.rs @@ -424,7 +424,7 @@ pub const fn _mm_min_epu32(a: __m128i, b: __m128i) -> __m128i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packus_epi32) #[inline] #[target_feature(enable = "sse4.1")] -#[cfg_attr(test, assert_instr(packusdw))] +#[cfg_attr(test, assert_instr(packusdw, limit(2)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm_packus_epi32(a: __m128i, b: __m128i) -> __m128i { unsafe { transmute(packusdw(a.as_i32x4(), b.as_i32x4())) } diff --git a/crates/stdarch-test/src/lib.rs b/crates/stdarch-test/src/lib.rs index 35f6454aa4..8c07610d23 100644 --- a/crates/stdarch-test/src/lib.rs +++ b/crates/stdarch-test/src/lib.rs @@ -54,7 +54,7 @@ impl hash::Hash for Function { /// /// This asserts that the function at `fnptr` contains the instruction /// `expected` provided. -pub fn assert(shim_addr: usize, fnname: &str, expected: &str, not: &[&str]) { +pub fn assert(shim_addr: usize, fnname: &str, expected: &str, not: &[&str], limit: Option) { // Make sure that the shim is not removed black_box(shim_addr); @@ -131,68 +131,67 @@ pub fn assert(shim_addr: usize, fnname: &str, expected: &str, not: &[&str]) { let instruction_limit = std::env::var("STDARCH_ASSERT_INSTR_LIMIT") .ok() - .map_or_else( - || match expected { - // `cpuid` returns a pretty big aggregate structure, so exempt - // it from the slightly more restrictive 22 instructions below. - "cpuid" => 30, - - // These require 8 loads and stores, so it _just_ overflows the limit - "aesencwide128kl" | "aesencwide256kl" | "aesdecwide128kl" | "aesdecwide256kl" => 24, - - // Apparently, on Windows, LLVM generates a bunch of - // saves/restores of xmm registers around these instructions, - // which exceeds the limit of 20 below. As it seems dictated by - // Windows's ABI (I believe?), we probably can't do much - // about it. - "vzeroall" | "vzeroupper" if cfg!(windows) => 30, - - // Intrinsics using `cvtpi2ps` are typically "composites" and - // in some cases exceed the limit. - "cvtpi2ps" => 25, - // core_arch/src/arm_shared/simd32 - // vfmaq_n_f32_vfma : #instructions = 26 >= 22 (limit) - "usad8" | "vfma" | "vfms" => 27, - "qadd8" | "qsub8" | "sadd8" | "sel" | "shadd8" | "shsub8" | "usub8" | "ssub8" => 29, - // core_arch/src/arm_shared/simd32 - // vst1q_s64_x4_vst1 : #instructions = 27 >= 22 (limit) - "vld3" => 28, - // core_arch/src/arm_shared/simd32 - // vld4q_lane_u32_vld4 : #instructions = 36 >= 22 (limit) - "vld4" => 37, - // core_arch/src/arm_shared/simd32 - // vst1q_s64_x4_vst1 : #instructions = 40 >= 22 (limit) - "vst1" => 41, - // core_arch/src/arm_shared/simd32 - // vst3q_u32_vst3 : #instructions = 25 >= 22 (limit) - "vst3" => 26, - // core_arch/src/arm_shared/simd32 - // vst4q_u32_vst4 : #instructions = 33 >= 22 (limit) - "vst4" => 34, - - // core_arch/src/arm_shared/simd32 - // vst1q_p64_x4_nop : #instructions = 33 >= 22 (limit) - "nop" if fnname.contains("vst1q_p64") => 34, - - // AMX intrinsics generate a lot of move instructions to load/store the tile registers - // due to Rust ABI - _ if fnname.contains("___tile") => 165, - - // Original limit was 20 instructions, but ARM DSP Intrinsics - // are exactly 20 instructions long. So, bump the limit to 22 - // instead of adding here a long list of exceptions. - _ => { - // aarch64_be may add reverse instructions which increases - // the number of instructions generated. - if cfg!(all(target_endian = "big", target_arch = "aarch64")) { - 32 - } else { - 22 - } + .map(|v| v.parse().unwrap()) + .or(limit.map(|n| n + 1)) // adjust for the `<` below: `limit = 2` means 2 instructions is okay + .unwrap_or_else(|| match expected { + // `cpuid` returns a pretty big aggregate structure, so exempt + // it from the slightly more restrictive 22 instructions below. + "cpuid" => 30, + + // These require 8 loads and stores, so it _just_ overflows the limit + "aesencwide128kl" | "aesencwide256kl" | "aesdecwide128kl" | "aesdecwide256kl" => 24, + + // Apparently, on Windows, LLVM generates a bunch of + // saves/restores of xmm registers around these instructions, + // which exceeds the limit of 20 below. As it seems dictated by + // Windows's ABI (I believe?), we probably can't do much + // about it. + "vzeroall" | "vzeroupper" if cfg!(windows) => 30, + + // Intrinsics using `cvtpi2ps` are typically "composites" and + // in some cases exceed the limit. + "cvtpi2ps" => 25, + // core_arch/src/arm_shared/simd32 + // vfmaq_n_f32_vfma : #instructions = 26 >= 22 (limit) + "usad8" | "vfma" | "vfms" => 27, + "qadd8" | "qsub8" | "sadd8" | "sel" | "shadd8" | "shsub8" | "usub8" | "ssub8" => 29, + // core_arch/src/arm_shared/simd32 + // vst1q_s64_x4_vst1 : #instructions = 27 >= 22 (limit) + "vld3" => 28, + // core_arch/src/arm_shared/simd32 + // vld4q_lane_u32_vld4 : #instructions = 36 >= 22 (limit) + "vld4" => 37, + // core_arch/src/arm_shared/simd32 + // vst1q_s64_x4_vst1 : #instructions = 40 >= 22 (limit) + "vst1" => 41, + // core_arch/src/arm_shared/simd32 + // vst3q_u32_vst3 : #instructions = 25 >= 22 (limit) + "vst3" => 26, + // core_arch/src/arm_shared/simd32 + // vst4q_u32_vst4 : #instructions = 33 >= 22 (limit) + "vst4" => 34, + + // core_arch/src/arm_shared/simd32 + // vst1q_p64_x4_nop : #instructions = 33 >= 22 (limit) + "nop" if fnname.contains("vst1q_p64") => 34, + + // AMX intrinsics generate a lot of move instructions to load/store the tile registers + // due to Rust ABI + _ if fnname.contains("___tile") => 165, + + // Original limit was 20 instructions, but ARM DSP Intrinsics + // are exactly 20 instructions long. So, bump the limit to 22 + // instead of adding here a long list of exceptions. + _ => { + // aarch64_be may add reverse instructions which increases + // the number of instructions generated. + if cfg!(all(target_endian = "big", target_arch = "aarch64")) { + 32 + } else { + 22 } - }, - |v| v.parse().unwrap(), - ); + } + }); let probably_only_one_instruction = instrs.len() < instruction_limit; if found && found_bad.is_none() && probably_only_one_instruction && !inlining_failed { From 38a6da0343dc06183d4ca9e7cebdab0cb47076bd Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 20 Aug 2026 07:50:03 +0200 Subject: [PATCH 3/4] make x86-64 asm more consistent --- ci/run.sh | 8 +++++++- crates/core_arch/src/x86/avx2.rs | 8 ++++---- crates/core_arch/src/x86/sse2.rs | 6 +++--- crates/core_arch/src/x86/sse41.rs | 2 +- 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/ci/run.sh b/ci/run.sh index 8c75df6afb..eb0b60fc3e 100755 --- a/ci/run.sh +++ b/ci/run.sh @@ -32,8 +32,14 @@ case ${TARGET} in ;; # Some x86_64 targets enable by default more features beyond SSE2, # which cause some instruction assertion checks to fail. + # (This does not disable tests that need more features, they get enabled based on + # what the host actually supports.) x86_64-*) - export RUSTFLAGS="${RUSTFLAGS} -C target-feature=-sse3" + # We want frame pointers to be consistent across targets. On the ios_macabi target + # we cannot turn them off, so let's turn them on everywhere. + # If we ever turn these off, a bunch of `limit(...)` clauses should be reduced to + # avoid tests becoming less strict! + export RUSTFLAGS="${RUSTFLAGS} -C target-feature=-sse3 -Cforce-frame-pointers=on" ;; #Unoptimized build uses fast-isel which breaks with msa mips-* | mipsel-*) diff --git a/crates/core_arch/src/x86/avx2.rs b/crates/core_arch/src/x86/avx2.rs index 249c09ebce..efcbb23b74 100644 --- a/crates/core_arch/src/x86/avx2.rs +++ b/crates/core_arch/src/x86/avx2.rs @@ -2321,7 +2321,7 @@ pub const fn _mm256_or_si256(a: __m256i, b: __m256i) -> __m256i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_packs_epi16) #[inline] #[target_feature(enable = "avx2")] -#[cfg_attr(test, assert_instr(vpacksswb, limit(2)))] +#[cfg_attr(test, assert_instr(vpacksswb, limit(5)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm256_packs_epi16(a: __m256i, b: __m256i) -> __m256i { unsafe { transmute(packsswb(a.as_i16x16(), b.as_i16x16())) } @@ -2333,7 +2333,7 @@ pub fn _mm256_packs_epi16(a: __m256i, b: __m256i) -> __m256i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_packs_epi32) #[inline] #[target_feature(enable = "avx2")] -#[cfg_attr(test, assert_instr(vpackssdw, limit(2)))] +#[cfg_attr(test, assert_instr(vpackssdw, limit(5)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm256_packs_epi32(a: __m256i, b: __m256i) -> __m256i { unsafe { transmute(packssdw(a.as_i32x8(), b.as_i32x8())) } @@ -2345,7 +2345,7 @@ pub fn _mm256_packs_epi32(a: __m256i, b: __m256i) -> __m256i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_packus_epi16) #[inline] #[target_feature(enable = "avx2")] -#[cfg_attr(test, assert_instr(vpackuswb, limit(2)))] +#[cfg_attr(test, assert_instr(vpackuswb, limit(5)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm256_packus_epi16(a: __m256i, b: __m256i) -> __m256i { unsafe { transmute(packuswb(a.as_i16x16(), b.as_i16x16())) } @@ -2357,7 +2357,7 @@ pub fn _mm256_packus_epi16(a: __m256i, b: __m256i) -> __m256i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_packus_epi32) #[inline] #[target_feature(enable = "avx2")] -#[cfg_attr(test, assert_instr(vpackusdw, limit(2)))] +#[cfg_attr(test, assert_instr(vpackusdw, limit(5)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm256_packus_epi32(a: __m256i, b: __m256i) -> __m256i { unsafe { transmute(packusdw(a.as_i32x8(), b.as_i32x8())) } diff --git a/crates/core_arch/src/x86/sse2.rs b/crates/core_arch/src/x86/sse2.rs index 2e62687eab..1163b6efdf 100644 --- a/crates/core_arch/src/x86/sse2.rs +++ b/crates/core_arch/src/x86/sse2.rs @@ -1496,7 +1496,7 @@ pub const fn _mm_move_epi64(a: __m128i) -> __m128i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packs_epi16) #[inline] #[target_feature(enable = "sse2")] -#[cfg_attr(test, assert_instr(packsswb, limit(2), not(pminsw), not(pmaxsw)))] +#[cfg_attr(test, assert_instr(packsswb, limit(5), not(pminsw), not(pmaxsw)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm_packs_epi16(a: __m128i, b: __m128i) -> __m128i { unsafe { transmute(packsswb(a.as_i16x8(), b.as_i16x8())) } @@ -1508,7 +1508,7 @@ pub fn _mm_packs_epi16(a: __m128i, b: __m128i) -> __m128i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packs_epi32) #[inline] #[target_feature(enable = "sse2")] -#[cfg_attr(test, assert_instr(packssdw, limit(2)))] +#[cfg_attr(test, assert_instr(packssdw, limit(5)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm_packs_epi32(a: __m128i, b: __m128i) -> __m128i { unsafe { transmute(packssdw(a.as_i32x4(), b.as_i32x4())) } @@ -1520,7 +1520,7 @@ pub fn _mm_packs_epi32(a: __m128i, b: __m128i) -> __m128i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packus_epi16) #[inline] #[target_feature(enable = "sse2")] -#[cfg_attr(test, assert_instr(packuswb, limit(2), not(pminsw), not(pmaxsw)))] +#[cfg_attr(test, assert_instr(packuswb, limit(5), not(pminsw), not(pmaxsw)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm_packus_epi16(a: __m128i, b: __m128i) -> __m128i { unsafe { transmute(packuswb(a.as_i16x8(), b.as_i16x8())) } diff --git a/crates/core_arch/src/x86/sse41.rs b/crates/core_arch/src/x86/sse41.rs index 2f42b34992..96ff833454 100644 --- a/crates/core_arch/src/x86/sse41.rs +++ b/crates/core_arch/src/x86/sse41.rs @@ -424,7 +424,7 @@ pub const fn _mm_min_epu32(a: __m128i, b: __m128i) -> __m128i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packus_epi32) #[inline] #[target_feature(enable = "sse4.1")] -#[cfg_attr(test, assert_instr(packusdw, limit(2)))] +#[cfg_attr(test, assert_instr(packusdw, limit(5)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm_packus_epi32(a: __m128i, b: __m128i) -> __m128i { unsafe { transmute(packusdw(a.as_i32x4(), b.as_i32x4())) } From 814e70661e4e37f7539d91d5eda3ea9df894822f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 20 Aug 2026 14:29:24 +0200 Subject: [PATCH 4/4] add the new tests to more intrinsics --- crates/core_arch/src/x86/avx2.rs | 8 ++++---- crates/core_arch/src/x86/avx512bw.rs | 8 ++++---- crates/core_arch/src/x86/sse2.rs | 2 +- crates/core_arch/src/x86/sse41.rs | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/core_arch/src/x86/avx2.rs b/crates/core_arch/src/x86/avx2.rs index efcbb23b74..8194468296 100644 --- a/crates/core_arch/src/x86/avx2.rs +++ b/crates/core_arch/src/x86/avx2.rs @@ -2321,7 +2321,7 @@ pub const fn _mm256_or_si256(a: __m256i, b: __m256i) -> __m256i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_packs_epi16) #[inline] #[target_feature(enable = "avx2")] -#[cfg_attr(test, assert_instr(vpacksswb, limit(5)))] +#[cfg_attr(test, assert_instr(vpacksswb, limit(5), not(vpminsw), not(vpmaxsw)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm256_packs_epi16(a: __m256i, b: __m256i) -> __m256i { unsafe { transmute(packsswb(a.as_i16x16(), b.as_i16x16())) } @@ -2333,7 +2333,7 @@ pub fn _mm256_packs_epi16(a: __m256i, b: __m256i) -> __m256i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_packs_epi32) #[inline] #[target_feature(enable = "avx2")] -#[cfg_attr(test, assert_instr(vpackssdw, limit(5)))] +#[cfg_attr(test, assert_instr(vpackssdw, limit(5), not(vpminsd), not(vpmaxsd)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm256_packs_epi32(a: __m256i, b: __m256i) -> __m256i { unsafe { transmute(packssdw(a.as_i32x8(), b.as_i32x8())) } @@ -2345,7 +2345,7 @@ pub fn _mm256_packs_epi32(a: __m256i, b: __m256i) -> __m256i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_packus_epi16) #[inline] #[target_feature(enable = "avx2")] -#[cfg_attr(test, assert_instr(vpackuswb, limit(5)))] +#[cfg_attr(test, assert_instr(vpackuswb, limit(5), not(vpminsw), not(vpmaxsw)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm256_packus_epi16(a: __m256i, b: __m256i) -> __m256i { unsafe { transmute(packuswb(a.as_i16x16(), b.as_i16x16())) } @@ -2357,7 +2357,7 @@ pub fn _mm256_packus_epi16(a: __m256i, b: __m256i) -> __m256i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_packus_epi32) #[inline] #[target_feature(enable = "avx2")] -#[cfg_attr(test, assert_instr(vpackusdw, limit(5)))] +#[cfg_attr(test, assert_instr(vpackusdw, limit(5), not(vpminsd), not(vpmaxsd)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm256_packus_epi32(a: __m256i, b: __m256i) -> __m256i { unsafe { transmute(packusdw(a.as_i32x8(), b.as_i32x8())) } diff --git a/crates/core_arch/src/x86/avx512bw.rs b/crates/core_arch/src/x86/avx512bw.rs index 0747b65b35..4f0924f8e6 100644 --- a/crates/core_arch/src/x86/avx512bw.rs +++ b/crates/core_arch/src/x86/avx512bw.rs @@ -6522,7 +6522,7 @@ pub fn _mm_maskz_maddubs_epi16(k: __mmask8, a: __m128i, b: __m128i) -> __m128i { #[inline] #[target_feature(enable = "avx512bw")] #[stable(feature = "stdarch_x86_avx512", since = "1.89")] -#[cfg_attr(test, assert_instr(vpackssdw))] +#[cfg_attr(test, assert_instr(vpackssdw, limit(5), not(vpminsd), not(vpmaxsd)))] pub fn _mm512_packs_epi32(a: __m512i, b: __m512i) -> __m512i { unsafe { transmute(vpackssdw(a.as_i32x16(), b.as_i32x16())) } } @@ -6617,7 +6617,7 @@ pub fn _mm_maskz_packs_epi32(k: __mmask8, a: __m128i, b: __m128i) -> __m128i { #[inline] #[target_feature(enable = "avx512bw")] #[stable(feature = "stdarch_x86_avx512", since = "1.89")] -#[cfg_attr(test, assert_instr(vpacksswb))] +#[cfg_attr(test, assert_instr(vpacksswb, limit(5), not(vpminsw), not(vpmaxsw)))] pub fn _mm512_packs_epi16(a: __m512i, b: __m512i) -> __m512i { unsafe { transmute(vpacksswb(a.as_i16x32(), b.as_i16x32())) } } @@ -6712,7 +6712,7 @@ pub fn _mm_maskz_packs_epi16(k: __mmask16, a: __m128i, b: __m128i) -> __m128i { #[inline] #[target_feature(enable = "avx512bw")] #[stable(feature = "stdarch_x86_avx512", since = "1.89")] -#[cfg_attr(test, assert_instr(vpackusdw))] +#[cfg_attr(test, assert_instr(vpackusdw, limit(5), not(vpminsd), not(vpmaxsd)))] pub fn _mm512_packus_epi32(a: __m512i, b: __m512i) -> __m512i { unsafe { transmute(vpackusdw(a.as_i32x16(), b.as_i32x16())) } } @@ -6807,7 +6807,7 @@ pub fn _mm_maskz_packus_epi32(k: __mmask8, a: __m128i, b: __m128i) -> __m128i { #[inline] #[target_feature(enable = "avx512bw")] #[stable(feature = "stdarch_x86_avx512", since = "1.89")] -#[cfg_attr(test, assert_instr(vpackuswb))] +#[cfg_attr(test, assert_instr(vpackuswb, limit(5), not(vpminsw), not(vpmaxsw)))] pub fn _mm512_packus_epi16(a: __m512i, b: __m512i) -> __m512i { unsafe { transmute(vpackuswb(a.as_i16x32(), b.as_i16x32())) } } diff --git a/crates/core_arch/src/x86/sse2.rs b/crates/core_arch/src/x86/sse2.rs index 1163b6efdf..207f31967e 100644 --- a/crates/core_arch/src/x86/sse2.rs +++ b/crates/core_arch/src/x86/sse2.rs @@ -1508,7 +1508,7 @@ pub fn _mm_packs_epi16(a: __m128i, b: __m128i) -> __m128i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packs_epi32) #[inline] #[target_feature(enable = "sse2")] -#[cfg_attr(test, assert_instr(packssdw, limit(5)))] +#[cfg_attr(test, assert_instr(packssdw, limit(5), not(pminsd), not(pmaxsd)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm_packs_epi32(a: __m128i, b: __m128i) -> __m128i { unsafe { transmute(packssdw(a.as_i32x4(), b.as_i32x4())) } diff --git a/crates/core_arch/src/x86/sse41.rs b/crates/core_arch/src/x86/sse41.rs index 96ff833454..cfc9cff11d 100644 --- a/crates/core_arch/src/x86/sse41.rs +++ b/crates/core_arch/src/x86/sse41.rs @@ -424,7 +424,7 @@ pub const fn _mm_min_epu32(a: __m128i, b: __m128i) -> __m128i { /// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packus_epi32) #[inline] #[target_feature(enable = "sse4.1")] -#[cfg_attr(test, assert_instr(packusdw, limit(5)))] +#[cfg_attr(test, assert_instr(packusdw, limit(5), not(pminsd), not(pmaxsd)))] #[stable(feature = "simd_x86", since = "1.27.0")] pub fn _mm_packus_epi32(a: __m128i, b: __m128i) -> __m128i { unsafe { transmute(packusdw(a.as_i32x4(), b.as_i32x4())) }