1 // SPDX-License-Identifier: MIT
2 //
3 // SSA conversion
4 // Copyright (C) 2005 Luc Van Oostenryck
5 //
6
7 #include <assert.h>
8 #include "ssa.h"
9 #include "lib.h"
10 #include "sset.h"
11 #include "dominate.h"
12 #include "flowgraph.h"
13 #include "linearize.h"
14 #include "flow.h" // for convert_load_instruction()
15
16
17 // Is it possible and desirable for this to be promoted to a pseudo?
is_promotable(struct symbol * type)18 static inline bool is_promotable(struct symbol *type)
19 {
20 struct symbol *member;
21 int bf_seen;
22 int nbr;
23
24 if (type->type == SYM_NODE)
25 type = type->ctype.base_type;
26 switch (type->type) {
27 case SYM_ENUM:
28 case SYM_BITFIELD:
29 case SYM_PTR:
30 case SYM_RESTRICT: // OK, always integer types
31 return 1;
32 case SYM_STRUCT:
33 // we allow a single scalar field
34 // but a run of bitfields count for 1
35 nbr = 0;
36 bf_seen = 0;
37 FOR_EACH_PTR(type->symbol_list, member) {
38 if (is_bitfield_type(member)) {
39 if (bf_seen)
40 continue;
41 bf_seen = 1;
42 } else {
43 bf_seen = 0;
44 }
45 if (!is_scalar_type(member))
46 return 0;
47 if (nbr++)
48 return 0;
49 } END_FOR_EACH_PTR(member);
50 if (bf_seen && (type->bit_size > long_ctype.bit_size))
51 return 0;
52 return 1;
53 case SYM_UNION:
54 // FIXME: should be like struct but has problem
55 // when used with/for type cohercion
56 // -----> OK if only same sized integral types
57 FOR_EACH_PTR(type->symbol_list, member) {
58 if (member->bit_size != type->bit_size)
59 return 0;
60 if (!is_integral_type(member))
61 return 0;
62 } END_FOR_EACH_PTR(member);
63 return 1;
64 default:
65 break;
66 }
67 if (type->ctype.base_type == &int_type)
68 return 1;
69 if (type->ctype.base_type == &fp_type)
70 return 1;
71 return 0;
72 }
73
insn_before(struct instruction * a,struct instruction * b)74 static bool insn_before(struct instruction *a, struct instruction *b)
75 {
76 struct basic_block *bb = a->bb;
77 struct instruction *insn;
78
79 assert(b->bb == bb);
80 FOR_EACH_PTR(bb->insns, insn) {
81 if (insn == a)
82 return true;
83 if (insn == b)
84 return false;
85 } END_FOR_EACH_PTR(insn);
86 assert(0);
87 }
88
kill_store(struct instruction * insn)89 static void kill_store(struct instruction *insn)
90 {
91 remove_use(&insn->src);
92 remove_use(&insn->target);
93 insn->bb = NULL;
94 }
95
rewrite_local_var(struct basic_block * bb,pseudo_t addr,int nbr_stores,int nbr_uses)96 static void rewrite_local_var(struct basic_block *bb, pseudo_t addr, int nbr_stores, int nbr_uses)
97 {
98 struct instruction *insn;
99 pseudo_t val = NULL;
100
101 if (!bb)
102 return;
103
104 FOR_EACH_PTR(bb->insns, insn) {
105
106 if (!insn->bb || insn->src != addr)
107 continue;
108 switch (insn->opcode) {
109 case OP_LOAD:
110 if (!val)
111 val = undef_pseudo();
112 convert_load_instruction(insn, val);
113 break;
114 case OP_STORE:
115 val = insn->target;
116 // can't use kill_instruction() unless
117 // we add a fake user to val
118 kill_store(insn);
119 break;
120 }
121 } END_FOR_EACH_PTR(insn);
122 }
123
rewrite_single_store(struct instruction * store)124 static bool rewrite_single_store(struct instruction *store)
125 {
126 pseudo_t addr = store->src;
127 struct pseudo_user *pu;
128
129 FOR_EACH_PTR(addr->users, pu) {
130 struct instruction *insn = pu->insn;
131
132 if (insn->opcode != OP_LOAD)
133 continue;
134
135 // Let's try to replace the value of the load
136 // by the value from the store. This is only valid
137 // if the store dominate the load.
138
139 if (insn->bb == store->bb) {
140 // the load and the store are in the same BB
141 // we can convert if the load is after the store.
142 if (!insn_before(store, insn))
143 continue;
144 } else if (!domtree_dominates(store->bb, insn->bb)) {
145 // we can't convert this load
146 continue;
147 }
148
149 // OK, we can rewrite this load
150
151 // undefs ?
152
153 convert_load_instruction(insn, store->target);
154 } END_FOR_EACH_PTR(pu);
155
156 // is there some unconverted loads?
157 if (pseudo_user_list_size(addr->users) > 1)
158 return false;
159
160 kill_store(store);
161 return true;
162 }
163
164 static struct sset *processed;
165
166 // we would like to know:
167 // is there one or more stores?
168 // are all loads & stores local/done in a single block?
ssa_convert_one_var(struct entrypoint * ep,struct symbol * var)169 static void ssa_convert_one_var(struct entrypoint *ep, struct symbol *var)
170 {
171 struct basic_block_list *alpha = NULL;
172 struct basic_block_list *idf = NULL;
173 struct basic_block *samebb = NULL;
174 struct instruction *store = NULL;
175 struct basic_block *bb;
176 struct pseudo_user *pu;
177 unsigned long mod = var->ctype.modifiers;
178 bool local = true;
179 int nbr_stores = 0;
180 int nbr_uses = 0;
181 pseudo_t addr;
182
183 /* Never used as a symbol? */
184 addr = var->pseudo;
185 if (!addr)
186 return;
187
188 /* We don't do coverage analysis of volatiles.. */
189 if (mod & MOD_VOLATILE)
190 return;
191
192 /* ..and symbols with external visibility need more care */
193 mod &= (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE);
194 if (mod)
195 goto external_visibility;
196
197 if (!is_promotable(var))
198 return;
199
200 // 1) insert in the worklist all BBs that may modify var
201 sset_reset(processed);
202 FOR_EACH_PTR(addr->users, pu) {
203 struct instruction *insn = pu->insn;
204 struct basic_block *bb = insn->bb;
205
206 switch (insn->opcode) {
207 case OP_STORE:
208 nbr_stores++;
209 store = insn;
210 if (!sset_testset(processed, bb->nr))
211 add_bb(&alpha, bb);
212 /* fall through */
213 case OP_LOAD:
214 if (local) {
215 if (!samebb)
216 samebb = bb;
217 else if (samebb != bb)
218 local = false;
219 }
220 nbr_uses++;
221 break;
222 case OP_SYMADDR:
223 mod |= MOD_ADDRESSABLE;
224 goto external_visibility;
225 default:
226 warning(var->pos, "symbol '%s' pseudo used in unexpected way",
227 show_ident(var->ident));
228 }
229 } END_FOR_EACH_PTR(pu);
230
231 if (nbr_stores == 1) {
232 if (rewrite_single_store(store))
233 return;
234 }
235
236 // if all uses are local to a single block
237 // they can easily be rewritten and doesn't need phi-nodes
238 // FIXME: could be done for extended BB too
239 if (local) {
240 rewrite_local_var(samebb, addr, nbr_stores, nbr_uses);
241 return;
242 }
243
244 idf_compute(ep, &idf, alpha);
245 FOR_EACH_PTR(idf, bb) {
246 struct instruction *node = insert_phi_node(bb, var);
247 node->phi_var = var->pseudo;
248 } END_FOR_EACH_PTR(bb);
249 var->torename = 1;
250
251 external_visibility:
252 if (mod & (MOD_NONLOCAL | MOD_STATIC))
253 return;
254 kill_dead_stores(ep, addr, !mod);
255 }
256
lookup_var(struct basic_block * bb,struct symbol * var)257 static pseudo_t lookup_var(struct basic_block *bb, struct symbol *var)
258 {
259 do {
260 pseudo_t val = phi_map_lookup(bb->phi_map, var);
261 if (val)
262 return val;
263 } while ((bb = bb->idom));
264 return undef_pseudo();
265 }
266
267 static struct instruction_list *phis_all;
268 static struct instruction_list *phis_used;
269
ssa_rename_insn(struct basic_block * bb,struct instruction * insn)270 static void ssa_rename_insn(struct basic_block *bb, struct instruction *insn)
271 {
272 struct symbol *var;
273 pseudo_t addr;
274 pseudo_t val;
275
276 switch (insn->opcode) {
277 case OP_STORE:
278 addr = insn->src;
279 if (addr->type != PSEUDO_SYM)
280 break;
281 var = addr->sym;
282 if (!var || !var->torename)
283 break;
284 phi_map_update(&bb->phi_map, var, insn->target);
285 kill_store(insn);
286 break;
287 case OP_LOAD:
288 addr = insn->src;
289 if (addr->type != PSEUDO_SYM)
290 break;
291 var = addr->sym;
292 if (!var || !var->torename)
293 break;
294 val = lookup_var(bb, var);
295 convert_load_instruction(insn, val);
296 break;
297 case OP_PHI:
298 var = insn->type;
299 if (!var || !var->torename)
300 break;
301 phi_map_update(&bb->phi_map, var, insn->target);
302 add_instruction(&phis_all, insn);
303 break;
304 }
305 }
306
ssa_rename_insns(struct entrypoint * ep)307 static void ssa_rename_insns(struct entrypoint *ep)
308 {
309 struct basic_block *bb;
310
311 FOR_EACH_PTR(ep->bbs, bb) {
312 struct instruction *insn;
313 FOR_EACH_PTR(bb->insns, insn) {
314 if (!insn->bb)
315 continue;
316 ssa_rename_insn(bb, insn);
317 } END_FOR_EACH_PTR(insn);
318 } END_FOR_EACH_PTR(bb);
319 }
320
mark_phi_used(pseudo_t val)321 static void mark_phi_used(pseudo_t val)
322 {
323 struct instruction *node;
324
325 if (val->type != PSEUDO_REG)
326 return;
327 node = val->def;
328 if (node->opcode != OP_PHI)
329 return;
330 if (node->used)
331 return;
332 node->used = 1;
333 add_instruction(&phis_used, node);
334 }
335
ssa_rename_phi(struct instruction * insn)336 static void ssa_rename_phi(struct instruction *insn)
337 {
338 struct basic_block *par;
339 struct symbol *var;
340
341 if (!insn->phi_var)
342 return;
343 var = insn->phi_var->sym;
344 if (!var->torename)
345 return;
346 FOR_EACH_PTR(insn->bb->parents, par) {
347 struct instruction *term = delete_last_instruction(&par->insns);
348 pseudo_t val = lookup_var(par, var);
349 pseudo_t phi = alloc_phi(par, val, var);
350 phi->ident = var->ident;
351 add_instruction(&par->insns, term);
352 use_pseudo(insn, phi, add_pseudo(&insn->phi_list, phi));
353 mark_phi_used(val);
354 } END_FOR_EACH_PTR(par);
355 }
356
ssa_rename_phis(struct entrypoint * ep)357 static void ssa_rename_phis(struct entrypoint *ep)
358 {
359 struct instruction *phi;
360
361 phis_used = NULL;
362 FOR_EACH_PTR(phis_all, phi) {
363 if (has_users(phi->target)) {
364 phi->used = 1;
365 add_instruction(&phis_used, phi);
366 }
367 } END_FOR_EACH_PTR(phi);
368
369 FOR_EACH_PTR(phis_used, phi) {
370 if (!phi->bb)
371 continue;
372 ssa_rename_phi(phi);
373 } END_FOR_EACH_PTR(phi);
374 }
375
ssa_convert(struct entrypoint * ep)376 void ssa_convert(struct entrypoint *ep)
377 {
378 struct basic_block *bb;
379 pseudo_t pseudo;
380 int first, last;
381
382 // calculate the number of BBs
383 first = ep->entry->bb->nr;
384 last = first;
385 FOR_EACH_PTR(ep->bbs, bb) {
386 int nr = bb->nr;
387 if (nr > last)
388 last = nr;
389 } END_FOR_EACH_PTR(bb);
390
391 processed = sset_init(first, last);
392
393 // try to promote memory accesses to pseudos
394 FOR_EACH_PTR(ep->accesses, pseudo) {
395 ssa_convert_one_var(ep, pseudo->sym);
396 } END_FOR_EACH_PTR(pseudo);
397
398 // rename the converted accesses
399 phis_all = phis_used = NULL;
400 ssa_rename_insns(ep);
401 ssa_rename_phis(ep);
402 }
403