move backend into libfirm
[libfirm] / ir / be / beschedregpress.c
1 /**
2  * Regpressure node selector.
3  * Originally implemented by Sebastian Hack.
4  * @author Christian Wuerdig
5  * @date   29.08.2006
6  * @cvs-id $Id$
7  */
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
11
12 #include <stdlib.h>
13
14 #include "iredges_t.h"
15 #include "irgwalk.h"
16
17 #include "besched_t.h"
18 #include "belistsched.h"
19 #include "benode_t.h"
20
21
22 typedef struct _usage_stats_t {
23         ir_node *irn;
24         struct _usage_stats_t *next;
25         int max_hops;
26         int uses_in_block;      /**< Number of uses inside the current block. */
27         int already_consumed;   /**< Number of insns using this value already
28                                                         scheduled. */
29 } usage_stats_t;
30
31 typedef struct {
32         const list_sched_selector_t *vtab;
33         const arch_env_t *arch_env;
34 } reg_pressure_main_env_t;
35
36 typedef struct {
37         struct obstack obst;
38         const reg_pressure_main_env_t *main_env;
39         usage_stats_t *root;
40         ir_nodeset_t already_scheduled;
41 } reg_pressure_selector_env_t;
42
43
44 #if 0
45 /*
46 * Ugly global variable for the compare function
47 * since qsort(3) does not pass an extra pointer.
48 */
49 static ir_node *curr_bl = NULL;
50
51 static int cmp_usage(const void *a, const void *b)
52 {
53         struct trivial_sched_env *env;
54         const ir_node *p = a;
55         const ir_node *q = b;
56         int res = 0;
57
58         res = is_live_end(env->curr_bl, a) - is_live_end(env->curr_bl, b);
59
60         /*
61         * One of them is live at the end of the block.
62         * Then, that one shall be scheduled at after the other
63         */
64         if(res != 0)
65                 return res;
66
67
68         return res;
69 }
70 #endif
71
72 static INLINE usage_stats_t *get_or_set_usage_stats(reg_pressure_selector_env_t *env, ir_node *irn)
73 {
74         usage_stats_t *us = get_irn_link(irn);
75
76         if(!us) {
77                 us                   = obstack_alloc(&env->obst, sizeof(us[0]));
78                 us->irn              = irn;
79                 us->already_consumed = 0;
80                 us->max_hops         = INT_MAX;
81                 us->next             = env->root;
82                 env->root            = us;
83                 set_irn_link(irn, us);
84         }
85
86         return us;
87 }
88
89 static INLINE usage_stats_t *get_usage_stats(ir_node *irn)
90 {
91         usage_stats_t *us = get_irn_link(irn);
92         assert(us && "This node must have usage stats");
93         return us;
94 }
95
96 static int max_hops_walker(reg_pressure_selector_env_t *env, ir_node *irn, ir_node *curr_bl, int depth, unsigned visited_nr)
97 {
98         ir_node *bl = get_nodes_block(irn);
99         /*
100         * If the reached node is not in the block desired,
101         * return the value passed for this situation.
102         */
103         if(get_nodes_block(irn) != bl)
104                 return block_dominates(bl, curr_bl) ? 0 : INT_MAX;
105
106         /*
107         * If the node is in the current block but not
108         * yet scheduled, we keep on searching from that node.
109         */
110         if(!ir_nodeset_contains(&env->already_scheduled, irn)) {
111                 int i, n;
112                 int res = 0;
113                 for(i = 0, n = get_irn_ins_or_deps(irn); i < n; ++i) {
114                         ir_node *operand = get_irn_in_or_dep(irn, i);
115
116                         if(get_irn_visited(operand) < visited_nr) {
117                                 int tmp;
118
119                                 set_irn_visited(operand, visited_nr);
120                                 tmp = max_hops_walker(env, operand, bl, depth + 1, visited_nr);
121                                 res = MAX(tmp, res);
122                         }
123                 }
124
125                 return res;
126         }
127
128         /*
129         * If the node is in the current block and scheduled, return
130         * the depth which indicates the number of steps to the
131         * region of scheduled nodes.
132         */
133         return depth;
134 }
135
136 static int compute_max_hops(reg_pressure_selector_env_t *env, ir_node *irn)
137 {
138         ir_node *bl   = get_nodes_block(irn);
139         ir_graph *irg = get_irn_irg(bl);
140         int res       = 0;
141
142         const ir_edge_t *edge;
143
144         foreach_out_edge(irn, edge) {
145                 ir_node *user       = get_edge_src_irn(edge);
146                 unsigned visited_nr = get_irg_visited(irg) + 1;
147                 int max_hops;
148
149                 set_irg_visited(irg, visited_nr);
150                 max_hops = max_hops_walker(env, user, irn, 0, visited_nr);
151                 res      = MAX(res, max_hops);
152         }
153
154         return res;
155 }
156
157 static void *reg_pressure_graph_init(const list_sched_selector_t *vtab, const arch_env_t *arch_env, ir_graph *irg)
158 {
159         reg_pressure_main_env_t *main_env = xmalloc(sizeof(main_env[0]));
160
161         main_env->arch_env = arch_env;
162         main_env->vtab     = vtab;
163         irg_walk_graph(irg, firm_clear_link, NULL, NULL);
164
165         return main_env;
166 }
167
168 static INLINE int must_appear_in_schedule(const list_sched_selector_t *sel, void *block_env, const ir_node *irn)
169 {
170         int res = -1;
171
172         if(sel->to_appear_in_schedule)
173                 res = sel->to_appear_in_schedule(block_env, irn);
174
175         return res >= 0 ? res : (to_appear_in_schedule(irn) || be_is_Keep(irn) || be_is_CopyKeep(irn) || be_is_RegParams(irn));
176 }
177
178 static void *reg_pressure_block_init(void *graph_env, ir_node *bl)
179 {
180         ir_node *irn;
181         reg_pressure_selector_env_t *env  = xmalloc(sizeof(env[0]));
182
183         obstack_init(&env->obst);
184         ir_nodeset_init(&env->already_scheduled);
185         env->root              = NULL;
186         env->main_env          = graph_env;
187
188         /*
189         * Collect usage statistics.
190         */
191         sched_foreach(bl, irn) {
192                 if(must_appear_in_schedule(env->main_env->vtab, env, irn)) {
193                         int i, n;
194
195                         for(i = 0, n = get_irn_arity(irn); i < n; ++i) {
196                                 //ir_node *op = get_irn_n(irn, i);
197                                 if(must_appear_in_schedule(env->main_env->vtab, env, irn)) {
198                                         usage_stats_t *us = get_or_set_usage_stats(env, irn);
199 #if 0 /* Liveness is not computed here! */
200                                         if(is_live_end(bl, op))
201                                                 us->uses_in_block = 99999;
202                                         else
203 #endif
204                                                 us->uses_in_block++;
205                                 }
206                         }
207                 }
208         }
209
210         return env;
211 }
212
213 static void reg_pressure_block_free(void *block_env)
214 {
215         reg_pressure_selector_env_t *env = block_env;
216         usage_stats_t *us;
217
218         for(us = env->root; us; us = us->next)
219                 set_irn_link(us->irn, NULL);
220
221         obstack_free(&env->obst, NULL);
222         ir_nodeset_destroy(&env->already_scheduled);
223         free(env);
224 }
225
226 static int get_result_hops_sum(reg_pressure_selector_env_t *env, ir_node *irn)
227 {
228         int res = 0;
229         if(get_irn_mode(irn) == mode_T) {
230                 const ir_edge_t *edge;
231
232                 foreach_out_edge(irn, edge)
233                         res += get_result_hops_sum(env, get_edge_src_irn(edge));
234         }
235
236         else if(mode_is_data(get_irn_mode(irn)))
237                 res = compute_max_hops(env, irn);
238
239
240         return res;
241 }
242
243 static INLINE int reg_pr_costs(reg_pressure_selector_env_t *env, ir_node *irn)
244 {
245         int i, n;
246         int sum = 0;
247
248         for(i = 0, n = get_irn_arity(irn); i < n; ++i) {
249                 ir_node *op = get_irn_n(irn, i);
250
251                 if(must_appear_in_schedule(env->main_env->vtab, env, op))
252                         sum += compute_max_hops(env, op);
253         }
254
255         sum += get_result_hops_sum(env, irn);
256
257         return sum;
258 }
259
260 static ir_node *reg_pressure_select(void *block_env, ir_nodeset_t *ready_set,
261                                     ir_nodeset_t *live_set)
262 {
263         ir_nodeset_iterator_t iter;
264         reg_pressure_selector_env_t *env = block_env;
265         ir_node *irn, *res     = NULL;
266         int curr_cost          = INT_MAX;
267
268         assert(ir_nodeset_size(ready_set) > 0);
269
270         ir_nodeset_iterator_init(&iter, ready_set);
271         while( (irn = ir_nodeset_iterator_next(&iter)) != NULL) {
272                 /*
273                 Ignore branch instructions for the time being.
274                 They should only be scheduled if there is nothing else.
275                 */
276                 if (! arch_irn_class_is(env->main_env->arch_env, irn, branch)) {
277                         int costs = reg_pr_costs(env, irn);
278                         if (costs <= curr_cost) {
279                                 res       = irn;
280                                 curr_cost = costs;
281                         }
282                 }
283         }
284
285         /*
286         There was no result so we only saw a branch.
287         Take it and finish.
288         */
289
290         if(!res) {
291                 ir_nodeset_iterator_init(&iter, ready_set);
292                 res = ir_nodeset_iterator_next(&iter);
293
294                 assert(res && "There must be a node scheduled.");
295         }
296
297         ir_nodeset_insert(&env->already_scheduled, res);
298         return res;
299 }
300
301 static const list_sched_selector_t reg_pressure_selector_struct = {
302         reg_pressure_graph_init,
303         reg_pressure_block_init,
304         reg_pressure_select,
305         NULL,                    /* to_appear_in_schedule */
306         NULL,                    /* node_ready */
307         NULL,                    /* node_selected */
308         NULL,                    /* exectime */
309         NULL,                    /* latency */
310         reg_pressure_block_free,
311         free
312 };
313
314 const list_sched_selector_t *reg_pressure_selector = &reg_pressure_selector_struct;