Add a scheduler based on the strong normal form theorem. It seems to deliver good...
authorChristoph Mallon <christoph.mallon@gmx.de>
Sat, 18 Aug 2007 06:49:41 +0000 (06:49 +0000)
committerChristoph Mallon <christoph.mallon@gmx.de>
Sat, 18 Aug 2007 06:49:41 +0000 (06:49 +0000)
Caution: The implemententation is a cruel hack so far. Contents may be hot. Slippery when wet.

[r15564]

ir/be/belistsched.c
ir/be/belistsched.h
ir/be/beschednormal.c [new file with mode: 0644]

index 6bdb442..c378e82 100644 (file)
@@ -69,12 +69,13 @@ DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL);
 #define BE_SCHED_NODE(irn) (be_is_Keep(irn) || be_is_CopyKeep(irn) || be_is_RegParams(irn))
 
 enum {
-       BE_SCHED_SELECT_TRIVIAL  = 0,
-       BE_SCHED_SELECT_REGPRESS = 1,
-       BE_SCHED_SELECT_MUCHNIK  = 2,
-       BE_SCHED_SELECT_HEUR     = 3,
-       BE_SCHED_SELECT_HMUCHNIK = 4,
-       BE_SCHED_SELECT_RANDOM   = 5
+       BE_SCHED_SELECT_TRIVIAL,
+       BE_SCHED_SELECT_REGPRESS,
+       BE_SCHED_SELECT_MUCHNIK,
+       BE_SCHED_SELECT_HEUR,
+       BE_SCHED_SELECT_HMUCHNIK,
+       BE_SCHED_SELECT_RANDOM,
+       BE_SCHED_SELECT_NORMAL,
 };
 
 enum {
@@ -96,8 +97,9 @@ static list_sched_options_t list_sched_options = {
 /* schedule selector options. */
 static const lc_opt_enum_int_items_t sched_select_items[] = {
        { "trivial",  BE_SCHED_SELECT_TRIVIAL  },
-       { "random",   BE_SCHED_SELECT_RANDOM },
+       { "random",   BE_SCHED_SELECT_RANDOM   },
        { "regpress", BE_SCHED_SELECT_REGPRESS },
+       { "normal",   BE_SCHED_SELECT_NORMAL   },
        { "muchnik",  BE_SCHED_SELECT_MUCHNIK  },
        { "heur",     BE_SCHED_SELECT_HEUR     },
        { "hmuchnik", BE_SCHED_SELECT_HMUCHNIK },
@@ -548,24 +550,14 @@ void list_sched(be_irg_t *birg, be_options_t *be_opts)
 
        /* Select a scheduler based on backend options */
        switch (list_sched_options.select) {
-               case BE_SCHED_SELECT_TRIVIAL:
-                       memcpy(&sel, trivial_selector, sizeof(sel));
-                       break;
-               case BE_SCHED_SELECT_RANDOM:
-                       memcpy(&sel, random_selector, sizeof(sel));
-                       break;
-               case BE_SCHED_SELECT_REGPRESS:
-                       memcpy(&sel, reg_pressure_selector, sizeof(sel));
-                       break;
-               case BE_SCHED_SELECT_MUCHNIK:
-                       memcpy(&sel, muchnik_selector, sizeof(sel));
-                       break;
-               case BE_SCHED_SELECT_HEUR:
-                       memcpy(&sel, heuristic_selector, sizeof(sel));
-                       break;
-               case BE_SCHED_SELECT_HMUCHNIK:
+               case BE_SCHED_SELECT_TRIVIAL:  sel = *trivial_selector;      break;
+               case BE_SCHED_SELECT_RANDOM:   sel = *random_selector;       break;
+               case BE_SCHED_SELECT_REGPRESS: sel = *reg_pressure_selector; break;
+               case BE_SCHED_SELECT_MUCHNIK:  sel = *muchnik_selector;      break;
+               case BE_SCHED_SELECT_HEUR:     sel = *heuristic_selector;    break;
+               case BE_SCHED_SELECT_NORMAL:   sel = *normal_selector;       break;
                default:
-                       memcpy(&sel, trivial_selector, sizeof(sel));
+               case BE_SCHED_SELECT_HMUCHNIK: sel = *trivial_selector;      break;
        }
 
 #if 1
@@ -631,24 +623,14 @@ void list_sched_single_block(const be_irg_t *birg, ir_node *block,
 
        /* Select a scheduler based on backend options */
        switch (list_sched_options.select) {
-               case BE_SCHED_SELECT_TRIVIAL:
-                       memcpy(&sel, trivial_selector, sizeof(sel));
-                       break;
-               case BE_SCHED_SELECT_RANDOM:
-                       memcpy(&sel, random_selector, sizeof(sel));
-                       break;
-               case BE_SCHED_SELECT_REGPRESS:
-                       memcpy(&sel, reg_pressure_selector, sizeof(sel));
-                       break;
-               case BE_SCHED_SELECT_MUCHNIK:
-                       memcpy(&sel, muchnik_selector, sizeof(sel));
-                       break;
-               case BE_SCHED_SELECT_HEUR:
-                       memcpy(&sel, heuristic_selector, sizeof(sel));
-                       break;
-               case BE_SCHED_SELECT_HMUCHNIK:
+               case BE_SCHED_SELECT_TRIVIAL:  sel = *trivial_selector;      break;
+               case BE_SCHED_SELECT_RANDOM:   sel = *random_selector;       break;
+               case BE_SCHED_SELECT_REGPRESS: sel = *reg_pressure_selector; break;
+               case BE_SCHED_SELECT_MUCHNIK:  sel = *muchnik_selector;      break;
+               case BE_SCHED_SELECT_HEUR:     sel = *heuristic_selector;    break;
+               case BE_SCHED_SELECT_NORMAL:   sel = *normal_selector;       break;
                default:
-                       memcpy(&sel, trivial_selector, sizeof(sel));
+               case BE_SCHED_SELECT_HMUCHNIK: sel = *trivial_selector;      break;
        }
 
        /* Assure, that the out edges are computed */
index 16c4eee..454e22f 100644 (file)
@@ -153,6 +153,11 @@ extern const list_sched_selector_t *muchnik_selector;
  */
 extern const list_sched_selector_t *heuristic_selector;
 
+/**
+ * A selector based on the strng normal form theorem
+ */
+extern const list_sched_selector_t *normal_selector;
+
 /**
  * List schedule a graph.
  * Each block in the graph gets a list head to its link field being the
diff --git a/ir/be/beschednormal.c b/ir/be/beschednormal.c
new file mode 100644 (file)
index 0000000..74cffb9
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 1995-2007 University of Karlsruhe.  All right reserved.
+ *
+ * This file is part of libFirm.
+ *
+ * This file may be distributed and/or modified under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation and appearing in the file LICENSE.GPL included in the
+ * packaging of this file.
+ *
+ * Licensees holding valid libFirm Professional Edition licenses may use
+ * this file in accordance with the libFirm Commercial License.
+ * Agreement provided with the Software.
+ *
+ * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+ * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE.
+ */
+
+/**
+ * @brief   Use the strong normal form theorem (though it does not hold)
+ * @author  Christoph Mallon
+ * @version $Id: beschedrand.c 14604 2007-06-18 14:07:07Z matze $
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+
+#include "besched_t.h"
+#include "belistsched.h"
+#include "belive_t.h"
+#include "beutil.h"
+#include "irtools.h"
+#include "irgwalk.h"
+
+
+// XXX there is no one time init for schedulers
+//#define NORMAL_DBG
+
+
+static const arch_env_t *cur_arch_env;
+
+
+static ir_node *normal_select(void *block_env, ir_nodeset_t *ready_set,
+                              ir_nodeset_t *live_set)
+{
+       ir_nodeset_iterator_t iter;
+       ir_node*  block;
+       ir_node*  irn;
+       ir_node** sched;
+       int sched_count;
+
+       (void)block_env;
+       (void)live_set;
+
+       ir_nodeset_iterator_init(&iter, ready_set);
+       irn = ir_nodeset_iterator_next(&iter);
+       block = get_nodes_block(irn);
+       sched = get_irn_link(block);
+       sched_count = ARR_LEN(sched);
+       for (; sched_count-- != 0; ++sched) {
+               ir_node* irn = *sched;
+               if (ir_nodeset_contains(ready_set, irn) &&
+                               !arch_irn_class_is(cur_arch_env, irn, branch)) {
+#if defined NORMAL_DBG
+                       ir_fprintf(stderr, "scheduling %+F\n", irn);
+#endif
+                       return irn;
+               }
+       }
+
+       return irn;
+}
+
+
+typedef struct irn_cost_pair {
+       ir_node* irn;
+       int      cost;
+} irn_cost_pair;
+
+
+static int cost_cmp(const void* a, const void* b)
+{
+       const irn_cost_pair* a1 = a;
+       const irn_cost_pair* b1 = b;
+       return b1->cost - a1->cost;
+}
+
+
+typedef struct flag_and_cost {
+       int no_root;
+       irn_cost_pair costs[];
+} flag_and_cost;
+
+
+static int count_result(const ir_node* irn)
+{
+       const ir_mode* mode = get_irn_mode(irn);
+       return
+               mode != mode_M &&
+               mode != mode_X &&
+               !arch_irn_is(cur_arch_env, irn, ignore);
+}
+
+
+static int normal_tree_cost(ir_node* irn)
+{
+       flag_and_cost* fc    = get_irn_link(irn);
+       ir_node*       block = get_nodes_block(irn);
+       int            arity = get_irn_arity(irn);
+       int            cost_max  = 0;
+       int            count_max = 0;
+       int            n_res;
+       int            cost;
+
+       if (fc == NULL) {
+               irn_cost_pair* costs;
+               int            i;
+
+               fc = malloc(sizeof(*fc) + sizeof(*fc->costs) * arity);
+               fc->no_root = 0;
+               costs = fc->costs;
+
+               for (i = 0; i < arity; ++i) {
+                       ir_node* pred = get_irn_n(irn, i);
+                       int cost;
+
+                       if (is_Phi(irn) || get_irn_mode(pred) == mode_M) {
+                               cost = 0;
+                       } else if (get_nodes_block(pred) != block) {
+                               cost = 1;
+                       } else {
+                               cost = normal_tree_cost(pred);
+                               flag_and_cost* pred_fc = get_irn_link(pred);
+                               pred_fc->no_root = 1;
+#if defined NORMAL_DBG
+                               ir_fprintf(stderr, "%+F says that %+F is no root\n", irn, pred);
+#endif
+                       }
+
+                       costs[i].irn  = pred;
+                       costs[i].cost = cost;
+
+                       if (cost > cost_max) {
+                               cost_max  = cost;
+                               count_max = 1;
+                       } else if (cost == cost_max) {
+                               ++count_max;
+                       }
+               }
+
+               qsort(costs, arity, sizeof(*costs), cost_cmp);
+               set_irn_link(irn, fc);
+       } else {
+               irn_cost_pair* costs = fc->costs;
+               int            i;
+
+               if (arity > 0) {
+                       cost_max = costs[0].cost;
+
+                       for (i = 0; i < arity; ++i) {
+                               if (costs[i].cost < cost_max) break;
+                               ++count_max;
+                       }
+               }
+       }
+
+       n_res = count_result(irn);
+       if (cost_max == 0) {
+               cost = n_res;
+       } else {
+               cost = MAX(n_res, cost_max + count_max - 1);
+       }
+
+#if defined NORMAL_DBG
+       ir_fprintf(stderr, "reguse of %+F is %d\n", irn, cost);
+#endif
+
+       return cost;
+}
+
+
+static void normal_tree_sched(ir_node* irn)
+{
+       irn_cost_pair* irns  = get_irn_link(irn);
+       int            arity = get_irn_arity(irn);
+       int            i;
+
+       if (irns == NULL) return;
+
+       for (i = 0; i < arity; ++i) {
+               normal_tree_sched(irns[i].irn);
+       }
+
+       if (1) { // TODO check if node needs to be scheduled
+               ir_node*  block = get_nodes_block(irn);
+               ir_node** sched = get_irn_link(block);
+
+#if defined NORMAL_DBG
+               ir_fprintf(stderr, "scheduling %+F in array %p\n", irn, sched);
+#endif
+
+               if (sched == NULL) {
+                       sched = NEW_ARR_F(ir_node*, 0);
+               }
+               ARR_APP1(ir_node*, sched, irn);
+               set_irn_link(block, sched);
+       }
+
+       free(irns);
+       set_irn_link(irn, NULL);
+}
+
+
+static void normal_cost_walker(ir_node* irn, void* env)
+{
+       (void)env;
+
+#if defined NORMAL_DBG
+       ir_fprintf(stderr, "cost walking node %+F\n", irn);
+#endif
+       if (is_Block(irn)) return;
+       normal_tree_cost(irn);
+}
+
+
+static void collect_roots(ir_node* irn, void* env)
+{
+       flag_and_cost* fc;
+
+       (void)env;
+
+       if (is_Block(irn)) return;
+
+       fc = get_irn_link(irn);
+
+#if defined NORMAL_DBG
+       ir_fprintf(stderr, "%+F is %sroot\n", irn, fc->no_root ? "no " : "");
+#endif
+
+       if (!fc->no_root) {
+               ir_node* block = get_nodes_block(irn);
+               ir_node** roots = get_irn_link(block);
+               if (roots == NULL) {
+                       roots = NEW_ARR_F(ir_node*, 0);
+               }
+               ARR_APP1(ir_node*, roots, irn);
+               set_irn_link(block, roots);
+       }
+}
+
+
+static ir_node** sched_node(ir_node** sched, ir_node* irn)
+{
+       ir_node* block = get_nodes_block(irn);
+       int arity = get_irn_arity(irn);
+       int i;
+
+       if (!is_Phi(irn)) {
+               for (i = 0; i < arity; ++i) {
+                       ir_node* pred = get_irn_n(irn, i);
+                       if (get_nodes_block(pred) != block) continue;
+                       sched = sched_node(sched, pred);
+               }
+       }
+
+       ARR_APP1(ir_node*, sched, irn);
+       return sched;
+}
+
+
+static void normal_sched_block(ir_node* block, void* env)
+{
+       ir_node** roots = get_irn_link(block);
+       int            root_count;
+       irn_cost_pair* root_costs;
+       int i;
+       ir_node**      sched;
+
+       (void)env;
+
+#if defined NORMAL_DBG
+       ir_fprintf(stderr, "sched walking block %+F\n", block);
+#endif
+
+       if (roots == NULL) {
+#if defined NORMAL_DBG
+               fprintf(stderr, "has no roots\n");
+#endif
+               return;
+       }
+
+       root_count = ARR_LEN(roots);
+       NEW_ARR_A(irn_cost_pair, root_costs, root_count);
+       for (i = 0; i < root_count; ++i) {
+               root_costs[i].irn  = roots[i];
+               root_costs[i].cost = normal_tree_cost(roots[i]);
+       }
+       qsort(root_costs, root_count, sizeof(*root_costs), cost_cmp);
+
+       sched = NEW_ARR_F(ir_node*, 0);
+       for (i = 0; i < root_count; ++i) {
+               ir_node* irn = root_costs[i].irn;
+               sched = sched_node(sched, irn);
+       }
+       set_irn_link(block, sched);
+       DEL_ARR_F(roots);
+
+#if defined NORMAL_DBG
+       {
+               int n = ARR_LEN(sched);
+               int i;
+
+               ir_fprintf(stderr, "Scheduling of %+F:\n", block);
+               for (i = 0; i < n; ++i) {
+                       int j;
+                       for (j = 0; j < i; ++j) {
+                               if (sched[i] == sched[j]) goto skip;
+                       }
+                       ir_fprintf(stderr, "  %+F\n", sched[i]);
+skip:;
+               }
+               fprintf(stderr, "\n");
+       }
+#endif
+}
+
+
+static void *normal_init_graph(const list_sched_selector_t *vtab,
+                               const be_irg_t *birg)
+{
+       ir_graph* irg = be_get_birg_irg(birg);
+
+       (void)vtab;
+
+       cur_arch_env = be_get_birg_arch_env(birg);
+
+       be_clear_links(irg);
+
+       irg_walk_graph(irg, normal_cost_walker,  NULL, NULL);
+       irg_walk_graph(irg, collect_roots, NULL, NULL);
+       irg_block_walk_graph(irg, normal_sched_block, NULL, NULL);
+
+       return NULL;
+}
+
+
+static void *normal_init_block(void *graph_env, ir_node *block)
+{
+       (void)graph_env;
+       (void)block;
+
+       return NULL;
+}
+
+
+static const list_sched_selector_t normal_selector_struct = {
+       normal_init_graph,
+       normal_init_block,
+       normal_select,
+       NULL,              /* to_appear_in_schedule */
+       NULL,              /* node_ready */
+       NULL,              /* node_selected */
+       NULL,              /* exectime */
+       NULL,              /* latency */
+       NULL,              /* finish_block */
+       NULL               /* finish_graph */
+};
+
+const list_sched_selector_t *normal_selector = &normal_selector_struct;