v / vlib / v2 / ssa / optimize / cfg.v
176 lines · 164 sloc · 3.77 KB · 6c710c0b07df2d9824cda86d0ac59ffc2088e23d
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 v2.ssa
8
9// --- CFG Construction ---
10// Flat CFG data: succs and preds stored outside BasicBlock to avoid struct field access issues
11// in v3 (ARM64-compiled binary). Accessing m.blocks[i].succs returns garbage for large structs.
12struct CfgData {
13mut:
14 succs [][]int
15 preds [][]int
16}
17
18// Helper: check if value is already in a small array (avoids map allocation)
19fn arr_contains(arr []int, val int) bool {
20 for i in 0 .. arr.len {
21 if arr[i] == val {
22 return true
23 }
24 }
25 return false
26}
27
28pub fn build_cfg(mut m ssa.Module) CfgData {
29 n_blocks := m.blocks.len
30 n_values := m.values.len
31 n_funcs := m.funcs.len
32 n_instrs_total := m.instrs.len
33
34 mut cfg := CfgData{
35 succs: [][]int{len: n_blocks}
36 preds: [][]int{len: n_blocks}
37 }
38
39 for fi in 0 .. n_funcs {
40 func := m.funcs[fi]
41 n_func_blocks := func.blocks.len
42
43 for fbi2 in 0 .. n_func_blocks {
44 blk_id := func.blocks[fbi2]
45 blk := m.blocks[blk_id]
46 n_instrs := blk.instrs.len
47 if n_instrs == 0 {
48 continue
49 }
50 term_val_id := blk.instrs[n_instrs - 1]
51 if term_val_id < 0 || term_val_id >= n_values {
52 continue
53 }
54 term_val := m.values[term_val_id]
55 if term_val.index < 0 || term_val.index >= n_instrs_total {
56 continue
57 }
58 term := m.instrs[term_val.index]
59
60 n_operands := term.operands.len
61 match term.op {
62 .br {
63 if n_operands >= 3 {
64 op1 := term.operands[1]
65 op2 := term.operands[2]
66 mut s1 := -1
67 if op1 >= 0 {
68 if op1 < n_values {
69 v1 := m.values[op1]
70 s1 = v1.index
71 }
72 }
73 mut s2 := -1
74 if op2 >= 0 {
75 if op2 < n_values {
76 v2 := m.values[op2]
77 s2 = v2.index
78 }
79 }
80 if s1 >= 0 {
81 if s1 < n_blocks {
82 if !arr_contains(cfg.succs[blk_id], s1) {
83 cfg.succs[blk_id] << s1
84 }
85 }
86 }
87 if s2 >= 0 {
88 if s2 < n_blocks {
89 if !arr_contains(cfg.succs[blk_id], s2) {
90 cfg.succs[blk_id] << s2
91 }
92 }
93 }
94 }
95 }
96 .jmp {
97 if n_operands >= 1 {
98 op0 := term.operands[0]
99 mut s := -1
100 if op0 >= 0 {
101 if op0 < n_values {
102 v0 := m.values[op0]
103 s = v0.index
104 }
105 }
106 if s >= 0 {
107 if s < n_blocks {
108 cfg.succs[blk_id] << s
109 }
110 }
111 }
112 }
113 .switch_ {
114 if n_operands >= 2 {
115 op_def := term.operands[1]
116 mut s := -1
117 if op_def >= 0 {
118 if op_def < n_values {
119 vd := m.values[op_def]
120 s = vd.index
121 }
122 }
123 if s >= 0 {
124 if s < n_blocks {
125 if !arr_contains(cfg.succs[blk_id], s) {
126 cfg.succs[blk_id] << s
127 }
128 }
129 }
130 for i := 3; i < n_operands; i += 2 {
131 op_case := term.operands[i]
132 mut cs := -1
133 if op_case >= 0 {
134 if op_case < n_values {
135 vc := m.values[op_case]
136 cs = vc.index
137 }
138 }
139 if cs >= 0 {
140 if cs < n_blocks {
141 if !arr_contains(cfg.succs[blk_id], cs) {
142 cfg.succs[blk_id] << cs
143 }
144 }
145 }
146 }
147 }
148 }
149 else {}
150 }
151
152 // Build predecessors from succs
153 n_succs := cfg.succs[blk_id].len
154 for si in 0 .. n_succs {
155 s := cfg.succs[blk_id][si]
156 if s < 0 || s >= n_blocks {
157 continue
158 }
159 if !arr_contains(cfg.preds[s], blk_id) {
160 cfg.preds[s] << blk_id
161 }
162 }
163 }
164 }
165
166 // Also write back to m.blocks for compatibility with other passes
167 for bi in 0 .. n_blocks {
168 // Avoid m.blocks[X].succs/preds = ... -- chained field assign broken in ARM64 self-hosted
169 mut blk := m.blocks[bi]
170 blk.succs = cfg.succs[bi]
171 blk.preds = cfg.preds[bi]
172 m.blocks[bi] = blk
173 }
174
175 return cfg
176}
177