///|
fn oi_tee_reuse_tail_matches(
instrs : Array[@lib.Instruction],
index : Int32,
) -> Bool {
if index < 2 {
return false
}
match instrs[index] {
@lib.I32And | @lib.I32Or | @lib.I64And | @lib.I64Or => ()
_ => return false
}
match (instrs[index - 2], instrs[index - 1]) {
(@lib.LocalTee(left), @lib.LocalGet(right)) => left == right
_ => false
}
}
///|
fn oi_tee_reuse_has_candidate(body : Array[@lib.Instruction]) -> Bool {
let pending = [(body, 0)]
let mut visited = 0
let mut candidate = false
while pending.length() > 0 {
let (instrs, depth) = pending.pop().unwrap()
if depth > 128 {
return false
}
for index = 0; index < instrs.length(); index = index + 1 {
visited += 1
if visited > 200000 {
return false
}
candidate = oi_tee_reuse_tail_matches(instrs, index) || candidate
match instrs[index] {
@lib.Block(_, @lib.Expr(nested))
| @lib.Loop(_, @lib.Expr(nested))
| @lib.TryTable(_, _, @lib.Expr(nested)) =>
pending.push((nested, depth + 1))
@lib.If(_, then_body, else_body) => {
pending.push((then_body, depth + 1))
if else_body is Some(nested) {
pending.push((nested, depth + 1))
}
}
@lib.Try(_, @lib.Expr(nested), catches, _) => {
pending.push((nested, depth + 1))
for catch_ in catches {
match catch_ {
@lib.LegacyCatch(_, @lib.Expr(catch_body))
| @lib.LegacyCatchAll(@lib.Expr(catch_body)) =>
pending.push((catch_body, depth + 1))
}
}
}
_ => ()
}
}
}
candidate
}
///|
fn oi_tee_reuse_rewrite_body(
body : Array[@lib.Instruction],
) -> Array[@lib.Instruction] {
let out : Array[@lib.Instruction] = []
for instr in body {
let instr = pass_lower_map_children(instr, oi_tee_reuse_rewrite_body)
out.push(instr)
if oi_tee_reuse_tail_matches(out, out.length() - 1) {
// The tee and its complete producer stay in their original position.
// The immediately following get observes that write. Only integer
// idempotence applies; floating-point equality and effects are untouched.
ignore(out.pop())
ignore(out.pop())
}
}
out
}
///|
fn oi_tee_reuse_preclean(func : @lib.Func) -> (@lib.Func, Bool) {
let @lib.Func(locals, @lib.Expr(body)) = func
if !oi_tee_reuse_has_candidate(body) {
return (func, false)
}
(
@lib.Func::new(locals, @lib.Expr::new(oi_tee_reuse_rewrite_body(body))),
true,
)
}