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
40 #include "iredges_t.h"
45 #include "irprintf_t.h"
54 #include "belistsched.h"
60 #include "lc_opts_enum.h"
62 DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
65 * Scheduling environment for the whole graph.
67 typedef struct sched_env_t {
68 unsigned *scheduled; /**< bitset of already scheduled nodes */
69 const list_sched_selector_t *selector; /**< The node selector. */
70 void *selector_env; /**< A pointer to give to the selector. */
74 * Environment for a block scheduler.
76 typedef struct block_sched_env_t {
77 /** scheduling info per node, copied from the global scheduler object */
79 /** the set of candidates */
81 ir_node *block; /**< the current block */
82 sched_env_t *sched_env; /**< the scheduler environment */
83 const list_sched_selector_t *selector;
84 void *selector_block_env;
88 * Returns non-zero if the node is already scheduled
90 static bool is_already_scheduled(const sched_env_t *env, ir_node *n)
92 unsigned idx = get_irn_idx(n);
93 return rbitset_is_set(env->scheduled, idx);
97 * Mark a node as already scheduled
99 static void set_already_scheduled(sched_env_t *env, ir_node *n)
101 unsigned idx = get_irn_idx(n);
102 rbitset_set(env->scheduled, idx);
105 static void selected(block_sched_env_t *env, ir_node *irn);
106 static void add_to_sched(block_sched_env_t *env, ir_node *irn);
109 * Put a node in the ready set, or make it available immediately if it doesn't
110 * need to be scheduled
112 static void node_ready(block_sched_env_t *env, ir_node *pred, ir_node *irn)
115 || (arch_get_irn_flags(irn) & arch_irn_flags_not_scheduled)) {
117 DB((dbg, LEVEL_3, "\tmaking immediately available: %+F\n", irn));
118 } else if (be_is_Keep(irn) || be_is_CopyKeep(irn)) {
119 /* Keeps must be scheduled immediately */
120 add_to_sched(env, irn);
122 ir_nodeset_insert(&env->cands, irn);
124 /* Notify selector about the ready node. */
125 if (env->selector->node_ready)
126 env->selector->node_ready(env->selector_block_env, irn, pred);
128 DB((dbg, LEVEL_2, "\tmaking ready: %+F\n", irn));
133 * Try to put a node in the ready set.
134 * @param env The block scheduler environment.
135 * @param pred The previous scheduled node.
136 * @param irn The node to make ready.
137 * @return 1, if the node could be made ready, 0 else.
139 static void try_make_ready(block_sched_env_t *env, ir_node *pred, ir_node *irn)
143 /* we schedule one block at a time, so no need to consider users in other
145 if (is_Block(irn) || get_nodes_block(irn) != env->block)
147 if (is_Phi(irn) || is_End(irn))
149 /* check if all operands are already available */
150 n = get_irn_ins_or_deps(irn);
151 for (i = 0; i < n; ++i) {
152 ir_node *op = get_irn_in_or_dep(irn, i);
154 /* If the operand is local to the scheduled block and not yet
155 * scheduled, this nodes cannot be made ready, so exit. */
156 if (get_nodes_block(op) == env->block
157 && !is_already_scheduled(env->sched_env, op))
161 node_ready(env, pred, irn);
164 static void selected(block_sched_env_t *env, ir_node *node)
166 /* notify the selector about the finally selected node. */
167 if (env->selector->node_selected)
168 env->selector->node_selected(env->selector_block_env, node);
170 /* Insert the node in the set of all available scheduled nodes. */
171 set_already_scheduled(env->sched_env, node);
173 /* check users, they might be ready now */
174 foreach_out_edge(node, edge) {
175 ir_node *user = get_edge_src_irn(edge);
176 try_make_ready(env, node, user);
178 foreach_out_edge_kind(node, edge, EDGE_KIND_DEP) {
179 ir_node *user = get_edge_src_irn(edge);
180 try_make_ready(env, node, user);
185 * Append an instruction to a schedule.
186 * @param env The block scheduling environment.
187 * @param irn The node to add to the schedule.
188 * @return The given node.
190 static void add_to_sched(block_sched_env_t *env, ir_node *irn)
192 assert(! (arch_get_irn_flags(irn) & arch_irn_flags_not_scheduled));
194 sched_add_before(env->block, irn);
196 DB((dbg, LEVEL_2, "\tschedule %+F\n", irn));
198 /* Remove the node from the ready set */
199 ir_nodeset_remove(&env->cands, irn);
205 * Perform list scheduling on a block.
207 * Note, that the caller must compute a linked list of nodes in the block
208 * using the link field before calling this function.
210 * Also the outs must have been computed.
212 * @param block The block node.
213 * @param env Scheduling environment.
215 static void list_sched_block(ir_node *block, void *env_ptr)
217 sched_env_t *env = (sched_env_t*)env_ptr;
218 const list_sched_selector_t *selector = env->selector;
220 block_sched_env_t be;
221 ir_nodeset_t *cands = &be.cands;
223 /* Initialize the block's list head that will hold the schedule. */
224 sched_init_block(block);
226 /* Initialize the block scheduling environment */
228 be.selector = selector;
230 ir_nodeset_init_size(cands, get_irn_n_edges(block));
232 DB((dbg, LEVEL_1, "scheduling %+F\n", block));
234 if (selector->init_block)
235 be.selector_block_env = selector->init_block(env->selector_env, block);
237 /* Then one can add all nodes are ready to the set. */
238 foreach_out_edge(block, edge) {
239 ir_node *irn = get_edge_src_irn(edge);
242 /* Phi functions are scheduled immediately, since they only
243 * transfer data flow from the predecessors to this block. */
244 add_to_sched(&be, irn);
245 } else if (be_is_Start(irn)) {
246 /* The start block will be scheduled as the first node */
247 add_to_sched(&be, irn);
249 try_make_ready(&be, NULL, irn);
253 /* Iterate over all remaining nodes */
254 while (ir_nodeset_size(cands) > 0) {
255 ir_node *irn = be.selector->select(be.selector_block_env, cands);
256 DB((dbg, LEVEL_2, "\tpicked node %+F\n", irn));
258 /* remove the scheduled node from the ready list. */
259 ir_nodeset_remove(cands, irn);
260 /* Add the node to the schedule. */
261 add_to_sched(&be, irn);
264 ir_nodeset_destroy(cands);
266 if (selector->finish_block)
267 selector->finish_block(be.selector_block_env);
270 /* List schedule a graph. */
271 void be_list_sched_graph(ir_graph *irg, const list_sched_selector_t *selector)
276 /* Matze: This is very slow, we should avoid it to improve backend speed,
277 * we just have to make sure that we have no dangling out-edges at this
280 edges_deactivate(irg);
283 num_nodes = get_irg_last_idx(irg);
285 /* initialize environment for list scheduler */
286 memset(&env, 0, sizeof(env));
287 env.selector = selector;
288 env.scheduled = rbitset_malloc(num_nodes);
290 if (selector->init_graph != NULL)
291 env.selector_env = selector->init_graph(irg);
293 /* Schedule each single block. */
294 irg_block_walk_graph(irg, list_sched_block, NULL, &env);
296 if (selector->finish_graph != NULL)
297 selector->finish_graph(env.selector_env);
302 BE_REGISTER_MODULE_CONSTRUCTOR(be_init_listsched)
303 void be_init_listsched(void)
305 FIRM_DBG_REGISTER(dbg, "firm.be.sched");