v4 / vlib / v3 / ssa / worker.v
312 lines · 298 sloc · 8.7 KB · 6c4d26f1f98c5464b012a375667ab345e462f7e0
Raw
1module ssa
2
3// Parallel-SSA worker support, ported from v2's module.v and adapted to v3's
4// raw-block-id operand model: branch/phi/switch operands that name blocks are
5// remapped by the block offset, while ordinary value operands are remapped by
6// the value offset.
7
8// FuncSSAData holds the SSA a worker produced for a single function.
9pub struct FuncSSAData {
10pub:
11 func_idx int // index into the main module's funcs[]
12 blocks []BlockID // worker-local block ids
13 params []ValueID // worker-local param value ids
14}
15
16// is_block_operand reports whether operand `idx` of this instruction names a
17// basic block (a raw block id) rather than an SSA value.
18pub fn (i &Instruction) is_block_operand(idx int) bool {
19 return match i.op {
20 .jmp { idx == 0 }
21 .br { idx == 1 || idx == 2 }
22 // switch_: cond, default_blk, [case_val, blk]... -> blocks at odd indices.
23 .switch_ { idx % 2 == 1 }
24 // phi: [val, blk]... -> blocks at odd indices.
25 .phi { idx % 2 == 1 }
26 else { false }
27 }
28}
29
30// new_worker_module creates a lightweight Module for parallel SSA building. The
31// worker gets a deep copy of the type store and is seeded with the main module's
32// values/instrs/blocks so ids produced in earlier (serial) phases stay valid.
33pub fn (mut m Module) new_worker_module() &Module {
34 mut wf := []Function{cap: m.funcs.len}
35 for f in m.funcs {
36 wf << f
37 }
38 mut wg := []GlobalVar{cap: m.globals.len}
39 for g in m.globals {
40 wg << g
41 }
42 mut wts := TypeStore{
43 cache: map[string]TypeID{}
44 }
45 wts.types = []Type{cap: m.type_store.types.len}
46 for t in m.type_store.types {
47 wts.types << t
48 }
49 for k, v in m.type_store.cache {
50 wts.cache[k] = v
51 }
52
53 mut w := &Module{
54 name: m.name
55 target: m.target
56 type_store: wts
57 funcs: wf
58 globals: wg
59 c_struct_names: map[int]string{}
60 c_typedef_structs: map[int]bool{}
61 const_cache: m.const_cache.clone()
62 }
63 w.values = []Value{cap: m.values.len + 1024}
64 for v in m.values {
65 w.values << v
66 }
67 w.instrs = []Instruction{cap: m.instrs.len + 1024}
68 for instr in m.instrs {
69 w.instrs << instr
70 }
71 w.blocks = []BasicBlock{cap: m.blocks.len + 256}
72 for blk in m.blocks {
73 w.blocks << blk
74 }
75 return w
76}
77
78// worker_type_cache_key supports worker type cache key handling for ssa.
79fn worker_type_cache_key(t Type, type_remap []TypeID) string {
80 return match t.kind {
81 .int_t {
82 if t.is_unsigned {
83 'u${t.width}'
84 } else {
85 'i${t.width}'
86 }
87 }
88 .float_t {
89 'f${t.width}'
90 }
91 .ptr_t {
92 remapped := if int(t.elem_type) < type_remap.len {
93 type_remap[int(t.elem_type)]
94 } else {
95 t.elem_type
96 }
97 'p${remapped}'
98 }
99 .array_t {
100 remapped := if int(t.elem_type) < type_remap.len {
101 type_remap[int(t.elem_type)]
102 } else {
103 t.elem_type
104 }
105 'a${remapped}_${t.len}'
106 }
107 else {
108 ''
109 }
110 }
111}
112
113// merge_worker_module folds a worker's freshly-built SSA (everything beyond the
114// seed lengths) back into the main module, remapping type/value/instr/block/func
115// ids. func_data carries each built function's worker-local blocks/params.
116pub fn (mut m Module) merge_worker_module(w &Module, func_data []FuncSSAData, seed_values int, seed_instrs int, seed_blocks int, seed_types int, seed_funcs int) {
117 // 1. Remap worker types >= seed_types into the main type store.
118 mut type_remap := []TypeID{len: w.type_store.types.len, init: 0}
119 for ti := 0; ti < seed_types && ti < type_remap.len; ti++ {
120 type_remap[ti] = ti
121 }
122 for ti := seed_types; ti < w.type_store.types.len; ti++ {
123 wt := w.type_store.types[ti]
124 cache_key := worker_type_cache_key(wt, type_remap)
125 if cache_key.len > 0 {
126 if existing := m.type_store.cache[cache_key] {
127 if existing > 0 {
128 type_remap[ti] = existing
129 continue
130 }
131 }
132 }
133 remap_t := fn (id TypeID, type_remap []TypeID, seed_types int) TypeID {
134 if int(id) >= seed_types && int(id) < type_remap.len {
135 return type_remap[int(id)]
136 }
137 return id
138 }
139 mut new_fields := []TypeID{cap: wt.fields.len}
140 for f in wt.fields {
141 new_fields << remap_t(f, type_remap, seed_types)
142 }
143 mut new_params := []TypeID{cap: wt.params.len}
144 for p in wt.params {
145 new_params << remap_t(p, type_remap, seed_types)
146 }
147 new_type := Type{
148 kind: wt.kind
149 width: wt.width
150 is_unsigned: wt.is_unsigned
151 elem_type: remap_t(wt.elem_type, type_remap, seed_types)
152 len: wt.len
153 fields: new_fields
154 field_names: wt.field_names
155 params: new_params
156 ret_type: remap_t(wt.ret_type, type_remap, seed_types)
157 is_c_struct: wt.is_c_struct
158 is_union: wt.is_union
159 }
160 new_id := m.type_store.register(new_type)
161 if cache_key.len > 0 {
162 m.type_store.cache[cache_key] = new_id
163 }
164 type_remap[ti] = new_id
165 }
166
167 value_off := m.values.len - seed_values
168 instr_off := m.instrs.len - seed_instrs
169 block_off := m.blocks.len - seed_blocks
170
171 remap_val := fn (id ValueID, seed_values int, value_off int) ValueID {
172 return if id >= seed_values { id + value_off } else { id }
173 }
174 remap_blk := fn (id BlockID, seed_blocks int, block_off int) BlockID {
175 return if id >= seed_blocks { id + block_off } else { id }
176 }
177 remap_typ := fn (id TypeID, seed_types int, type_remap []TypeID) TypeID {
178 return if int(id) >= seed_types && int(id) < type_remap.len {
179 type_remap[int(id)]
180 } else {
181 id
182 }
183 }
184
185 // 2. Append worker values beyond the seed.
186 for wi := seed_values; wi < w.values.len; wi++ {
187 wv := w.values[wi]
188 mut new_index := wv.index
189 if wv.kind == .instruction {
190 new_index += instr_off
191 } else if wv.kind == .basic_block {
192 new_index += block_off
193 }
194 mut new_uses := []ValueID{cap: wv.uses.len}
195 for u in wv.uses {
196 new_uses << remap_val(u, seed_values, value_off)
197 }
198 m.values << Value{
199 id: wi + value_off
200 kind: wv.kind
201 typ: remap_typ(wv.typ, seed_types, type_remap)
202 name: wv.name
203 index: new_index
204 uses: new_uses
205 }
206 }
207
208 // 3. Append worker instructions beyond the seed, remapping operands by kind.
209 for ii := seed_instrs; ii < w.instrs.len; ii++ {
210 instr := w.instrs[ii]
211 mut new_ops := []ValueID{cap: instr.operands.len}
212 for oi in 0 .. instr.operands.len {
213 op := instr.operands[oi]
214 if instr.is_block_operand(oi) {
215 new_ops << ValueID(remap_blk(BlockID(op), seed_blocks, block_off))
216 } else {
217 new_ops << remap_val(op, seed_values, value_off)
218 }
219 }
220 m.instrs << Instruction{
221 op: instr.op
222 operands: new_ops
223 block: remap_blk(instr.block, seed_blocks, block_off)
224 typ: remap_typ(instr.typ, seed_types, type_remap)
225 pos: instr.pos
226 atomic_ord: instr.atomic_ord
227 inline: instr.inline
228 }
229 }
230
231 // 4. Append worker blocks beyond the seed.
232 for bi := seed_blocks; bi < w.blocks.len; bi++ {
233 blk := w.blocks[bi]
234 mut new_instrs := []ValueID{cap: blk.instrs.len}
235 for vi in blk.instrs {
236 new_instrs << remap_val(vi, seed_values, value_off)
237 }
238 mut new_preds := []BlockID{cap: blk.preds.len}
239 for p in blk.preds {
240 new_preds << remap_blk(p, seed_blocks, block_off)
241 }
242 mut new_succs := []BlockID{cap: blk.succs.len}
243 for s in blk.succs {
244 new_succs << remap_blk(s, seed_blocks, block_off)
245 }
246 m.blocks << BasicBlock{
247 id: blk.id + block_off
248 val_id: remap_val(blk.val_id, seed_values, value_off)
249 name: blk.name
250 parent: blk.parent
251 instrs: new_instrs
252 preds: new_preds
253 succs: new_succs
254 }
255 }
256
257 // 5. Patch the built functions' blocks/params in the main module.
258 for fd in func_data {
259 if fd.func_idx >= m.funcs.len {
260 continue
261 }
262 mut f := m.funcs[fd.func_idx]
263 if f.blocks.len > 0 {
264 continue // already merged by an earlier worker
265 }
266 mut new_blocks := []BlockID{cap: fd.blocks.len}
267 for blk_id in fd.blocks {
268 new_blocks << remap_blk(blk_id, seed_blocks, block_off)
269 }
270 f.blocks = new_blocks
271 mut new_params := []ValueID{cap: fd.params.len}
272 for p in fd.params {
273 new_params << remap_val(p, seed_values, value_off)
274 }
275 f.params = new_params
276 m.funcs[fd.func_idx] = f
277 }
278
279 // 6. Append any brand-new functions a worker created (runtime stubs, etc.).
280 for fi := seed_funcs; fi < w.funcs.len; fi++ {
281 wfunc := w.funcs[fi]
282 mut already := false
283 for existing in m.funcs {
284 if existing.name == wfunc.name {
285 already = true
286 break
287 }
288 }
289 if already {
290 continue
291 }
292 mut new_blocks := []BlockID{cap: wfunc.blocks.len}
293 for blk_id in wfunc.blocks {
294 new_blocks << remap_blk(blk_id, seed_blocks, block_off)
295 }
296 mut new_params := []ValueID{cap: wfunc.params.len}
297 for p in wfunc.params {
298 new_params << remap_val(p, seed_values, value_off)
299 }
300 m.funcs << Function{
301 id: m.funcs.len
302 name: wfunc.name
303 typ: remap_typ(wfunc.typ, seed_types, type_remap)
304 blocks: new_blocks
305 params: new_params
306 is_c_extern: wfunc.is_c_extern
307 is_prototype: wfunc.is_prototype
308 linkage: wfunc.linkage
309 call_conv: wfunc.call_conv
310 }
311 }
312}
313