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