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