5a2aaa6f29fef6de464a480b2725735d4da525ad
[libfirm] / ir / be / belistsched.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       Primitive list scheduling with different node selectors.
23  * @author      Sebastian Hack
24  * @date        20.10.2004
25  * @version     $Id$
26  */
27 #include "config.h"
28
29 #include <stdio.h>
30 #include <stdarg.h>
31 #include <string.h>
32 #include <limits.h>
33
34 #include "benode.h"
35 #include "be_t.h"
36
37 #include "obst.h"
38 #include "list.h"
39 #include "iterator.h"
40
41 #include "iredges_t.h"
42 #include "irgwalk.h"
43 #include "irnode_t.h"
44 #include "irmode_t.h"
45 #include "irdump.h"
46 #include "irprintf_t.h"
47 #include "array.h"
48 #include "debug.h"
49 #include "irtools.h"
50
51 #include "bemodule.h"
52 #include "besched.h"
53 #include "beutil.h"
54 #include "belive_t.h"
55 #include "belistsched.h"
56 #include "bearch.h"
57 #include "bestat.h"
58 #include "beirg.h"
59
60 #include "lc_opts.h"
61 #include "lc_opts_enum.h"
62
63 DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL);
64
65 /**
66  * All scheduling info needed per node.
67  */
68 typedef struct sched_irn_t {
69         unsigned num_not_sched_user; /**< The number of not yet scheduled users of this node */
70         unsigned already_sched : 1;  /**< Set if this node is already scheduled */
71 } sched_irn_t;
72
73 /**
74  * Scheduling environment for the whole graph.
75  */
76 typedef struct sched_env_t {
77         sched_irn_t *sched_info;                    /**< scheduling info per node */
78         const list_sched_selector_t *selector;      /**< The node selector. */
79         void *selector_env;                         /**< A pointer to give to the selector. */
80 } sched_env_t;
81
82 /**
83  * Environment for a block scheduler.
84  */
85 typedef struct block_sched_env_t {
86         sched_irn_t *sched_info;                    /**< scheduling info per node, copied from the global scheduler object */
87         ir_nodeset_t cands;                         /**< the set of candidates */
88         ir_node *block;                             /**< the current block */
89         sched_env_t *sched_env;                     /**< the scheduler environment */
90         ir_nodeset_t live;                          /**< simple liveness during scheduling */
91         const list_sched_selector_t *selector;
92         void *selector_block_env;
93 } block_sched_env_t;
94
95 /**
96  * Returns non-zero if the node is already scheduled
97  */
98 static inline int is_already_scheduled(block_sched_env_t *env, ir_node *n)
99 {
100         unsigned const idx = get_irn_idx(n);
101
102         assert(idx < ARR_LEN(env->sched_info));
103         return env->sched_info[idx].already_sched;
104 }
105
106 /**
107  * Mark a node as already scheduled
108  */
109 static inline void set_already_scheduled(block_sched_env_t *env, ir_node *n)
110 {
111         unsigned const idx = get_irn_idx(n);
112
113         assert(idx < ARR_LEN(env->sched_info));
114         env->sched_info[idx].already_sched = 1;
115 }
116
117 static void selected(block_sched_env_t *env, ir_node *irn);
118
119 /**
120  * Try to put a node in the ready set.
121  * @param env   The block scheduler environment.
122  * @param pred  The previous scheduled node.
123  * @param irn   The node to make ready.
124  * @return 1, if the node could be made ready, 0 else.
125  */
126 static inline int make_ready(block_sched_env_t *env, ir_node *pred, ir_node *irn)
127 {
128         int i, n;
129
130         /* Blocks cannot be scheduled. */
131         if (is_Block(irn) || get_irn_n_edges(irn) == 0)
132                 return 0;
133
134         /*
135          * Check, if the given ir node is in a different block as the
136          * currently scheduled one. If that is so, don't make the node ready.
137          */
138         if (env->block != get_nodes_block(irn))
139                 return 0;
140
141         for (i = 0, n = get_irn_ins_or_deps(irn); i < n; ++i) {
142                 ir_node *op = get_irn_in_or_dep(irn, i);
143
144                 /* if irn is an End we have keep-alives and op might be a block, skip that */
145                 if (is_Block(op)) {
146                         assert(is_End(irn));
147                         continue;
148                 }
149
150                 /* If the operand is local to the scheduled block and not yet
151                  * scheduled, this nodes cannot be made ready, so exit. */
152                 if (! is_already_scheduled(env, op) && get_nodes_block(op) == env->block)
153                         return 0;
154         }
155
156         if (is_Proj(irn) || (arch_irn_get_flags(irn) & arch_irn_flags_not_scheduled)) {
157                 selected(env, irn);
158                 DB((dbg, LEVEL_3, "\tmaking immediately available: %+F\n", irn));
159         } else {
160                 ir_nodeset_insert(&env->cands, irn);
161
162                 /* Notify selector about the ready node. */
163                 if (env->selector->node_ready)
164                         env->selector->node_ready(env->selector_block_env, irn, pred);
165
166                 DB((dbg, LEVEL_2, "\tmaking ready: %+F\n", irn));
167         }
168
169     return 1;
170 }
171
172 /**
173  * Try, to make all users of a node ready.
174  * In fact, a usage node can only be made ready, if all its operands
175  * have already been scheduled yet. This is checked by make_ready().
176  * @param env The block schedule environment.
177  * @param irn The node, which usages (successors) are to be made ready.
178  */
179 static void make_users_ready(block_sched_env_t *env, ir_node *irn)
180 {
181         const ir_edge_t *edge;
182
183         /* make all data users ready */
184         foreach_out_edge(irn, edge) {
185                 ir_node *user = get_edge_src_irn(edge);
186
187                 if (! is_Phi(user))
188                         make_ready(env, irn, user);
189         }
190
191         /* and the dependent nodes as well */
192         foreach_out_edge_kind(irn, edge, EDGE_KIND_DEP) {
193                 ir_node *user = get_edge_src_irn(edge);
194
195                 if (! is_Phi(user))
196                         make_ready(env, irn, user);
197         }
198 }
199
200 /**
201  * Returns the number of not yet schedules users.
202  */
203 static inline int get_irn_not_sched_user(block_sched_env_t *env, ir_node *n)
204 {
205         unsigned const idx = get_irn_idx(n);
206
207         assert(idx < ARR_LEN(env->sched_info));
208         return env->sched_info[idx].num_not_sched_user;
209 }
210
211 /**
212  * Sets the number of not yet schedules users.
213  */
214 static inline void set_irn_not_sched_user(block_sched_env_t *env, ir_node *n, int num)
215 {
216         unsigned const idx = get_irn_idx(n);
217
218         assert(idx < ARR_LEN(env->sched_info));
219         env->sched_info[idx].num_not_sched_user = num;
220 }
221
222 /**
223  * Add @p num to the number of not yet schedules users and returns the result.
224  */
225 static inline int add_irn_not_sched_user(block_sched_env_t *env, ir_node *n, int num)
226 {
227         unsigned const idx = get_irn_idx(n);
228
229         assert(idx < ARR_LEN(env->sched_info));
230         env->sched_info[idx].num_not_sched_user += num;
231         return env->sched_info[idx].num_not_sched_user;
232 }
233
234 /**
235  * Returns the number of users of a node having mode datab.
236  */
237 static int get_num_successors(ir_node *irn)
238 {
239         int             sum = 0;
240         const ir_edge_t *edge;
241
242         if (get_irn_mode(irn) == mode_T) {
243                 /* for mode_T nodes: count the users of all Projs */
244                 foreach_out_edge(irn, edge) {
245                         ir_node *proj = get_edge_src_irn(edge);
246                         ir_mode *mode = get_irn_mode(proj);
247
248                         if (mode == mode_T) {
249                                 sum += get_num_successors(proj);
250                         } else if (mode_is_datab(mode)) {
251                                 sum += get_irn_n_edges(proj);
252                         }
253                 }
254         }
255         else {
256                 /* do not count keep-alive edges */
257                 foreach_out_edge(irn, edge) {
258                         if (get_irn_opcode(get_edge_src_irn(edge)) != iro_End)
259                                 sum++;
260                 }
261         }
262
263         return sum;
264 }
265
266 /**
267  * Adds irn to @p live, updates all inputs that this user is scheduled
268  * and counts all of its non scheduled users.
269  */
270 static void update_sched_liveness(block_sched_env_t *env, ir_node *irn)
271 {
272         int i;
273
274         /* ignore Projs */
275         if (is_Proj(irn))
276                 return;
277
278         for (i = get_irn_ins_or_deps(irn) - 1; i >= 0; --i) {
279                 ir_node *in = get_irn_in_or_dep(irn, i);
280
281                 /* if in is a proj: update predecessor */
282                 in = skip_Proj(in);
283
284                 /* if in is still in the live set: reduce number of users by one */
285                 if (ir_nodeset_contains(&env->live, in)) {
286                         if (add_irn_not_sched_user(env, in, -1) <= 0)
287                                 ir_nodeset_remove(&env->live, in);
288                 }
289         }
290
291         /*
292                 get_num_successors returns the number of all users. This includes
293                 users in different blocks as well. As the each block is scheduled separately
294                 the liveness info of those users will not be updated and so these
295                 users will keep up the register pressure as it is desired.
296         */
297         i = get_num_successors(irn);
298         if (i > 0) {
299                 set_irn_not_sched_user(env, irn, i);
300                 ir_nodeset_insert(&env->live, irn);
301         }
302 }
303
304 static void selected(block_sched_env_t *env, ir_node *node)
305 {
306         /* notify the selector about the finally selected node. */
307         if (env->selector->node_selected)
308                 env->selector->node_selected(env->selector_block_env, node);
309
310     /* Insert the node in the set of all available scheduled nodes. */
311     set_already_scheduled(env, node);
312
313         make_users_ready(env, node);
314 }
315
316 /**
317  * Append an instruction to a schedule.
318  * @param env The block scheduling environment.
319  * @param irn The node to add to the schedule.
320  * @return    The given node.
321  */
322 static void add_to_sched(block_sched_env_t *env, ir_node *irn)
323 {
324         assert(! (arch_irn_get_flags(irn) & arch_irn_flags_not_scheduled));
325
326         update_sched_liveness(env, irn);
327         sched_add_before(env->block, irn);
328
329         DBG((dbg, LEVEL_2, "\tadding %+F\n", irn));
330
331         /* Remove the node from the ready set */
332         ir_nodeset_remove(&env->cands, irn);
333
334         selected(env, irn);
335 }
336
337 /**
338  * Perform list scheduling on a block.
339  *
340  * Note, that the caller must compute a linked list of nodes in the block
341  * using the link field before calling this function.
342  *
343  * Also the outs must have been computed.
344  *
345  * @param block The block node.
346  * @param env Scheduling environment.
347  */
348 static void list_sched_block(ir_node *block, void *env_ptr)
349 {
350         sched_env_t *env                      = (sched_env_t*)env_ptr;
351         const list_sched_selector_t *selector = env->selector;
352
353         block_sched_env_t be;
354         const ir_edge_t *edge;
355         ir_node *irn;
356         int j, m;
357
358         /* Initialize the block's list head that will hold the schedule. */
359         sched_init_block(block);
360
361         /* Initialize the block scheduling environment */
362         be.sched_info = env->sched_info;
363         be.block      = block;
364         ir_nodeset_init_size(&be.cands, get_irn_n_edges(block));
365         ir_nodeset_init_size(&be.live, get_irn_n_edges(block));
366         be.selector   = selector;
367         be.sched_env  = env;
368
369         DBG((dbg, LEVEL_1, "scheduling %+F\n", block));
370
371         if (selector->init_block)
372                 be.selector_block_env = selector->init_block(env->selector_env, block);
373
374         /* Then one can add all nodes are ready to the set. */
375         foreach_out_edge(block, edge) {
376                 ir_node   *irn  = get_edge_src_irn(edge);
377                 unsigned   code = get_irn_opcode(irn);
378                 int users;
379
380                 if (code == iro_End) {
381                         /* Skip the end node because of keep-alive edges. */
382                         continue;
383                 }
384
385                 users = get_irn_n_edges(irn);
386                 if (users == 0) {
387                         continue;
388                 } else if (users == 1) {
389                         /* ignore nodes that are only hold by the anchor */
390                         const ir_edge_t *edge = get_irn_out_edge_first_kind(irn, EDGE_KIND_NORMAL);
391                         ir_node *user = get_edge_src_irn(edge);
392                         if (is_Anchor(user))
393                                 continue;
394                 }
395
396                 if (is_Phi(irn)) {
397                         /* Phi functions are scheduled immediately, since they only
398                          * transfer data flow from the predecessors to this block. */
399                         add_to_sched(&be, irn);
400                 } else if (be_is_Start(irn)) {
401                         /* The start block will be scheduled as the first node */
402                         add_to_sched(&be, irn);
403                 } else {
404                         /* Other nodes must have all operands in other blocks to be made
405                          * ready */
406                         int ready = 1;
407
408                         /* Check, if the operands of a node are not local to this block */
409                         for (j = 0, m = get_irn_ins_or_deps(irn); j < m; ++j) {
410                                 ir_node *operand = get_irn_in_or_dep(irn, j);
411
412                                 if (get_nodes_block(operand) == block) {
413                                         ready = 0;
414                                         break;
415                                 } else {
416                                         /* live in values increase register pressure */
417                                         ir_nodeset_insert(&be.live, operand);
418                                 }
419                         }
420
421                         /* Make the node ready, if all operands live in a foreign block */
422                         if (ready) {
423                                 DBG((dbg, LEVEL_2, "\timmediately ready: %+F\n", irn));
424                                 make_ready(&be, NULL, irn);
425                         }
426                 }
427         }
428
429         /* Iterate over all remaining nodes */
430         while (ir_nodeset_size(&be.cands) > 0) {
431                 ir_nodeset_iterator_t iter;
432
433                 /* Keeps must be scheduled immediately */
434                 foreach_ir_nodeset(&be.cands, irn, iter) {
435                         if (be_is_Keep(irn) || be_is_CopyKeep(irn) || is_Sync(irn)) {
436                                 break;
437                         }
438                 }
439
440                 if (irn == NULL) {
441                         irn = be.selector->select(be.selector_block_env, &be.cands, &be.live);
442                 }
443
444                 DB((dbg, LEVEL_2, "\tpicked node %+F\n", irn));
445
446                 /* Add the node to the schedule. */
447                 add_to_sched(&be, irn);
448
449                 /* remove the scheduled node from the ready list. */
450                 ir_nodeset_remove(&be.cands, irn);
451         }
452
453         if (selector->finish_block)
454                 selector->finish_block(be.selector_block_env);
455
456         ir_nodeset_destroy(&be.cands);
457         ir_nodeset_destroy(&be.live);
458 }
459
460 /* List schedule a graph. */
461 void be_list_sched_graph(ir_graph *irg, const list_sched_selector_t *selector)
462 {
463         int num_nodes;
464         sched_env_t env;
465
466 #if 1
467         /* Matze: This is very slow, we should avoid it to improve backend speed,
468          * we just have to make sure that we have no dangling out-edges at this
469          * point...
470          */
471
472         /* Assure, that we have no dangling out-edges to deleted stuff */
473         edges_deactivate(irg);
474         edges_activate(irg);
475 #endif
476
477         num_nodes = get_irg_last_idx(irg);
478
479         /* initialize environment for list scheduler */
480         memset(&env, 0, sizeof(env));
481         env.selector   = selector;
482         env.sched_info = NEW_ARR_F(sched_irn_t, num_nodes);
483
484         memset(env.sched_info, 0, num_nodes * sizeof(env.sched_info[0]));
485
486         if (selector->init_graph != NULL)
487                 env.selector_env = selector->init_graph(irg);
488
489         /* Schedule each single block. */
490         irg_block_walk_graph(irg, list_sched_block, NULL, &env);
491
492         if (selector->finish_graph != NULL)
493                 selector->finish_graph(env.selector_env);
494
495         DEL_ARR_F(env.sched_info);
496 }
497
498 BE_REGISTER_MODULE_CONSTRUCTOR(be_init_listsched);
499 void be_init_listsched(void)
500 {
501         FIRM_DBG_REGISTER(dbg, "firm.be.sched");
502 }