===================================================================
@@ -831,6 +831,7 @@ init_optimization_passes (void)
NEXT_PASS (pass_all_optimizations);
{
struct opt_pass **p = &pass_all_optimizations.pass.sub;
+ extern struct gimple_opt_pass pass_bprop_extends;
NEXT_PASS (pass_remove_cgraph_callee_edges);
/* Initial scalar cleanups before alias computation.
They ensure memory accesses are not indirect wherever possible. */
@@ -838,6 +839,7 @@ init_optimization_passes (void)
NEXT_PASS (pass_update_address_taken);
NEXT_PASS (pass_rename_ssa_copies);
NEXT_PASS (pass_complete_unrolli);
+ NEXT_PASS (pass_bprop_extends);
NEXT_PASS (pass_ccp);
NEXT_PASS (pass_forwprop);
NEXT_PASS (pass_call_cdce);
===================================================================
@@ -1999,3 +1999,263 @@ struct gimple_opt_pass pass_fold_builtin
| TODO_update_ssa /* todo_flags_finish */
}
};
+
+#if 1
+static bool
+promote_through_insn_p (gimple def, tree *prhs1, tree *prhs2)
+{
+ tree lhs, rhs1, rhs2;
+ if (!is_gimple_assign (def))
+ return false;
+ lhs = gimple_assign_lhs (def);
+ rhs1 = rhs2 = NULL;
+ switch (gimple_assign_rhs_class (def))
+ {
+ case GIMPLE_SINGLE_RHS:
+ rhs1 = gimple_assign_rhs1 (def);
+ if (TREE_CODE (rhs1) != SSA_NAME)
+ return false;
+ break;
+ case GIMPLE_UNARY_RHS:
+ rhs1 = gimple_assign_rhs1 (def);
+ if (TREE_TYPE (gimple_expr_type (def)) != TREE_TYPE (rhs1))
+ return false;
+ break;
+ case GIMPLE_BINARY_RHS:
+ rhs1 = gimple_assign_rhs1 (def);
+ rhs2 = gimple_assign_rhs2 (def);
+
+ switch (gimple_assign_rhs_code (def))
+ {
+ case LSHIFT_EXPR: case RSHIFT_EXPR:
+ case LROTATE_EXPR: case RROTATE_EXPR:
+ rhs2 = NULL;
+ if (TREE_TYPE (lhs) != TREE_TYPE (rhs1))
+ return false;
+ break;
+ case PLUS_EXPR: case MINUS_EXPR:
+ case MULT_EXPR: case EXACT_DIV_EXPR:
+ case TRUNC_DIV_EXPR: case CEIL_DIV_EXPR:
+ case FLOOR_DIV_EXPR: case ROUND_DIV_EXPR:
+ case TRUNC_MOD_EXPR: case CEIL_MOD_EXPR:
+ case FLOOR_MOD_EXPR: case ROUND_MOD_EXPR:
+ case RDIV_EXPR:
+ case MIN_EXPR: case MAX_EXPR:
+ case BIT_IOR_EXPR: case BIT_XOR_EXPR: case BIT_AND_EXPR:
+ if (TREE_TYPE (lhs) != TREE_TYPE (rhs1)
+ || TREE_TYPE (lhs) != TREE_TYPE (rhs2))
+ return false;
+ break;
+ default:
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+ if (rhs1 && TREE_CODE (rhs1) != SSA_NAME)
+ rhs1 = NULL;
+ if (prhs1)
+ *prhs1 = rhs1;
+ if (rhs2 && TREE_CODE (rhs2) != SSA_NAME)
+ rhs2 = NULL;
+ if (prhs2)
+ *prhs2 = rhs2;
+ return true;
+}
+
+static tree
+get_extended_version (tree newtype, tree name, bool force)
+{
+ tree ret = TREE_CHAIN (name);
+ tree rhs1, rhs2;
+ gimple def;
+ /* If we already have a version of NAME, try to use it. If it
+ doesn't match in type, fail. */
+ if (ret)
+ {
+ if (TREE_TYPE (ret) == newtype)
+ return ret;
+ else
+ return NULL_TREE;
+ }
+ def = SSA_NAME_DEF_STMT (name);
+ /* If we can propagate through our defining insn, try to do that. */
+ if (promote_through_insn_p (def, &rhs1, &rhs2))
+ {
+ gimple stmt;
+ tree extrhs1, extrhs2;
+ gimple_stmt_iterator gsi;
+ enum tree_code code;
+ if (rhs1)
+ {
+ extrhs1 = get_extended_version (newtype, rhs1, true);
+ if (!extrhs1)
+ /* ??? We could force here. */
+ return NULL_TREE;
+ }
+ else
+ extrhs1 = gimple_assign_rhs1 (def);
+ if (extrhs1 && !useless_type_conversion_p (newtype, TREE_TYPE (extrhs1)))
+ {
+ gcc_assert (is_gimple_min_invariant (extrhs1));
+ extrhs1 = fold_convert (newtype, extrhs1);
+ }
+ if (rhs2)
+ {
+ extrhs2 = get_extended_version (newtype, rhs2, true);
+ if (!extrhs2)
+ return NULL_TREE;
+ }
+ else
+ extrhs2 = gimple_assign_rhs2 (def);
+ code = gimple_assign_rhs_code (def);
+ if (extrhs2
+ && code != LSHIFT_EXPR && code != RSHIFT_EXPR
+ && code != LROTATE_EXPR && code != RROTATE_EXPR
+ && !useless_type_conversion_p (newtype, TREE_TYPE (extrhs2)))
+ {
+ gcc_assert (is_gimple_min_invariant (extrhs2));
+ extrhs2 = fold_convert (newtype, extrhs2);
+ }
+ ret = create_tmp_reg (newtype, NULL);
+ add_referenced_var (ret);
+ ret = make_ssa_name (ret, NULL);
+ stmt = gimple_build_assign_with_ops (gimple_assign_rhs_code (def),
+ ret, extrhs1, extrhs2);
+ gsi = gsi_for_stmt (def);
+ gsi_insert_after (&gsi, stmt, GSI_NEW_STMT);
+ }
+ else if (force)
+ {
+ gimple stmt;
+ gimple_stmt_iterator gsi;
+ /* We can't propagate through our defining statement, so emit
+ a conversion explicitely. */
+ ret = create_tmp_reg (newtype, NULL);
+ add_referenced_var (ret);
+ ret = make_ssa_name (ret, NULL);
+ stmt = gimple_build_assign_with_ops (NOP_EXPR, ret, name, NULL);
+ if (gimple_nop_p (def))
+ {
+ gsi = gsi_after_labels (single_succ (ENTRY_BLOCK_PTR));
+ gsi_insert_before (&gsi, stmt, GSI_NEW_STMT);
+ }
+ else if (gimple_code (def) == GIMPLE_PHI)
+ {
+ gsi = gsi_after_labels (gimple_bb (def));
+ gsi_insert_before (&gsi, stmt, GSI_NEW_STMT);
+ }
+ else if (!stmt_ends_bb_p (def))
+ {
+ gsi = gsi_for_stmt (def);
+ gsi_insert_after (&gsi, stmt, GSI_NEW_STMT);
+ }
+ else
+ {
+ /* XXX We could search the fallthru edge, and insert there.
+ That's correct because if this statement ends the BB it
+ must be throwing (it can't be a conditional), and hence
+ the result (which we want to extend) actually is only
+ available on the fallthru edge. But inserting on edges
+ might create new basic blocks, and that doesn't seem
+ worth it. We could do that if the fallthru successor
+ has only one incoming edge. Actually we can be sure that
+ this is the case, as NAME can't be used in a PHI node
+ (we don't call ourself on them), hence it must be used
+ in something we dominate and therefore our fallthru successor
+ must have only one incoming edge. */
+#if 0
+ edge e;
+ edge_iterator ei;
+
+ FOR_EACH_EDGE (e, ei, gimple_bb (def)->succs)
+ if (e->flags & EDGE_FALLTHRU)
+ gsi_insert_on_edge_immediate (e, sum);
+#endif
+ return NULL_TREE;
+ }
+ }
+ TREE_CHAIN (name) = ret;
+ return ret;
+}
+
+static unsigned int
+execute_bprop_extends (void)
+{
+ unsigned int todoflags = 0;
+ size_t i;
+ /*bitmap_iterator bi;*/
+ bitmap new_extend = BITMAP_ALLOC (NULL);
+
+ /* Collect SSA names that we'd like to have in a wider type. */
+ for (i = 0; i < num_ssa_names; i++)
+ {
+ tree lhs = ssa_name (i);
+ if (lhs)
+ {
+ gimple def = SSA_NAME_DEF_STMT (lhs);
+ tree rhs;
+ TREE_CHAIN (lhs) = NULL;
+ if (!is_gimple_assign (def)
+ || !CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (def)))
+ continue;
+ rhs = gimple_assign_rhs1 (def);
+ /* We could handle int->pointer conversions with some care
+ through which insns we look through. Don't bother for now. */
+ if (POINTER_TYPE_P (TREE_TYPE (lhs))
+ || POINTER_TYPE_P (TREE_TYPE (rhs)))
+ continue;
+ if (TYPE_PRECISION (TREE_TYPE (lhs))
+ <= TYPE_PRECISION (TREE_TYPE (rhs)))
+ continue;
+ if (!nowrap_type_p (TREE_TYPE (rhs)))
+ continue;
+ bitmap_set_bit (new_extend, SSA_NAME_VERSION (lhs));
+ }
+ }
+
+ while (!bitmap_empty_p (new_extend))
+ {
+ tree lhs, rhs, extlhs;
+ gimple def;
+ i = bitmap_first_set_bit (new_extend);
+ bitmap_clear_bit (new_extend, i);
+ lhs = ssa_name (i);
+ gcc_assert (lhs);
+ def = SSA_NAME_DEF_STMT (lhs);
+ rhs = gimple_assign_rhs1 (def);
+ extlhs = get_extended_version (TREE_TYPE (lhs), rhs, false);
+ if (extlhs)
+ {
+ gimple_assign_set_rhs1 (def, extlhs);
+ gimple_assign_set_rhs_code (def, SSA_NAME);
+ update_stmt (def);
+ }
+ }
+
+ BITMAP_FREE (new_extend);
+ return todoflags;
+}
+
+struct gimple_opt_pass pass_bprop_extends =
+{
+ {
+ GIMPLE_PASS,
+ "bprop", /* name */
+ NULL, /* gate */
+ execute_bprop_extends, /* execute */
+ NULL, /* sub */
+ NULL, /* next */
+ 0, /* static_pass_number */
+ TV_NONE, /* tv_id */
+ PROP_cfg | PROP_ssa, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_dump_func
+ | TODO_verify_ssa
+ | TODO_update_ssa /* todo_flags_finish */
+ }
+};
+#endif