154 lines
3.6 KiB
V
154 lines
3.6 KiB
V
module optimize
|
|
|
|
import v3.ssa
|
|
|
|
fn remove_unreachable_blocks(mut m ssa.Module) {
|
|
build_cfg(mut m)
|
|
for fi in 0 .. m.funcs.len {
|
|
if m.funcs[fi].blocks.len == 0 {
|
|
continue
|
|
}
|
|
mut reachable := map[int]bool{}
|
|
mut q := []int{}
|
|
q << m.funcs[fi].blocks[0]
|
|
reachable[m.funcs[fi].blocks[0]] = true
|
|
|
|
for q.len > 0 {
|
|
curr := q.last()
|
|
q.delete_last()
|
|
for succ in m.blocks[curr].succs {
|
|
if !reachable[succ] {
|
|
reachable[succ] = true
|
|
q << succ
|
|
}
|
|
}
|
|
}
|
|
|
|
mut new_blocks := []int{}
|
|
for blk in m.funcs[fi].blocks {
|
|
if reachable[blk] {
|
|
new_blocks << blk
|
|
}
|
|
}
|
|
mut func := m.funcs[fi]
|
|
func.blocks = new_blocks
|
|
m.funcs[fi] = func
|
|
}
|
|
}
|
|
|
|
fn merge_blocks(mut m ssa.Module) {
|
|
mut changed := true
|
|
mut first := true
|
|
for changed {
|
|
changed = false
|
|
if first {
|
|
build_cfg(mut m)
|
|
first = false
|
|
}
|
|
|
|
for fi in 0 .. m.funcs.len {
|
|
mut merged := map[int]bool{}
|
|
|
|
for blk_id in m.funcs[fi].blocks {
|
|
if merged[blk_id] {
|
|
continue
|
|
}
|
|
if m.blocks[blk_id].instrs.len == 0 {
|
|
continue
|
|
}
|
|
last_val := m.blocks[blk_id].instrs.last()
|
|
last_instr := m.instrs[m.values[last_val].index]
|
|
if last_instr.op != .jmp || last_instr.operands.len < 1 {
|
|
continue
|
|
}
|
|
target_id := int(last_instr.operands[0])
|
|
if target_id < 0 || target_id >= m.blocks.len || target_id == blk_id {
|
|
continue
|
|
}
|
|
if m.blocks[target_id].preds.len != 1 || m.blocks[target_id].preds[0] != blk_id {
|
|
continue
|
|
}
|
|
|
|
// Phi-safety: don't merge into a block that holds phi nodes. Its phis
|
|
// reference predecessors by block id; collapsing the edge would leave
|
|
// dangling predecessor operands.
|
|
mut target_has_phi := false
|
|
for vid in m.blocks[target_id].instrs {
|
|
if vid > 0 && vid < m.values.len && m.values[vid].kind == .instruction {
|
|
if m.instrs[m.values[vid].index].op == .phi {
|
|
target_has_phi = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if target_has_phi {
|
|
continue
|
|
}
|
|
|
|
// Merge: remove jmp from A, append B's instrs (and reparent them).
|
|
mut blk := m.blocks[blk_id]
|
|
blk.instrs.delete_last()
|
|
for moved_val in m.blocks[target_id].instrs {
|
|
blk.instrs << moved_val
|
|
if moved_val > 0 && moved_val < m.values.len
|
|
&& m.values[moved_val].kind == .instruction {
|
|
instr_idx := m.values[moved_val].index
|
|
if instr_idx >= 0 && instr_idx < m.instrs.len {
|
|
mut instr := m.instrs[instr_idx]
|
|
instr.block = blk_id
|
|
m.instrs[instr_idx] = instr
|
|
}
|
|
}
|
|
}
|
|
m.blocks[blk_id] = blk
|
|
|
|
// B's successors now have A as a predecessor instead of B. Rewrite any
|
|
// phi predecessor operands that named B (raw block id) to name A.
|
|
for succ_id in m.blocks[target_id].succs {
|
|
if succ_id < 0 || succ_id >= m.blocks.len {
|
|
continue
|
|
}
|
|
for iv in m.blocks[succ_id].instrs {
|
|
if iv <= 0 || iv >= m.values.len || m.values[iv].kind != .instruction {
|
|
continue
|
|
}
|
|
idx := m.values[iv].index
|
|
if m.instrs[idx].op != .phi {
|
|
continue
|
|
}
|
|
mut phi_ins := m.instrs[idx]
|
|
mut phi_modified := false
|
|
for i := 1; i < phi_ins.operands.len; i += 2 {
|
|
if int(phi_ins.operands[i]) == target_id {
|
|
phi_ins.operands[i] = ssa.ValueID(blk_id)
|
|
phi_modified = true
|
|
}
|
|
}
|
|
if phi_modified {
|
|
m.instrs[idx] = phi_ins
|
|
}
|
|
}
|
|
}
|
|
|
|
merged[target_id] = true
|
|
changed = true
|
|
}
|
|
|
|
if merged.len > 0 {
|
|
mut new_blks := []int{}
|
|
for b in m.funcs[fi].blocks {
|
|
if !merged[b] {
|
|
new_blks << b
|
|
}
|
|
}
|
|
mut func := m.funcs[fi]
|
|
func.blocks = new_blks
|
|
m.funcs[fi] = func
|
|
}
|
|
}
|
|
if changed {
|
|
build_cfg(mut m)
|
|
}
|
|
}
|
|
}
|