118 lines
4.5 KiB
Rust
118 lines
4.5 KiB
Rust
//! merge_stores on byte stores of constants (an unrolled `[0u8; 8]` fill): word stores of the
|
|
//! combined constant, so sroa sees the slot as one cell.
|
|
use relax_backend::rir::*;
|
|
use relax_codegen::loops;
|
|
|
|
fn push(f: &mut Func, b: u32, i: Inst) {
|
|
f.blocks[b as usize].insts.push(i);
|
|
f.blocks[b as usize].pos.push(1);
|
|
}
|
|
|
|
/// b0: k_j = consts; slot bytes 0..n = k_j; x = load 8 of the slot; ret x
|
|
fn fill(bytes: &[i64]) -> (Func, VReg) {
|
|
let mut f = Func::new("fill".into(), 0);
|
|
f.rets.push(Cls::I);
|
|
let s = f.slot(8, 8);
|
|
f.block();
|
|
let p = f.vreg(Cls::I);
|
|
push(&mut f, 0, Inst::SlotAddr(p, s));
|
|
let ks: Vec<VReg> = bytes.iter().map(|_| f.vreg(Cls::I)).collect();
|
|
for (k, &b) in ks.iter().zip(bytes) {
|
|
push(&mut f, 0, Inst::Iconst(*k, b));
|
|
}
|
|
for (j, k) in ks.iter().enumerate() {
|
|
push(&mut f, 0, Inst::Store(Mem::Int(1, false), p, j as i32, *k));
|
|
}
|
|
let x = f.vreg(Cls::I);
|
|
push(&mut f, 0, Inst::Load(Mem::Int(8, false), x, p, 0));
|
|
f.blocks[0].term = Term::Ret(vec![x]);
|
|
(f, x)
|
|
}
|
|
|
|
fn stores(f: &Func) -> Vec<(u8, i32, i64)> {
|
|
let mut kv = std::collections::HashMap::new();
|
|
for i in &f.blocks[0].insts {
|
|
if let Inst::Iconst(d, k) = *i {
|
|
kv.insert(d.0, k);
|
|
}
|
|
}
|
|
f.blocks[0].insts.iter().filter_map(|i| match *i {
|
|
Inst::Store(Mem::Int(w, _), _, off, v) => Some((w, off, kv[&v.0])),
|
|
_ => None,
|
|
}).collect()
|
|
}
|
|
|
|
#[test]
|
|
fn a_byte_fill_of_zeros_is_one_word_store() {
|
|
let (mut f, _) = fill(&[0; 8]);
|
|
assert!(loops::merge_stores(&relax_session::Options::default(), &mut f));
|
|
f.verify().unwrap();
|
|
assert_eq!(stores(&f), vec![(8, 0, 0)]);
|
|
// the slot is one 8-byte cell now: sroa takes it
|
|
assert!(relax_backend::passes::sroa(&mut f));
|
|
assert!(!f.blocks[0].insts.iter().any(|i| matches!(i, Inst::Load(..) | Inst::Store(..))));
|
|
}
|
|
|
|
#[test]
|
|
fn constant_bytes_combine_little_endian_in_word_pieces() {
|
|
let (mut f, _) = fill(&[1, 2, 3, 4, 5, 6, 0x87, 0xff]);
|
|
assert!(loops::merge_stores(&relax_session::Options::default(), &mut f));
|
|
f.verify().unwrap();
|
|
assert_eq!(stores(&f), vec![(8, 0, 0xff87_0605_0403_0201u64 as i64)]);
|
|
// 7 bytes: 4 + 2 + one byte left as it was
|
|
let (mut f, _) = fill(&[1, 2, 3, 4, 5, 6, 7]);
|
|
assert!(loops::merge_stores(&relax_session::Options::default(), &mut f));
|
|
f.verify().unwrap();
|
|
assert_eq!(stores(&f), vec![(4, 0, 0x0403_0201), (2, 4, 0x0605), (1, 6, 7)]);
|
|
}
|
|
|
|
#[test]
|
|
fn a_load_between_ends_the_run() {
|
|
let (mut f, x) = fill(&[0; 4]);
|
|
// a load of the slot between byte 1 and byte 2 reads the first two bytes as stored
|
|
let y = f.vreg(Cls::I);
|
|
let p = match f.blocks[0].insts[0] { Inst::SlotAddr(p, _) => p, _ => unreachable!() };
|
|
let at = f.blocks[0].insts.iter().position(|i| matches!(i, Inst::Store(_, _, 2, _))).unwrap();
|
|
f.blocks[0].insts.insert(at, Inst::Load(Mem::Int(2, false), y, p, 0));
|
|
f.blocks[0].pos.insert(at, 1);
|
|
assert!(loops::merge_stores(&relax_session::Options::default(), &mut f));
|
|
f.verify().unwrap();
|
|
assert_eq!(stores(&f), vec![(2, 0, 0), (2, 2, 0)]);
|
|
let _ = x;
|
|
}
|
|
|
|
/// stores of constants of the given widths, back to back from offset 0
|
|
fn wide_fill(parts: &[(u8, i64)]) -> Func {
|
|
let mut f = Func::new("wide_fill".into(), 0);
|
|
let s = f.slot(16, 8);
|
|
f.block();
|
|
let p = f.vreg(Cls::I);
|
|
push(&mut f, 0, Inst::SlotAddr(p, s));
|
|
let mut off = 0;
|
|
for &(w, k) in parts {
|
|
let v = f.vreg(Cls::I);
|
|
push(&mut f, 0, Inst::Iconst(v, k));
|
|
push(&mut f, 0, Inst::Store(Mem::Int(w, false), p, off, v));
|
|
off += w as i32;
|
|
}
|
|
f.blocks[0].term = Term::Ret(vec![]);
|
|
f
|
|
}
|
|
|
|
#[test]
|
|
fn word_stores_of_constants_combine() {
|
|
// two u32 zeros: one 8-byte store
|
|
let mut f = wide_fill(&[(4, 0), (4, 0)]);
|
|
assert!(loops::merge_stores(&relax_session::Options::default(), &mut f));
|
|
f.verify().unwrap();
|
|
assert_eq!(stores(&f), vec![(8, 0, 0)]);
|
|
// u32, u16, u8, u8 then u32 + u32: 8 + 8, little endian
|
|
let mut f = wide_fill(&[(4, 0x0403_0201), (2, 0x0605), (1, 7), (1, 8), (4, 1), (4, 2)]);
|
|
assert!(loops::merge_stores(&relax_session::Options::default(), &mut f));
|
|
f.verify().unwrap();
|
|
assert_eq!(stores(&f), vec![(8, 0, 0x0807_0605_0403_0201), (8, 8, 0x0000_0002_0000_0001)]);
|
|
// a u16 then a u32 at 2: a 4-byte store at 0 would cut the u32, so both stay
|
|
let mut f = wide_fill(&[(2, 1), (4, 2)]);
|
|
assert!(!loops::merge_stores(&relax_session::Options::default(), &mut f));
|
|
assert_eq!(stores(&f), vec![(2, 0, 1), (4, 2, 2)]);
|
|
}
|