4 * Copyright: (c) Universitaet Karlsruhe
5 * Licence: This file protected by GPL - GNU GENERAL PUBLIC LICENSE.
7 * Performs SSA-Destruction.
17 #include "iredges_t.h"
23 #include "bechordal_t.h"
26 #include "besched_t.h"
28 static firm_dbg_module_t *dbg = NULL;
29 #define DEBUG_LVL SET_LEVEL_2
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)
36 * Maps blocks to perm nodes inserted during phi destruction.
38 typedef struct _block2perm_t {
39 ir_node *block, *perm;
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;
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)
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;
56 set *b2p = chordal_env->data;
57 const arch_env_t *arch_env = chordal_env->arch_env;
60 /* iff needed insert perm node */
61 DBG((dbg, LEVEL_1, " Getting perm in %+F\n", block));
63 /* .if the perm is in the pset return it */
66 found = set_insert(b2p, &find, sizeof(find), HASH_PTR(find.block));
68 DBG((dbg, LEVEL_1, " found it %+F in map\n", found->perm));
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))))
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);
83 /* insert perm into pset and return it*/
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))
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
97 static void adjust_phi_arguments(be_main_session_env_t *session, be_chordal_env_t *chordal_env, ir_node *phi) {
99 ir_node *arg, *phi_block, *arg_block;
100 arch_env_t *arch_env = session->main_env->arch_env;
101 const arch_register_t *phi_reg, *arg_reg;
102 const arch_register_class_t *cls;
104 assert(is_Phi(phi) && "Can only handle phi-destruction :)");
105 DBG((dbg, LEVEL_1, " for %+F\n", phi));
107 cls = arch_get_irn_reg_class(session->main_env->arch_env, phi, arch_pos_make_out(0));
108 phi_block = get_nodes_block(phi);
109 phi_reg = get_reg(phi);
111 /* process all arguments of the phi */
112 for(i=0, max=get_irn_arity(phi); i<max; ++i) {
115 arg = get_irn_n(phi, i);
116 arg_block = get_nodes_block(arg);
117 arg_reg = get_reg(arg);
118 perm = get_Proj_pred(arg);
119 assert(is_Perm(perm));
121 DBG((dbg, LEVEL_1, " arg %+F has perm %+F\n", arg, perm));
122 /* if registers don't match ...*/
123 if (phi_reg != arg_reg) {
124 DBG((dbg, LEVEL_1, " regs don't match %d %d\n", phi_reg, arg_reg));
126 /* First check if there is another phi in the same block
127 * having arg at the same pos in its arg-list and the same color as arg */
128 if (!is_pinned(arg)) {
129 DBG((dbg, LEVEL_1, " arg is not pinned\n"));
130 ir_node *other_phi = phi;
131 while ((other_phi = get_irn_link(other_phi)) != phi) {
132 assert(is_Phi(other_phi) && get_nodes_block(phi) == get_nodes_block(other_phi) && "link fields are screwed up");
133 if (get_irn_n(other_phi, i) == arg && get_reg(other_phi) == arg_reg) {
134 DBG((dbg, LEVEL_1, " other phi pinned the argument\n"));
135 pin_irn(arg, phi_block);
140 /* If arg is pinned, another phi set the color of arg and pinned it.
141 * So this phi can't change the color again and a duplicate must be inserted.
143 * If arg interferes with phi, one can never set the same color for both
144 * Hence, a duplicate must be inserted */
145 if (is_pinned(arg) || nodes_interfere(chordal_env, phi, arg)) {
147 assert(get_pinning_block(arg) == phi_block && "If arg is pinned it must be due to a phi in the same block");
149 dupl = new_Copy(session->main_env->node_factory, cls, session->irg, arg_block, arg);
150 set_irn_n(phi, i, dupl);
151 set_reg(dupl, phi_reg);
152 DBG((dbg, LEVEL_1, " inserting dupl %+F\n", dupl));
154 /* Add dupl to schedule */
155 tmp = sched_next(perm);
156 while (is_Proj(tmp) && sched_has_next(tmp))
157 tmp = sched_next(tmp);
158 sched_add_after(tmp, dupl);
160 /* Add dupl to chained list of duplicates. Ptrs starting at the Perm */
162 while (get_irn_link(tmp))
163 tmp = get_irn_link(tmp);
164 set_irn_link(tmp, dupl);
165 set_irn_link(dupl, NULL);
167 /* now the arg is the dupl */
170 /* Arg is not pinned. So set its color to the color of the phi.
171 * If the phi color is used by another proj of this perm
172 * one must NOT swap the colors. Proof: Critical edges removed,
173 * livein(PhiBl) = liveout(ArgBl), if all phis are processed then
174 * every color is used exactly once.
176 DBG((dbg, LEVEL_1, " just set color\n"));
177 set_reg(arg, phi_reg);
181 /* Now the color of the arg (arg may be a dupl now) and the phi-result are equal.
182 * Pin it, so everyone knows and it never gets changed again.
183 * An arg never is a phi, because perms were inserted. So the link field is free */
184 DBG((dbg, LEVEL_1, " arg has correct color (now), so pin it\n"));
185 pin_irn(arg, phi_block);
190 static void insert_all_perms(be_main_session_env_t *session, be_chordal_env_t *chordal_env) {
193 ir_node *first_phi, *recent_phi;
195 DBG((dbg, LEVEL_1, "Placing perms...\n"));
197 /* place perms in cf-preds of phis */
198 pmap_foreach(chordal_env->border_heads, pme) {
200 struct list_head *head = pme->value;
203 /* iterate over the first ops in the block until a non-phi is reached */
204 list_for_each_entry(border_t, curr, head, list) {
205 ir_node *phi = curr->irn;
207 if (curr->is_def && curr->is_real && is_Phi(phi)) {
208 set_irn_link(phi, NULL);
209 /* chain of phis in a block */
210 if (first_phi == NULL)
213 set_irn_link(recent_phi, phi);
217 DBG((dbg, LEVEL_1, " for %+F\n", phi));
218 for(i=0, max=get_irn_arity(phi); i<max; ++i) {
221 perm = get_or_insert_perm(session, chordal_env, get_Block_cfgpred_block(get_nodes_block(phi), i));
222 DBG((dbg, LEVEL_1, " %+F in block %N\n", perm, get_Block_cfgpred_block(get_nodes_block(phi), i)));
223 set_irn_link(perm, NULL);
228 set_irn_link(recent_phi, first_phi);
232 static void set_regs_or_place_dupls(be_main_session_env_t *session, be_chordal_env_t *chordal_env) {
235 DBG((dbg, LEVEL_1, "Setting regs and placing dupls...\n"));
237 /* iterate over all blocks and correct color of arguments*/
238 pmap_foreach(chordal_env->border_heads, pme) {
240 struct list_head *head = pme->value;
242 /* iterate over the first ops in the block until a non-phi is reached */
243 list_for_each_entry(border_t, curr, head, list)
244 if (curr->is_def && curr->is_real && is_Phi(curr->irn))
245 adjust_phi_arguments(session, chordal_env, curr->irn);
249 void be_ssa_destruction(be_main_session_env_t *session, be_chordal_env_t *chordal_env) {
252 dbg = firm_dbg_register("ir.be.ssadestr");
253 firm_dbg_set_mask(dbg, DEBUG_LVL);
255 /* create a map for fast lookup of perms: block --> perm */
256 b2p = new_set(set_cmp_b2p, 32);
257 chordal_env->data = b2p;
259 insert_all_perms(session, chordal_env);
260 dump_ir_block_graph(session->irg, "-ssa_destr_perms_placed");
262 set_regs_or_place_dupls(session, chordal_env);
263 dump_ir_block_graph(session->irg, "-ssa_destr_regs_set");
268 void be_ssa_destruction_check(be_main_session_env_t *session, be_chordal_env_t *chordal_env) {
272 /* iterate over all blocks */
273 pmap_foreach(chordal_env->border_heads, pme) {
275 struct list_head *head = pme->value;
277 /* iterate over the first ops in the block */
278 list_for_each_entry(border_t, curr, head, list)
279 if (curr->is_def && curr->is_real && is_Phi(curr->irn)) {
280 const arch_register_t *phi_reg, *arg_reg;
281 ir_node *phi = curr->irn;
283 phi_reg = get_reg(phi);
284 /* iterate over all args of phi */
285 for(i=0, max=get_irn_arity(phi); i<max; ++i) {
286 ir_node *arg = get_irn_n(phi, i);
287 arg_reg = get_reg(arg);
288 if(phi_reg != arg_reg) {
289 ir_printf("Registers of %+F and %+F differ: %s %s\n", phi, arg, phi_reg->name, arg_reg->name);
290 assert(0 && "Registers of phi and arg differ\n");
293 ir_printf("Warning: Arg %+F not pinned\n", arg);