v4 / vlib / v3 / ssa / optimize / fold.v
310 lines · 296 sloc · 7.19 KB · 6c4d26f1f98c5464b012a375667ab345e462f7e0
Raw
1module optimize
2
3import v3.ssa
4
5// constant_fold supports constant fold handling for optimize.
6fn constant_fold(mut m ssa.Module) bool {
7 mut changed := false
8 for fi in 0 .. m.funcs.len {
9 for blk_id in m.funcs[fi].blocks {
10 for val_id in m.blocks[blk_id].instrs {
11 if m.values[val_id].kind != .instruction {
12 continue
13 }
14 instr := m.instrs[m.values[val_id].index]
15 if instr.operands.len != 2 {
16 continue
17 }
18 lhs := m.values[instr.operands[0]]
19 rhs := m.values[instr.operands[1]]
20
21 // Skip undef values - can't fold/simplify with undefined.
22 if lhs.kind == .constant && lhs.name == 'undef' {
23 continue
24 }
25 if rhs.kind == .constant && rhs.name == 'undef' {
26 continue
27 }
28
29 // Algebraic simplifications first (work even with non-constant operands):
30 // x+0, x*1, x*0, x-x, x^x, x&x, x|x, x<<0, x*2 -> x<<1, etc.
31 repl, needs_zero := try_algebraic_simplify(m, val_id, instr, lhs, rhs)
32 if repl >= 0 || repl == -2 {
33 if repl == -2 {
34 // x * 2 -> x << 1
35 other_id := if lhs.kind == .constant {
36 instr.operands[1]
37 } else {
38 instr.operands[0]
39 }
40 typ := m.values[val_id].typ
41 one_val := m.get_or_add_const(typ, '1')
42 mut shl_instr := m.instrs[m.values[val_id].index]
43 shl_instr.op = .shl
44 shl_instr.operands = [other_id, one_val]
45 m.instrs[m.values[val_id].index] = shl_instr
46 changed = true
47 continue
48 } else if needs_zero {
49 // x * 0, x & 0, x - x, x ^ x -> 0
50 typ := m.values[val_id].typ
51 zero_val := m.get_or_add_const(typ, '0')
52 m.replace_uses(val_id, zero_val)
53 } else {
54 m.replace_uses(val_id, repl)
55 }
56 changed = true
57 continue
58 }
59
60 if lhs.kind != .constant || rhs.kind != .constant {
61 continue
62 }
63 l_int := lhs.name.i64()
64 r_int := rhs.name.i64()
65
66 mut result := i64(0)
67 mut folded := false
68
69 match instr.op {
70 .add {
71 result = l_int + r_int
72 folded = true
73 }
74 .sub {
75 result = l_int - r_int
76 folded = true
77 }
78 .mul {
79 result = l_int * r_int
80 folded = true
81 }
82 .sdiv {
83 if r_int != 0 {
84 result = l_int / r_int
85 folded = true
86 }
87 }
88 .srem {
89 if r_int != 0 {
90 result = l_int % r_int
91 folded = true
92 }
93 }
94 .udiv {
95 if r_int != 0 {
96 result = i64(u64(l_int) / u64(r_int))
97 folded = true
98 }
99 }
100 .urem {
101 if r_int != 0 {
102 result = i64(u64(l_int) % u64(r_int))
103 folded = true
104 }
105 }
106 .and_ {
107 result = l_int & r_int
108 folded = true
109 }
110 .or_ {
111 result = l_int | r_int
112 folded = true
113 }
114 .xor {
115 result = l_int ^ r_int
116 folded = true
117 }
118 .shl {
119 if r_int >= 0 && r_int < 64 {
120 result = i64(u64(l_int) << u64(r_int))
121 folded = true
122 }
123 }
124 .ashr {
125 if r_int >= 0 && r_int < 64 {
126 result = l_int >> u64(r_int)
127 folded = true
128 }
129 }
130 .lshr {
131 if r_int >= 0 && r_int < 64 {
132 result = i64(u64(l_int) >> u64(r_int))
133 folded = true
134 }
135 }
136 .eq {
137 result = if l_int == r_int { 1 } else { 0 }
138 folded = true
139 }
140 .ne {
141 result = if l_int != r_int { 1 } else { 0 }
142 folded = true
143 }
144 .lt {
145 result = if l_int < r_int { 1 } else { 0 }
146 folded = true
147 }
148 .gt {
149 result = if l_int > r_int { 1 } else { 0 }
150 folded = true
151 }
152 .le {
153 result = if l_int <= r_int { 1 } else { 0 }
154 folded = true
155 }
156 .ge {
157 result = if l_int >= r_int { 1 } else { 0 }
158 folded = true
159 }
160 .ult {
161 result = if u64(l_int) < u64(r_int) { 1 } else { 0 }
162 folded = true
163 }
164 .ugt {
165 result = if u64(l_int) > u64(r_int) { 1 } else { 0 }
166 folded = true
167 }
168 .ule {
169 result = if u64(l_int) <= u64(r_int) { 1 } else { 0 }
170 folded = true
171 }
172 .uge {
173 result = if u64(l_int) >= u64(r_int) { 1 } else { 0 }
174 folded = true
175 }
176 else {}
177 }
178
179 if folded {
180 typ := m.values[val_id].typ
181 const_val := m.get_or_add_const(typ, '${result}')
182 m.replace_uses(val_id, const_val)
183 changed = true
184 }
185 }
186 }
187 }
188 return changed
189}
190
191// try_algebraic_simplify implements identity/strength-reduction simplifications:
192// x+0=x, x*1=x, x*0=0, x-x=0, x^x=0, x&x=x, x|x=x, x<<0=x, x*2=x<<1, x/1=x.
193// Returns (replacement_id, needs_zero):
194// repl >= 0 -> replace all uses of val_id with `repl`
195// repl == -2 -> rewrite as `x << 1` (caller builds the shift)
196// needs_zero == true -> replace all uses with a fresh zero constant
197// repl == -1 -> no simplification
198fn try_algebraic_simplify(_m &ssa.Module, val_id int, instr ssa.Instruction, lhs ssa.Value, rhs ssa.Value) (int, bool) {
199 lhs_id := instr.operands[0]
200 rhs_id := instr.operands[1]
201
202 // Same-operand simplifications (x op x). Integer ops only.
203 if lhs_id == rhs_id {
204 match instr.op {
205 .sub, .xor {
206 return val_id, true // x - x = 0, x ^ x = 0
207 }
208 .and_, .or_ {
209 return lhs_id, false // x & x = x, x | x = x
210 }
211 else {}
212 }
213 }
214
215 mut const_val := i64(0)
216 mut const_is_rhs := false
217 mut other_id := 0
218 if lhs.kind == .constant && lhs.name != 'undef' {
219 const_val = lhs.name.i64()
220 const_is_rhs = false
221 other_id = instr.operands[1]
222 } else if rhs.kind == .constant && rhs.name != 'undef' {
223 const_val = rhs.name.i64()
224 const_is_rhs = true
225 other_id = instr.operands[0]
226 } else {
227 return -1, false
228 }
229
230 match instr.op {
231 .add {
232 if const_val == 0 {
233 return other_id, false // x + 0 = x
234 }
235 }
236 .sub {
237 if const_is_rhs && const_val == 0 {
238 return other_id, false // x - 0 = x
239 }
240 }
241 .mul {
242 if const_val == 0 {
243 return val_id, true // x * 0 = 0
244 }
245 if const_val == 1 {
246 return other_id, false // x * 1 = x
247 }
248 if const_val == 2 {
249 return -2, false // x * 2 = x << 1
250 }
251 }
252 .sdiv, .udiv {
253 if const_is_rhs && const_val == 1 {
254 return other_id, false // x / 1 = x
255 }
256 }
257 .and_ {
258 if const_val == 0 {
259 return val_id, true // x & 0 = 0
260 }
261 }
262 .or_ {
263 if const_val == 0 {
264 return other_id, false // x | 0 = x
265 }
266 }
267 .xor {
268 if const_val == 0 {
269 return other_id, false // x ^ 0 = x
270 }
271 }
272 .shl, .ashr, .lshr {
273 if const_is_rhs && const_val == 0 {
274 return other_id, false // x << 0 = x, x >> 0 = x
275 }
276 }
277 else {}
278 }
279
280 return -1, false
281}
282
283// branch_fold supports branch fold handling for optimize.
284fn branch_fold(mut m ssa.Module) bool {
285 mut changed := false
286 for fi in 0 .. m.funcs.len {
287 for blk_id in m.funcs[fi].blocks {
288 if m.blocks[blk_id].instrs.len == 0 {
289 continue
290 }
291 term_val_id := m.blocks[blk_id].instrs.last()
292 term := m.instrs[m.values[term_val_id].index]
293 if term.op == .br && term.operands.len >= 3 {
294 cond_val := m.values[term.operands[0]]
295 if cond_val.kind == .constant {
296 cond_int := cond_val.name.i64()
297 target := if cond_int != 0 { term.operands[1] } else { term.operands[2] }
298 mut jmp_instr := m.instrs[m.values[term_val_id].index]
299 jmp_instr.op = .jmp
300 mut new_ops := []ssa.ValueID{}
301 new_ops << target
302 jmp_instr.operands = new_ops
303 m.instrs[m.values[term_val_id].index] = jmp_instr
304 changed = true
305 }
306 }
307 }
308 }
309 return changed
310}
311