1 // SPDX-License-Identifier: MIT
4 // Copyright (C) 2005 Luc Van Oostenryck
12 #include "flowgraph.h"
13 #include "linearize.h"
18 // Is it possible and desirable for this to be promoted to a pseudo?
19 static inline bool is_promotable(struct symbol
*type
)
21 struct symbol
*member
;
25 if (type
->type
== SYM_NODE
)
26 type
= type
->ctype
.base_type
;
31 case SYM_RESTRICT
: // OK, always integer types
34 // we allow a single scalar field
35 // but a run of bitfields count for 1
38 FOR_EACH_PTR(type
->symbol_list
, member
) {
39 if (is_bitfield_type(member
)) {
46 if (!is_scalar_type(member
))
50 } END_FOR_EACH_PTR(member
);
51 if (bf_seen
&& (type
->bit_size
> long_ctype
.bit_size
))
55 // FIXME: should be like struct but has problem
56 // when used with/for type cohercion
57 // -----> OK if only same sized integral types
58 FOR_EACH_PTR(type
->symbol_list
, member
) {
59 if (member
->bit_size
!= type
->bit_size
)
61 if (!is_integral_type(member
))
63 } END_FOR_EACH_PTR(member
);
68 if (type
->ctype
.base_type
== &int_type
)
70 if (type
->ctype
.base_type
== &fp_type
)
75 static bool insn_before(struct instruction
*a
, struct instruction
*b
)
77 struct basic_block
*bb
= a
->bb
;
78 struct instruction
*insn
;
81 FOR_EACH_PTR(bb
->insns
, insn
) {
86 } END_FOR_EACH_PTR(insn
);
90 static void kill_store(struct instruction
*insn
)
92 remove_use(&insn
->src
);
93 remove_use(&insn
->target
);
97 static void rewrite_local_var(struct basic_block
*bb
, pseudo_t addr
, int nbr_stores
, int nbr_uses
)
99 struct instruction
*insn
;
105 FOR_EACH_PTR(bb
->insns
, insn
) {
107 if (!insn
->bb
|| insn
->src
!= addr
)
109 switch (insn
->opcode
) {
112 val
= undef_pseudo();
113 replace_with_pseudo(insn
, val
);
117 // can't use kill_instruction() unless
118 // we add a fake user to val
122 } END_FOR_EACH_PTR(insn
);
125 static bool rewrite_single_store(struct instruction
*store
)
127 pseudo_t addr
= store
->src
;
128 struct pseudo_user
*pu
;
130 FOR_EACH_PTR(addr
->users
, pu
) {
131 struct instruction
*insn
= pu
->insn
;
133 if (insn
->opcode
!= OP_LOAD
)
136 // Let's try to replace the value of the load
137 // by the value from the store. This is only valid
138 // if the store dominate the load.
140 if (insn
->bb
== store
->bb
) {
141 // the load and the store are in the same BB
142 // we can convert if the load is after the store.
143 if (!insn_before(store
, insn
))
145 } else if (!domtree_dominates(store
->bb
, insn
->bb
)) {
146 // we can't convert this load
150 // OK, we can rewrite this load
154 replace_with_pseudo(insn
, store
->target
);
155 } END_FOR_EACH_PTR(pu
);
157 // is there some unconverted loads?
158 if (pseudo_user_list_size(addr
->users
) > 1)
165 static struct sset
*processed
;
167 // we would like to know:
168 // is there one or more stores?
169 // are all loads & stores local/done in a single block?
170 static void ssa_convert_one_var(struct entrypoint
*ep
, struct symbol
*var
)
172 struct basic_block_list
*alpha
= NULL
;
173 struct basic_block_list
*idf
= NULL
;
174 struct basic_block
*samebb
= NULL
;
175 struct instruction
*store
= NULL
;
176 struct basic_block
*bb
;
177 struct pseudo_user
*pu
;
178 unsigned long mod
= var
->ctype
.modifiers
;
184 /* Never used as a symbol? */
189 /* We don't do coverage analysis of volatiles.. */
190 if (mod
& MOD_VOLATILE
)
193 /* ..and symbols with external visibility need more care */
194 mod
&= (MOD_NONLOCAL
| MOD_STATIC
| MOD_ADDRESSABLE
);
196 goto external_visibility
;
198 if (!is_promotable(var
))
201 // 1) insert in the worklist all BBs that may modify var
202 sset_reset(processed
);
203 FOR_EACH_PTR(addr
->users
, pu
) {
204 struct instruction
*insn
= pu
->insn
;
205 struct basic_block
*bb
= insn
->bb
;
207 switch (insn
->opcode
) {
211 if (!sset_testset(processed
, bb
->nr
))
218 else if (samebb
!= bb
)
224 mod
|= MOD_ADDRESSABLE
;
225 goto external_visibility
;
227 warning(var
->pos
, "symbol '%s' pseudo used in unexpected way",
228 show_ident(var
->ident
));
230 } END_FOR_EACH_PTR(pu
);
232 if (nbr_stores
== 1) {
233 if (rewrite_single_store(store
))
237 // if all uses are local to a single block
238 // they can easily be rewritten and doesn't need phi-nodes
239 // FIXME: could be done for extended BB too
241 rewrite_local_var(samebb
, addr
, nbr_stores
, nbr_uses
);
245 idf_compute(ep
, &idf
, alpha
);
246 FOR_EACH_PTR(idf
, bb
) {
247 struct instruction
*node
= insert_phi_node(bb
, var
);
248 node
->phi_var
= var
->pseudo
;
249 } END_FOR_EACH_PTR(bb
);
253 if (mod
& (MOD_NONLOCAL
| MOD_STATIC
))
255 kill_dead_stores(ep
, addr
, !mod
);
258 static pseudo_t
lookup_var(struct basic_block
*bb
, struct symbol
*var
)
261 pseudo_t val
= phi_map_lookup(bb
->phi_map
, var
);
264 } while ((bb
= bb
->idom
));
265 return undef_pseudo();
268 static struct instruction_list
*phis_all
;
269 static struct instruction_list
*phis_used
;
271 static void ssa_rename_insn(struct basic_block
*bb
, struct instruction
*insn
)
277 switch (insn
->opcode
) {
280 if (addr
->type
!= PSEUDO_SYM
)
283 if (!var
|| !var
->torename
)
285 phi_map_update(&bb
->phi_map
, var
, insn
->target
);
290 if (addr
->type
!= PSEUDO_SYM
)
293 if (!var
|| !var
->torename
)
295 val
= lookup_var(bb
, var
);
296 replace_with_pseudo(insn
, val
);
300 if (!var
|| !var
->torename
)
302 phi_map_update(&bb
->phi_map
, var
, insn
->target
);
303 add_instruction(&phis_all
, insn
);
308 static void ssa_rename_insns(struct entrypoint
*ep
)
310 struct basic_block
*bb
;
312 FOR_EACH_PTR(ep
->bbs
, bb
) {
313 struct instruction
*insn
;
314 FOR_EACH_PTR(bb
->insns
, insn
) {
317 ssa_rename_insn(bb
, insn
);
318 } END_FOR_EACH_PTR(insn
);
319 } END_FOR_EACH_PTR(bb
);
322 static void mark_phi_used(pseudo_t val
)
324 struct instruction
*node
;
326 if (val
->type
!= PSEUDO_REG
)
329 if (node
->opcode
!= OP_PHI
)
334 add_instruction(&phis_used
, node
);
337 static void ssa_rename_phi(struct instruction
*insn
)
339 struct basic_block
*par
;
344 var
= insn
->phi_var
->sym
;
347 FOR_EACH_PTR(insn
->bb
->parents
, par
) {
348 struct instruction
*term
= delete_last_instruction(&par
->insns
);
349 pseudo_t val
= lookup_var(par
, var
);
350 pseudo_t phi
= alloc_phi(par
, val
, var
);
351 phi
->ident
= var
->ident
;
352 add_instruction(&par
->insns
, term
);
353 use_pseudo(insn
, phi
, add_pseudo(&insn
->phi_list
, phi
));
355 } END_FOR_EACH_PTR(par
);
358 static void ssa_rename_phis(struct entrypoint
*ep
)
360 struct instruction
*phi
;
363 FOR_EACH_PTR(phis_all
, phi
) {
364 if (has_users(phi
->target
)) {
366 add_instruction(&phis_used
, phi
);
368 } END_FOR_EACH_PTR(phi
);
370 FOR_EACH_PTR(phis_used
, phi
) {
374 } END_FOR_EACH_PTR(phi
);
377 void ssa_convert(struct entrypoint
*ep
)
379 struct basic_block
*bb
;
383 // calculate the number of BBs
384 first
= ep
->entry
->bb
->nr
;
386 FOR_EACH_PTR(ep
->bbs
, bb
) {
391 } END_FOR_EACH_PTR(bb
);
393 processed
= sset_init(first
, last
);
395 // try to promote memory accesses to pseudos
396 FOR_EACH_PTR(ep
->accesses
, pseudo
) {
397 ssa_convert_one_var(ep
, pseudo
->sym
);
398 } END_FOR_EACH_PTR(pseudo
);
400 // rename the converted accesses
401 phis_all
= phis_used
= NULL
;
402 ssa_rename_insns(ep
);