vxx / vlib / v3 / ssa / optimize / dce.v
142 lines · 131 sloc · 3.4 KB · 6c4d26f1f98c5464b012a375667ab345e462f7e0
Raw
1module optimize
2
3import v3.ssa
4
5// dead_code_elimination supports dead code elimination handling for optimize.
6fn 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.
58fn 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.
72fn 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