big refactoring of arch_XXX functions
[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 #include "config.h"
28
29 #include <stdlib.h>
30
31 #include "iredges_t.h"
32 #include "irgwalk.h"
33 #include "irtools.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         const ir_edge_t *edge;
156
157         foreach_out_edge(irn, edge) {
158                 ir_node *user       = get_edge_src_irn(edge);
159                 unsigned visited_nr = get_irg_visited(irg) + 1;
160                 int max_hops;
161
162                 set_irg_visited(irg, visited_nr);
163                 max_hops = max_hops_walker(env, user, irn, 0, visited_nr);
164                 res      = MAX(res, max_hops);
165         }
166
167         return res;
168 }
169
170 static void *reg_pressure_graph_init(ir_graph *irg)
171 {
172         irg_walk_graph(irg, firm_clear_link, NULL, NULL);
173
174         return NULL;
175 }
176
177 static void *reg_pressure_block_init(void *graph_env, ir_node *bl)
178 {
179         ir_node *irn;
180         reg_pressure_selector_env_t *env = XMALLOC(reg_pressure_selector_env_t);
181         (void) graph_env;
182
183         obstack_init(&env->obst);
184         ir_nodeset_init(&env->already_scheduled);
185         env->root              = NULL;
186
187         /*
188         * Collect usage statistics.
189         */
190         sched_foreach(bl, irn) {
191                 int i, n;
192                 if (is_Proj(irn)
193                                 || (arch_get_irn_flags(irn) & arch_irn_flags_not_scheduled))
194                         continue;
195
196                 for (i = 0, n = get_irn_arity(irn); i < n; ++i) {
197                         usage_stats_t *us = get_or_set_usage_stats(env, irn);
198 #if 0 /* Liveness is not computed here! */
199                         if (is_live_end(bl, op))
200                                 us->uses_in_block = 99999;
201                         else
202 #endif
203                                 us->uses_in_block++;
204                 }
205         }
206
207         return env;
208 }
209
210 static void reg_pressure_block_free(void *block_env)
211 {
212         reg_pressure_selector_env_t *env = (reg_pressure_selector_env_t*)block_env;
213         usage_stats_t *us;
214
215         for (us = env->root; us; us = us->next)
216                 set_irn_link(us->irn, NULL);
217
218         obstack_free(&env->obst, NULL);
219         ir_nodeset_destroy(&env->already_scheduled);
220         free(env);
221 }
222
223 static int get_result_hops_sum(reg_pressure_selector_env_t *env, ir_node *irn)
224 {
225         int res = 0;
226         if (get_irn_mode(irn) == mode_T) {
227                 const ir_edge_t *edge;
228
229                 foreach_out_edge(irn, edge)
230                         res += get_result_hops_sum(env, get_edge_src_irn(edge));
231         }
232
233         else if (mode_is_data(get_irn_mode(irn)))
234                 res = compute_max_hops(env, irn);
235
236
237         return res;
238 }
239
240 static inline int reg_pr_costs(reg_pressure_selector_env_t *env, ir_node *irn)
241 {
242         int i, n;
243         int sum = 0;
244
245         for (i = 0, n = get_irn_arity(irn); i < n; ++i) {
246                 ir_node *op = get_irn_n(irn, i);
247
248                 if (is_Proj(op)
249                     || (arch_get_irn_flags(op) & arch_irn_flags_not_scheduled))
250                         continue;
251
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 {
262         ir_nodeset_iterator_t iter;
263         reg_pressure_selector_env_t *env = (reg_pressure_selector_env_t*)block_env;
264         ir_node *irn, *res     = NULL;
265         int curr_cost          = INT_MAX;
266
267         assert(ir_nodeset_size(ready_set) > 0);
268
269         ir_nodeset_iterator_init(&iter, ready_set);
270         while ( (irn = ir_nodeset_iterator_next(&iter)) != NULL) {
271                 /*
272                 Ignore branch instructions for the time being.
273                 They should only be scheduled if there is nothing else.
274                 */
275                 if (!is_cfop(irn)) {
276                         int costs = reg_pr_costs(env, irn);
277                         if (costs <= curr_cost) {
278                                 res       = irn;
279                                 curr_cost = costs;
280                         }
281                 }
282         }
283
284         /*
285         There was no result so we only saw a branch.
286         Take it and finish.
287         */
288
289         if (!res) {
290                 ir_nodeset_iterator_init(&iter, ready_set);
291                 res = ir_nodeset_iterator_next(&iter);
292
293                 assert(res && "There must be a node scheduled.");
294         }
295
296         ir_nodeset_insert(&env->already_scheduled, res);
297         return res;
298 }
299
300 static void sched_reg_pressure(ir_graph *irg)
301 {
302         static const list_sched_selector_t reg_pressure_selector = {
303                 reg_pressure_graph_init,
304                 reg_pressure_block_init,
305                 reg_pressure_select,
306                 NULL,                    /* node_ready */
307                 NULL,                    /* node_selected */
308                 reg_pressure_block_free,
309                 free
310         };
311         be_list_sched_graph(irg, &reg_pressure_selector);
312 }
313
314 BE_REGISTER_MODULE_CONSTRUCTOR(be_init_sched_regpress)
315 void be_init_sched_regpress(void)
316 {
317         be_register_scheduler("regpress", sched_reg_pressure);
318 }