Revert "Let the block walker enter endless loops only at kept blocks, not Phis."
[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 #include <stdbool.h>
34
35 #include "benode.h"
36 #include "be_t.h"
37
38 #include "obst.h"
39 #include "list.h"
40 #include "iterator.h"
41
42 #include "iredges_t.h"
43 #include "irgwalk.h"
44 #include "irnode_t.h"
45 #include "irmode_t.h"
46 #include "irdump.h"
47 #include "irprintf_t.h"
48 #include "array.h"
49 #include "debug.h"
50 #include "irtools.h"
51
52 #include "bemodule.h"
53 #include "besched.h"
54 #include "beutil.h"
55 #include "belive_t.h"
56 #include "belistsched.h"
57 #include "bearch.h"
58 #include "bestat.h"
59 #include "beirg.h"
60
61 #include "lc_opts.h"
62 #include "lc_opts_enum.h"
63
64 DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL);
65
66 /**
67  * Scheduling environment for the whole graph.
68  */
69 typedef struct sched_env_t {
70         unsigned *scheduled;                   /**< bitset of already scheduled nodes */
71         const list_sched_selector_t *selector; /**< The node selector. */
72         void *selector_env;                    /**< A pointer to give to the selector. */
73 } sched_env_t;
74
75 /**
76  * Environment for a block scheduler.
77  */
78 typedef struct block_sched_env_t {
79         /** scheduling info per node, copied from the global scheduler object */
80         unsigned                    *scheduled;
81         /** the set of candidates */
82         ir_nodeset_t                 cands;
83         ir_node                     *block;     /**< the current block */
84         sched_env_t                 *sched_env; /**< the scheduler environment */
85         const list_sched_selector_t *selector;
86         void                        *selector_block_env;
87 } block_sched_env_t;
88
89 /**
90  * Returns non-zero if the node is already scheduled
91  */
92 static bool is_already_scheduled(const sched_env_t *env, ir_node *n)
93 {
94         unsigned idx = get_irn_idx(n);
95         return rbitset_is_set(env->scheduled, idx);
96 }
97
98 /**
99  * Mark a node as already scheduled
100  */
101 static void set_already_scheduled(sched_env_t *env, ir_node *n)
102 {
103         unsigned idx = get_irn_idx(n);
104         rbitset_set(env->scheduled, idx);
105 }
106
107 static void selected(block_sched_env_t *env, ir_node *irn);
108 static void add_to_sched(block_sched_env_t *env, ir_node *irn);
109
110 /**
111  * Put a node in the ready set, or make it available immediately if it doesn't
112  * need to be scheduled
113  */
114 static void node_ready(block_sched_env_t *env, ir_node *pred, ir_node *irn)
115 {
116         if (is_Proj(irn)
117             || (arch_irn_get_flags(irn) & arch_irn_flags_not_scheduled)) {
118                 selected(env, irn);
119                 DB((dbg, LEVEL_3, "\tmaking immediately available: %+F\n", irn));
120         } else if (be_is_Keep(irn) || be_is_CopyKeep(irn)) {
121                 /* Keeps must be scheduled immediately */
122                 add_to_sched(env, irn);
123         } else {
124                 ir_nodeset_insert(&env->cands, irn);
125
126                 /* Notify selector about the ready node. */
127                 if (env->selector->node_ready)
128                         env->selector->node_ready(env->selector_block_env, irn, pred);
129
130                 DB((dbg, LEVEL_2, "\tmaking ready: %+F\n", irn));
131         }
132 }
133
134 /**
135  * Try to put a node in the ready set.
136  * @param env   The block scheduler environment.
137  * @param pred  The previous scheduled node.
138  * @param irn   The node to make ready.
139  * @return 1, if the node could be made ready, 0 else.
140  */
141 static void try_make_ready(block_sched_env_t *env, ir_node *pred, ir_node *irn)
142 {
143         int i, n;
144
145         /* we schedule one block at a time, so no need to consider users in other
146          * blocks */
147         if (is_Block(irn) || get_nodes_block(irn) != env->block)
148                 return;
149         if (is_Phi(irn) || is_End(irn))
150                 return;
151         /* check if all operands are already available */
152         n = get_irn_ins_or_deps(irn);
153         for (i = 0; i < n; ++i) {
154                 ir_node *op = get_irn_in_or_dep(irn, i);
155
156                 /* If the operand is local to the scheduled block and not yet
157                  * scheduled, this nodes cannot be made ready, so exit. */
158                 if (get_nodes_block(op) == env->block
159                                 && !is_already_scheduled(env->sched_env, op))
160                         return;
161         }
162
163         node_ready(env, pred, irn);
164 }
165
166 static void selected(block_sched_env_t *env, ir_node *node)
167 {
168         const ir_edge_t *edge;
169
170         /* notify the selector about the finally selected node. */
171         if (env->selector->node_selected)
172                 env->selector->node_selected(env->selector_block_env, node);
173
174     /* Insert the node in the set of all available scheduled nodes. */
175     set_already_scheduled(env->sched_env, node);
176
177     /* check users, they might be ready now */
178         foreach_out_edge(node, edge) {
179                 ir_node *user = get_edge_src_irn(edge);
180                 try_make_ready(env, node, user);
181         }
182         foreach_out_edge_kind(node, edge, EDGE_KIND_DEP) {
183                 ir_node *user = get_edge_src_irn(edge);
184                 try_make_ready(env, node, user);
185         }
186 }
187
188 /**
189  * Append an instruction to a schedule.
190  * @param env The block scheduling environment.
191  * @param irn The node to add to the schedule.
192  * @return    The given node.
193  */
194 static void add_to_sched(block_sched_env_t *env, ir_node *irn)
195 {
196         assert(! (arch_irn_get_flags(irn) & arch_irn_flags_not_scheduled));
197
198         sched_add_before(env->block, irn);
199
200         DB((dbg, LEVEL_2, "\tschedule %+F\n", irn));
201
202         /* Remove the node from the ready set */
203         ir_nodeset_remove(&env->cands, irn);
204
205         selected(env, irn);
206 }
207
208 /**
209  * Perform list scheduling on a block.
210  *
211  * Note, that the caller must compute a linked list of nodes in the block
212  * using the link field before calling this function.
213  *
214  * Also the outs must have been computed.
215  *
216  * @param block The block node.
217  * @param env Scheduling environment.
218  */
219 static void list_sched_block(ir_node *block, void *env_ptr)
220 {
221         sched_env_t *env                      = (sched_env_t*)env_ptr;
222         const list_sched_selector_t *selector = env->selector;
223
224         block_sched_env_t be;
225         const ir_edge_t *edge;
226         ir_nodeset_t *cands = &be.cands;
227
228         /* Initialize the block's list head that will hold the schedule. */
229         sched_init_block(block);
230
231         /* Initialize the block scheduling environment */
232         be.block     = block;
233         be.selector  = selector;
234         be.sched_env = env;
235         ir_nodeset_init_size(cands, get_irn_n_edges(block));
236
237         DB((dbg, LEVEL_1, "scheduling %+F\n", block));
238
239         if (selector->init_block)
240                 be.selector_block_env = selector->init_block(env->selector_env, block);
241
242         /* Then one can add all nodes are ready to the set. */
243         foreach_out_edge(block, edge) {
244                 ir_node *irn = get_edge_src_irn(edge);
245
246                 if (is_Phi(irn)) {
247                         /* Phi functions are scheduled immediately, since they only
248                          * transfer data flow from the predecessors to this block. */
249                         add_to_sched(&be, irn);
250                 } else if (be_is_Start(irn)) {
251                         /* The start block will be scheduled as the first node */
252                         add_to_sched(&be, irn);
253                 } else {
254                         try_make_ready(&be, NULL, irn);
255                 }
256         }
257
258         /* Iterate over all remaining nodes */
259         while (ir_nodeset_size(cands) > 0) {
260                 ir_node *irn = be.selector->select(be.selector_block_env, cands);
261                 DB((dbg, LEVEL_2, "\tpicked node %+F\n", irn));
262
263                 /* remove the scheduled node from the ready list. */
264                 ir_nodeset_remove(cands, irn);
265                 /* Add the node to the schedule. */
266                 add_to_sched(&be, irn);
267         }
268
269         if (selector->finish_block)
270                 selector->finish_block(be.selector_block_env);
271 }
272
273 /* List schedule a graph. */
274 void be_list_sched_graph(ir_graph *irg, const list_sched_selector_t *selector)
275 {
276         int num_nodes;
277         sched_env_t env;
278
279         /* Matze: This is very slow, we should avoid it to improve backend speed,
280          * we just have to make sure that we have no dangling out-edges at this
281          * point...
282          */
283         edges_deactivate(irg);
284         edges_activate(irg);
285
286         num_nodes = get_irg_last_idx(irg);
287
288         /* initialize environment for list scheduler */
289         memset(&env, 0, sizeof(env));
290         env.selector  = selector;
291         env.scheduled = rbitset_malloc(num_nodes);
292
293         if (selector->init_graph != NULL)
294                 env.selector_env = selector->init_graph(irg);
295
296         /* Schedule each single block. */
297         irg_block_walk_graph(irg, list_sched_block, NULL, &env);
298
299         if (selector->finish_graph != NULL)
300                 selector->finish_graph(env.selector_env);
301
302         free(env.scheduled);
303 }
304
305 BE_REGISTER_MODULE_CONSTRUCTOR(be_init_listsched);
306 void be_init_listsched(void)
307 {
308         FIRM_DBG_REGISTER(dbg, "firm.be.sched");
309 }