1 // SPDX-License-Identifier: MIT
4 // Copyright (C) 2005 Luc Van Oostenryck
12 #include "flowgraph.h"
13 #include "linearize.h"
14 #include "flow.h" // for convert_load_instruction()
17 // Is it possible and desirable for this to be promoted to a pseudo?
18 static inline bool is_promotable(struct symbol
*type
)
20 struct symbol
*member
;
24 if (type
->type
== SYM_NODE
)
25 type
= type
->ctype
.base_type
;
30 case SYM_RESTRICT
: // OK, always integer types
33 // we allow a single scalar field
34 // but a run of bitfields count for 1
37 FOR_EACH_PTR(type
->symbol_list
, member
) {
38 if (is_bitfield_type(member
)) {
45 if (!is_scalar_type(member
))
49 } END_FOR_EACH_PTR(member
);
50 if (bf_seen
&& (type
->bit_size
> long_ctype
.bit_size
))
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
)
60 if (!is_integral_type(member
))
62 } END_FOR_EACH_PTR(member
);
67 if (type
->ctype
.base_type
== &int_type
)
69 if (type
->ctype
.base_type
== &fp_type
)
74 static bool insn_before(struct instruction
*a
, struct instruction
*b
)
76 struct basic_block
*bb
= a
->bb
;
77 struct instruction
*insn
;
80 FOR_EACH_PTR(bb
->insns
, insn
) {
85 } END_FOR_EACH_PTR(insn
);
89 static void kill_store(struct instruction
*insn
)
91 remove_use(&insn
->src
);
92 remove_use(&insn
->target
);
96 static void rewrite_local_var(struct basic_block
*bb
, pseudo_t addr
, int nbr_stores
, int nbr_uses
)
98 struct instruction
*insn
;
104 FOR_EACH_PTR(bb
->insns
, insn
) {
106 if (!insn
->bb
|| insn
->src
!= addr
)
108 switch (insn
->opcode
) {
111 val
= undef_pseudo();
112 convert_load_instruction(insn
, val
);
116 // can't use kill_instruction() unless
117 // we add a fake user to val
121 } END_FOR_EACH_PTR(insn
);
124 static bool rewrite_single_store(struct instruction
*store
)
126 pseudo_t addr
= store
->src
;
127 struct pseudo_user
*pu
;
129 FOR_EACH_PTR(addr
->users
, pu
) {
130 struct instruction
*insn
= pu
->insn
;
132 if (insn
->opcode
!= OP_LOAD
)
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.
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
))
144 } else if (!domtree_dominates(store
->bb
, insn
->bb
)) {
145 // we can't convert this load
149 // OK, we can rewrite this load
153 convert_load_instruction(insn
, store
->target
);
154 } END_FOR_EACH_PTR(pu
);
156 // is there some unconverted loads?
157 if (pseudo_user_list_size(addr
->users
) > 1)
164 static struct sset
*processed
;
166 // we would like to know:
167 // is there one or more stores?
168 // are all loads & stores local/done in a single block?
169 static void ssa_convert_one_var(struct entrypoint
*ep
, struct symbol
*var
)
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
;
183 /* Never used as a symbol? */
188 /* We don't do coverage analysis of volatiles.. */
189 if (mod
& MOD_VOLATILE
)
192 /* ..and symbols with external visibility need more care */
193 mod
&= (MOD_NONLOCAL
| MOD_STATIC
| MOD_ADDRESSABLE
);
195 goto external_visibility
;
197 if (!is_promotable(var
))
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
;
206 switch (insn
->opcode
) {
210 if (!sset_testset(processed
, bb
->nr
))
217 else if (samebb
!= bb
)
223 mod
|= MOD_ADDRESSABLE
;
224 goto external_visibility
;
226 warning(var
->pos
, "symbol '%s' pseudo used in unexpected way",
227 show_ident(var
->ident
));
229 } END_FOR_EACH_PTR(pu
);
231 if (nbr_stores
== 1) {
232 if (rewrite_single_store(store
))
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
240 rewrite_local_var(samebb
, addr
, nbr_stores
, nbr_uses
);
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
);
252 if (mod
& (MOD_NONLOCAL
| MOD_STATIC
))
254 kill_dead_stores(ep
, addr
, !mod
);
257 static pseudo_t
lookup_var(struct basic_block
*bb
, struct symbol
*var
)
260 pseudo_t val
= phi_map_lookup(bb
->phi_map
, var
);
263 } while ((bb
= bb
->idom
));
264 return undef_pseudo();
267 static struct instruction_list
*phis_all
;
268 static struct instruction_list
*phis_used
;
270 static void ssa_rename_insn(struct basic_block
*bb
, struct instruction
*insn
)
276 switch (insn
->opcode
) {
279 if (addr
->type
!= PSEUDO_SYM
)
282 if (!var
|| !var
->torename
)
284 phi_map_update(&bb
->phi_map
, var
, insn
->target
);
289 if (addr
->type
!= PSEUDO_SYM
)
292 if (!var
|| !var
->torename
)
294 val
= lookup_var(bb
, var
);
295 convert_load_instruction(insn
, val
);
299 if (!var
|| !var
->torename
)
301 phi_map_update(&bb
->phi_map
, var
, insn
->target
);
302 add_instruction(&phis_all
, insn
);
307 static void ssa_rename_insns(struct entrypoint
*ep
)
309 struct basic_block
*bb
;
311 FOR_EACH_PTR(ep
->bbs
, bb
) {
312 struct instruction
*insn
;
313 FOR_EACH_PTR(bb
->insns
, insn
) {
316 ssa_rename_insn(bb
, insn
);
317 } END_FOR_EACH_PTR(insn
);
318 } END_FOR_EACH_PTR(bb
);
321 static void mark_phi_used(pseudo_t val
)
323 struct instruction
*node
;
325 if (val
->type
!= PSEUDO_REG
)
328 if (node
->opcode
!= OP_PHI
)
333 add_instruction(&phis_used
, node
);
336 static void ssa_rename_phi(struct instruction
*insn
)
338 struct basic_block
*par
;
343 var
= insn
->phi_var
->sym
;
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
));
354 } END_FOR_EACH_PTR(par
);
357 static void ssa_rename_phis(struct entrypoint
*ep
)
359 struct instruction
*phi
;
362 FOR_EACH_PTR(phis_all
, phi
) {
363 if (has_users(phi
->target
)) {
365 add_instruction(&phis_used
, phi
);
367 } END_FOR_EACH_PTR(phi
);
369 FOR_EACH_PTR(phis_used
, phi
) {
373 } END_FOR_EACH_PTR(phi
);
376 void ssa_convert(struct entrypoint
*ep
)
378 struct basic_block
*bb
;
382 // calculate the number of BBs
383 first
= ep
->entry
->bb
->nr
;
385 FOR_EACH_PTR(ep
->bbs
, bb
) {
389 } END_FOR_EACH_PTR(bb
);
391 processed
= sset_init(first
, last
);
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
);
398 // rename the converted accesses
399 phis_all
= phis_used
= NULL
;
400 ssa_rename_insns(ep
);