v / vlib / v2 / ssa / optimize / optimize.v
147 lines · 134 sloc · 3.89 KB · 81a5657604ec6da99c25e26546870c6888d6fdde
Raw
1// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
2// Use of this source code is governed by an MIT license
3// that can be found in the LICENSE file.
4
5module optimize
6
7import os
8import v2.ssa
9import time
10
11pub struct OptimizeOptions {
12pub:
13 verify_each_pass bool
14 strict_verify bool
15}
16
17// Optimize Module
18pub fn optimize(mut m ssa.Module) {
19 strict_verify := os.getenv('V2_VERIFY_STRICT') != ''
20 optimize_with_options(mut m, OptimizeOptions{
21 verify_each_pass: os.getenv('V2_VERIFY') != ''
22 strict_verify: strict_verify
23 })
24}
25
26pub fn optimize_with_options(mut m ssa.Module, opts OptimizeOptions) {
27 mut t0 := time.ticks()
28 verify_pipeline_checkpoint(m, opts, 'input')
29 rebuild_use_lists(mut m)
30
31 // 1. Build Control Flow Graph (Predecessors)
32 cfg := build_cfg(mut m)
33 verify_pipeline_checkpoint(m, opts, 'build_cfg')
34 mut t1 := time.ticks()
35 eprintln(' opt: build_cfg: ${t1 - t0}ms')
36 t0 = t1
37
38 // 2. Compute Dominator Tree (Lengauer-Tarjan)
39 dom := compute_dominators(mut m, &cfg)
40 verify_pipeline_checkpoint(m, opts, 'compute_dominators')
41 t1 = time.ticks()
42 // Count dom tree stats
43 mut n_with_idom := 0
44 mut n_in_dom_tree := 0
45 mut n_no_idom := 0
46 mut n_idom_self := 0
47 mut n_idom_diff := 0
48 for bi in 0 .. dom.idom.len {
49 if dom.idom[bi] >= 0 {
50 n_with_idom += 1
51 if dom.idom[bi] == bi {
52 n_idom_self += 1
53 } else {
54 n_idom_diff += 1
55 }
56 } else {
57 n_no_idom += 1
58 }
59 if bi < dom.dom_tree.len {
60 n_in_dom_tree += dom.dom_tree[bi].len
61 }
62 }
63 eprintln(' opt: compute_dominators: ${t1 - t0}ms (with_idom=${n_with_idom} no_idom=${n_no_idom} self=${n_idom_self} diff=${n_idom_diff} dom_tree=${n_in_dom_tree})')
64 t0 = t1
65
66 // 3. Promote Memory to Register (Construct SSA / Phi Nodes)
67 promote_memory_to_register(mut m, dom, &cfg)
68 verify_pipeline_checkpoint(m, opts, 'promote_memory_to_register')
69 rebuild_use_lists(mut m)
70 t1 = time.ticks()
71 // Count remaining allocas
72 mut n_alloca := 0
73 for vi in 0 .. m.values.len {
74 val := m.values[vi]
75 if val.kind == .instruction {
76 idx := val.index
77 if idx >= 0 && idx < m.instrs.len {
78 if m.instrs[idx].op == .alloca {
79 n_alloca += 1
80 }
81 }
82 }
83 }
84 eprintln(' opt: mem2reg: ${t1 - t0}ms (remaining allocas: ${n_alloca})')
85 t0 = t1
86
87 // 4. Simplify trivial Phi Nodes
88 simplify_phi_nodes(mut m)
89 verify_pipeline_checkpoint(m, opts, 'simplify_phi_nodes')
90 rebuild_use_lists(mut m)
91 t1 = time.ticks()
92 eprintln(' opt: simplify_phi: ${t1 - t0}ms')
93 t0 = t1
94
95 // 5. Eliminate Phi Nodes (convert to copies in predecessor blocks)
96 eliminate_phi_nodes(mut m)
97 verify_pipeline_checkpoint(m, opts, 'eliminate_phi_nodes')
98 rebuild_use_lists(mut m)
99 t1 = time.ticks()
100 eprintln(' opt: eliminate_phi: ${t1 - t0}ms')
101 t0 = t1
102
103 // 6. Remove dead instructions after structural SSA rewrites are complete.
104 dead_code_elimination(mut m)
105 verify_pipeline_checkpoint(m, opts, 'dead_code_elimination')
106 t1 = time.ticks()
107 eprintln(' opt: dce: ${t1 - t0}ms')
108}
109
110fn rebuild_use_lists(mut m ssa.Module) {
111 for vi in 0 .. m.values.len {
112 mut val := m.values[vi]
113 val.uses = []
114 m.values[vi] = val
115 }
116 for fi in 0 .. m.funcs.len {
117 for blk_id in m.funcs[fi].blocks {
118 if blk_id < 0 || blk_id >= m.blocks.len {
119 continue
120 }
121 for val_id in m.blocks[blk_id].instrs {
122 if val_id <= 0 || val_id >= m.values.len || m.values[val_id].kind != .instruction {
123 continue
124 }
125 instr_idx := m.values[val_id].index
126 if instr_idx < 0 || instr_idx >= m.instrs.len {
127 continue
128 }
129 for op_id in m.instrs[instr_idx].operands {
130 if op_id >= 0 && op_id < m.values.len && val_id !in m.values[op_id].uses {
131 mut op_val := m.values[op_id]
132 op_val.uses << val_id
133 m.values[op_id] = op_val
134 }
135 }
136 }
137 }
138 }
139}
140
141fn verify_pipeline_checkpoint(m &ssa.Module, opts OptimizeOptions, pass_name string) {
142 if opts.verify_each_pass || opts.strict_verify {
143 verify_and_panic_with_options(m, pass_name, VerifyPanicOptions{
144 allow_noncritical: !opts.strict_verify
145 })
146 }
147}
148