a478e3890423fb9ebf77948920876c10cc20d8a9
[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.h"
17 #include "iredges_t.h"
18 #include "irdump.h"
19 #include "irprintf.h"
20
21 #include "be_t.h"
22 #include "beutil.h"
23 #include "bechordal_t.h"
24 #include "bearch.h"
25 #include "benode_t.h"
26 #include "besched_t.h"
27
28 static firm_dbg_module_t *dbg = NULL;
29 #define DEBUG_LVL SET_LEVEL_2
30
31
32 #define get_reg(irn) arch_get_irn_register(chordal_env->arch_env, irn, 0)
33 #define set_reg(irn, reg) arch_set_irn_register(chordal_env->arch_env, irn, 0, reg)
34
35 /**
36  * Maps blocks to perm nodes inserted during phi destruction.
37  */
38 typedef struct _block2perm_t {
39   ir_node *block, *perm;
40 } block2perm_t;
41
42 static int set_cmp_b2p(const void *x, const void *y, size_t size) {
43   const block2perm_t *b1 = x;
44   const block2perm_t *b2 = y;
45   return b1->block != b2->block;
46 }
47
48 #define is_Branch(irn)          (arch_irn_classify(arch_env, irn) == arch_irn_class_branch)
49 #define is_Perm(irn)            (arch_irn_classify(arch_env, irn) == arch_irn_class_perm)
50 #define get_reg_cls(irn)        (arch_get_irn_reg_class(arch_env, irn, arch_pos_make_out(0)))
51 #define is_curr_reg_class(irn)  (get_reg_cls(p) == chordal_env->cls)
52
53 static ir_node *get_or_insert_perm(be_main_session_env_t *session, be_chordal_env_t *chordal_env, ir_node *block) {
54         block2perm_t find, *found;
55         ir_node *p;
56         set *b2p = chordal_env->data;
57         const arch_env_t *arch_env = chordal_env->arch_env;
58
59
60         /* iff needed insert perm node */
61         DBG((dbg, LEVEL_1, "    Getting perm in %+F\n", block));
62
63         /* .if the perm is in the pset return it */
64         find.block = block;
65         find.perm = NULL;
66         found = set_insert(b2p, &find, sizeof(find), HASH_PTR(find.block));
67         if (found->perm) {
68                 DBG((dbg, LEVEL_1, "      found it %+F in map\n", found->perm));
69                 return found->perm;
70         }
71
72         /* .else look for a perm of right register class in the schedule */
73         p = sched_last(find.block);
74         while (!is_Block(p) && (is_Branch(p) || (is_Perm(p) && !is_curr_reg_class(p))))
75                 p = sched_prev(p);
76
77         /* if we haven't found a perm of the right register class create a new one */
78         if (! (is_Perm(p) && is_curr_reg_class(p))) {
79                 DBG((dbg, LEVEL_1, "      insert it after %+F\n", p));
80                 p = insert_Perm_after(session, chordal_env->cls, p);
81         }
82
83         /* insert perm into pset and return it*/
84         found->perm = p;
85         return p;
86 }
87
88 #define is_pinned(irn) (get_irn_link(irn))
89 #define get_pinning_block(irn) ((ir_node *)get_irn_link(irn))
90 #define pin_irn(irn, lock) (set_irn_link(irn, lock))
91
92 /**
93  * Adjusts the register allocation for the phi-operands
94  * by inserting perm nodes, if necessary.
95  * @param phi The phi node to adjust operands for
96  */
97 static void adjust_phi_arguments(be_main_session_env_t *session, be_chordal_env_t *chordal_env, ir_node *phi) {
98         int i, max;
99         ir_node *arg, *phi_block, *arg_block;
100         const arch_register_t *phi_reg, *arg_reg;
101         const arch_register_class_t *cls;
102
103         assert(is_Phi(phi) && "Can only handle phi-destruction :)");
104         DBG((dbg, LEVEL_1, "  for %+F\n", phi));
105
106         cls = arch_get_irn_reg_class(session->main_env->arch_env, phi, arch_pos_make_out(0));
107         phi_block = get_nodes_block(phi);
108         phi_reg = get_reg(phi);
109
110         /* process all arguments of the phi */
111         for(i=0, max=get_irn_arity(phi); i<max; ++i) {
112                 ir_node *perm;
113
114                 arg = get_irn_n(phi, i);
115                 arg_block = get_nodes_block(arg);
116                 arg_reg = get_reg(arg);
117                 perm = get_Proj_pred(arg);
118
119                 DBG((dbg, LEVEL_1, "    arg %+F has perm %+F\n", arg, perm));
120                 /* if registers don't match ...*/
121                 if (phi_reg != arg_reg) {
122                         DBG((dbg, LEVEL_1, "      regs don't match %d %d\n", phi_reg, arg_reg));
123
124                         /* First check if there is another phi in the same block
125                          * having arg at the same pos in its arg-list and the same color as arg */
126                         if (!is_pinned(arg)) {
127                                 DBG((dbg, LEVEL_1, "      arg is not pinned\n"));
128                                 ir_node *other_phi = phi;
129                                 while ((other_phi = get_irn_link(other_phi)) != phi) {
130                                         assert(is_Phi(other_phi) && get_nodes_block(phi) == get_nodes_block(other_phi) && "link fields are screwed up");
131                                         if (get_irn_n(other_phi, i) == arg && get_reg(other_phi) == arg_reg) {
132                                                 DBG((dbg, LEVEL_1, "      other phi pinned the argument\n"));
133                                                 pin_irn(arg, phi_block);
134                                         }
135                                 }
136                         }
137
138                         /* If arg is pinned, another phi set the color of arg and pinned it.
139                          * So this phi can't change the color again and a duplicate must be inserted.
140                          *
141                          * If arg interferes with phi, one can never set the same color for both
142                          * Hence, a duplicate must be inserted */
143                         if (is_pinned(arg) || nodes_interfere(chordal_env, phi, arg)) {
144                                 ir_node *dupl, *tmp;
145                                 assert(get_pinning_block(arg) == phi_block && "If arg is pinned it must be due to a phi in the same block");
146
147                                 dupl = new_Copy(session->main_env->node_factory, cls, session->irg, arg_block, arg);
148                                 set_irn_n(phi, i, dupl);
149                                 set_reg(dupl, phi_reg);
150                                 DBG((dbg, LEVEL_1, "      inserting dupl %+F\n", dupl));
151
152                                 /* Add dupl to schedule */
153                                 tmp = sched_next(perm);
154                                 while (is_Proj(tmp) && sched_has_next(tmp))
155                                         tmp = sched_next(tmp);
156                                 sched_add_after(tmp, dupl);
157
158                                 /* Add dupl to chained list of duplicates. Ptrs starting at the Perm */
159                                 tmp = perm;
160                                 while (get_irn_link(tmp))
161                                         tmp = get_irn_link(tmp);
162                                 set_irn_link(tmp, dupl);
163                                 set_irn_link(dupl, NULL);
164
165                                 /* now the arg is the dupl */
166                                 arg = dupl;
167                         } else {
168                                 /* Arg is not pinned. So set its color to the color of the phi.
169                                  * If the phi color is used by another proj of this perm
170                                  * one must NOT swap the colors. Proof: Critical edges removed,
171                                  * livein(PhiBl) = liveout(ArgBl), if all phis are processed then
172                                  * every color is used exactly once.
173                                  */
174                                 DBG((dbg, LEVEL_1, "      just set color\n"));
175                                 set_reg(arg, phi_reg);
176                         }
177                 }
178
179                 /* Now the color of the arg (arg may be a dupl now) and the phi-result are equal.
180                  * Pin it, so everyone knows and it never gets changed again.
181                  * An arg never is a phi, because perms were inserted. So the link field is free */
182                 DBG((dbg, LEVEL_1, "      arg has correct color (now), so pin it\n"));
183                 pin_irn(arg, phi_block);
184         }
185 }
186
187
188 static void insert_all_perms(be_main_session_env_t *session, be_chordal_env_t *chordal_env) {
189         pmap_entry *pme;
190         int i, max;
191         ir_node *first_phi, *recent_phi;
192
193         DBG((dbg, LEVEL_1, "Placing perms...\n"));
194
195         /* place perms in cf-preds of phis */
196         pmap_foreach(chordal_env->border_heads, pme) {
197                 border_t *curr;
198                 struct list_head *head = pme->value;
199
200                 first_phi = NULL;
201                 /* iterate over the first ops in the block until a non-phi is reached */
202                 list_for_each_entry(border_t, curr, head, list) {
203                         ir_node *phi = curr->irn;
204
205                         if (curr->is_def && curr->is_real && is_Phi(phi)) {
206                                 set_irn_link(phi, NULL);
207                                 /* chain of phis in a block */
208                                 if (first_phi == NULL)
209                                         first_phi = phi;
210                                 else
211                                         set_irn_link(recent_phi, phi);
212                                 recent_phi = phi;
213
214                                 /* insert perms */
215                                 DBG((dbg, LEVEL_1, "  for %+F\n", phi));
216                                 for(i=0, max=get_irn_arity(phi); i<max; ++i) {
217                                         ir_node *perm;
218
219                                         perm = get_or_insert_perm(session, chordal_env, get_Block_cfgpred_block(get_nodes_block(phi), i));
220                                         DBG((dbg, LEVEL_1, "    %+F in block %N\n", perm, get_Block_cfgpred_block(get_nodes_block(phi), i)));
221                                         set_irn_link(perm, NULL);
222                                 }
223                         }
224                 }
225                 if (first_phi)
226                         set_irn_link(recent_phi, first_phi);
227         }
228 }
229
230 static void     set_regs_or_place_dupls(be_main_session_env_t *session, be_chordal_env_t *chordal_env) {
231         pmap_entry *pme;
232
233         DBG((dbg, LEVEL_1, "Setting regs and placing dupls...\n"));
234
235         /* iterate over all blocks and correct color of arguments*/
236         pmap_foreach(chordal_env->border_heads, pme) {
237                 border_t *curr;
238                 struct list_head *head = pme->value;
239
240                 /* iterate over the first ops in the block until a non-phi is reached */
241                 list_for_each_entry(border_t, curr, head, list)
242                         if (curr->is_def && curr->is_real && is_Phi(curr->irn))
243                                 adjust_phi_arguments(session, chordal_env, curr->irn);
244         }
245 }
246
247 void be_ssa_destruction(be_main_session_env_t *session, be_chordal_env_t *chordal_env) {
248         set *b2p;
249
250         dbg = firm_dbg_register("ir.be.ssadestr");
251         firm_dbg_set_mask(dbg, DEBUG_LVL);
252
253         /* create a map for fast lookup of perms: block --> perm */
254         b2p = new_set(set_cmp_b2p, 32);
255         chordal_env->data = b2p;
256
257         insert_all_perms(session, chordal_env);
258         dump_ir_block_graph(session->irg, "-ssa_destr_perms_placed");
259
260         set_regs_or_place_dupls(session, chordal_env);
261         dump_ir_block_graph(session->irg, "-ssa_destr_regs_set");
262
263         del_set(b2p);
264 }
265
266 void be_ssa_destruction_check(be_main_session_env_t *session, be_chordal_env_t *chordal_env) {
267         pmap_entry *pme;
268         int i, max;
269
270         /* iterate over all blocks */
271         pmap_foreach(chordal_env->border_heads, pme) {
272                 border_t *curr;
273                 struct list_head *head = pme->value;
274
275                 /* iterate over the first ops in the block */
276                 list_for_each_entry(border_t, curr, head, list)
277                         if (curr->is_def && curr->is_real && is_Phi(curr->irn)) {
278                                 const arch_register_t *phi_reg, *arg_reg;
279                                 ir_node *phi = curr->irn;
280
281                                 phi_reg = get_reg(phi);
282                                 /* iterate over all args of phi */
283                                 for(i=0, max=get_irn_arity(phi); i<max; ++i) {
284                                         ir_node *arg = get_irn_n(phi, i);
285                                         arg_reg = get_reg(arg);
286                                         if(phi_reg != arg_reg) {
287                                                 ir_printf("Registers of %+F and %+F differ: %s %s\n", phi, arg, phi_reg->name, arg_reg->name);
288                                                 assert(0 && "Registers of phi and arg differ\n");
289                                         }
290                                         if(!is_pinned(arg))
291                                                 ir_printf("Warning: Arg %+F not pinned\n", arg);
292                                 }
293                         }
294         }
295 }