| 1 | module optimize |
| 2 | |
| 3 | import v3.ssa |
| 4 | |
| 5 | // dead_code_elimination supports dead code elimination handling for optimize. |
| 6 | fn dead_code_elimination(mut m ssa.Module) { |
| 7 | mut changed := true |
| 8 | for changed { |
| 9 | changed = false |
| 10 | for fi in 0 .. m.funcs.len { |
| 11 | dead_stores := find_dead_stores(m, m.funcs[fi]) |
| 12 | |
| 13 | for blk_id in m.funcs[fi].blocks { |
| 14 | mut dead_instrs := map[int]bool{} |
| 15 | for val_id in m.blocks[blk_id].instrs { |
| 16 | val := m.values[val_id] |
| 17 | if val.kind != .instruction { |
| 18 | continue |
| 19 | } |
| 20 | instr := m.instrs[val.index] |
| 21 | if instr.op == .store && val_id in dead_stores { |
| 22 | dead_instrs[val_id] = true |
| 23 | continue |
| 24 | } |
| 25 | has_side_effects := instr.op in [.store, .call, .call_indirect, .call_sret, |
| 26 | .ret, .br, .jmp, .switch_, .unreachable, .assign, .fence, .cmpxchg, |
| 27 | .atomicrmw, .go_call, .spawn_call] |
| 28 | if !has_side_effects && val.uses.len == 0 { |
| 29 | dead_instrs[val_id] = true |
| 30 | } |
| 31 | } |
| 32 | |
| 33 | if dead_instrs.len > 0 { |
| 34 | for val_id, _ in dead_instrs { |
| 35 | instr := m.instrs[m.values[val_id].index] |
| 36 | |
| 37 | for op_id in instr.value_operands() { |
| 38 | remove_use(mut m, op_id, val_id) |
| 39 | } |
| 40 | } |
| 41 | mut new_instrs := []int{cap: m.blocks[blk_id].instrs.len} |
| 42 | for val_id in m.blocks[blk_id].instrs { |
| 43 | if val_id !in dead_instrs { |
| 44 | new_instrs << val_id |
| 45 | } |
| 46 | } |
| 47 | mut blk := m.blocks[blk_id] |
| 48 | blk.instrs = new_instrs |
| 49 | m.blocks[blk_id] = blk |
| 50 | changed = true |
| 51 | } |
| 52 | } |
| 53 | } |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | // remove_use updates remove use state for optimize. |
| 58 | fn remove_use(mut m ssa.Module, val_id int, user_id int) { |
| 59 | if val_id < 0 || val_id >= m.values.len { |
| 60 | return |
| 61 | } |
| 62 | mut val := m.values[val_id] |
| 63 | for i := val.uses.len - 1; i >= 0; i-- { |
| 64 | if val.uses[i] == user_id { |
| 65 | val.uses.delete(i) |
| 66 | } |
| 67 | } |
| 68 | m.values[val_id] = val |
| 69 | } |
| 70 | |
| 71 | // find_dead_stores resolves find dead stores information for optimize. |
| 72 | fn find_dead_stores(m &ssa.Module, func ssa.Function) map[int]bool { |
| 73 | mut dead_stores := map[int]bool{} |
| 74 | mut allocas := map[int]bool{} |
| 75 | mut alloca_stores := map[int][]int{} |
| 76 | mut alloca_escapes := map[int]bool{} |
| 77 | |
| 78 | for blk_id in func.blocks { |
| 79 | for val_id in m.blocks[blk_id].instrs { |
| 80 | val := m.values[val_id] |
| 81 | if val.kind != .instruction { |
| 82 | continue |
| 83 | } |
| 84 | op := m.instrs[val.index].op |
| 85 | if op == .alloca || op == .heap_alloc { |
| 86 | allocas[val_id] = true |
| 87 | } |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | for alloca_id, _ in allocas { |
| 92 | for use_id in m.values[alloca_id].uses { |
| 93 | if use_id < 0 || use_id >= m.values.len { |
| 94 | continue |
| 95 | } |
| 96 | val := m.values[use_id] |
| 97 | if val.kind != .instruction { |
| 98 | alloca_escapes[alloca_id] = true |
| 99 | continue |
| 100 | } |
| 101 | instr := m.instrs[val.index] |
| 102 | match instr.op { |
| 103 | .store { |
| 104 | if instr.operands.len > 1 && instr.operands[1] == alloca_id { |
| 105 | alloca_stores[alloca_id] << use_id |
| 106 | } else { |
| 107 | alloca_escapes[alloca_id] = true |
| 108 | } |
| 109 | } |
| 110 | .load {} |
| 111 | else { |
| 112 | alloca_escapes[alloca_id] = true |
| 113 | } |
| 114 | } |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | // An alloca with no loads and no escapes → all stores to it are dead |
| 119 | for alloca_id, _ in allocas { |
| 120 | if alloca_id in alloca_escapes { |
| 121 | continue |
| 122 | } |
| 123 | mut has_load := false |
| 124 | for use_id in m.values[alloca_id].uses { |
| 125 | if use_id >= 0 && use_id < m.values.len && m.values[use_id].kind == .instruction { |
| 126 | if m.instrs[m.values[use_id].index].op == .load { |
| 127 | has_load = true |
| 128 | break |
| 129 | } |
| 130 | } |
| 131 | } |
| 132 | if !has_load { |
| 133 | if store_ids := alloca_stores[alloca_id] { |
| 134 | for store_id in store_ids { |
| 135 | dead_stores[store_id] = true |
| 136 | } |
| 137 | } |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | return dead_stores |
| 142 | } |
| 143 | |