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