pleliminary version of VanDrunen's GVN_PRE for Firm
authorMichael Beck <beck@ipd.info.uni-karlsruhe.de>
Thu, 12 Jan 2006 13:25:38 +0000 (13:25 +0000)
committerMichael Beck <beck@ipd.info.uni-karlsruhe.de>
Thu, 12 Jan 2006 13:25:38 +0000 (13:25 +0000)
[r7220]

ir/opt/gvn_pre.c [new file with mode: 0644]

diff --git a/ir/opt/gvn_pre.c b/ir/opt/gvn_pre.c
new file mode 100644 (file)
index 0000000..27481a9
--- /dev/null
@@ -0,0 +1,458 @@
+/*
+ * Project:     libFIRM
+ * File name:   ir/opt/gvn_pre.c
+ * Purpose:     Global Value Numbering Partial Redundancy Elimination
+ *              (VanDrunen Hosking 2004)
+ * Author:      Michael Beck, Rubino Geiss
+ * Created:
+ * CVS-ID:      $Id$
+ * Copyright:   (c) 1998-2006 Universität Karlsruhe
+ * Licence:     This file protected by GPL -  GNU GENERAL PUBLIC LICENSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <assert.h>
+
+#include "irgraph_t.h"
+#include "irgwalk.h"
+#include "irdom.h"
+#include "irouts.h"
+#include "pset.h"
+#include "irgopt.h"
+#include "iropt_t.h"
+#include "irprintf.h"
+#include "irnode_t.h"
+
+/* */
+typedef struct block_info {
+  pset *nodes;        /**< the set of nodes per block */
+  pset *avail_out;    /**< the Avail_out set for a block */
+  pset *antic_in;     /**< the Antic_in set for a block */
+  struct block_info *next;
+} block_info;
+
+typedef struct avail_env {
+  struct obstack *obst;   /**< the obstack to allocate on */
+  ir_node *start_block;
+  ir_node *end_block;
+  block_info *list;       /**< links all block info entires for easier recovery */
+  int changes;            /**< non-zero, if calculation of Antic_in has changed */
+} avail_env;
+
+/**
+ * returns non-zero if a node is movable.
+ */
+static int is_nice_value(ir_node *n) {
+  ir_mode *mode = get_irn_mode(n);
+
+  if (mode == mode_M || mode == mode_X)
+    return 0;
+  return (get_irn_pinned(n) != op_pin_state_pinned);
+}
+
+/** computes dst = dst \/ src */
+static void pset_union(pset *dst, pset *src, unsigned (*hash)(void *))
+{
+  void *entry;
+
+  for (entry = pset_first(src); entry; entry = pset_next(src)) {
+    pset_insert(dst, entry, ir_node_hash(entry));
+  }
+}
+
+/**
+ * computes Avail_out(block):
+ *
+ * Avail_in(block)  = Avail_out(dom(block))
+ * Avail_out(block) = Avail_in(block) \/ Nodes(block)
+ *
+ * Precondition:
+ *  This function must be called in the top-down dominance order:
+ *  Then, it computes Leader(Nodes(block)) instead of Nodes(block) !
+ */
+static void compute_avail_top_down(ir_node *block, void *ctx)
+{
+  avail_env *env = ctx;
+  block_info *dom_info;
+  block_info *info = get_irn_link(block);
+  ir_node *dom_blk;
+  int i;
+
+  /* the root has no dominator */
+  if (block != env->start_block) {
+    dom_blk = get_Block_idom(block);
+    assert(is_Block(dom_blk));
+
+    dom_info = get_irn_link(dom_blk);
+    assert(dom_info);
+
+    pset_union(info->avail_out, dom_info->avail_out, ir_node_hash);
+    pset_union(info->avail_out, info->nodes, ir_node_hash);
+  }
+#ifdef _DEBUG
+  {
+    ir_node *n;
+
+    ir_printf("Avail_out(%+F) = {\n", block);
+    for (i = 0, n = pset_first(info->avail_out); n; ++i, n = pset_next(info->avail_out)) {
+      if ((i & 3) == 3)
+        printf("\n");
+      ir_printf(" %+F,", n);
+    }
+    printf("\n}\n");
+  }
+#endif
+}
+
+/*
+ * Implement phi_translate
+ */
+static ir_node *phi_translate(ir_node *node, ir_node *block, int pos, avail_env *env)
+{
+  ir_node *pred_block;
+  ir_node *res;
+  int i, arity = get_irn_intra_arity(node);
+  struct obstack *old;
+
+  if (is_Phi(node)) {
+    if (get_nodes_block(node) == block)
+      return get_Phi_pred(node, pos);
+    return node;
+  }
+
+  /* check if the node has at least one Phi predecessor */
+  for (i = 0; i < arity; ++i) {
+    ir_node *phi = get_irn_intra_n(node, i);
+    if (is_Phi(phi) && get_nodes_block(phi) == block)
+      break;
+  }
+  if (i >= arity) {
+    /* no Phi in the predecessors */
+    return node;
+  }
+
+  pred_block = get_Block_cfgpred_block(block, pos);
+
+  /* Create a copy of the node in the pos'th predecessor block.
+     Use our environmental obstack, as these nodes are always
+     temporary. */
+  old = current_ir_graph->obst;
+  current_ir_graph->obst = env->obst;
+  res   = new_ir_node(
+            get_irn_dbg_info(node),
+            current_ir_graph,
+            pred_block,
+            get_irn_op(node),
+            get_irn_mode(node),
+            arity,
+            get_irn_in(node));
+  /* We need the attribute copy here, because the Hash value of a
+     node might depend on that. */
+  copy_node_attr(node, res);
+  current_ir_graph->obst = old;
+
+  for (i = -1; i < arity; ++i) {
+    ir_node *pred = get_irn_intra_n(node, i);
+
+    if (! is_Phi(pred))
+      set_irn_n(res, i, pred);
+    else
+      set_irn_n(res, i, get_Phi_pred(pred, pos));
+  }
+  set_irn_link(res, node);
+  return res;
+}
+
+/**
+ * Retranslate a Phi-translated node back
+ */
+static ir_node *phi_retrans(ir_node *n, avail_env *env)
+{
+  if (node_is_in_irgs_storage(current_ir_graph, n))
+    return n;
+  return get_irn_link(n);
+}
+
+/**
+ * computes Antic_in(block):
+ *
+ */
+static void compute_antic(ir_node *block, void *ctx)
+{
+  avail_env *env = ctx;
+  block_info *succ_info;
+  block_info *info = get_irn_link(block);
+  ir_node *succ;
+  int i, size;
+
+  size = pset_count(info->antic_in);
+
+  /* the root has no dominator */
+  if (block != env->end_block) {
+    int n_succ = get_Block_n_cfg_outs(block);
+
+    if (n_succ == 1) {
+      ir_node *node;
+      int i, pos = -1;
+      pset *nodes = new_pset(identities_cmp, 8);
+
+      pset_union(nodes, info->nodes, ir_node_hash);
+
+      /* find blocks position in succ's block predecessors */
+      succ = get_Block_cfg_out(block, 0);
+      for (i = get_Block_n_cfgpreds(succ) - 1; i >= 0; --i) {
+        if (get_Block_cfgpred_block(succ, i) == block) {
+          pos = i;
+          break;
+        }
+      }
+      assert(pos >= 0);
+
+      succ_info = get_irn_link(succ);
+      for (node = pset_first(succ_info->antic_in);
+           node;
+           node = pset_next(succ_info->antic_in)) {
+        ir_node *trans = phi_translate(node, succ, pos, env);
+
+        identify_remember(nodes, trans);
+
+        /* add all predecessors of node */
+        for (i = get_irn_arity(node) - 1; i >= 0; --i) {
+          ir_node *pred = get_irn_n(node, i);
+          ir_node *trans = phi_translate(pred, succ, pos, env);
+
+          if (is_nice_value(trans))
+            identify_remember(nodes, trans);
+        }
+      }
+     /* this step calculates Antic_in(b) = Antic_out(b) \/ Nodes(b) */
+     pset_union(info->antic_in, nodes, ir_node_hash);
+     del_pset(nodes);
+   }
+    else {
+      ir_node *n, *succ0;
+      block_info *succ0_info;
+      int i;
+
+      assert(n_succ > 1);
+
+      /* Select a successor to compute the disjoint of all Nodes
+         sets, it might be useful to select the block with the
+         smallest number of nodes.  For simplicity we choose the
+         first one. */
+      succ0 = get_Block_cfg_out(block, 0);
+      succ0_info = get_irn_link(succ0);
+      for (n = pset_first(succ0_info->antic_in);
+           n;
+           n = pset_next(succ0_info->antic_in)) {
+        /* we need the disjoint */
+        for (i = 1; i < n_succ; ++i) {
+          ir_node *succ = get_Block_cfg_out(block, i);
+          block_info *succ_info = get_irn_link(succ);
+          if (pset_find(succ_info->antic_in, n, ir_node_hash(n)) == NULL)
+            break;
+        }
+        if (i >= n_succ) {
+          /* we found a node that is common in all Antic_in(succ(b)),
+             put it in Antic_in(b) */
+          identify_remember(info->antic_in, n);
+        }
+      }
+      /* this step calculates Antic_in(b) = Antic_out(b) \/ Nodes(b) */
+      pset_union(info->antic_in, info->nodes, ir_node_hash);
+    }
+  }
+
+  if (size != pset_count(info->antic_in))
+    /* the Antic_in set has changed */
+    env->changes |= 1;
+
+#ifdef _DEBUG
+  {
+    ir_node *n;
+
+    ir_printf("Antic_in(%+F) = {\n", block);
+    for (i = 0, n = pset_first(info->antic_in); n; ++i, n = pset_next(info->antic_in)) {
+      ir_node *orig = phi_retrans(n, env);
+      if ((i & 3) == 3)
+        printf("\n");
+      ir_printf(" %+F%", n);
+      if (orig != n)
+        ir_printf("{%+F}", orig);
+      printf(", ");
+    }
+    printf("\n}\n");
+  }
+#endif
+}
+
+/**
+ * allocate a block info
+ */
+static void alloc_blk_info(ir_node *block, void *ctx)
+{
+  int i;
+  avail_env *env = ctx;
+  block_info *info = obstack_alloc(env->obst, sizeof(block_info));
+
+  set_irn_link(block, info);
+  info->nodes     = new_pset(identities_cmp, 8);
+  info->antic_in  = new_pset(identities_cmp, 8);
+  info->avail_out = new_pset(identities_cmp, 8);
+  info->next      = env->list;
+  env->list       = info->next;
+
+  /* fill the nodes set, we will need it later */
+  for (i = get_irn_n_outs(block) - 1; i >= 0; --i) {
+    ir_node *n = get_irn_out(block, i);
+
+    /* we cannot optimize pinned nodes, so do not remember them */
+    if (is_nice_value(n))
+      identify_remember(info->nodes, n);
+    else if (is_Phi(n) && get_irn_mode(n) != mode_M) {
+      /*
+       * Phis are "temporaries" and must be handled special:
+       * They are avail, but are not in Antic_in
+       */
+      identify_remember(info->avail_out, n);
+    }
+  }
+}
+
+/**
+ * Insert the nodes.
+ */
+static void insert_nodes(ir_node *block, void *ctx)
+{
+  avail_env *env = ctx;
+  ir_node *v, *idom, *first_s;
+  block_info *info, *idom_info;
+  int pos, arity = get_irn_intra_arity(block);
+  int all_same, by_some;
+
+  if (arity <= 1)
+    return;
+
+  info = get_irn_link(block);
+
+  idom = get_Block_idom(block);
+  idom_info = get_irn_link(idom);
+  for (v = pset_first(info->antic_in);
+       v;
+       v = pset_next(info->antic_in)) {
+    /* If the value was already computed in the dominator, then
+       it is totally redundant.  Hence we have nothing to insert. */
+    if (pset_find(idom_info->avail_out, v, ir_node_hash(v))) {
+//      ir_printf("Found %+F from block %+F avail in dom %+F\n", v, block, idom);
+      continue;
+    }
+
+    all_same = 1;
+    by_some  = 0;
+    first_s  = NULL;
+
+    for (pos = 0; pos < arity; ++pos) {
+      block_info *pred_info;
+      ir_node *pred = get_Block_cfgpred_block(block, pos);
+      ir_node *trans, *found;
+
+      if (is_Bad(pred))
+        continue;
+
+      trans = phi_translate(v, block, pos, env);
+
+      pred_info = get_irn_link(pred);
+      found = pset_find(pred_info->avail_out, trans, ir_node_hash(trans));
+
+      if (found == NULL) {
+        all_same = 0;
+      }
+      else {
+        by_some = 1;
+        if (first_s == NULL)
+          first_s = found;
+        else if (first_s != found)
+          all_same = 0;
+
+        ir_printf("Found %+F from block %+F as %+F in pred %+F\n", v, block, found, pred);
+      }
+    }
+
+    if (! all_same && by_some) {
+      ir_printf("Partial redundant %+F from block %+F found\n", v, block);
+    }
+  }
+}
+
+void do_gvn_pre(ir_graph *irg)
+{
+  struct obstack obst;
+  avail_env a_env;
+  optimization_state_t state;
+  block_info *p;
+  int iter = 0;
+
+  obstack_init(&obst);
+  a_env.obst        = &obst;
+  a_env.list        = NULL;
+  a_env.start_block = get_irg_start_block(irg);
+  a_env.end_block   = get_irg_end_block(irg);
+
+  remove_critical_cf_edges(irg);
+
+  /* we need dominator AND post dominator information */
+  if (get_irg_dom_state(irg) != dom_consistent)
+    compute_doms(irg);
+  if (get_irg_postdom_state(irg) != dom_consistent)
+    compute_postdoms(irg);
+  if (get_irg_outs_state(irg) != outs_consistent)
+    compute_irg_outs(irg);
+
+  save_optimization_state(&state);
+  set_opt_global_cse(1);
+
+  /* allocate block info for all blocks */
+  irg_block_walk_graph(irg, NULL, alloc_blk_info, &a_env);
+
+  /* compute the available value sets for all blocks */
+  dom_tree_walk_irg(irg, compute_avail_top_down, NULL, &a_env);
+
+  /* compute the anticipated value sets for all blocks */
+  do {
+#ifdef _DEBUG
+    printf("Antic_in Iteration %d starts ...\n", ++iter);
+#endif /* _DEBUG */
+    a_env.changes = 0;
+    irg_block_walk_graph(irg, compute_antic, NULL, &a_env);
+//    postdom_tree_walk_irg(irg, compute_antic, NULL, &a_env);
+#ifdef _DEBUG
+    printf("------------------------\n");
+#endif /* _DEBUG */
+  } while (a_env.changes != 0);
+
+  iter = 0;
+  do {
+#ifdef _DEBUG
+    printf("Insert Iteration %d starts ...\n", ++iter);
+#endif /* _DEBUG */
+    a_env.changes = 0;
+    irg_block_walk_graph(irg, insert_nodes, NULL, &a_env);
+//    dom_tree_walk_irg(irg, insert_nodes, NULL, &a_env);
+#ifdef _DEBUG
+    printf("------------------------\n");
+#endif /* _DEBUG */
+  } while (a_env.changes != 0);
+
+  restore_optimization_state(&state);
+
+  for (p = a_env.list; p != NULL; p = p->next) {
+    if (p->antic_in)
+      del_pset(p->antic_in);
+    if (p->avail_out)
+      del_pset(p->avail_out);
+  }
+  obstack_free(&obst, NULL);
+}