4e42ac1243db6e57c47d9bbf3543038d0a64b55c
[libfirm] / ir / be / bessadestr.c
1 /**
2  * Author:      Daniel Grund
3  * Date:                25.05.2005
4  * Copyright:   (c) Universitaet Karlsruhe
5  * Licence:     This file protected by GPL -  GNU GENERAL PUBLIC LICENSE.
6  *
7  * Performs SSA-Destruction.
8  */
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12
13 #include "debug.h"
14 #include "set.h"
15 #include "pmap.h"
16 #include "irnode_t.h"
17 #include "ircons_t.h"
18 #include "iredges_t.h"
19 #include "irgmod.h"
20 #include "irdump.h"
21 #include "irprintf.h"
22
23 #include "be_t.h"
24 #include "beutil.h"
25 #include "bechordal_t.h"
26 #include "bearch.h"
27 #include "benode_t.h"
28 #include "besched_t.h"
29
30 static firm_dbg_module_t *dbg = NULL;
31 #define DEBUG_LVL SET_LEVEL_2
32
33
34 #define get_chordal_arch(ce) ((ce)->session_env->main_env->arch_env)
35 #define get_reg(irn) arch_get_irn_register(get_chordal_arch(chordal_env), irn, 0)
36 #define set_reg(irn, reg) arch_set_irn_register(get_chordal_arch(chordal_env), irn, 0, reg)
37
38 #define is_Branch(irn)          (arch_irn_classify(arch_env, irn) == arch_irn_class_branch)
39 #define is_Perm(irn)            (arch_irn_classify(arch_env, irn) == arch_irn_class_perm)
40 #define get_reg_cls(irn)        (arch_get_irn_reg_class(arch_env, irn, arch_pos_make_out(0)))
41 #define is_curr_reg_class(irn)  (get_reg_cls(p) == chordal_env->cls)
42
43 static void clear_link(ir_node *irn, void *data)
44 {
45   set_irn_link(irn, NULL);
46 }
47
48 /**
49  * Build a list of phis of a block.
50  */
51 static void collect_phis(ir_node *irn, void *data)
52 {
53   be_chordal_env_t *env = data;
54   if(is_Phi(irn) &&
55       arch_irn_has_reg_class(env->session_env->main_env->arch_env,
56         irn, arch_pos_make_out(0), env->cls)) {
57
58     ir_node *bl = get_nodes_block(irn);
59     set_irn_link(irn, get_irn_link(bl));
60     set_irn_link(bl, irn);
61   }
62 }
63
64 /**
65  * Build a ring of phis for each block in the link field.
66  * @param env The chordal env.
67  */
68 static INLINE void build_phi_rings(be_chordal_env_t *env)
69 {
70   irg_walk_graph(env->session_env->irg, clear_link, collect_phis, env);
71 }
72
73 static int skip_cf_predicator(const ir_node *irn, void *data) {
74   be_chordal_env_t *ce = data;
75   arch_env_t *ae = ce->session_env->main_env->arch_env;
76   return arch_irn_classify(ae, irn) == arch_irn_class_branch;
77 }
78
79 #define is_pinned(irn) (get_irn_link(irn))
80 #define get_pinning_block(irn) ((ir_node *)get_irn_link(irn))
81 #define pin_irn(irn, lock) (set_irn_link(irn, lock))
82
83 /**
84  * Adjusts the register allocation for the phi-operands
85  * by inserting perm nodes, if necessary.
86  * @param phi The phi node to adjust operands for
87  */
88 static void adjust_phi_arguments(be_chordal_env_t *chordal_env, ir_node *phi) {
89         int i, max;
90         ir_node *arg, *phi_block, *arg_block;
91         arch_env_t *arch_env = get_chordal_arch(chordal_env);
92   const be_main_session_env_t *session = chordal_env->session_env;
93         const arch_register_t *phi_reg, *arg_reg;
94         const arch_register_class_t *cls;
95
96         assert(is_Phi(phi) && "Can only handle phi-destruction :)");
97         DBG((dbg, LEVEL_1, "  for %+F\n", phi));
98
99         cls = arch_get_irn_reg_class(get_chordal_arch(chordal_env), phi, arch_pos_make_out(0));
100         phi_block = get_nodes_block(phi);
101         phi_reg = get_reg(phi);
102
103         /* process all arguments of the phi */
104         for(i=0, max=get_irn_arity(phi); i<max; ++i) {
105                 ir_node *perm;
106
107                 arg = get_irn_n(phi, i);
108                 arg_block = get_nodes_block(arg);
109                 arg_reg = get_reg(arg);
110                 perm = get_Proj_pred(arg);
111                 // assert(is_Perm(perm));
112
113                 DBG((dbg, LEVEL_1, "    arg %+F has perm %+F\n", arg, perm));
114                 /* if registers don't match ...*/
115                 if (phi_reg != arg_reg) {
116                         DBG((dbg, LEVEL_1, "      regs don't match %d %d\n", phi_reg, arg_reg));
117
118                         /* First check if there is another phi in the same block
119                          * having arg at the same pos in its arg-list and the same color as arg */
120                         if (!is_pinned(arg)) {
121                                 DBG((dbg, LEVEL_1, "      arg is not pinned\n"));
122                                 ir_node *other_phi = phi;
123         for(other_phi = get_irn_link(phi_block); other_phi; other_phi = get_irn_link(other_phi)) {
124           if(other_phi == phi)
125             continue;
126                                         assert(is_Phi(other_phi) && get_nodes_block(phi) == get_nodes_block(other_phi) && "link fields are screwed up");
127                                         if (get_irn_n(other_phi, i) == arg && get_reg(other_phi) == arg_reg) {
128                                                 DBG((dbg, LEVEL_1, "      other phi pinned the argument\n"));
129                                                 pin_irn(arg, phi_block);
130                                         }
131                                 }
132                         }
133
134                         /* If arg is pinned, another phi set the color of arg and pinned it.
135                          * So this phi can't change the color again and a duplicate must be inserted.
136                          *
137                          * If arg interferes with phi, one can never set the same color for both
138                          * Hence, a duplicate must be inserted */
139                         if (is_pinned(arg) || nodes_interfere(chordal_env, phi, arg)) {
140                                 ir_node *dupl, *tmp;
141                                 assert(get_pinning_block(arg) == phi_block && "If arg is pinned it must be due to a phi in the same block");
142
143                                 dupl = new_Copy(session->main_env->node_factory, cls, session->irg, arg_block, arg);
144                                 set_irn_n(phi, i, dupl);
145                                 set_reg(dupl, phi_reg);
146                                 DBG((dbg, LEVEL_1, "      inserting dupl %+F\n", dupl));
147
148                                 /* Add dupl to schedule */
149                                 tmp = sched_next(perm);
150                                 while (is_Proj(tmp) && sched_has_next(tmp))
151                                         tmp = sched_next(tmp);
152                                 sched_add_after(tmp, dupl);
153
154                                 /* Add dupl to chained list of duplicates. Ptrs starting at the Perm */
155                                 tmp = perm;
156                                 while (get_irn_link(tmp))
157                                         tmp = get_irn_link(tmp);
158                                 set_irn_link(tmp, dupl);
159                                 set_irn_link(dupl, NULL);
160
161                                 /* now the arg is the dupl */
162                                 arg = dupl;
163                         } else {
164                                 /* Arg is not pinned. So set its color to the color of the phi.
165                                  * If the phi color is used by another proj of this perm
166                                  * one must NOT swap the colors. Proof: Critical edges removed,
167                                  * livein(PhiBl) = liveout(ArgBl), if all phis are processed then
168                                  * every color is used exactly once.
169                                  */
170                                 DBG((dbg, LEVEL_1, "      just set color\n"));
171                                 set_reg(arg, phi_reg);
172                         }
173                 }
174
175                 /* Now the color of the arg (arg may be a dupl now) and the phi-result are equal.
176                  * Pin it, so everyone knows and it never gets changed again.
177                  * An arg never is a phi, because perms were inserted. So the link field is free */
178                 DBG((dbg, LEVEL_1, "      arg has correct color (now), so pin it\n"));
179                 pin_irn(arg, phi_block);
180         }
181 }
182
183 static void insert_all_perms_walker(ir_node *bl, void *data)
184 {
185   be_chordal_env_t *ce = data;
186   pmap *perm_map = ce->data;
187   ir_graph *irg = ce->session_env->irg;
188   const be_node_factory_t *fact = ce->session_env->main_env->node_factory;
189
190   /* Dummy targets for the projs */
191   ir_node *dummy = new_rd_Unknown(irg, mode_T);
192
193   assert(is_Block(bl));
194
195   /* If the link flag is NULL, this block has no phis. */
196   if(get_irn_link(bl)) {
197     int i, n;
198
199     /* Look at all predecessors of the phi block */
200     for(i = 0, n = get_irn_arity(bl); i < n; ++i) {
201       ir_node *pred_bl = get_Block_cfgpred_block(bl, i);
202       ir_node *phi, *perm, *insert_after;
203       ir_node **in;
204       int j, n_projs = 0;
205       pmap_entry *ent;
206       pmap *arg_map = pmap_create();
207
208       assert(!pmap_contains(perm_map, pred_bl) && "Already permed that block");
209
210       /*
211        * Note that all phis in the ring are in the same register class
212        * by construction.
213        */
214       for(phi = get_irn_link(bl); phi; phi = get_irn_link(phi)) {
215         ir_node *arg = get_irn_n(phi, i);
216         ir_node *proj = pmap_get(arg_map, arg);
217
218         if(!proj) {
219           proj = new_r_Proj(irg, pred_bl, dummy, get_irn_mode(arg), n_projs++);
220           pmap_insert(arg_map, arg, proj);
221         }
222
223         set_irn_n(phi, i, proj);
224       }
225
226       j = 0;
227       in = malloc(n_projs * sizeof(in[0]));
228       pmap_foreach(arg_map, ent)
229         in[j++] = ent->key;
230
231       perm = new_Perm(fact, ce->cls, irg, pred_bl, n_projs, in);
232       insert_after = sched_skip(sched_last(pred_bl), 0, skip_cf_predicator, ce);
233       sched_add_after(insert_after, perm);
234       exchange(dummy, perm);
235
236       free(in);
237       pmap_destroy(arg_map);
238
239       /* register in perm map */
240       pmap_insert(perm_map, pred_bl, perm);
241     }
242   }
243 }
244
245 static void insert_all_perms(be_chordal_env_t *chordal_env) {
246         DBG((dbg, LEVEL_1, "Placing perms...\n"));
247   irg_block_walk_graph(chordal_env->session_env->irg, insert_all_perms_walker, NULL, chordal_env);
248 }
249
250 static void     set_regs_or_place_dupls_walker(ir_node *bl, void *data) {
251   be_chordal_env_t *chordal_env = data;
252   ir_node *phi;
253
254         DBG((dbg, LEVEL_1, "Setting regs and placing dupls...\n"));
255   for(phi = get_irn_link(bl); phi; phi = get_irn_link(phi))
256     adjust_phi_arguments(chordal_env, phi);
257 }
258
259 static void     set_regs_or_place_dupls(be_chordal_env_t *chordal_env)
260 {
261   irg_block_walk_graph(chordal_env->session_env->irg,
262       set_regs_or_place_dupls_walker, NULL, chordal_env);
263 }
264
265
266 void be_ssa_destruction(be_chordal_env_t *chordal_env) {
267         pmap *perm_map = pmap_create();
268   ir_graph *irg = chordal_env->session_env->irg;
269
270         dbg = firm_dbg_register("ir.be.ssadestr");
271         firm_dbg_set_mask(dbg, DEBUG_LVL);
272
273         /* create a map for fast lookup of perms: block --> perm */
274         chordal_env->data = perm_map;
275
276   build_phi_rings(chordal_env);
277         insert_all_perms(chordal_env);
278         dump_ir_block_graph(irg, "-ssa_destr_perms_placed");
279
280         set_regs_or_place_dupls(chordal_env);
281         dump_ir_block_graph(irg, "-ssa_destr_regs_set");
282
283         pmap_destroy(perm_map);
284 }
285
286 static void ssa_destruction_check_walker(ir_node *bl, void *data)
287 {
288   be_chordal_env_t *chordal_env = data;
289   ir_node *phi;
290         int i, max;
291
292   for(phi = get_irn_link(bl); phi; phi = get_irn_link(phi)) {
293     const arch_register_t *phi_reg, *arg_reg;
294
295     phi_reg = get_reg(phi);
296     /* iterate over all args of phi */
297     for(i=0, max=get_irn_arity(phi); i<max; ++i) {
298       ir_node *arg = get_irn_n(phi, i);
299       arg_reg = get_reg(arg);
300       if(phi_reg != arg_reg) {
301         ir_printf("Registers of %+F and %+F differ: %s %s\n",
302             phi, arg, phi_reg->name, arg_reg->name);
303         assert(0 && "Registers of phi and arg differ\n");
304       }
305       if(!is_pinned(arg))
306         ir_printf("Warning: Arg %+F not pinned\n", arg);
307     }
308   }
309 }
310
311 void be_ssa_destruction_check(be_chordal_env_t *chordal_env) {
312   irg_block_walk_graph(chordal_env->session_env->irg,
313       ssa_destruction_check_walker, NULL, chordal_env);
314 }