Skip to content

Commit 7e05b7e

Browse files
committed
make assert_instr total instruction limit configurable per-test
1 parent f786221 commit 7e05b7e

4 files changed

Lines changed: 91 additions & 73 deletions

File tree

crates/assert-instr-macro/src/lib.rs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ pub fn assert_instr(
3636

3737
let instr = &invoc.instr;
3838
let not = invoc.not;
39+
let limit = match invoc.limit {
40+
None => quote! { None },
41+
Some(l) => quote! { Some(#l) },
42+
};
43+
3944
let name = &func.sig.ident;
4045
let maybe_allow_deprecated = if func
4146
.attrs
@@ -180,7 +185,7 @@ pub fn assert_instr(
180185
fn #assert_name() {
181186
#to_test
182187

183-
::stdarch_test::assert(#shim_name as usize, stringify!(#shim_name), #instr, &[#(#not),*]);
188+
::stdarch_test::assert(#shim_name as usize, stringify!(#shim_name), #instr, &[#(#not),*], #limit);
184189
}
185190
};
186191

@@ -194,6 +199,7 @@ pub fn assert_instr(
194199
struct Invoc {
195200
instr: String,
196201
not: Vec<String>,
202+
limit: Option<syn::LitInt>,
197203
args: Vec<(syn::Ident, syn::Expr)>,
198204
}
199205

@@ -229,6 +235,7 @@ impl syn::parse::Parse for Invoc {
229235

230236
let instr = parse_instr(input)?;
231237
let mut not = Vec::new();
238+
let mut limit = None;
232239
let mut args = Vec::new();
233240

234241
while !input.is_empty() {
@@ -237,8 +244,8 @@ impl syn::parse::Parse for Invoc {
237244
return Err(input.error("extra tokens at end"));
238245
}
239246

240-
// This is either `not(instr)` or `arg = val`.
241-
// We treat `not` as a magic identifier here.
247+
// This is either `not(instr)` or `limit(n)` or `arg = val`.
248+
// We treat `not` and `limit` as a magic identifier here.
242249

243250
let name = input.parse::<syn::Ident>()?;
244251

@@ -252,12 +259,24 @@ impl syn::parse::Parse for Invoc {
252259
not.push(not_instr);
253260
continue;
254261
}
262+
if name == "limit" {
263+
let content;
264+
parenthesized!(content in input);
265+
let n = content.parse::<syn::LitInt>()?;
266+
limit = Some(n);
267+
continue;
268+
}
255269

256270
input.parse::<Token![=]>()?;
257271
let expr = input.parse::<syn::Expr>()?;
258272
args.push((name, expr));
259273
}
260-
Ok(Self { instr, not, args })
274+
Ok(Self {
275+
instr,
276+
not,
277+
limit,
278+
args,
279+
})
261280
}
262281
}
263282

crates/core_arch/src/x86/avx2.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2321,7 +2321,7 @@ pub const fn _mm256_or_si256(a: __m256i, b: __m256i) -> __m256i {
23212321
/// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_packs_epi16)
23222322
#[inline]
23232323
#[target_feature(enable = "avx2")]
2324-
#[cfg_attr(test, assert_instr(vpacksswb))]
2324+
#[cfg_attr(test, assert_instr(vpacksswb, limit(2)))]
23252325
#[stable(feature = "simd_x86", since = "1.27.0")]
23262326
pub fn _mm256_packs_epi16(a: __m256i, b: __m256i) -> __m256i {
23272327
unsafe { transmute(packsswb(a.as_i16x16(), b.as_i16x16())) }
@@ -2333,7 +2333,7 @@ pub fn _mm256_packs_epi16(a: __m256i, b: __m256i) -> __m256i {
23332333
/// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_packs_epi32)
23342334
#[inline]
23352335
#[target_feature(enable = "avx2")]
2336-
#[cfg_attr(test, assert_instr(vpackssdw))]
2336+
#[cfg_attr(test, assert_instr(vpackssdw, limit(2)))]
23372337
#[stable(feature = "simd_x86", since = "1.27.0")]
23382338
pub fn _mm256_packs_epi32(a: __m256i, b: __m256i) -> __m256i {
23392339
unsafe { transmute(packssdw(a.as_i32x8(), b.as_i32x8())) }
@@ -2345,7 +2345,7 @@ pub fn _mm256_packs_epi32(a: __m256i, b: __m256i) -> __m256i {
23452345
/// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_packus_epi16)
23462346
#[inline]
23472347
#[target_feature(enable = "avx2")]
2348-
#[cfg_attr(test, assert_instr(vpackuswb))]
2348+
#[cfg_attr(test, assert_instr(vpackuswb, limit(2)))]
23492349
#[stable(feature = "simd_x86", since = "1.27.0")]
23502350
pub fn _mm256_packus_epi16(a: __m256i, b: __m256i) -> __m256i {
23512351
unsafe { transmute(packuswb(a.as_i16x16(), b.as_i16x16())) }
@@ -2357,7 +2357,7 @@ pub fn _mm256_packus_epi16(a: __m256i, b: __m256i) -> __m256i {
23572357
/// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_packus_epi32)
23582358
#[inline]
23592359
#[target_feature(enable = "avx2")]
2360-
#[cfg_attr(test, assert_instr(vpackusdw))]
2360+
#[cfg_attr(test, assert_instr(vpackusdw, limit(2)))]
23612361
#[stable(feature = "simd_x86", since = "1.27.0")]
23622362
pub fn _mm256_packus_epi32(a: __m256i, b: __m256i) -> __m256i {
23632363
unsafe { transmute(packusdw(a.as_i32x8(), b.as_i32x8())) }

crates/core_arch/src/x86/sse2.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,7 +1496,7 @@ pub const fn _mm_move_epi64(a: __m128i) -> __m128i {
14961496
/// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packs_epi16)
14971497
#[inline]
14981498
#[target_feature(enable = "sse2")]
1499-
#[cfg_attr(test, assert_instr(packsswb, not(pminsw), not(pmaxsw)))]
1499+
#[cfg_attr(test, assert_instr(packsswb, limit(2)))]
15001500
#[stable(feature = "simd_x86", since = "1.27.0")]
15011501
pub fn _mm_packs_epi16(a: __m128i, b: __m128i) -> __m128i {
15021502
unsafe { transmute(packsswb(a.as_i16x8(), b.as_i16x8())) }
@@ -1508,7 +1508,7 @@ pub fn _mm_packs_epi16(a: __m128i, b: __m128i) -> __m128i {
15081508
/// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packs_epi32)
15091509
#[inline]
15101510
#[target_feature(enable = "sse2")]
1511-
#[cfg_attr(test, assert_instr(packssdw))]
1511+
#[cfg_attr(test, assert_instr(packssdw, limit(2)))]
15121512
#[stable(feature = "simd_x86", since = "1.27.0")]
15131513
pub fn _mm_packs_epi32(a: __m128i, b: __m128i) -> __m128i {
15141514
unsafe { transmute(packssdw(a.as_i32x4(), b.as_i32x4())) }
@@ -1520,7 +1520,7 @@ pub fn _mm_packs_epi32(a: __m128i, b: __m128i) -> __m128i {
15201520
/// [Intel's documentation](https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_packus_epi16)
15211521
#[inline]
15221522
#[target_feature(enable = "sse2")]
1523-
#[cfg_attr(test, assert_instr(packuswb, not(pminsw), not(pmaxsw)))]
1523+
#[cfg_attr(test, assert_instr(packuswb, limit(2)))]
15241524
#[stable(feature = "simd_x86", since = "1.27.0")]
15251525
pub fn _mm_packus_epi16(a: __m128i, b: __m128i) -> __m128i {
15261526
unsafe { transmute(packuswb(a.as_i16x8(), b.as_i16x8())) }

crates/stdarch-test/src/lib.rs

Lines changed: 61 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ impl hash::Hash for Function {
5454
///
5555
/// This asserts that the function at `fnptr` contains the instruction
5656
/// `expected` provided.
57-
pub fn assert(shim_addr: usize, fnname: &str, expected: &str, not: &[&str]) {
57+
pub fn assert(shim_addr: usize, fnname: &str, expected: &str, not: &[&str], limit: Option<usize>) {
5858
// Make sure that the shim is not removed
5959
black_box(shim_addr);
6060

@@ -131,68 +131,67 @@ pub fn assert(shim_addr: usize, fnname: &str, expected: &str, not: &[&str]) {
131131

132132
let instruction_limit = std::env::var("STDARCH_ASSERT_INSTR_LIMIT")
133133
.ok()
134-
.map_or_else(
135-
|| match expected {
136-
// `cpuid` returns a pretty big aggregate structure, so exempt
137-
// it from the slightly more restrictive 22 instructions below.
138-
"cpuid" => 30,
139-
140-
// These require 8 loads and stores, so it _just_ overflows the limit
141-
"aesencwide128kl" | "aesencwide256kl" | "aesdecwide128kl" | "aesdecwide256kl" => 24,
142-
143-
// Apparently, on Windows, LLVM generates a bunch of
144-
// saves/restores of xmm registers around these instructions,
145-
// which exceeds the limit of 20 below. As it seems dictated by
146-
// Windows's ABI (I believe?), we probably can't do much
147-
// about it.
148-
"vzeroall" | "vzeroupper" if cfg!(windows) => 30,
149-
150-
// Intrinsics using `cvtpi2ps` are typically "composites" and
151-
// in some cases exceed the limit.
152-
"cvtpi2ps" => 25,
153-
// core_arch/src/arm_shared/simd32
154-
// vfmaq_n_f32_vfma : #instructions = 26 >= 22 (limit)
155-
"usad8" | "vfma" | "vfms" => 27,
156-
"qadd8" | "qsub8" | "sadd8" | "sel" | "shadd8" | "shsub8" | "usub8" | "ssub8" => 29,
157-
// core_arch/src/arm_shared/simd32
158-
// vst1q_s64_x4_vst1 : #instructions = 27 >= 22 (limit)
159-
"vld3" => 28,
160-
// core_arch/src/arm_shared/simd32
161-
// vld4q_lane_u32_vld4 : #instructions = 36 >= 22 (limit)
162-
"vld4" => 37,
163-
// core_arch/src/arm_shared/simd32
164-
// vst1q_s64_x4_vst1 : #instructions = 40 >= 22 (limit)
165-
"vst1" => 41,
166-
// core_arch/src/arm_shared/simd32
167-
// vst3q_u32_vst3 : #instructions = 25 >= 22 (limit)
168-
"vst3" => 26,
169-
// core_arch/src/arm_shared/simd32
170-
// vst4q_u32_vst4 : #instructions = 33 >= 22 (limit)
171-
"vst4" => 34,
172-
173-
// core_arch/src/arm_shared/simd32
174-
// vst1q_p64_x4_nop : #instructions = 33 >= 22 (limit)
175-
"nop" if fnname.contains("vst1q_p64") => 34,
176-
177-
// AMX intrinsics generate a lot of move instructions to load/store the tile registers
178-
// due to Rust ABI
179-
_ if fnname.contains("___tile") => 165,
180-
181-
// Original limit was 20 instructions, but ARM DSP Intrinsics
182-
// are exactly 20 instructions long. So, bump the limit to 22
183-
// instead of adding here a long list of exceptions.
184-
_ => {
185-
// aarch64_be may add reverse instructions which increases
186-
// the number of instructions generated.
187-
if cfg!(all(target_endian = "big", target_arch = "aarch64")) {
188-
32
189-
} else {
190-
22
191-
}
134+
.map(|v| v.parse().unwrap())
135+
.or(limit.map(|n| n + 1)) // adjust for the `<` below: `limit = 2` means 2 instructions is okay
136+
.unwrap_or_else(|| match expected {
137+
// `cpuid` returns a pretty big aggregate structure, so exempt
138+
// it from the slightly more restrictive 22 instructions below.
139+
"cpuid" => 30,
140+
141+
// These require 8 loads and stores, so it _just_ overflows the limit
142+
"aesencwide128kl" | "aesencwide256kl" | "aesdecwide128kl" | "aesdecwide256kl" => 24,
143+
144+
// Apparently, on Windows, LLVM generates a bunch of
145+
// saves/restores of xmm registers around these instructions,
146+
// which exceeds the limit of 20 below. As it seems dictated by
147+
// Windows's ABI (I believe?), we probably can't do much
148+
// about it.
149+
"vzeroall" | "vzeroupper" if cfg!(windows) => 30,
150+
151+
// Intrinsics using `cvtpi2ps` are typically "composites" and
152+
// in some cases exceed the limit.
153+
"cvtpi2ps" => 25,
154+
// core_arch/src/arm_shared/simd32
155+
// vfmaq_n_f32_vfma : #instructions = 26 >= 22 (limit)
156+
"usad8" | "vfma" | "vfms" => 27,
157+
"qadd8" | "qsub8" | "sadd8" | "sel" | "shadd8" | "shsub8" | "usub8" | "ssub8" => 29,
158+
// core_arch/src/arm_shared/simd32
159+
// vst1q_s64_x4_vst1 : #instructions = 27 >= 22 (limit)
160+
"vld3" => 28,
161+
// core_arch/src/arm_shared/simd32
162+
// vld4q_lane_u32_vld4 : #instructions = 36 >= 22 (limit)
163+
"vld4" => 37,
164+
// core_arch/src/arm_shared/simd32
165+
// vst1q_s64_x4_vst1 : #instructions = 40 >= 22 (limit)
166+
"vst1" => 41,
167+
// core_arch/src/arm_shared/simd32
168+
// vst3q_u32_vst3 : #instructions = 25 >= 22 (limit)
169+
"vst3" => 26,
170+
// core_arch/src/arm_shared/simd32
171+
// vst4q_u32_vst4 : #instructions = 33 >= 22 (limit)
172+
"vst4" => 34,
173+
174+
// core_arch/src/arm_shared/simd32
175+
// vst1q_p64_x4_nop : #instructions = 33 >= 22 (limit)
176+
"nop" if fnname.contains("vst1q_p64") => 34,
177+
178+
// AMX intrinsics generate a lot of move instructions to load/store the tile registers
179+
// due to Rust ABI
180+
_ if fnname.contains("___tile") => 165,
181+
182+
// Original limit was 20 instructions, but ARM DSP Intrinsics
183+
// are exactly 20 instructions long. So, bump the limit to 22
184+
// instead of adding here a long list of exceptions.
185+
_ => {
186+
// aarch64_be may add reverse instructions which increases
187+
// the number of instructions generated.
188+
if cfg!(all(target_endian = "big", target_arch = "aarch64")) {
189+
32
190+
} else {
191+
22
192192
}
193-
},
194-
|v| v.parse().unwrap(),
195-
);
193+
}
194+
});
196195
let probably_only_one_instruction = instrs.len() < instruction_limit;
197196

198197
if found && found_bad.is_none() && probably_only_one_instruction && !inlining_failed {

0 commit comments

Comments
 (0)