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"
54 #include "belistsched.h"
59 #include "lc_opts_enum.h"
61 DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
64 * Scheduling environment for the whole graph.
66 typedef struct sched_env_t {
67 unsigned *scheduled; /**< bitset of already scheduled nodes */
68 const list_sched_selector_t *selector; /**< The node selector. */
69 void *selector_env; /**< A pointer to give to the selector. */
73 * Environment for a block scheduler.
75 typedef struct block_sched_env_t {
76 /** scheduling info per node, copied from the global scheduler object */
78 /** the set of candidates */
80 ir_node *block; /**< the current block */
81 sched_env_t *sched_env; /**< the scheduler environment */
82 const list_sched_selector_t *selector;
83 void *selector_block_env;
87 * Returns non-zero if the node is already scheduled
89 static bool is_already_scheduled(const sched_env_t *env, ir_node *n)
91 unsigned idx = get_irn_idx(n);
92 return rbitset_is_set(env->scheduled, idx);
96 * Mark a node as already scheduled
98 static void set_already_scheduled(sched_env_t *env, ir_node *n)
100 unsigned idx = get_irn_idx(n);
101 rbitset_set(env->scheduled, idx);
104 static void selected(block_sched_env_t *env, ir_node *irn);
105 static void add_to_sched(block_sched_env_t *env, ir_node *irn);
108 * Put a node in the ready set, or make it available immediately if it doesn't
109 * need to be scheduled
111 static void node_ready(block_sched_env_t *env, ir_node *pred, ir_node *irn)
114 || (arch_get_irn_flags(irn) & arch_irn_flags_not_scheduled)) {
116 DB((dbg, LEVEL_3, "\tmaking immediately available: %+F\n", irn));
117 } else if (be_is_Keep(irn) || be_is_CopyKeep(irn)) {
118 /* Keeps must be scheduled immediately */
119 add_to_sched(env, irn);
121 ir_nodeset_insert(&env->cands, irn);
123 /* Notify selector about the ready node. */
124 if (env->selector->node_ready)
125 env->selector->node_ready(env->selector_block_env, irn, pred);
127 DB((dbg, LEVEL_2, "\tmaking ready: %+F\n", irn));
132 * Try to put a node in the ready set.
133 * @param env The block scheduler environment.
134 * @param pred The previous scheduled node.
135 * @param irn The node to make ready.
136 * @return 1, if the node could be made ready, 0 else.
138 static void try_make_ready(block_sched_env_t *env, ir_node *pred, ir_node *irn)
142 /* we schedule one block at a time, so no need to consider users in other
144 if (is_Block(irn) || get_nodes_block(irn) != env->block)
146 if (is_Phi(irn) || is_End(irn))
148 /* check if all operands are already available */
149 n = get_irn_ins_or_deps(irn);
150 for (i = 0; i < n; ++i) {
151 ir_node *op = get_irn_in_or_dep(irn, i);
153 /* If the operand is local to the scheduled block and not yet
154 * scheduled, this nodes cannot be made ready, so exit. */
155 if (get_nodes_block(op) == env->block
156 && !is_already_scheduled(env->sched_env, op))
160 node_ready(env, pred, irn);
163 static void selected(block_sched_env_t *env, ir_node *node)
165 /* notify the selector about the finally selected node. */
166 if (env->selector->node_selected)
167 env->selector->node_selected(env->selector_block_env, node);
169 /* Insert the node in the set of all available scheduled nodes. */
170 set_already_scheduled(env->sched_env, node);
172 /* check users, they might be ready now */
173 foreach_out_edge(node, edge) {
174 ir_node *user = get_edge_src_irn(edge);
175 try_make_ready(env, node, user);
177 foreach_out_edge_kind(node, edge, EDGE_KIND_DEP) {
178 ir_node *user = get_edge_src_irn(edge);
179 try_make_ready(env, node, user);
184 * Append an instruction to a schedule.
185 * @param env The block scheduling environment.
186 * @param irn The node to add to the schedule.
187 * @return The given node.
189 static void add_to_sched(block_sched_env_t *env, ir_node *irn)
191 assert(! (arch_get_irn_flags(irn) & arch_irn_flags_not_scheduled));
193 sched_add_before(env->block, irn);
195 DB((dbg, LEVEL_2, "\tschedule %+F\n", irn));
197 /* Remove the node from the ready set */
198 ir_nodeset_remove(&env->cands, irn);
204 * Perform list scheduling on a block.
206 * Note, that the caller must compute a linked list of nodes in the block
207 * using the link field before calling this function.
209 * Also the outs must have been computed.
211 * @param block The block node.
212 * @param env Scheduling environment.
214 static void list_sched_block(ir_node *block, void *env_ptr)
216 sched_env_t *env = (sched_env_t*)env_ptr;
217 const list_sched_selector_t *selector = env->selector;
219 block_sched_env_t be;
220 ir_nodeset_t *cands = &be.cands;
222 /* Initialize the block's list head that will hold the schedule. */
223 sched_init_block(block);
225 /* Initialize the block scheduling environment */
227 be.selector = selector;
229 ir_nodeset_init_size(cands, get_irn_n_edges(block));
231 DB((dbg, LEVEL_1, "scheduling %+F\n", block));
233 if (selector->init_block)
234 be.selector_block_env = selector->init_block(env->selector_env, block);
236 /* Then one can add all nodes are ready to the set. */
237 foreach_out_edge(block, edge) {
238 ir_node *irn = get_edge_src_irn(edge);
241 /* Phi functions are scheduled immediately, since they only
242 * transfer data flow from the predecessors to this block. */
243 add_to_sched(&be, irn);
244 } else if (be_is_Start(irn)) {
245 /* The start block will be scheduled as the first node */
246 add_to_sched(&be, irn);
248 try_make_ready(&be, NULL, irn);
252 /* Iterate over all remaining nodes */
253 while (ir_nodeset_size(cands) > 0) {
254 ir_node *irn = be.selector->select(be.selector_block_env, cands);
255 DB((dbg, LEVEL_2, "\tpicked node %+F\n", irn));
257 /* remove the scheduled node from the ready list. */
258 ir_nodeset_remove(cands, irn);
259 /* Add the node to the schedule. */
260 add_to_sched(&be, irn);
263 ir_nodeset_destroy(cands);
265 if (selector->finish_block)
266 selector->finish_block(be.selector_block_env);
269 /* List schedule a graph. */
270 void be_list_sched_graph(ir_graph *irg, const list_sched_selector_t *selector)
275 /* Matze: This is very slow, we should avoid it to improve backend speed,
276 * we just have to make sure that we have no dangling out-edges at this
279 edges_deactivate(irg);
282 num_nodes = get_irg_last_idx(irg);
284 /* initialize environment for list scheduler */
285 memset(&env, 0, sizeof(env));
286 env.selector = selector;
287 env.scheduled = rbitset_malloc(num_nodes);
289 if (selector->init_graph != NULL)
290 env.selector_env = selector->init_graph(irg);
292 /* Schedule each single block. */
293 irg_block_walk_graph(irg, list_sched_block, NULL, &env);
295 if (selector->finish_graph != NULL)
296 selector->finish_graph(env.selector_env);
301 BE_REGISTER_MODULE_CONSTRUCTOR(be_init_listsched)
302 void be_init_listsched(void)
304 FIRM_DBG_REGISTER(dbg, "firm.be.sched");