makepad/tools/run/fixture/diff/pr1326_fixes.rs

287 lines
11 KiB
Rust

// diff_fast corpus program: the fixes of PR #1326 (round 2) that change what a program computes or
// whether it compiles. `D <case> <value>` per case, equal to rustc's output.
// - by-reference pattern bindings take the scrutinee place in place (lower.rs scan_addr_taken: `ref` /
// `ref mut` / match ergonomics in match, if let, while let, let and let-else), so writes through the
// binding reach the place and reads see it;
// - a let-else that binds by reference matches its place in place (no copy, no drop of the place);
// - a closure capturing through a `&mut` root (`*r`) stores the reference (layout/closure.rs);
// - `x?` whose Output is still an inference variable falls back like rustc's diverging match arm;
// - `macro_rules!` separatorless repetitions: the inner follow set is the suffix's, not FIRST(inner);
// - a braced macro call followed by `.method()` / `?` continues as an expression;
// - cfg-stripped match arms, struct fields, array elements and statements are not expanded;
// - std: Saturating ops, (A, B): FromIterator, Ord / PartialOrd for &mut, OsString: From<&T>.
// x86-64 only: the integer intrinsics the PR added, on random bits, one hash each.
#![allow(unused_unsafe, dead_code, unused_mut, unused_variables, unreachable_code)]
macro_rules! d { ($($a:tt)*) => { println!("D {}", format!($($a)*)) } }
fn rounds(f: fn()) {
let ms: u64 = std::env::var("DIFF_SLEEP_MS").ok().and_then(|v| v.parse().ok()).unwrap_or(0);
for r in 0..4 { f(); if r < 3 && ms > 0 { std::thread::sleep(std::time::Duration::from_millis(ms)); } }
}
#[test]
fn run() { rounds(work); }
use std::hint::black_box;
#[derive(Debug, Clone, PartialEq)]
struct P { a: u32, b: String }
fn ref_patterns() {
// match with `ref mut`: the write reaches the scrutinee
let mut x = black_box(7u32);
match x { ref mut y => *y += 5 }
d!("match_ref_mut {}", x);
// if let with `ref mut` inside an Option
let mut o = black_box(Some(3i64));
if let Some(ref mut v) = o { *v *= 11; }
d!("if_let_ref_mut {:?}", o);
// match ergonomics: `&mut Option<P>` scrutinee binds `p: &mut P`
let mut op = Some(P { a: 1, b: "x".to_string() });
match &mut op { Some(p) => { p.a += 40; p.b.push('y'); } None => {} }
d!("ergonomics {:?}", op);
// while let with `ref mut` on a local array slot
let mut arr = black_box([1u8, 2, 3, 4]);
let mut i = 0;
while let [ref mut first, ..] = arr[i..] { *first = first.wrapping_mul(9); i += 1; if i == arr.len() { break; } }
d!("while_let_ref_mut {:?}", arr);
// `let` with a ref binding: a reference to the local, not to a copy
let mut n = black_box(100u64);
let ref mut m = n;
*m += 1;
d!("let_ref_mut {}", n);
// a tuple scrutinee field by ref, then the tuple read back
let mut t = black_box((5u16, 6u16));
match t { (ref mut a, _) => *a = 50 }
d!("tuple_field_ref {:?}", t);
}
fn let_else_ref() -> u32 {
let mut o = black_box(Some(P { a: 9, b: "p".to_string() }));
let Some(ref mut p) = o else { return 0 };
p.a *= 3;
p.b.push_str("q");
d!("let_else_ref_mut {:?}", o);
let v = black_box(vec![10u32, 20, 30]);
let [ref first, .., ref last] = v[..] else { return 1 };
d!("let_else_slice_ref {} {} len {}", first, last, v.len());
let none: Option<P> = black_box(None);
let Some(ref q) = none else { d!("let_else_fail ok"); return 2 };
3
}
fn closure_capture_through_mut_ref() {
let mut x = black_box(1u32);
{
let r = &mut x;
let mut c = || { *r += 10; *r * 2 };
let a = c();
let b = c();
d!("closure_mut_root {} {}", a, b);
}
d!("closure_mut_root_after {}", x);
let mut v = vec![1, 2, 3];
{
let r = &mut v;
let mut push = |k: i32| r.push(k * 7);
push(1);
push(2);
}
d!("closure_mut_root_vec {:?}", v);
let mut s = String::from("a");
let r = &mut s;
let f = move || { r.push('b'); r.len() };
let mut f = f;
let l = f();
d!("closure_mut_root_move {} {}", l, s);
}
fn try_fallback() -> Result<u32, String> {
// the Output of `?` here is an unconstrained inference variable: rustc's diverging fallback gives ()
if black_box(false) { let _y = Err("e".to_string())?; }
let r: Result<u32, String> = black_box(Ok(5));
let v = r?;
Ok(v + 1)
}
macro_rules! sum_exprs {
($($e:expr)*) => { 0 $(+ $e)* };
}
macro_rules! idents_tts {
($($i:ident)* ; $($t:tt)*) => { [$(stringify!($i)),*].len() + [$(stringify!($t)),*].len() };
}
macro_rules! braced {
{$($e:expr),*} => { vec![$($e),*] };
}
fn macro_shapes() {
d!("sep_less_expr {}", sum_exprs!(1 2 3 {4}));
d!("sep_less_ident {}", idents_tts!(a b c ; x + y));
// a braced macro followed by a method call continues as an expression
let n = braced!{1u8, 2, 3}.len();
d!("braced_postfix {}", n);
let s = braced!{"a", "bb"}.iter().map(|s| s.len()).sum::<usize>();
d!("braced_postfix_chain {}", s);
// in statement position: still an expression statement (rustc parse_stmt_mac: `.` / `?` after `}`)
let mut acc = 0u32;
braced!{4u32, 5, 6}.iter().for_each(|x| acc += x);
d!("braced_postfix_stmt {}", acc);
}
struct F { a: u8, #[cfg(any())] b: u8, c: u8 }
fn cfg_stripped() {
let x = black_box(2u8);
let r = match x {
#[cfg(any())]
2 => this_macro_does_not_exist!(),
2 => 20,
_ => 0,
};
d!("cfg_arm {}", r);
let f = F { a: 1, #[cfg(any())] b: no_such_macro!(), c: 3 };
d!("cfg_field {} {}", f.a, f.c);
let v = [1u8, #[cfg(any())] nope!(), 3];
d!("cfg_elem {:?}", v);
#[cfg(any())]
let z = undefined_macro!();
#[cfg(any())]
undefined_stmt_macro! { whatever };
d!("cfg_stmt ok");
}
fn std_bits() {
use std::num::Saturating;
let mut s = Saturating(black_box(250u8));
s += Saturating(10);
s += 1;
let t = Saturating(black_box(3u8)) - Saturating(10);
let m = Saturating(black_box(100i8)) * Saturating(2);
let q = Saturating(black_box(i8::MIN)) / Saturating(-1);
d!("saturating {} {} {} {} {}", s, t, m, q, !Saturating(black_box(0u8)));
let (evens, odds): (Vec<u32>, Vec<u32>) = (0..10u32).map(|i| (i * 2, i * 2 + 1)).collect();
d!("tuple_from_iter {:?} {:?}", evens, odds);
let mut a = black_box(3u32);
let mut b = black_box(9u32);
let (ra, rb) = (&mut a, &mut b);
d!("ord_mut {:?} {:?} {}", ra.cmp(&rb), ra.partial_cmp(&rb), ra < rb);
let mut refs = vec![&mut a, &mut b];
refs.sort();
refs.reverse();
d!("sort_mut_refs {:?}", refs);
let st = String::from("os");
let os = std::ffi::OsString::from(&st);
let os2 = std::ffi::OsString::from(std::path::Path::new("/p"));
d!("os_string {:?} {:?}", os, os2);
}
fn work() {
ref_patterns();
d!("let_else_ret {}", let_else_ref());
closure_capture_through_mut_ref();
d!("try_fallback {:?}", try_fallback());
macro_shapes();
cfg_stripped();
std_bits();
arch();
}
#[cfg(not(target_arch = "x86_64"))]
fn arch() {
d!("arch: x86-64 only");
}
#[cfg(target_arch = "x86_64")]
fn arch() {
unsafe { x::sse2() };
if std::is_x86_feature_detected!("sse4.1") { unsafe { x::sse41() } } else { d!("no sse4.1"); }
if std::is_x86_feature_detected!("avx2") { unsafe { x::avx2() } } else { d!("no avx2"); }
if std::is_x86_feature_detected!("pclmulqdq") { unsafe { x::clmul() } } else { d!("no pclmulqdq"); }
}
#[cfg(target_arch = "x86_64")]
mod x {
use core::arch::x86_64::*;
use std::hint::black_box;
struct Rng(u64);
impl Rng {
fn next(&mut self) -> u64 {
self.0 ^= self.0 << 13;
self.0 ^= self.0 >> 7;
self.0 ^= self.0 << 17;
self.0
}
}
struct H(u64);
impl H {
fn add(&mut self, v: u64) {
self.0 = (self.0 ^ v).wrapping_mul(0x100000001b3);
}
}
unsafe fn m128(r: &mut Rng) -> __m128i { _mm_set_epi64x(r.next() as i64, r.next() as i64) }
unsafe fn m256(r: &mut Rng) -> __m256i { _mm256_set_epi64x(r.next() as i64, r.next() as i64, r.next() as i64, r.next() as i64) }
unsafe fn h128(h: &mut H, v: __m128i) {
let a: [u64; 2] = std::mem::transmute(v);
h.add(a[0]);
h.add(a[1]);
}
unsafe fn h256(h: &mut H, v: __m256i) {
let a: [u64; 4] = std::mem::transmute(v);
for x in a { h.add(x); }
}
macro_rules! case128 {
($name:literal, $r:ident, |$a:ident, $b:ident| $e:expr) => {{
let mut h = H(0xcbf29ce484222325);
for _ in 0..64 { let $a = m128(&mut $r); let $b = m128(&mut $r); h128(&mut h, black_box($e)); }
d!("{} {:016x}", $name, h.0);
}};
}
macro_rules! case256 {
($name:literal, $r:ident, |$a:ident, $b:ident| $e:expr) => {{
let mut h = H(0xcbf29ce484222325);
for _ in 0..64 { let $a = m256(&mut $r); let $b = m256(&mut $r); h256(&mut h, black_box($e)); }
d!("{} {:016x}", $name, h.0);
}};
}
pub unsafe fn sse2() {
let mut r = Rng(0x9e3779b97f4a7c15);
case128!("mm_mul_epu32", r, |a, b| _mm_mul_epu32(a, b));
case128!("mm_mullo_epi16", r, |a, b| _mm_mullo_epi16(a, b));
let mut h = H(0xcbf29ce484222325);
for _ in 0..64 {
let a = m128(&mut r);
h.add(_mm_cvtsi128_si64(a) as u64);
h.add(_mm_cvtsi128_si64x(a) as u64);
h128(&mut h, _mm_cvtsi64_si128(r.next() as i64));
h128(&mut h, _mm_cvtsi64x_si128(r.next() as i64));
}
d!("mm_cvtsi64 {:016x}", h.0);
}
#[target_feature(enable = "sse4.1")]
pub unsafe fn sse41() {
let mut r = Rng(0x2545f4914f6cdd1d);
let mut h = H(0xcbf29ce484222325);
for k in 0..64 {
let a = m128(&mut r);
let b = if k % 4 == 0 { _mm_setzero_si128() } else { m128(&mut r) };
h.add(_mm_testz_si128(a, b) as u64);
h.add(_mm_testz_si128(a, _mm_andnot_si128(a, b)) as u64);
}
d!("mm_testz_si128 {:016x}", h.0);
}
#[target_feature(enable = "avx2")]
pub unsafe fn avx2() {
let mut r = Rng(0xda942042e4dd58b5);
case256!("mm256_cmpgt_epi8", r, |a, b| _mm256_cmpgt_epi8(a, b));
case256!("mm256_xor_si256", r, |a, b| _mm256_xor_si256(a, b));
case256!("mm256_subs_epu8", r, |a, b| _mm256_subs_epu8(a, b));
case256!("mm256_alignr_epi8_5", r, |a, b| _mm256_alignr_epi8::<5>(a, b));
case256!("mm256_alignr_epi8_17", r, |a, b| _mm256_alignr_epi8::<17>(a, b));
case256!("mm256_srli_epi16_3", r, |a, b| _mm256_srli_epi16::<3>(a));
}
#[target_feature(enable = "pclmulqdq")]
pub unsafe fn clmul() {
let mut r = Rng(0x1234567887654321);
case128!("mm_clmulepi64_si128_00", r, |a, b| _mm_clmulepi64_si128::<0x00>(a, b));
case128!("mm_clmulepi64_si128_11", r, |a, b| _mm_clmulepi64_si128::<0x11>(a, b));
}
}