added doxygen comments
[libfirm] / ir / opt / loop_unrolling.c
index 459b1c2..e08c896 100644 (file)
 # include "loop_unrolling.h"
 
 # include "irgwalk.h"
-# include "irnode_t.h"
+# include "ircons.h"
+# include "irgmod.h"
 # include "irloop_t.h"
+# include "irgopt_t.h"
+# include "irnode_t.h"
+# include "irouts.h"
+# include "hashptr.h"
+# include "pset.h"
+# include "strength_red.h"
 
-static void do_loop_unroll(ir_node *n, void *env){
+#define MAX_UNROLL 4
+
+/*We will know if the head of the loop to be copy.
+ * Default don't copy.*/
+static int copy_loop_head = 0;
+
+typedef struct {
+  ir_node *irn ;                /* Node of the loop to be unrolling*/
+  ir_node *copy[MAX_UNROLL] ;   /* The copy of the node */
+} copies_t;
+
+/**
+ * compare two elements of the copies_t set
+ */
+static int set_cmp(const void *elt, const void *key, size_t size)
+{
+  const copies_t *c1 = elt;
+  const copies_t *c2 = key;
+
+  return c1->irn != c2->irn;
+}
+
+static INLINE int * new_backedge_arr(struct obstack *obst, int size)
+{
+  int *res = NEW_ARR_D (int, obst, size);
+  memset(res, 0, sizeof(int) * size);
+  return res;
+}
+
+static INLINE void new_backedge_info(ir_node *n) {
+  switch(get_irn_opcode(n)) {
+  case iro_Block:
+    n->attr.block.cg_backedge = NULL;
+    n->attr.block.backedge = new_backedge_arr(current_ir_graph->obst, get_irn_arity(n));
+    break;
+  case iro_Phi:
+    n->attr.phi_backedge = new_backedge_arr(current_ir_graph->obst, get_irn_arity(n));
+    break;
+  case iro_Filter:
+    n->attr.filter.backedge = new_backedge_arr(current_ir_graph->obst, get_irn_arity(n));
+    break;
+  default: ;
+  }
+}
+
+/**
+ * Remember the new node in the old node by using a field all nodes have.
+ */
+
+static INLINE void
+set_new_node (ir_node *old, ir_node *new)
+{
+  old->link = new;
+}
+
+/**
+ * Copies the node to the new obstack. The Ins of the new node point to
+ * the predecessors on the old obstack.  For block/phi nodes not all
+ * predecessors might be copied.  n->link points to the new node.
+ * For Phi and Block nodes the function allocates in-arrays with an arity
+ * only for useful predecessors.  The arity is determined by counting
+ * the non-bad predecessors of the block.
+ *
+ * @param n    The node to be copied
+ * @param env  if non-NULL, the node number attribute will be copied to the new node
+ */
+
+static void
+copy_node (ir_node *n, void *env)
+{
+  ir_node *nn, *block;
+  int new_arity;
+  opcode op = get_irn_opcode(n);
+  int copy_node_nr = env != NULL;
+
+  /* The end node looses it's flexible in array.  This doesn't matter,
+     as dead node elimination builds End by hand, inlineing doesn't use
+     the End node. */
+  /* assert(n->op == op_End ||  ((_ARR_DESCR(n->in))->cookie != ARR_F_MAGIC)); */
+
+  if (op == iro_Bad)
+    /* node copied already */
+    return;
+
+  new_arity = get_irn_arity(n);
+
+  nn = new_ir_node(get_irn_dbg_info(n),
+           current_ir_graph,
+           block,
+           get_irn_op(n),
+           get_irn_mode(n),
+           new_arity,
+           get_irn_in(n));
+
+
+  /* Copy the attributes.  These might point to additional data.  If this
+     was allocated on the old obstack the pointers now are dangling.  This
+     frees e.g. the memory of the graph_arr allocated in new_immBlock. */
+  copy_node_attr(n, nn);
+  new_backedge_info(nn);
+  set_new_node(n, nn);
+
+#if DEBUG_libfirm
+  if (copy_node_nr) {
+    /* for easier debugging, we want to copy the node numbers too */
+    nn->node_nr = n->node_nr;
+  }
+#endif
+
+}
+
+static int is_Phi_in_loop_head(ir_node *phi, ir_node *loop_head) {
+  assert(is_Block(loop_head));
+  return is_Phi(phi) && (get_nodes_block(phi) == loop_head);
+}
+
+/**
+ * Copies predecessors of node to copies of node.
+ * If the predecessor is a loop invariant, then the copy get it
+ * as predecessor, else the copy of the predecessor.
+ *
+ * @param *l_n                A set, where the node of the loop are saved.
+ * @param *value              A element of the set.
+ * @param *info               Contains information about the induction variable.
+ * @param *unroll_factor      A integer power of 2.
+ * @param *env                Free environment pointer.
+ */
+static void
+set_preds (set *l_n, copies_t *value, induct_var_info *info, int unroll_factor, void *env)
+{
+  int i, irn_arity;
+  copies_t *value_pred;
+
+  ir_node *loop_head = get_loop_node(info->l_itervar_phi, 0);
+
+  irn_arity = get_irn_arity(value->irn);
+
+  for (i = 0; i < irn_arity; i++) {
+    ir_node *pred = get_irn_n(value->irn, i);
+
+    copies_t key;
+
+    key.irn = pred;
+    value_pred = set_find(l_n, &key, sizeof(key), HASH_PTR(pred));
+
+    if (value->irn != loop_head && !is_Phi_in_loop_head(value->irn, loop_head)) {
+      if (value_pred == NULL) {
+         /* Is loop invariant. */
+       for(int p = 0; p < unroll_factor -1; p++)
+         set_irn_n (value->copy[p], i, pred);
+
+      } else
+       for(int p = 0; p < unroll_factor -1; p++)
+         set_irn_n (value->copy[p], i, value_pred->copy[p]);
+    }
+  }
+}
+
+/* Set the backedge of phi in the loop head.The backedge of phis in the loop head
+ * must now point to the value defined
+ * in the last copie of the loop body.
+ *
+ * @param *l_n                A set, where the node of the loop are saved.
+ * @param *value              A phi in the loop head.
+ * @param *info               Contains information about the induction variable.
+ * @param *unroll_factor      A integer power of 2.
+ */
+static void
+set_phi_backedge(set *l_n, copies_t *value, induct_var_info *info, int unroll_factor)
+{
+  copies_t key, *value_pred;
+  key.irn = get_irn_n(value->irn, info->op_pred_pos);
+  value_pred = set_find(l_n, &key, sizeof(key), HASH_PTR(key.irn));
+
+  set_Phi_pred(value->irn, info->op_pred_pos, value_pred->copy[unroll_factor - 2]);
+}
+
+/** Test for a loop head.
+ *
+ *  Returns true if the node has predecessors in the loop _and_ out of
+ *  the loop.  Then it is a loop head: The loop can be entered through
+ *  this node.
+ *
+ *  @param *n      The node to be tested.
+ *  @param *info   Contains the loop information.
+ */
+static int
+is_loop_head(ir_node *n, induct_var_info *info)
+{
+  int i, arity;
+  int some_outof_loop = 0, some_in_loop = 0;
+
+  assert(get_irn_op(n) == op_Block);
+  arity = get_Block_n_cfgpreds(n);
+
+  for (i = 0; i < arity; i++) {
+    ir_node *pred = get_Block_cfgpred(n, i);
+    assert(pred);
+    if (is_loop_invariant(pred, get_loop_node(info->l_itervar_phi, 0))) {
+      some_outof_loop = 1;
+    } else
+      some_in_loop = 1;
+  }
+
+  return some_outof_loop && some_in_loop;
+}
+
+
+/** Test wether the passed loop is a natural loop.
+ *
+ * Returns true if the loop has only one loop header and only a single
+ * back edge.
+ *
+ * @param *info  Contains the loop information.
+ */
+static int
+is_natural_loop ( induct_var_info *info)
+{
+  ir_node *l_node;
+  int l_n_node = 0;
+  l_n_node = get_loop_n_nodes (info->l_itervar_phi);
+
+  for (int i = 1; i < (l_n_node); i ++) {
+    l_node = get_loop_node (info->l_itervar_phi, i);
+    if (is_loop_head(l_node, info)) return 0;
+
+    if (has_backedges(l_node) && i != l_n_node-1) return 0;
+  }
+
+  return 1;
+}
+
+/** Serch for all nodes of a loop.
+ *
+ * @param  *node       The induction variable of the loop.
+ * @param  *loop_head  The head of the loop.
+ * @param  *l_n        A set, where the node of the loop are saved.
+ */
+static void
+find_loop_nodes(ir_node *node, ir_node *loop_head, set *l_n)
+{
+  copies_t key, *value;
+
+  /* Add this node to the list. */
+  key.irn  = node;
+  for(int i = 0; i < 4 ;i++)
+    key.copy[i] = NULL;
+  value = set_insert(l_n, &key, sizeof(key), HASH_PTR(key.irn));
+
+  /* Add all outs of this node to the list, if they are within the loop. */
+  for (int i = get_irn_n_outs(node) - 1; i >= 0; i--) {
+    ir_node *pred = get_irn_out(node, i);
+    key.irn = pred;
+    if (!is_loop_invariant(pred, loop_head)         &&
+       set_find( l_n, &key, sizeof(key), HASH_PTR(key.irn)) == NULL ) {
+      find_loop_nodes(pred, loop_head, l_n);
+    }
+  }
+
+  /* Add all ins if they are within the loop. */
+  for(int i = get_irn_arity(node) -1; i >=0; i--) {
+    ir_node *pred = get_irn_n(node, i);
+    key.irn = pred;
+    if (!is_loop_invariant(pred, loop_head)         &&
+       set_find( l_n, &key, sizeof(key), HASH_PTR(key.irn)) == NULL ){
+      find_loop_nodes(pred, loop_head, l_n);
+    }
+  }
+}
+
+/* Make a new loop head if copy_loop_head = 1.
+ *
+ * @param *l_n              A set, where the node of the loop are saved.
+ * @param *info             Contains the loop information.
+ * @param *value            A element of the set.
+ * @param *unroll_factor    A integer power of 2.
+ *
+ */
+static void
+new_loop_head (set *l_n, induct_var_info *info, copies_t *value, int unroll_factor)
+{
+  copies_t block, *value_backedge_jmp, *backedge_jmp_block;
+
+  ir_node *backedge_jmp = get_Block_cfgpred(value->irn, info->op_pred_pos);
+  block.irn = backedge_jmp;
+
+  value_backedge_jmp = set_find( l_n, &block, sizeof(block), HASH_PTR(block.irn));
+  if(copy_loop_head){
+    ir_node *new_loop_head = new_Block(1, &backedge_jmp);
+    value->copy[0] = new_loop_head;
+
+    for(int i = 1; i<unroll_factor - 1; i++){
+      ir_node *new_loop_head1 = new_Block(1, &value_backedge_jmp->copy[i-1]);
+      value->copy[i] = new_loop_head1;
+    }
+  }else{
+
+    block.irn = get_nodes_block(backedge_jmp);
+    backedge_jmp_block =  set_find( l_n, &block, sizeof(block), HASH_PTR(block.irn));
+
+    set_irn_n(backedge_jmp_block->copy[0], 0, value_backedge_jmp->irn) ;
+
+    for(int i = 1; i<unroll_factor - 1; i++)
+      set_irn_n(backedge_jmp_block->copy[i], 0, value_backedge_jmp->copy[i - 1]);
+  }
+
+}
+
+/* Set all copies of the induction variable.
+ *
+ * @param *phi             A phi node in the loop head block.
+ * @param *phi_pred        The predecessor of the phi along the backedge.
+ * @param *unroll_factor   A integer power of 2.
+ *
+ */
+static void
+set_Phi_copies(copies_t *phi, copies_t *phi_pred, int unroll_factor)
+{
+  phi->copy[0] = phi_pred->irn;
+  for(int p = 1; p < unroll_factor -1; p++)
+    phi->copy[p] =  phi_pred->copy[p -1];
+}
+
+/* Decide if the loop head to be copy. A head with important nodes
+ * mus be copy.
+ *
+ * @param *l_n                A set, where the node of the loop are saved.
+ * @param *info               Contains information about the induction variable.
+ */
+static void
+loop_head_nodes(set *l_n, induct_var_info *info)
+{
+  copies_t *value;
+  ir_node *loop_head = get_loop_node(info->l_itervar_phi, 0);
+
+  for (value = set_first(l_n); value != NULL; value = set_next(l_n))
+    if(value->irn->op != op_Block &&
+       get_nodes_block(value->irn) == loop_head)
+      switch(get_irn_opcode(value->irn)) {
+      case iro_Cond:
+       break;
+      case iro_Phi:
+       break;
+      case iro_Proj:
+       break;
+      case iro_Const:
+       break;
+      case iro_Cmp:
+       break;
+      default:
+       copy_loop_head = 1;
+      }
+}
+
+/** Copy all loop nodes.
+ *
+ * @param *l_n    Contains all nodes of the loop.
+ * @param *info   Contains the loop information.
+ * @param *unroll_factor   A integer power of 2.
+ */
+static void
+copy_loop_body(set *l_n, induct_var_info *info, int unroll_factor)
+{
+  copies_t *value, *info_op, *phi, *loop_h, key, *value1;
+
+  ir_node *loop_head = get_loop_node(info->l_itervar_phi, 0);
+
+
+  for (value = set_first(l_n); value != NULL; value = set_next(l_n)) {
+    if(value->irn == loop_head)
+      loop_h = value;
+    else if (is_Phi_in_loop_head(value->irn, loop_head))
+      phi = value;
+    else if(copy_loop_head){
+      for (int i = 0; i<unroll_factor -1; i++){
+       copy_node(value->irn, NULL);
+       value->copy[i] = get_irn_link(value->irn);
+      }
+    } else {
+      if((value->irn->op == op_Block            &&
+         value->irn != loop_head)              ||
+        (value->irn->op != op_Block             &&
+         get_nodes_block(value->irn) != loop_head))
+       for (int i = 0; i<unroll_factor -1; i++){
+         copy_node(value->irn, NULL);
+         value->copy[i] = get_irn_link(value->irn);
+       }
+    }
+  }
+  /* Treat the loop head block */
+  new_loop_head (l_n, info, loop_h, unroll_factor);
+
+  /* Similarily treat the Phis in the loop head block. */
+  key.irn = info->op;
+  info_op = set_find( l_n, &key, sizeof(key), HASH_PTR(key.irn));
+  assert(info_op->irn == get_Phi_pred(info->itervar_phi, info->op_pred_pos));
+  for (int i = 0; i < get_irn_n_outs(loop_head); ++i) {
+    ir_node *phi = get_irn_out(loop_head, i);
+
+    if (is_Phi(phi)) {
+      key.irn = get_Phi_pred(phi, info->op_pred_pos); // info->op;
+      copies_t *phi_pred = set_find(l_n, &key, sizeof(key), HASH_PTR(key.irn));
+      key.irn = phi;
+      copies_t *phi_op = set_find(l_n, &key, sizeof(key), HASH_PTR(key.irn));
+      set_Phi_copies(phi_op, phi_pred, unroll_factor);
+    }
+  }
+
+
+  for (value = set_first(l_n); value != NULL; value = set_next(l_n)) {
+
+    if(copy_loop_head)
+      set_preds(l_n, value, info, unroll_factor, NULL);
+    else if((value->irn->op != op_Block) && get_nodes_block(value->irn) != loop_head)
+      set_preds(l_n, value, info, unroll_factor, NULL);
+
+    if (is_Phi_in_loop_head(value->irn, loop_head))
+      set_phi_backedge(l_n, value, info, unroll_factor);
+
+    if ((value->irn->op != op_Block) && !is_Phi_in_loop_head(value->irn, loop_head)) {
+      ir_node *nodes_block = get_nodes_block(value->irn);
+
+      if(!copy_loop_head && nodes_block == loop_head)
+       continue;
+
+      key.irn = nodes_block;
+      value1 = set_find( l_n, &key, sizeof(key), HASH_PTR(key.irn));
+
+      for(int p = 0; p < unroll_factor - 1; p++){
+       set_nodes_block(value->copy[p], value1->copy[p]);
+       //add_End_keepalive(get_irg_end(current_ir_graph), value->copy[p]);
+      }
+    }
+  }
+}
+
+static void
+set_loop_outs(set *l_n, induct_var_info *info, int unroll_factor)
+{
+  copies_t *value, key;
+  ir_node *loop_head = get_loop_node(info->l_itervar_phi, 0);
+
+  value = set_first(l_n);
+  for( ; value != NULL; value = set_next(l_n))
+    if(value->irn != info->op && !is_Phi_in_loop_head(value->irn, loop_head) &&
+       get_irn_opcode(value->irn) != iro_Proj)
+    for(int i = 0; i < get_irn_n_outs(value->irn); i++){
+      key.irn = get_irn_out(value->irn, i);
+       if(set_find( l_n, &key, sizeof(key), HASH_PTR(key.irn)) == NULL)
+         for(int p = 0; p < get_irn_arity(key.irn); p++)
+           if(value->irn == get_irn_n(key.irn, p))
+             set_irn_n (key.irn, p, value->copy[unroll_factor-2]);
+    }
 }
 
 
+/** Unroll the loop boby with a factor that must be power of two.
+ *
+ *  @param *n        A ir node.
+ *  @param *env      Free environment pointer.
+ */
+static void do_loop_unroll(ir_node *n, void *env){
+
+  induct_var_info info;
+  info.itervar_phi = n;
+  int l_sons = 0, unroll_factor = 0;
+
+  /* The ir node must be a induction varible. */
+
+  if (get_irn_op (n) == op_Phi) {
+    if (is_induction_variable (&info) == NULL) return;
+  } else return;
+
+  /* Brute force limiting of loop body size. */
+  if (get_loop_n_nodes(info.l_itervar_phi) > 2 ) return;
+
+  /* Only unroll loops that compare against a constant for exiting. */
+  if (info.cmp == NULL) return;
+
+  /* We only want to unroll innermost loops. */
+  l_sons = get_loop_n_sons (info.l_itervar_phi);
+  if ( l_sons != 0)
+    return;
+
+  ir_node* cmp_out = get_irn_out(info.cmp, 0);
+
+  if(!is_Proj(cmp_out)) return;
+  if(get_irn_op(info.increment) != op_Const) return;
+
+  int cmp_typ =  get_Proj_proj(cmp_out);
+  int init = get_tarval_long(get_Const_tarval
+                             (get_Phi_pred(info.itervar_phi, info.init_pred_pos)));
+  int iter_end = get_tarval_long(get_Const_tarval(info.cmp_const));
+  int iter_increment = get_tarval_long(get_Const_tarval(info.increment));
+  int diff, iter_number;
+
+  if(iter_end < init){
+    int p = iter_end;
+    iter_end = init;
+    init = p;
+  }
 
+  iter_increment = iter_increment < 0 ? -iter_increment : iter_increment;
+  diff = iter_end - init;
 
+  if (diff == 0 || iter_increment == 0) return;
 
+  iter_number = diff/iter_increment;
+  if((cmp_typ == 3 || cmp_typ == 5) && (iter_end % iter_increment == 0))
+    iter_number ++;
 
+  if(iter_number % 4 == 0)
+    unroll_factor = 4;
+  else if(iter_number % 3 == 0)
+    unroll_factor = 3;
+  else if(iter_number % 2 == 0)
+    unroll_factor = 2;
+  else return;
+
+
+  printf("\ninit %d,\n iter_end %d, \n diff %d, cmp_typ\n %d, \n unroll_factor %d", init, iter_end, diff, cmp_typ, unroll_factor);
+
+  // int unroll_factor = 4;  /* Must be power of 2. */
+  assert(unroll_factor <= MAX_UNROLL);
+
+  ir_node *loop_head;
+
+  loop_head = (is_natural_loop(&info)) ? get_loop_node(info.l_itervar_phi, 0) : NULL;
+
+  assert(loop_head != NULL && is_Block(loop_head));
+
+ /* We assume, the loop head has exactly one backedge.  The position of
+    the backedge is in the following variable: */
+  int backedge_pos ;
+  backedge_pos = (is_backedge(loop_head, 0)) ? 0:1;
+
+  /* A set with the nodes to copy. */
+  set *loop_nodes;
+  loop_nodes = new_set(set_cmp, 8);
+
+  ir_node *backedge_jmp = get_Block_cfgpred(loop_head, backedge_pos);
+
+  find_loop_nodes(info.itervar_phi, get_loop_node(info.l_itervar_phi, 0), loop_nodes);
+  loop_head_nodes(loop_nodes, &info);
+  copy_loop_body(loop_nodes, &info, unroll_factor);
+
+  copies_t *value;
+  for (value = set_first(loop_nodes); value != NULL; value = set_next(loop_nodes)) {
+    if(value->irn == backedge_jmp)
+      set_Block_cfgpred(loop_head, backedge_pos, value->copy[unroll_factor-2]);
+  }
+
+  set_loop_outs(loop_nodes, &info, unroll_factor);
+
+  /*
+  if (needs_preloop(unroll_factor)) {
+    return;    for now ...
+    make_preloop(unroll_factor);
+  }
+*/
+
+
+  // adapt_result_usage();
+
+}
 
 /* Performs loop unrolling for the passed graph. */
-void optimize_loop_unrolling(ir_graph *irg) {
+void optimize_loop_unrolling(ir_graph *irg /* unroll factor, max body size */) {
+  ir_graph *rem = current_ir_graph;
+  current_ir_graph = irg;
+
+  if ( !get_opt_loop_unrolling()) return;
 
-  if (!get_optimize() || !get_opt_loop_unrolling()) return;
+  /* -- Precompute some information -- */
+  /* Call algorithm that computes the backedges */
+  construct_cf_backedges(irg);
+ /* Call algorithm that computes the dominator trees. */
+  compute_doms(irg);
+  /* Call algorithm that computes the out edges */
+  compute_outs(irg);
+  collect_phiprojs(irg);
 
   /* -- Search expressions that can be optimized -- */
   irg_walk_graph(irg, NULL, do_loop_unroll, NULL);
 
+  current_ir_graph = rem;
 }