22ce8022e6d05f8261322b4178200ccdb2fdee46
[libfirm] / ir / be / belistsched.c
1 /**
2  * Scheduling algorithms.
3  * Just a simple list scheduling algorithm is here.
4  * @date 20.10.2004
5  * @author Sebastian Hack
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
11
12 #include <stdio.h>
13 #include <stdarg.h>
14 #include <string.h>
15 #include <limits.h>
16
17 #include "benode_t.h"
18
19 #include "obst.h"
20 #include "list.h"
21 #include "iterator.h"
22
23 #include "iredges_t.h"
24 #include "irgwalk.h"
25 #include "irnode_t.h"
26 #include "irmode_t.h"
27 #include "irdump.h"
28 #include "irprintf_t.h"
29 #include "debug.h"
30
31 #include "besched_t.h"
32 #include "beutil.h"
33 #include "belive_t.h"
34 #include "belistsched.h"
35 #include "bearch.h"
36 #include "bestat.h"
37
38 #define MAX(x,y) ((x) > (y) ? (x) : (y))
39 #define MIN(x,y) ((x) < (y) ? (x) : (y))
40
41 /**
42  * Scheduling environment for the whole graph.
43  */
44 typedef struct _sched_env_t {
45         const list_sched_selector_t *selector;      /**< The node selector. */
46         const arch_env_t *arch_env;                 /**< The architecture enviromnent. */
47         const ir_graph *irg;                        /**< The graph to schedule. */
48         void *selector_env;                         /**< A pointer to give to the selector. */
49 } sched_env_t;
50
51 #if 0
52 /*
53  * Ugly global variable for the compare function
54  * since qsort(3) does not pass an extra pointer.
55  */
56 static ir_node *curr_bl = NULL;
57
58 static int cmp_usage(const void *a, const void *b)
59 {
60         struct trivial_sched_env *env;
61         const ir_node *p = a;
62         const ir_node *q = b;
63         int res = 0;
64
65         res = is_live_end(env->curr_bl, a) - is_live_end(env->curr_bl, b);
66
67         /*
68          * One of them is live at the end of the block.
69          * Then, that one shall be scheduled at after the other
70          */
71         if(res != 0)
72                 return res;
73
74
75         return res;
76 }
77 #endif
78
79 /**
80  * The trivial selector:
81  * Just assure that branches are executed last, otherwise select
82  * the first node ready.
83  */
84 static ir_node *trivial_select(void *block_env, nodeset *ready_set)
85 {
86         const arch_env_t *arch_env = block_env;
87         ir_node *irn = NULL;
88         int const_last = 0;
89
90         /* assure that branches and constants are executed last */
91         for (irn = nodeset_first(ready_set); irn; irn = nodeset_next(ready_set)) {
92                 arch_irn_class_t irn_class = arch_irn_classify(arch_env, irn);
93
94                 if (irn_class != arch_irn_class_branch && (const_last ? (irn_class != arch_irn_class_const) : 1)) {
95                         nodeset_break(ready_set);
96                         return irn;
97                 }
98         }
99
100         /* assure that constants are executed before branches */
101         if (const_last) {
102                 for (irn = nodeset_first(ready_set); irn; irn = nodeset_next(ready_set)) {
103                         if (arch_irn_classify(arch_env, irn) != arch_irn_class_branch) {
104                                 nodeset_break(ready_set);
105                                 return irn;
106                         }
107                 }
108         }
109
110
111         /* at last: schedule branches */
112         irn = nodeset_first(ready_set);
113         nodeset_break(ready_set);
114
115         return irn;
116 }
117
118 static void *trivial_init_graph(const list_sched_selector_t *vtab, const arch_env_t *arch_env, ir_graph *irg)
119 {
120         return (void *) arch_env;
121 }
122
123 static void *trivial_init_block(void *graph_env, ir_node *bl)
124 {
125         return graph_env;
126 }
127
128 static INLINE int must_appear_in_schedule(const list_sched_selector_t *sel, void *block_env, const ir_node *irn)
129 {
130         int res = 0;
131
132         if(sel->to_appear_in_schedule)
133                 res = sel->to_appear_in_schedule(block_env, irn);
134
135         return res || to_appear_in_schedule(irn) || be_is_Keep(irn) || be_is_RegParams(irn);
136 }
137
138 static const list_sched_selector_t trivial_selector_struct = {
139         trivial_init_graph,
140         trivial_init_block,
141         trivial_select,
142         NULL,
143         NULL,
144         NULL
145 };
146
147 const list_sched_selector_t *trivial_selector = &trivial_selector_struct;
148
149 typedef struct _usage_stats_t {
150         ir_node *irn;
151         struct _usage_stats_t *next;
152         int max_hops;
153         int uses_in_block;      /**< Number of uses inside the current block. */
154         int already_consumed;   /**< Number of insns using this value already
155                                                           scheduled. */
156 } usage_stats_t;
157
158 typedef struct {
159         const list_sched_selector_t *vtab;
160         const arch_env_t *arch_env;
161 } reg_pressure_main_env_t;
162
163 typedef struct {
164         struct obstack obst;
165         const reg_pressure_main_env_t *main_env;
166         usage_stats_t *root;
167         nodeset *already_scheduled;
168 } reg_pressure_selector_env_t;
169
170 static INLINE usage_stats_t *get_or_set_usage_stats(reg_pressure_selector_env_t *env, ir_node *irn)
171 {
172         usage_stats_t *us = get_irn_link(irn);
173
174         if(!us) {
175                 us                   = obstack_alloc(&env->obst, sizeof(us[0]));
176                 us->irn              = irn;
177                 us->already_consumed = 0;
178                 us->max_hops         = INT_MAX;
179                 us->next             = env->root;
180                 env->root            = us;
181                 set_irn_link(irn, us);
182         }
183
184         return us;
185 }
186
187 static INLINE usage_stats_t *get_usage_stats(ir_node *irn)
188 {
189         usage_stats_t *us = get_irn_link(irn);
190         assert(us && "This node must have usage stats");
191         return us;
192 }
193
194 static int max_hops_walker(reg_pressure_selector_env_t *env, ir_node *irn, ir_node *curr_bl, int depth, unsigned visited_nr)
195 {
196         ir_node *bl = get_nodes_block(irn);
197         /*
198          * If the reached node is not in the block desired,
199          * return the value passed for this situation.
200          */
201         if(get_nodes_block(irn) != bl)
202                 return block_dominates(bl, curr_bl) ? 0 : INT_MAX;
203
204         /*
205          * If the node is in the current block but not
206          * yet scheduled, we keep on searching from that node.
207          */
208         if(!nodeset_find(env->already_scheduled, irn)) {
209                 int i, n;
210                 int res = 0;
211                 for(i = 0, n = get_irn_arity(irn); i < n; ++i) {
212                         ir_node *operand = get_irn_n(irn, i);
213
214                         if(get_irn_visited(operand) < visited_nr) {
215                                 int tmp;
216
217                                 set_irn_visited(operand, visited_nr);
218                                 tmp = max_hops_walker(env, operand, bl, depth + 1, visited_nr);
219                                 res = MAX(tmp, res);
220                         }
221                 }
222
223                 return res;
224         }
225
226         /*
227          * If the node is in the current block and scheduled, return
228          * the depth which indicates the number of steps to the
229          * region of scheduled nodes.
230          */
231         return depth;
232 }
233
234 static int compute_max_hops(reg_pressure_selector_env_t *env, ir_node *irn)
235 {
236         ir_node *bl   = get_nodes_block(irn);
237         ir_graph *irg = get_irn_irg(bl);
238         int res       = 0;
239
240         const ir_edge_t *edge;
241
242         foreach_out_edge(irn, edge) {
243                 ir_node *user       = get_edge_src_irn(edge);
244                 unsigned visited_nr = get_irg_visited(irg) + 1;
245                 int max_hops;
246
247                 set_irg_visited(irg, visited_nr);
248                 max_hops = max_hops_walker(env, user, irn, 0, visited_nr);
249                 res      = MAX(res, max_hops);
250         }
251
252         return res;
253 }
254
255 static void *reg_pressure_graph_init(const list_sched_selector_t *vtab, const arch_env_t *arch_env, ir_graph *irg)
256 {
257         reg_pressure_main_env_t *main_env = xmalloc(sizeof(main_env[0]));
258
259         main_env->arch_env = arch_env;
260         main_env->vtab     = vtab;
261         irg_walk_graph(irg, firm_clear_link, NULL, NULL);
262
263         return main_env;
264 }
265
266 static void *reg_pressure_block_init(void *graph_env, ir_node *bl)
267 {
268         ir_node *irn;
269         reg_pressure_selector_env_t *env  = xmalloc(sizeof(env[0]));
270
271         obstack_init(&env->obst);
272         env->already_scheduled = new_nodeset(32);
273         env->root              = NULL;
274         env->main_env          = graph_env;
275
276         /*
277          * Collect usage statistics.
278          */
279         sched_foreach(bl, irn) {
280                 if(must_appear_in_schedule(env->main_env->vtab, env, irn)) {
281                         int i, n;
282
283                         for(i = 0, n = get_irn_arity(irn); i < n; ++i) {
284                                 ir_node *op = get_irn_n(irn, i);
285                                 if(must_appear_in_schedule(env->main_env->vtab, env, irn)) {
286                                         usage_stats_t *us = get_or_set_usage_stats(env, irn);
287                                         if(is_live_end(bl, op))
288                                                 us->uses_in_block = 99999;
289                                         else
290                                                 us->uses_in_block++;
291                                 }
292                         }
293                 }
294         }
295
296         return env;
297 }
298
299 static void reg_pressure_block_free(void *block_env)
300 {
301         reg_pressure_selector_env_t *env = block_env;
302         usage_stats_t *us;
303
304         for(us = env->root; us; us = us->next)
305                 set_irn_link(us->irn, NULL);
306
307         obstack_free(&env->obst, NULL);
308         del_nodeset(env->already_scheduled);
309         free(env);
310 }
311
312 static int get_result_hops_sum(reg_pressure_selector_env_t *env, ir_node *irn)
313 {
314         int res = 0;
315         if(get_irn_mode(irn) == mode_T) {
316                 const ir_edge_t *edge;
317
318                 foreach_out_edge(irn, edge)
319                         res += get_result_hops_sum(env, get_edge_src_irn(edge));
320         }
321
322         else if(mode_is_data(get_irn_mode(irn)))
323                 res = compute_max_hops(env, irn);
324
325
326         return res;
327 }
328
329 static INLINE int reg_pr_costs(reg_pressure_selector_env_t *env, ir_node *irn)
330 {
331         int i, n;
332         int sum = 0;
333
334         for(i = 0, n = get_irn_arity(irn); i < n; ++i) {
335                 ir_node *op = get_irn_n(irn, i);
336
337                 if(must_appear_in_schedule(env->main_env->vtab, env, op))
338                         sum += compute_max_hops(env, op);
339         }
340
341         sum += get_result_hops_sum(env, irn);
342
343         return sum;
344 }
345
346 static ir_node *reg_pressure_select(void *block_env, nodeset *ready_set)
347 {
348         reg_pressure_selector_env_t *env = block_env;
349         ir_node *irn, *res     = NULL;
350         int curr_cost          = INT_MAX;
351
352         assert(nodeset_count(ready_set) > 0);
353
354         for (irn = nodeset_first(ready_set); irn; irn = nodeset_next(ready_set)) {
355                 /*
356                         Ignore branch instructions for the time being.
357                         They should only be scheduled if there is nothing else.
358                 */
359                 if (arch_irn_classify(env->main_env->arch_env, irn) != arch_irn_class_branch) {
360                         int costs = reg_pr_costs(env, irn);
361                         if (costs <= curr_cost) {
362                                 res       = irn;
363                                 curr_cost = costs;
364                         }
365                 }
366         }
367
368         /*
369                 There was no result so we only saw a branch.
370                 Take it and finish.
371         */
372
373         if(!res) {
374                 res = nodeset_first(ready_set);
375                 nodeset_break(ready_set);
376
377                 assert(res && "There must be a node scheduled.");
378         }
379
380         nodeset_insert(env->already_scheduled, res);
381         return res;
382 }
383
384 static const list_sched_selector_t reg_pressure_selector_struct = {
385         reg_pressure_graph_init,
386         reg_pressure_block_init,
387         reg_pressure_select,
388         NULL,
389         reg_pressure_block_free,
390         free
391 };
392
393 const list_sched_selector_t *reg_pressure_selector = &reg_pressure_selector_struct;
394
395 static void list_sched_block(ir_node *block, void *env_ptr);
396
397 void list_sched(const arch_env_t *arch_env, ir_graph *irg)
398 {
399         sched_env_t env;
400
401         memset(&env, 0, sizeof(env));
402         env.selector = arch_env->isa->impl->get_list_sched_selector(arch_env->isa);
403         env.arch_env = arch_env;
404         env.irg      = irg;
405
406         if(env.selector->init_graph)
407                 env.selector_env = env.selector->init_graph(env.selector, arch_env, irg);
408
409         /* Assure, that the out edges are computed */
410         edges_assure(irg);
411
412         /* Schedule each single block. */
413         irg_block_walk_graph(irg, list_sched_block, NULL, &env);
414
415         if(env.selector->finish_graph)
416                 env.selector->finish_graph(env.selector_env);
417 }
418
419
420 /**
421  * Environment for a block scheduler.
422  */
423 typedef struct _block_sched_env_t {
424         int curr_time;
425         nodeset *ready_set;
426         nodeset *already_scheduled;
427         ir_node *block;
428         const list_sched_selector_t *selector;
429         void *selector_block_env;
430         DEBUG_ONLY(firm_dbg_module_t *dbg;)
431 } block_sched_env_t;
432
433 /**
434  * Try to put a node in the ready set.
435  * @param env The block scheduler environment.
436  * @param irn The node to make ready.
437  * @return 1, if the node could be made ready, 0 else.
438  */
439 static INLINE int make_ready(block_sched_env_t *env, ir_node *irn)
440 {
441     int i, n;
442
443     /* Blocks cannot be scheduled. */
444     if(is_Block(irn))
445         return 0;
446
447     /*
448      * Check, if the given ir node is in a different block as the
449      * currently scheduled one. If that is so, don't make the node ready.
450      */
451     if(env->block != get_nodes_block(irn))
452         return 0;
453
454     for(i = 0, n = get_irn_arity(irn); i < n; ++i) {
455         ir_node *op = get_irn_n(irn, i);
456
457         /* if irn is an End we have keep-alives and op might be a block, skip that */
458         if (is_Block(op)) {
459           assert(get_irn_op(irn) == op_End);
460           continue;
461         }
462
463         /* If the operand is local to the scheduled block and not yet
464          * scheduled, this nodes cannot be made ready, so exit. */
465         if(!nodeset_find(env->already_scheduled, op) && get_nodes_block(op) == env->block)
466             return 0;
467     }
468
469     DBG((env->dbg, LEVEL_2, "\tmaking ready: %+F\n", irn));
470     nodeset_insert(env->ready_set, irn);
471
472     return 1;
473 }
474
475 /**
476  * Check, if a node is ready in a block schedule.
477  * @param env The block schedule environment.
478  * @param irn The node to check for.
479  * @return 1 if the node was ready, 0 if not.
480  */
481 #define is_ready(env,irn) \
482   (nodeset_find((env)->ready_set, irn) != NULL)
483
484 /**
485  * Check, if a node has already been schedules.
486  * @param env The block schedule environment.
487  * @param irn The node to check for.
488  * @return 1 if the node was already scheduled, 0 if not.
489  */
490 #define is_scheduled(env,irn) \
491   (nodeset_find((env)->already_scheduled, irn) != NULL)
492
493 /**
494  * Try, to make all users of a node ready.
495  * In fact, a usage node can only be made ready, if all its operands
496  * have already been scheduled yet. This is checked my make_ready().
497  * @param env The block schedule environment.
498  * @param irn The node, which usages (successors) are to be made ready.
499  */
500 static INLINE void make_users_ready(block_sched_env_t *env, ir_node *irn)
501 {
502         const ir_edge_t *edge;
503
504         foreach_out_edge(irn, edge) {
505                 ir_node *user = edge->src;
506                 if(!is_Phi(user))
507                         make_ready(env, user);
508         }
509 }
510
511 /**
512  * Compare to nodes using pointer equality.
513  * @param p1 Node one.
514  * @param p2 Node two.
515  * @return 0 if they are identical.
516  */
517 static int node_cmp_func(const void *p1, const void *p2)
518 {
519     return p1 != p2;
520 }
521
522 /**
523  * Append an instruction to a schedule.
524  * @param env The block scheduling environment.
525  * @param irn The node to add to the schedule.
526  * @return    The given node.
527  */
528 static ir_node *add_to_sched(block_sched_env_t *env, ir_node *irn)
529 {
530     /* If the node consumes/produces data, it is appended to the schedule
531      * list, otherwise, it is not put into the list */
532     if(must_appear_in_schedule(env->selector, env->selector_block_env, irn)) {
533         sched_info_t *info = get_irn_sched_info(irn);
534         INIT_LIST_HEAD(&info->list);
535         info->scheduled = 1;
536         sched_add_before(env->block, irn);
537
538         DBG((env->dbg, LEVEL_2, "\tadding %+F\n", irn));
539     }
540
541     /* Insert the node in the set of all already scheduled nodes. */
542     nodeset_insert(env->already_scheduled, irn);
543
544     /* Remove the node from the ready set */
545     if(nodeset_find(env->ready_set, irn))
546         nodeset_remove(env->ready_set, irn);
547
548     return irn;
549 }
550
551 /**
552  * Add the proj nodes of a tuple-mode irn to the schedule immediately
553  * after the tuple-moded irn. By pinning the projs after the irn, no
554  * other nodes can create a new lifetime between the tuple-moded irn and
555  * one of its projs. This should render a realistic image of a
556  * tuple-moded irn, which in fact models a node which defines multiple
557  * values.
558  *
559  * @param irn The tuple-moded irn.
560  * @param list The schedule list to append all the projs.
561  * @param time The time step to which the irn and all its projs are
562  * related to.
563  * @param obst The obstack the scheduling data structures shall be
564  * created upon.
565  * @param ready_set The ready set of the list scheduler.
566  * @param already_scheduled A set containing all nodes already
567  * scheduled.
568  */
569 static void add_tuple_projs(block_sched_env_t *env, ir_node *irn)
570 {
571         const ir_edge_t *edge;
572
573         assert(get_irn_mode(irn) == mode_T && "Mode of node must be tuple");
574
575         foreach_out_edge(irn, edge) {
576                 ir_node *out = edge->src;
577
578                 assert(is_Proj(out) && "successor of a modeT node must be a proj");
579
580                 if(get_irn_mode(out) == mode_T)
581                         add_tuple_projs(env, out);
582                 else {
583                         add_to_sched(env, out);
584                         make_users_ready(env, out);
585                 }
586         }
587 }
588
589 static ir_node *select_node(block_sched_env_t *be)
590 {
591         ir_node *irn;
592
593         for (irn = nodeset_first(be->ready_set); irn; irn = nodeset_next(be->ready_set)) {
594                 if (be_is_Keep(irn)) {
595                         nodeset_break(be->ready_set);
596                         return irn;
597                 }
598         }
599
600         return be->selector->select(be->selector_block_env, be->ready_set);
601 }
602
603 /**
604  * Perform list scheduling on a block.
605  *
606  * Note, that the caller must compute a linked list of nodes in the block
607  * using the link field before calling this function.
608  *
609  * Also the outs must have been computed.
610  *
611  * @param block The block node.
612  * @param env Scheduling environment.
613  */
614 static void list_sched_block(ir_node *block, void *env_ptr)
615 {
616         sched_env_t *env                      = env_ptr;
617         const list_sched_selector_t *selector = env->selector;
618         ir_node *start_node                   = get_irg_start(get_irn_irg(block));
619         int phi_seen                          = 0;
620         sched_info_t *info                    = get_irn_sched_info(block);
621
622         block_sched_env_t be;
623         const ir_edge_t *edge;
624         ir_node *irn;
625         int j, m;
626
627         /* Initialize the block's list head that will hold the schedule. */
628         INIT_LIST_HEAD(&info->list);
629
630         /* Initialize the block scheduling environment */
631         be.block             = block;
632         be.curr_time         = 0;
633         be.ready_set         = new_nodeset(get_irn_n_edges(block));
634         be.already_scheduled = new_nodeset(get_irn_n_edges(block));
635         be.selector          = selector;
636         FIRM_DBG_REGISTER(be.dbg, "firm.be.sched");
637
638         if(selector->init_block)
639                 be.selector_block_env = selector->init_block(env->selector_env, block);
640
641         DBG((be.dbg, LEVEL_1, "scheduling %+F\n", block));
642
643         /* Then one can add all nodes are ready to the set. */
644         foreach_out_edge(block, edge) {
645                 ir_node *irn = get_edge_src_irn(edge);
646
647                 /* Skip the end node because of keepalive edges. */
648                 if(get_irn_opcode(irn) == iro_End)
649                         continue;
650
651                 /* Phi functions are scheduled immediately, since they only transfer
652                  * data flow from the predecessors to this block. */
653                 if(is_Phi(irn)) {
654                         add_to_sched(&be, irn);
655                         make_users_ready(&be, irn);
656                         phi_seen = 1;
657                 }
658
659                 /* The start block will be scheduled as the first node */
660                 else if(irn == start_node) {
661                         add_to_sched(&be, irn);
662                         add_tuple_projs(&be, irn);
663                 }
664
665
666                 /* Other nodes must have all operands in other blocks to be made
667                  * ready */
668                 else {
669                         int ready = 1;
670
671                         /* Check, if the operands of a node are not local to this block */
672                         for(j = 0, m = get_irn_arity(irn); j < m; ++j) {
673                                 ir_node *operand = get_irn_n(irn, j);
674
675                                 if(get_nodes_block(operand) == block) {
676                                         ready = 0;
677                                         break;
678                                 }
679                         }
680
681                         /* Make the node ready, if all operands live in a foreign block */
682                         if(ready) {
683                                 DBG((be.dbg, LEVEL_2, "\timmediately ready: %+F\n", irn));
684                                 make_ready(&be, irn);
685                         }
686                 }
687         }
688
689         /* Increase the time, if some phi functions have been scheduled */
690         be.curr_time += phi_seen;
691
692         while (nodeset_count(be.ready_set) > 0) {
693                 /* collect statitics about amount of ready nodes */
694                 be_do_stat_sched_ready(block, be.ready_set);
695
696                 /* select a node to be scheduled and check if it was ready */
697                 irn = select_node(&be);
698
699                 DBG((be.dbg, LEVEL_3, "\tpicked node %+F\n", irn));
700
701                 /* Add the node to the schedule. */
702                 add_to_sched(&be, irn);
703
704                 if(get_irn_mode(irn) == mode_T)
705                         add_tuple_projs(&be, irn);
706                 else
707                         make_users_ready(&be, irn);
708
709                 /* Increase the time step. */
710                 be.curr_time += 1;
711
712                 /* remove the scheduled node from the ready list. */
713                 if (nodeset_find(be.ready_set, irn))
714                         nodeset_remove(be.ready_set, irn);
715         }
716
717         if(selector->finish_block)
718                 selector->finish_block(be.selector_block_env);
719
720         del_nodeset(be.ready_set);
721         del_nodeset(be.already_scheduled);
722 }