use crate::op::*;
pub(super) fn apply_peephole(code: &mut [Op], constants: &[Const]) {
if code.len() < 3 { return; }
let jump_targets = collect_jump_targets(code);
let n = code.len();
let mut k = 0;
while k + 2 < n {
if let (Op::LoadLocal(local_idx), Op::PushConst(imm_const_idx), Op::IntAdd)
= (code[k], code[k + 1], code[k + 2])
{
let imm_is_int = matches!(
constants.get(imm_const_idx as usize),
Some(Const::Int(_))
);
let safe = imm_is_int
&& !jump_targets.contains(&(k + 1))
&& !jump_targets.contains(&(k + 2));
if safe {
code[k] = Op::LoadLocalAddIntConst { local_idx, imm_const_idx };
k += 3;
continue;
}
}
k += 1;
}
}
pub(super) fn apply_peephole_slice2(code: &mut [Op]) {
if code.len() < 4 { return; }
let jump_targets = collect_jump_targets(code);
let n = code.len();
let mut k = 0;
while k + 3 < n {
if let (
Op::LoadLocalAddIntConst { local_idx: src, imm_const_idx },
_,
_,
Op::StoreLocal(dest),
) = (code[k], code[k + 1], code[k + 2], code[k + 3])
{
let safe = !jump_targets.contains(&(k + 1))
&& !jump_targets.contains(&(k + 2))
&& !jump_targets.contains(&(k + 3));
if safe {
code[k] = Op::LoadLocalAddIntConstStoreLocal {
src,
imm_const_idx,
dest,
};
k += 4;
continue;
}
}
k += 1;
}
}
pub(super) fn apply_peephole_slice3(code: &mut [Op]) {
if code.len() < 3 { return; }
let jump_targets = collect_jump_targets(code);
let n = code.len();
let mut k = 0;
while k + 2 < n {
if let (Op::LoadLocal(lhs_idx), Op::LoadLocal(rhs_idx), Op::IntAdd)
= (code[k], code[k + 1], code[k + 2])
{
let safe = !jump_targets.contains(&(k + 1))
&& !jump_targets.contains(&(k + 2));
if safe {
code[k] = Op::LoadLocalAddLocal { lhs_idx, rhs_idx };
k += 3;
continue;
}
}
k += 1;
}
}
pub(super) fn apply_peephole_slice4(code: &mut [Op]) {
if code.len() < 3 { return; }
let jump_targets = collect_jump_targets(code);
let n = code.len();
let mut k = 0;
while k + 2 < n {
if let (Op::LoadLocal(lhs_idx), Op::LoadLocal(rhs_idx), terminator)
= (code[k], code[k + 1], code[k + 2])
{
let fused = match terminator {
Op::IntSub => Some(Op::LoadLocalSubLocal { lhs_idx, rhs_idx }),
Op::IntMul => Some(Op::LoadLocalMulLocal { lhs_idx, rhs_idx }),
_ => None,
};
if let Some(fused_op) = fused {
let safe = !jump_targets.contains(&(k + 1))
&& !jump_targets.contains(&(k + 2));
if safe {
code[k] = fused_op;
k += 3;
continue;
}
}
}
k += 1;
}
}
pub(super) fn apply_peephole_slice5(code: &mut [Op], constants: &[Const]) {
if code.len() < 4 { return; }
let jump_targets = collect_jump_targets(code);
let n = code.len();
let mut k = 0;
while k + 3 < n {
let lhs_idx = match code[k] {
Op::LoadLocal(i) => i,
_ => { k += 1; continue; }
};
let fused = match (code[k + 1], code[k + 2], code[k + 3]) {
(Op::PushConst(imm_const_idx), Op::IntEq, Op::JumpIfNot(jump_offset))
if matches!(constants.get(imm_const_idx as usize), Some(Const::Int(_))) =>
Some(Op::LoadLocalEqIntConstJumpIfNot {
local_idx: lhs_idx, imm_const_idx, jump_offset,
}),
_ => None,
};
if let Some(fused_op) = fused {
let safe = !jump_targets.contains(&(k + 1))
&& !jump_targets.contains(&(k + 2))
&& !jump_targets.contains(&(k + 3));
if safe {
code[k] = fused_op;
k += 4;
continue;
}
}
k += 1;
}
}
pub(super) fn apply_peephole_slice6(code: &mut [Op]) {
if code.len() < 3 { return; }
let jump_targets = collect_jump_targets(code);
let n = code.len();
let mut k = 0;
while k + 2 < n {
if let (
Op::LoadLocal(src),
Op::StoreLocal(dst),
Op::LoadLocalEqIntConstJumpIfNot { local_idx, imm_const_idx, jump_offset },
) = (code[k], code[k + 1], code[k + 2]) {
if local_idx == dst {
let safe = !jump_targets.contains(&(k + 1))
&& !jump_targets.contains(&(k + 2));
if safe {
code[k] = Op::LoadLocalStoreEqIntConstJumpIfNot {
src, dst, imm_const_idx, jump_offset,
};
k += 3;
continue;
}
}
}
k += 1;
}
}
pub(super) fn apply_peephole_slice7(code: &mut [Op]) {
if code.len() < 3 { return; }
let jump_targets = collect_jump_targets(code);
let n = code.len();
let mut k = 0;
while k + 2 < n {
if let (Op::LoadLocal(local_idx), Op::GetField { name_idx, site_idx })
= (code[k], code[k + 1])
{
let fused = match code[k + 2] {
Op::IntAdd => Some(Op::LoadLocalGetFieldAdd { local_idx, name_idx, site_idx }),
Op::IntSub => Some(Op::LoadLocalGetFieldSub { local_idx, name_idx, site_idx }),
Op::IntMul => Some(Op::LoadLocalGetFieldMul { local_idx, name_idx, site_idx }),
_ => None,
};
if let Some(op) = fused {
let safe = !jump_targets.contains(&(k + 1))
&& !jump_targets.contains(&(k + 2));
if safe {
code[k] = op;
k += 3;
continue;
}
}
}
k += 1;
}
}
pub(super) fn apply_peephole_slice9(code: &mut [Op]) {
if code.len() < 2 { return; }
let jump_targets = collect_jump_targets(code);
let n = code.len();
let mut k = 0;
while k + 1 < n {
if let (Op::LoadLocal(local_idx), Op::GetField { name_idx, site_idx })
= (code[k], code[k + 1])
{
if !jump_targets.contains(&(k + 1)) {
code[k] = Op::LoadLocalGetField { local_idx, name_idx, site_idx };
k += 2;
continue;
}
}
k += 1;
}
}
fn collect_jump_targets(code: &[Op]) -> std::collections::HashSet<usize> {
let mut targets = std::collections::HashSet::new();
for (pc, op) in code.iter().enumerate() {
let off = match op {
Op::Jump(off) | Op::JumpIf(off) | Op::JumpIfNot(off) => Some(*off),
_ => None,
};
if let Some(off) = off {
let target = (pc as i32 + 1 + off) as usize;
targets.insert(target);
}
}
targets
}