2 * Copyright (C) 1995-2008 University of Karlsruhe. All right reserved.
4 * This file is part of libFirm.
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.
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.
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
22 * @brief Primitive list scheduling with different node selectors.
23 * @author Sebastian Hack
42 #include "iredges_t.h"
47 #include "irprintf_t.h"
56 #include "belistsched.h"
62 #include "lc_opts_enum.h"
64 DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
67 * Scheduling environment for the whole graph.
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. */
76 * Environment for a block scheduler.
78 typedef struct block_sched_env_t {
79 /** scheduling info per node, copied from the global scheduler object */
81 /** the set of candidates */
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;
90 * Returns non-zero if the node is already scheduled
92 static bool is_already_scheduled(const sched_env_t *env, ir_node *n)
94 unsigned idx = get_irn_idx(n);
95 return rbitset_is_set(env->scheduled, idx);
99 * Mark a node as already scheduled
101 static void set_already_scheduled(sched_env_t *env, ir_node *n)
103 unsigned idx = get_irn_idx(n);
104 rbitset_set(env->scheduled, idx);
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);
111 * Put a node in the ready set, or make it available immediately if it doesn't
112 * need to be scheduled
114 static void node_ready(block_sched_env_t *env, ir_node *pred, ir_node *irn)
117 || (arch_get_irn_flags(irn) & arch_irn_flags_not_scheduled)) {
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);
124 ir_nodeset_insert(&env->cands, irn);
126 /* Notify selector about the ready node. */
127 if (env->selector->node_ready)
128 env->selector->node_ready(env->selector_block_env, irn, pred);
130 DB((dbg, LEVEL_2, "\tmaking ready: %+F\n", irn));
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.
141 static void try_make_ready(block_sched_env_t *env, ir_node *pred, ir_node *irn)
145 /* we schedule one block at a time, so no need to consider users in other
147 if (is_Block(irn) || get_nodes_block(irn) != env->block)
149 if (is_Phi(irn) || is_End(irn))
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);
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))
163 node_ready(env, pred, irn);
166 static void selected(block_sched_env_t *env, ir_node *node)
168 const ir_edge_t *edge;
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);
174 /* Insert the node in the set of all available scheduled nodes. */
175 set_already_scheduled(env->sched_env, node);
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);
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);
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.
194 static void add_to_sched(block_sched_env_t *env, ir_node *irn)
196 assert(! (arch_get_irn_flags(irn) & arch_irn_flags_not_scheduled));
198 sched_add_before(env->block, irn);
200 DB((dbg, LEVEL_2, "\tschedule %+F\n", irn));
202 /* Remove the node from the ready set */
203 ir_nodeset_remove(&env->cands, irn);
209 * Perform list scheduling on a block.
211 * Note, that the caller must compute a linked list of nodes in the block
212 * using the link field before calling this function.
214 * Also the outs must have been computed.
216 * @param block The block node.
217 * @param env Scheduling environment.
219 static void list_sched_block(ir_node *block, void *env_ptr)
221 sched_env_t *env = (sched_env_t*)env_ptr;
222 const list_sched_selector_t *selector = env->selector;
224 block_sched_env_t be;
225 const ir_edge_t *edge;
226 ir_nodeset_t *cands = &be.cands;
228 /* Initialize the block's list head that will hold the schedule. */
229 sched_init_block(block);
231 /* Initialize the block scheduling environment */
233 be.selector = selector;
235 ir_nodeset_init_size(cands, get_irn_n_edges(block));
237 DB((dbg, LEVEL_1, "scheduling %+F\n", block));
239 if (selector->init_block)
240 be.selector_block_env = selector->init_block(env->selector_env, block);
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);
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);
254 try_make_ready(&be, NULL, irn);
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));
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);
269 if (selector->finish_block)
270 selector->finish_block(be.selector_block_env);
273 /* List schedule a graph. */
274 void be_list_sched_graph(ir_graph *irg, const list_sched_selector_t *selector)
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
283 edges_deactivate(irg);
286 num_nodes = get_irg_last_idx(irg);
288 /* initialize environment for list scheduler */
289 memset(&env, 0, sizeof(env));
290 env.selector = selector;
291 env.scheduled = rbitset_malloc(num_nodes);
293 if (selector->init_graph != NULL)
294 env.selector_env = selector->init_graph(irg);
296 /* Schedule each single block. */
297 irg_block_walk_graph(irg, list_sched_block, NULL, &env);
299 if (selector->finish_graph != NULL)
300 selector->finish_graph(env.selector_env);
305 BE_REGISTER_MODULE_CONSTRUCTOR(be_init_listsched)
306 void be_init_listsched(void)
308 FIRM_DBG_REGISTER(dbg, "firm.be.sched");