BugFix r19562: get_nodes_block(skip_Proj(get_irn_n(n,i))) is subtile
[libfirm] / ir / ana / structure.c
index b59a4c1..96b8247 100644 (file)
@@ -1,13 +1,28 @@
 /*
- * Project:     libFIRM
- * File name:   ir/ana/structure.c
- * Purpose:     Structure Analysis
- * Author:      Michael Beck
- * Modified by:
- * Created:     5.4.2007
- * CVS-ID:      $Id: $
- * Copyright:   (c) 2007 Universität Karlsruhe
- * Licence:     This file protected by GPL -  GNU GENERAL PUBLIC LICENSE.
+ * Copyright (C) 1995-2008 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.
+ */
+
+/**
+ * @file
+ * @brief    Structure Analysis
+ * @author   Michael Beck
+ * @date     5.4.2007
+ * @version  $Id$
  */
 #ifdef HAVE_CONFIG_H
 #include "config.h"
@@ -21,7 +36,7 @@
 #include "irtools.h"
 #include "irgwalk.h"
 #include "array.h"
-#include "irphase_t.h"
+#include "irdump.h"
 
 #include "debug.h"
 
@@ -36,17 +51,19 @@ struct ir_reg_tree {
 
 /* A region. */
 struct ir_region {
-       firm_kind      kind;    /**< Must be k_ir_region. */
-       ir_region_kind type;    /**< The type of this region. */
-       ir_region      *parent; /**< points to the parent. */
-       ir_reg_or_blk  *parts;  /**< The list of all region parts. */
-       ir_region      **pred;  /**< The predecessor (control flow) regions of this region. */
-       ir_region      **succ;  /**< The successor (control flow) regions of this region. */
-       unsigned       prenum;  /**< DFS pre-oder number */
-       unsigned       postnum; /**< DFS post-oder number */
-       void           *link;   /**< A link field. */
-       unsigned long  nr;      /**< for debugging */
-       char           visited; /**< The visited flag. */
+       firm_kind      kind;      /**< Must be k_ir_region. */
+       ir_region_kind type;      /**< The type of this region. */
+       ir_region      *parent;   /**< points to the parent. */
+       ir_reg_or_blk  *parts;    /**< The list of all region parts. */
+       ir_region      **pred;    /**< The predecessor (control flow) regions of this region. */
+       ir_region      **succ;    /**< The successor (control flow) regions of this region. */
+       unsigned       prenum;    /**< DFS pre-oder number */
+       unsigned       postnum;   /**< DFS post-oder number */
+       void           *link;     /**< A link field. */
+       unsigned long  nr;        /**< for debugging */
+       unsigned       visited:1; /**< The visited flag. */
+       unsigned       exit:1;    /**< If set, the parent region can be left by this node. */
+       unsigned       enter:1;   /**< If set, the parent region can be entered by this node. */
 };
 
 /* A helper type for unioning blocks and regions. */
@@ -57,7 +74,7 @@ union ir_reg_or_blk {
 };
 
 /* The debug handle. */
-DEBUG_ONLY(firm_dbg_module_t *dbg;)
+DEBUG_ONLY(static firm_dbg_module_t *dbg;)
 
 /**
  * Returns the link of a region.
@@ -78,7 +95,7 @@ void set_region_link(ir_region *reg, void *data) {
  */
 ir_region *get_block_region(const ir_node *block) {
        assert(is_Block(block));
-       return get_irn_link(block);
+       return block->attr.block.region;
 }
 
 /**
@@ -86,7 +103,7 @@ ir_region *get_block_region(const ir_node *block) {
  */
 void set_block_region(ir_node *block, ir_region *reg) {
        assert(is_Block(block));
-       set_irn_link(block, reg);
+       block->attr.block.region = reg;
 }
 
 /**
@@ -205,21 +222,23 @@ static void dfs_walk(ir_graph *irg, walk_env *env) {
 }
 
 /**
- * Post-walker: wrap all blocks with a Block region
+ * Post-walker: wrap all blocks with a BasicBlock region
  * and count them
  */
-static void wrap_blocks(ir_node *block, void *ctx) {
+static void wrap_BasicBlocks(ir_node *block, void *ctx) {
        walk_env *env = ctx;
        ir_region *reg;
 
        /* Allocate a Block wrapper */
        reg          = obstack_alloc(env->obst, sizeof(*reg));
        reg->kind    = k_ir_region;
-       reg->type    = ir_rk_Block;
+       reg->type    = ir_rk_BasicBlock;
        reg->parent  = NULL;
        reg->prenum  = 0;
        reg->postnum = 0;
        reg->visited = 0;
+       reg->exit    = 0;
+       reg->enter   = 0;
        reg->link    = NULL;
        reg->nr      = get_irn_node_nr(block);
        reg->parts   = NEW_ARR_D(ir_reg_or_blk, env->obst, 1);
@@ -228,13 +247,13 @@ static void wrap_blocks(ir_node *block, void *ctx) {
        set_irn_link(block, reg);
 
        ++env->l_post;
-}  /* wrap_blocks */
+}  /* wrap_BasicBlocks */
 
 /**
  * Create the pred and succ edges for Block wrapper.
  * Kill edges to the Start and End blocks.
  */
-static void update_Block_regions(ir_node *blk, void *ctx) {
+static void update_BasicBlock_regions(ir_node *blk, void *ctx) {
        walk_env *env = ctx;
        ir_region *reg = get_irn_link(blk);
        int i, j, len;
@@ -259,7 +278,7 @@ static void update_Block_regions(ir_node *blk, void *ctx) {
                reg->succ[j++] = get_irn_link(succ);
        }
        ARR_SHRINKLEN(reg->succ, j);
-}
+}  /* update_BasicBlock_regions */
 
 /** Allocate a new region of a obstack */
 #define ALLOC_REG(obst, reg, tp) \
@@ -271,31 +290,39 @@ static void update_Block_regions(ir_node *blk, void *ctx) {
                (reg)->prenum  = 0; \
                (reg)->postnum = 0; \
                (reg)->visited = 0; \
+               (reg)->exit    = 0; \
+               (reg)->enter   = 0; \
                (reg)->link    = NULL; \
        } while (0)
 
 /**
  * Creates a new Sequence region.
  */
-static ir_region *new_Sequence(struct obstack *obst, ir_region *nset[]) {
-       ir_region *reg;
-       int i, len = ARR_LEN(nset);
+static ir_region *new_Sequence(struct obstack *obst, ir_region *nset, int nset_len) {
+       ir_region *reg, *next;
+       int i;
 
        ALLOC_REG(obst, reg, ir_rk_Sequence);
 
-       reg->nr    = nset[0]->nr;
-       reg->parts = CLONE_ARR_D(ir_reg_or_blk, obst, nset);
-       reg->pred  = DUP_ARR_D(ir_region *, obst, nset[0]->pred);
-       reg->succ  = DUP_ARR_D(ir_region *, obst, nset[len - 1]->succ);
+       reg->parts = NEW_ARR_D(ir_reg_or_blk, obst, nset_len);
 
-       for (i = 0; i < len; ++i) {
-               reg->parts[i].region = nset[i];
-               nset[i]->parent = reg;
+       /* beware: list is in reverse order, reverse */
+       next = nset;
+       for (i = nset_len - 1; i >= 0; --i) {
+               nset = next;
+               reg->parts[i].region = nset;
+               nset->parent = reg;
+               next = nset->link;
+               nset->link = NULL;
        }
 
+       reg->nr   = reg->parts[0].region->nr;
+       reg->pred = DUP_ARR_D(ir_region *, obst, reg->parts[0].region->pred);
+       reg->succ = DUP_ARR_D(ir_region *, obst, reg->parts[nset_len - 1].region->succ);
+
        DEBUG_ONLY(
                DB((dbg, LEVEL_2, " Created Sequence "));
-               for (i = 0; i < len; ++i) {
+               for (i = 0; i < nset_len; ++i) {
                        DB((dbg, LEVEL_2, "(%u)", reg->parts[i].region->nr));
                }
                DB((dbg, LEVEL_2, "\n"));
@@ -351,29 +378,47 @@ static ir_region *new_IfThen(struct obstack *obst, ir_region *if_b, ir_region *t
 /**
  * Create a new Switch/case region.
  */
-static ir_region *new_SwitchCase(struct obstack *obst, ir_region_kind type, ir_region *head, ir_region *end, pset *cases) {
-       ir_region *reg, *c;
+static ir_region *new_SwitchCase(struct obstack *obst, ir_region_kind type, ir_region *head, ir_region *exit,
+                                 ir_region *cases, int cases_len) {
+       ir_region *reg, *c, *n;
        int i;
+       int add = 1;
+
+       /* check, if the exit block is in the list */
+       for (c = cases; c != NULL; c = c->link) {
+               if (c == exit) {
+                       add = 0;
+                       break;
+               }
+       }
 
        ALLOC_REG(obst, reg, type);
 
        reg->nr    = head->nr;
-       reg->parts = NEW_ARR_D(ir_reg_or_blk, obst, pset_count(cases) + 1);
+       reg->parts = NEW_ARR_D(ir_reg_or_blk, obst, cases_len + add);
 
        reg->parts[0].region = head; head->parent = reg;
-       i = 0;
-       foreach_pset(cases, c) {
-               reg->parts[i].region = c;
-               c->parent = reg;
+       i = 1;
+       for (c = cases; c != NULL; c = n) {
+               n = c->link;
+               if (c != exit) {
+                       reg->parts[i++].region = c;
+                       c->parent = reg;
+               }
+               c->link = NULL;
        }
 
        reg->pred = DUP_ARR_D(ir_region *, obst, head->pred);
        reg->succ = NEW_ARR_D(ir_region *, obst, 1);
-       reg->succ[0] = end;
-
-       DB((dbg, LEVEL_2, " Created Switch(%u)Exit(%u)\n", reg->nr, end->nr));
+       reg->succ[0] = exit;
 
-       del_pset(cases);
+       DEBUG_ONLY(
+               DB((dbg, LEVEL_2, " Created %s(%u)\n", reg->type == ir_rk_Switch ? "Switch" : "Case", reg->nr));
+               for (i = 1; i < ARR_LEN(reg->parts); ++i) {
+                       DB((dbg, LEVEL_2, "  Case(%u)\n", reg->parts[i].region->nr));
+               }
+               DB((dbg, LEVEL_2, "  Exit(%u)\n", exit->nr));
+       )
        return reg;
 }  /* new_SwitchCase */
 
@@ -484,6 +529,75 @@ static ir_region *new_WhileLoop(struct obstack *obst, ir_region *head) {
        return reg;
 }  /* new_WhileLoop */
 
+/**
+ * Create a new new_NaturalLoop region.
+ */
+static ir_region *new_NaturalLoop(struct obstack *obst, ir_region *head) {
+       ir_region *reg, *c, *n;
+       int i, j, k, len, n_pred, n_succ;
+
+       /* count number of parts */
+       for (len = 0, c = head; c != NULL; c = c->link)
+               ++len;
+
+       ALLOC_REG(obst, reg, ir_rk_WhileLoop);
+
+       reg->nr      = head->nr;
+       reg->parts   = NEW_ARR_D(ir_reg_or_blk, obst, len);
+
+       /* enter all parts */
+       for (i = 0, c = head; c != NULL; c = n) {
+               reg->parts[i++].region = c;
+               c->parent = reg;
+               n = c->link;
+               c->link = NULL;
+       }
+
+       /* count number of preds */
+       n_pred = 0;
+       for (i = get_region_n_preds(head) - 1; i >= 0; --i) {
+               ir_region *pred = get_region_pred(head, i);
+               if (pred->parent != reg)
+                       ++n_pred;
+       }
+       reg->pred = NEW_ARR_D(ir_region *, obst, n_pred);
+       for (j = 0, i = get_region_n_preds(head) - 1; i >= 0; --i) {
+               ir_region *pred = get_region_pred(head, i);
+               if (pred->parent != reg)
+                       reg->pred[j++] = pred;
+       }
+
+       /* count number of succs */
+       n_succ = 0;
+       for (j = 0; j < len; ++j) {
+               ir_region *c = reg->parts[j].region;
+               for (i = get_region_n_succs(c) - 1; i >= 0; --i) {
+                       ir_region *succ = get_region_succ(c, i);
+                       if (succ->parent != reg)
+                               ++n_succ;
+               }
+       }
+       reg->succ = NEW_ARR_D(ir_region *, obst, n_succ);
+       k = 0;
+       for (j = 0; j < len; ++j) {
+               ir_region *c = reg->parts[j].region;
+               for (i = get_region_n_succs(c) - 1; i >= 0; --i) {
+                       ir_region *succ = get_region_succ(c, i);
+                       if (succ->parent != reg)
+                               reg->succ[k++] = succ;
+               }
+       }
+
+       DEBUG_ONLY(
+               DB((dbg, LEVEL_2, " Created NaturalLoop(%u)Head(%u)\n", reg->nr, head->nr));
+               for (i = 1; i < len; ++i) {
+                       ir_region *p = reg->parts[i].region;
+                       DB((dbg, LEVEL_2, "  Body(%u)\n", p->nr));
+               }
+       )
+       return reg;
+}  /* new_NaturalLoop */
+
 /**
  * Return true if a is an ancestor of b in DFS search.
  */
@@ -491,6 +605,18 @@ static int is_ancestor(const ir_region *a, const ir_region *b) {
        return (a->prenum <= b->prenum && a->postnum > b->postnum);
 }
 
+/**
+ *  Return true if region pred is a predecessor of region n.
+ */
+static int pred_of(const ir_region *pred, const ir_region *n) {
+       int i;
+       for (i = get_region_n_preds(n) - 1; i >= 0; --i) {
+               if (get_region_pred(n, i) == pred)
+                       return 1;
+       }
+       return 0;
+}
+
 /**
  *  Return true if region succ is a successor of region n.
  */
@@ -556,7 +682,8 @@ static ir_region *find_cyclic_region(ir_region *node) {
        }
 
        if (node->link && improper) {
-               /* found an improper region */
+               /* found an improper region, do minimization */
+
        }
        return node;
 }
@@ -581,21 +708,40 @@ static ir_region *cyclic_region_type(struct obstack *obst, ir_region *node) {
        }
        list = find_cyclic_region(node);
 
-       if (list->link && !LINK(list)->link && get_region_n_succs(list->link) == 1) {
-               return new_WhileLoop(obst, list);
+       if (list->link) {
+               if (!LINK(list)->link && get_region_n_succs(list->link) == 1) {
+                       /* only one body block with only one successor (the head) */
+                       return new_WhileLoop(obst, list);
+               }
+               /* A Loop with one head */
+               return new_NaturalLoop(obst, list);
        }
 
        return NULL;
 }
 
+/**
+ * Clear all links on a list. Needed, because we expect cleared links-
+ */
+static void clear_list(ir_region *list) {
+       ir_region *next;
+
+       for (next = list; next; list = next) {
+               next = list->link;
+               list->link = NULL;
+       }
+}
+
+#define ADD_LIST(list, n) do { n->link = list; list = n; ++list##_len; } while(0)
+
 /**
  * Detect an acyclic region.
  */
 static ir_region *acyclic_region_type(struct obstack *obst, ir_region *node) {
        ir_region *n, *m;
        int p, s, i, k;
-       ir_region **nset = NEW_ARR_F(ir_region *, 0);
-       pset *set;
+       ir_region *nset = NULL;
+       int nset_len = 0;
        ir_region *res;
 
        /* check for a block containing node */
@@ -610,21 +756,19 @@ static ir_region *acyclic_region_type(struct obstack *obst, ir_region *node) {
        p = 1;
        s = get_region_n_succs(n) == 1;
        while (p & s) {
-               ARR_APP1(ir_region *, nset, n);
+               ADD_LIST(nset, n);
                n = get_region_succ(n, 0);
                p = get_region_n_preds(n) == 1;
                s = get_region_n_succs(n) == 1;
        }
        if (p) {
-               ARR_APP1(ir_region *, nset, n);
+               ADD_LIST(nset, n);
        }
-       if (ARR_LEN(nset) >= 2) {
+       if (nset_len > 1) {
                /* node --> .. --> .. */
-               res = new_Sequence(obst, nset);
-               DEL_ARR_F(nset);
+               res = new_Sequence(obst, nset, nset_len);
                return res;
        }
-       DEL_ARR_F(nset);
        node = n;
 
        /* check for IfThenElse */
@@ -648,20 +792,18 @@ static ir_region *acyclic_region_type(struct obstack *obst, ir_region *node) {
                         */
                        return new_IfThenElse(obst, node, n, m);
                }
-               if (n_succs == 1 && m_preds == 2 &&
+               if (n_succs == 1 &&
                    get_region_succ(n, 0) == m &&
-                   (get_region_pred(m, 0) == node ||
-                    get_region_pred(m, 1) == node)) {
+                   pred_of(node, m)) {
                        /*
                         * node -->n-->m
                         *    \-------/
                         */
                        return new_IfThen(obst, node, n);
                }
-               if (m_succs == 1 && n_preds == 2 &&
+               if (m_succs == 1 &&
                    get_region_succ(m, 0) == m &&
-                   (get_region_pred(n, 0) == node ||
-                    get_region_pred(n, 1) == node)) {
+                   pred_of(node, n)) {
                        /*
                         * node -->m-->n
                         *    \-------/
@@ -670,73 +812,95 @@ static ir_region *acyclic_region_type(struct obstack *obst, ir_region *node) {
                }
        }
        /* check for Switch, case */
-       set = pset_new_ptr_default();
-       p   = k > 0;
-       for (i = k - 1; i >= 0; --i) {
-               n = get_region_succ(node, i);
-               pset_insert_ptr(set, n);
-               if (get_region_n_succs(n) != 1) {
-                       p = 0;
-                       break;
-               }
-       }
-       if (p) {
-               ir_region_kind kind = ir_rk_Case;
-
-               m = pset_first(set);
-               if (pset_find_ptr(set, m) != NULL) {
-                       /* must be a switch, if any, find the exit */
-                       kind = ir_rk_Switch;
-
-                       for (m = pset_next(set); m != NULL; m = pset_next(set)) {
-                               if (pset_find_ptr(set, m) == NULL) {
-                                       pset_break(set);
+       if (k > 0) {
+               ir_region *exit = NULL;
+               nset = NULL; nset_len = 0;
+               p = 0;
+               for (i = k - 1; i >= 0; --i) {
+                       n = get_region_succ(node, i);
+                       ADD_LIST(nset, n);
+                       if (get_region_n_succs(n) != 1) {
+                               /* must be the exit */
+                               exit = n;
+                               ++p;
+                               if (p > 1)
                                        break;
-                               }
                        }
                }
-               if (m != NULL) {
-                       /* m ist the exit, do the checks */
-                       foreach_pset(set, n) {
-                               if (n == m) {
-                                       /* good, switch to exit */
-                                       continue;
-                               }
-                               if (pset_find_ptr(set, n) == NULL) {
-                                       /* another exit */
-                                       pset_break(set);
-                                       break;
+               if (p <= 1) {
+                       ir_region_kind kind = ir_rk_Case;
+                       ir_region *pos_exit_1 = NULL;
+                       ir_region *pos_exit_2 = NULL;
+
+                       /* find the exit */
+                       for (m = nset; m != NULL; m = m->link) {
+                               if (get_region_n_succs(m) != 1) {
+                                       /* must be the exit block */
+                                       if (exit == NULL) {
+                                               exit = m;
+                                       } else if (exit != m) {
+                                               /* two exits */
+                                               exit = NULL;
+                                               break;
+                                       }
+                               } else {
+                                       ir_region *succ = get_region_succ(m, 0);
+
+                                       if (succ->link == NULL) {
+                                               if (exit == NULL) {
+                                                       if (succ == pos_exit_1)
+                                                               exit = succ;
+                                                       else if (succ == pos_exit_2)
+                                                               exit = succ;
+                                                       else if (pos_exit_1 == NULL)
+                                                               pos_exit_1 = succ;
+                                                       else if (pos_exit_2 == NULL)
+                                                               pos_exit_2 = succ;
+                                                       else {
+                                                               /* more than two possible exits */
+                                                               break;
+                                                       }
+                                               } else if (exit != succ) {
+                                                       /* two exits */
+                                                       exit = NULL;
+                                                       break;
+                                               }
+                                       }
                                }
-                               kind = ir_rk_Switch;
                        }
+                       if (exit != NULL) {
+                               /* do the checks */
+                               for (n = nset; n != NULL; n = n->link) {
+                                       ir_region *succ;
+                                       if (n == exit) {
+                                               /* good, default fall through */
+                                               continue;
+                                       }
+                                       succ = get_region_succ(n, 0);
+                                       if (succ == exit) {
+                                               /* good, switch to exit */
+                                               continue;
+                                       }
+                                       if (succ->link == NULL) {
+                                               /* another exit */
+                                               break;
+                                       } else {
+                                               /* a fall through */
+                                               kind = ir_rk_Switch;
+                                       }
+                               }
 
-                       if (n == NULL) {
-                               /* detected */
-                               return new_SwitchCase(obst, kind, node, m, set);
+                               if (n == NULL) {
+                                       /* detected */
+                                       return new_SwitchCase(obst, kind, node, exit, nset, nset_len);
+                               }
                        }
                }
+               clear_list(nset);
        }
-       /* check for proper */
-
        return NULL;
 }
 
-/**
- * Fill the given set recursively with all parts of the region (including itself)
- */
-static void fill_parts(pset *set, ir_region *reg) {
-       int i;
-
-       if (reg->type == ir_rk_Block) {
-               pset_insert_ptr(set, reg);
-               return;
-       }
-
-       for (i = ARR_LEN(reg->parts) - 1; i >= 0; --i) {
-               fill_parts(set, reg->parts[i].region);
-       }
-}
-
 /**
  * replace all pred edges from region pred that points to any of the set set
  * to ONE edge to reg.
@@ -760,6 +924,9 @@ static void replace_pred(ir_region *succ, ir_region *reg) {
                                r = reg;
                        }
                        set_region_pred(succ, i, r);
+               } else {
+                       /* the current region can be entered by this node */
+                       pred->enter = 1;
                }
        }
        ARR_SHRINKLEN(succ->pred, len);
@@ -788,6 +955,9 @@ static void replace_succ(ir_region *pred, ir_region *reg) {
                                r = reg;
                        }
                        set_region_succ(pred, i, r);
+               } else {
+                       /* current region can be left by this node */
+                       succ->exit = 1;
                }
        }
        ARR_SHRINKLEN(pred->succ, len);
@@ -856,8 +1026,8 @@ ir_reg_tree *construct_region_tree(ir_graph *irg) {
        /* create the Block wrapper and count them */
        env.l_post = 0;
        env.obst   = &res->obst;
-       irg_block_walk_graph(irg, NULL, wrap_blocks, &env);
-       irg_block_walk_graph(irg, NULL, update_Block_regions, &env);
+       irg_block_walk_graph(irg, NULL, wrap_BasicBlocks, &env);
+       irg_block_walk_graph(irg, NULL, update_BasicBlock_regions, &env);
 
        env.post = NEW_ARR_F(ir_region *, env.l_post);
 
@@ -912,7 +1082,7 @@ static void region_tree_walk2(ir_region *reg, irg_reg_walk_func *pre, irg_reg_wa
 
        if (pre)
                pre(reg, env);
-       if (reg->type != ir_rk_Block) {
+       if (reg->type != ir_rk_BasicBlock) {
                for (i = 0, n = ARR_LEN(reg->parts); i < n; ++i)
                        region_tree_walk2(reg->parts[i].region, pre, post, env);
        }