Improve cfopt
[libfirm] / ir / opt / cfopt.c
index d833f8f..ac9c746 100644 (file)
@@ -79,7 +79,8 @@ static bool is_Block_removable(ir_node *block)
 }
 
 /** checks if a given Cond node is a switch Cond. */
-static bool is_switch_Cond(ir_node *cond) {
+static bool is_switch_Cond(ir_node *cond)
+{
        ir_node *sel = get_Cond_selector(cond);
        return get_irn_mode(sel) != mode_b;
 }
@@ -283,6 +284,11 @@ static void optimize_blocks(ir_node *b, void *ctx)
        ir_node **in;
        merge_env *env = (merge_env*)ctx;
 
+       if (get_Block_dom_depth(b) < 0) {
+               /* ignore unreachable blocks */
+               return;
+       }
+
        /* Count the number of predecessor if this block is merged with pred blocks
           that are empty. */
        max_preds = 0;
@@ -597,38 +603,56 @@ typedef enum block_flags_t {
        BF_IS_UNKNOWN_JUMP_TARGET = 1 << 2,
 } block_flags_t;
 
-static bool get_phase_flag(ir_phase *block_info, ir_node *block, int flag) {
-       return ((int)phase_get_irn_data(block_info, block)) & flag;
+static bool get_phase_flag(ir_phase *block_info, ir_node *block, int flag)
+{
+       return PTR_TO_INT(phase_get_irn_data(block_info, block)) & flag;
 }
-static void set_phase_flag(ir_phase *block_info, ir_node *block, int flag) {
-       int data = (int)phase_get_irn_data(block_info, block);
+
+static void set_phase_flag(ir_phase *block_info, ir_node *block,
+                           block_flags_t flag)
+{
+       int data = PTR_TO_INT(phase_get_irn_data(block_info, block));
        data |= flag;
-       phase_set_irn_data(block_info, block, (void*)data);
+       phase_set_irn_data(block_info, block, INT_TO_PTR(data));
+}
+
+static void clear_phase_flag(ir_phase *block_info, ir_node *block)
+{
+       phase_set_irn_data(block_info, block, NULL);
 }
 
-static bool has_operations(ir_phase *block_info, ir_node *block) {
+static bool has_operations(ir_phase *block_info, ir_node *block)
+{
        return get_phase_flag(block_info, block, BF_HAS_OPERATIONS);
 }
-static void set_has_operations(ir_phase *block_info, ir_node *block) {
+
+static void set_has_operations(ir_phase *block_info, ir_node *block)
+{
        set_phase_flag(block_info, block, BF_HAS_OPERATIONS);
 }
 
-static bool has_phis(ir_phase *block_info, ir_node *block) {
+static bool has_phis(ir_phase *block_info, ir_node *block)
+{
        return get_phase_flag(block_info, block, BF_HAS_PHIS);
 }
-static void set_has_phis(ir_phase *block_info, ir_node *block) {
+
+static void set_has_phis(ir_phase *block_info, ir_node *block)
+{
        set_phase_flag(block_info, block, BF_HAS_PHIS);
 }
 
-static bool is_unknown_jump_target(ir_phase *block_info, ir_node *block) {
+static bool is_unknown_jump_target(ir_phase *block_info, ir_node *block)
+{
        return get_phase_flag(block_info, block, BF_IS_UNKNOWN_JUMP_TARGET);
 }
-static void set_is_unknown_jump_target(ir_phase *block_info, ir_node *block) {
+
+static void set_is_unknown_jump_target(ir_phase *block_info, ir_node *block)
+{
        set_phase_flag(block_info, block, BF_IS_UNKNOWN_JUMP_TARGET);
 }
 
 /**
- * Walker: fill block info information.
+ * Pre-Walker: fill block info information.
  */
 static void compute_block_info(ir_node *n, void *x)
 {
@@ -653,6 +677,12 @@ static void compute_block_info(ir_node *n, void *x)
        }
 }
 
+static void clear_block_info(ir_node *block, void *x)
+{
+       ir_phase *block_info = (ir_phase *)x;
+       clear_phase_flag(block_info, block);
+}
+
 typedef struct skip_env {
        bool changed;
        ir_phase *phase;
@@ -681,8 +711,8 @@ static void optimize_ifs(ir_node *block, void *x)
 }
 
 /**
- * Post-Block walker: remove empty blocks that are
- * predecessors of the current block.
+ * Pre-Block walker: remove empty blocks (only contain a Jmp)
+ * that are control flow predecessors of the current block.
  */
 static void remove_empty_blocks(ir_node *block, void *x)
 {
@@ -692,49 +722,106 @@ static void remove_empty_blocks(ir_node *block, void *x)
 
        for (i = 0; i < n_preds; ++i) {
                ir_node *jmp, *jmp_block, *pred, *pred_block;
+               int n_jpreds = NULL;
 
                jmp = get_Block_cfgpred(block, i);
                if (!is_Jmp(jmp))
                        continue;
                jmp_block = get_nodes_block(jmp);
+               if (jmp_block == block)
+                       continue; /* this infinite loop cannot be optimized any further */
                if (is_unknown_jump_target(env->phase, jmp_block))
-                       continue;
+                       continue; /* unknown jump target must not be optimized */
                if (has_operations(env->phase,jmp_block))
-                       continue;
-               /* jmp_block is an empty block! */
+                       continue; /* this block contains operations and cannot be skipped */
+               if (has_phis(env->phase,jmp_block))
+                       continue; /* this block contains Phis and is not skipped */
 
-               if (get_Block_n_cfgpreds(jmp_block) != 1)
-                       continue;
-               pred = get_Block_cfgpred(jmp_block, 0);
-               exchange(jmp, pred);
-               env->changed = true;
+               /* jmp_block is an empty block and can be optimized! */
 
-               /* cleanup: jmp_block might have a Keep edge! */
-               pred_block = get_nodes_block(pred);
-               exchange(jmp_block, pred_block);
+               n_jpreds = get_Block_n_cfgpreds(jmp_block);
+               /**
+                * If the jmp block has only one predecessor this is straightforward.
+                * However, if there are more predecessors, we only handle this,
+                * if block has no Phis.
+                */
+               if (n_jpreds == 1) {
+                       /* skip jmp block by rerouting its predecessor to block
+                        *
+                        *     A              A
+                        *     |              |
+                        *  jmp_block   =>    |
+                        *     |              |
+                        *   block          block
+                        */
+                       pred = get_Block_cfgpred(jmp_block, 0);
+                       exchange(jmp, pred);
+
+                       /* cleanup: jmp_block might have a Keep edge! */
+                       pred_block = get_nodes_block(pred);
+                       exchange(jmp_block, pred_block);
+                       env->changed = true;
+               } else if (! has_phis(env->phase, block)) {
+                       /* all predecessors can skip the jmp block, so block gets some new predecessors
+                        *
+                        *  A     B                 A  B
+                        *   \   /                  |  |
+                        * jmp_block  C  =>  Bad  C |  |
+                        *      \    /          \ | | /
+                        *      block            block
+                        */
+                       ir_node **ins = NULL;
+                       int j;
+                       NEW_ARR_A(ir_node *, ins, n_preds+n_jpreds);
+                       /* first copy the old predecessors, because the outer loop (i) still walks over them */
+                       for (j = 0; j < n_preds; ++j) {
+                               ins[j] = get_Block_cfgpred(block, j);
+                       }
+                       /* now append the new predecessors */
+                       for (j = 0; j < n_jpreds; ++j) {
+                               pred = get_Block_cfgpred(jmp_block, j);
+                               ins[n_preds+j] = pred;
+                       }
+                       set_irn_in(block, n_preds+n_jpreds, ins);
+                       /* convert the jmp_block to Bad */
+                       ir_graph *irg = get_irn_irg(block);
+                       exchange(jmp_block, new_r_Bad(irg, mode_BB));
+                       exchange(jmp, new_r_Bad(irg, mode_X));
+                       /* let the outer loop walk over the new predecessors as well */
+                       n_preds += n_jpreds;
+                       env->changed = true;
+                       // TODO What if jmp_block had a KeepAlive edge?
+               } else {
+                       /* This would involve Phis ... */
+               }
        }
 }
 
 /*
- * Some cfg optimizations, which do not touch Phi nodes
+ * All cfg optimizations, which do not touch Phi nodes.
+ *
+ * Note that this might create critical edges.
  */
-static void cfgopt_ignoring_phis(ir_graph *irg) {
+static void cfgopt_ignoring_phis(ir_graph *irg)
+{
        ir_phase *block_info = new_phase(irg, NULL);
-       skip_env env = { false, block_info };
-
-       irg_walk_graph(irg, compute_block_info, NULL, block_info);
+       skip_env env = { true, block_info };
 
-       for (;;) {
+       while (env.changed) {
+               irg_walk_graph(irg, compute_block_info, NULL, block_info);
                env.changed = false;
 
-               /* useless if optimization: will not touch empty blocks */
+               /* Remove blocks, which only consist of a Jmp */
+               irg_block_walk_graph(irg, remove_empty_blocks, NULL, &env);
+
+               /* Optimize Cond->Jmp, where then- and else-block are the same. */
                irg_block_walk_graph(irg, NULL, optimize_ifs, &env);
 
-               /* Remove empty blocks */
-               irg_block_walk_graph(irg, NULL, remove_empty_blocks, &env);
                if (env.changed) {
                        set_irg_doms_inconsistent(irg);
-                       /* Removing blocks might enable more Cond optimizations */
+                       /* clear block info, because it must be recomputed */
+                       irg_block_walk_graph(irg, clear_block_info, NULL, block_info);
+                       /* Removing blocks and Conds might enable more optimizations */
                        continue;
                } else {
                        break;
@@ -762,10 +849,9 @@ void optimize_cf(ir_graph *irg)
        assert(get_irg_pinned(irg) != op_pin_state_floats &&
               "Control flow optimization need a pinned graph");
 
-       /* FIXME: control flow opt destroys block edges. So edges are deactivated
-        * here. Fix the edges! */
        edges_deactivate(irg);
 
+       /* First the "simple" optimizations, which do not touch Phis */
        cfgopt_ignoring_phis(irg);
 
        /* we use the mark flag to mark removable blocks */
@@ -799,7 +885,6 @@ void optimize_cf(ir_graph *irg)
                if (!changed)
                        break;
 
-               set_irg_outs_inconsistent(irg);
                set_irg_doms_inconsistent(irg);
                set_irg_extblk_inconsistent(irg);
                set_irg_entity_usage_state(irg, ir_entity_usage_not_computed);
@@ -864,7 +949,6 @@ void optimize_cf(ir_graph *irg)
 
        if (env.changed) {
                /* Handle graph state if was changed. */
-               set_irg_outs_inconsistent(irg);
                set_irg_doms_inconsistent(irg);
                set_irg_extblk_inconsistent(irg);
                set_irg_entity_usage_state(irg, ir_entity_usage_not_computed);