/* Forward propagation of expressions for single use variables.
- Copyright (C) 2004, 2005, 2007, 2008, 2009, 2010, 2011
- Free Software Foundation, Inc.
+ Copyright (C) 2004-2013 Free Software Foundation, Inc.
This file is part of GCC.
#include "tree.h"
#include "tm_p.h"
#include "basic-block.h"
-#include "timevar.h"
#include "gimple-pretty-print.h"
#include "tree-flow.h"
#include "tree-pass.h"
-#include "tree-dump.h"
#include "langhooks.h"
#include "flags.h"
#include "gimple.h"
#include "expr.h"
+#include "cfgloop.h"
+#include "optabs.h"
+#include "tree-ssa-propagate.h"
/* This pass propagates the RHS of assignment statements into use
sites of the LHS of the assignment. It's basically a specialized
static bool forward_propagate_addr_expr (tree name, tree rhs);
-/* Set to true if we delete EH edges during the optimization. */
+/* Set to true if we delete dead edges during the optimization. */
static bool cfg_changed;
static tree rhs_to_tree (tree type, gimple stmt);
if (!is_gimple_assign (def_stmt))
return NULL;
- /* If def_stmt is not a simple copy, we possibly found it. */
- if (!gimple_assign_ssa_name_copy_p (def_stmt))
+ /* If def_stmt is a simple copy, continue looking. */
+ if (gimple_assign_rhs_code (def_stmt) == SSA_NAME)
+ name = gimple_assign_rhs1 (def_stmt);
+ else
{
- tree rhs;
-
if (!single_use_only && single_use_p)
*single_use_p = single_use;
- /* We can look through pointer conversions in the search
- for a useful stmt for the comparison folding. */
- rhs = gimple_assign_rhs1 (def_stmt);
- if (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (def_stmt))
- && TREE_CODE (rhs) == SSA_NAME
- && POINTER_TYPE_P (TREE_TYPE (gimple_assign_lhs (def_stmt)))
- && POINTER_TYPE_P (TREE_TYPE (rhs)))
- name = rhs;
- else
- return def_stmt;
- }
- else
- {
- /* Continue searching the def of the copy source name. */
- name = gimple_assign_rhs1 (def_stmt);
+ return def_stmt;
}
} while (1);
}
bb = gimple_bb (stmt);
gsi = gsi_for_stmt (stmt);
unlink_stmt_vdef (stmt);
- gsi_remove (&gsi, true);
+ if (gsi_remove (&gsi, true))
+ cfg_changed |= gimple_purge_dead_eh_edges (bb);
release_defs (stmt);
- cfg_changed |= gimple_purge_dead_eh_edges (bb);
name = is_gimple_assign (stmt) ? gimple_assign_rhs1 (stmt) : NULL_TREE;
} while (name && TREE_CODE (name) == SSA_NAME);
gimple stmt = gsi_stmt (*gsi_p);
tree tmp = NULL_TREE;
tree cond = gimple_assign_rhs1 (stmt);
+ enum tree_code code = gimple_assign_rhs_code (stmt);
bool swap = false;
/* We can do tree combining on SSA_NAME and comparison expressions. */
if (COMPARISON_CLASS_P (cond))
tmp = forward_propagate_into_comparison_1 (stmt, TREE_CODE (cond),
- boolean_type_node,
+ TREE_TYPE (cond),
TREE_OPERAND (cond, 0),
TREE_OPERAND (cond, 1));
else if (TREE_CODE (cond) == SSA_NAME)
{
- enum tree_code code;
+ enum tree_code def_code;
tree name = cond;
gimple def_stmt = get_prop_source_stmt (name, true, NULL);
if (!def_stmt || !can_propagate_from (def_stmt))
return 0;
- code = gimple_assign_rhs_code (def_stmt);
- if (TREE_CODE_CLASS (code) == tcc_comparison)
+ def_code = gimple_assign_rhs_code (def_stmt);
+ if (TREE_CODE_CLASS (def_code) == tcc_comparison)
tmp = fold_build2_loc (gimple_location (def_stmt),
- code,
- boolean_type_node,
+ def_code,
+ TREE_TYPE (cond),
gimple_assign_rhs1 (def_stmt),
gimple_assign_rhs2 (def_stmt));
- else if ((code == BIT_NOT_EXPR
- && TYPE_PRECISION (TREE_TYPE (cond)) == 1)
- || (code == BIT_XOR_EXPR
- && integer_onep (gimple_assign_rhs2 (def_stmt))))
+ else if (code == COND_EXPR
+ && ((def_code == BIT_NOT_EXPR
+ && TYPE_PRECISION (TREE_TYPE (cond)) == 1)
+ || (def_code == BIT_XOR_EXPR
+ && integer_onep (gimple_assign_rhs2 (def_stmt)))))
{
tmp = gimple_assign_rhs1 (def_stmt);
swap = true;
fprintf (dump_file, "'\n");
}
- if (integer_onep (tmp))
+ if ((code == VEC_COND_EXPR) ? integer_all_onesp (tmp)
+ : integer_onep (tmp))
gimple_assign_set_rhs_from_tree (gsi_p, gimple_assign_rhs2 (stmt));
else if (integer_zerop (tmp))
gimple_assign_set_rhs_from_tree (gsi_p, gimple_assign_rhs3 (stmt));
return 0;
}
+/* Propagate from the ssa name definition statements of COND_EXPR
+ values in the rhs of statement STMT into the conditional arms
+ if that simplifies it.
+ Returns true if the stmt was changed. */
+
+static bool
+combine_cond_exprs (gimple_stmt_iterator *gsi_p)
+{
+ gimple stmt = gsi_stmt (*gsi_p);
+ tree cond, val1, val2;
+ bool changed = false;
+
+ cond = gimple_assign_rhs1 (stmt);
+ val1 = gimple_assign_rhs2 (stmt);
+ if (TREE_CODE (val1) == SSA_NAME)
+ {
+ gimple def_stmt = SSA_NAME_DEF_STMT (val1);
+ if (is_gimple_assign (def_stmt)
+ && gimple_assign_rhs_code (def_stmt) == gimple_assign_rhs_code (stmt)
+ && operand_equal_p (gimple_assign_rhs1 (def_stmt), cond, 0))
+ {
+ val1 = unshare_expr (gimple_assign_rhs2 (def_stmt));
+ gimple_assign_set_rhs2 (stmt, val1);
+ changed = true;
+ }
+ }
+ val2 = gimple_assign_rhs3 (stmt);
+ if (TREE_CODE (val2) == SSA_NAME)
+ {
+ gimple def_stmt = SSA_NAME_DEF_STMT (val2);
+ if (is_gimple_assign (def_stmt)
+ && gimple_assign_rhs_code (def_stmt) == gimple_assign_rhs_code (stmt)
+ && operand_equal_p (gimple_assign_rhs1 (def_stmt), cond, 0))
+ {
+ val2 = unshare_expr (gimple_assign_rhs3 (def_stmt));
+ gimple_assign_set_rhs3 (stmt, val2);
+ changed = true;
+ }
+ }
+ if (operand_equal_p (val1, val2, 0))
+ {
+ gimple_assign_set_rhs_from_tree (gsi_p, val1);
+ stmt = gsi_stmt (*gsi_p);
+ changed = true;
+ }
+
+ if (changed)
+ update_stmt (stmt);
+
+ return changed;
+}
+
/* We've just substituted an ADDR_EXPR into stmt. Update all the
relevant data structures to match. */
recompute_tree_invariant_for_addr_expr (gimple_assign_rhs1 (stmt));
}
-/* DEF_RHS contains the address of the 0th element in an array.
- USE_STMT uses type of DEF_RHS to compute the address of an
- arbitrary element within the array. The (variable) byte offset
- of the element is contained in OFFSET.
-
- We walk back through the use-def chains of OFFSET to verify that
- it is indeed computing the offset of an element within the array
- and extract the index corresponding to the given byte offset.
-
- We then try to fold the entire address expression into a form
- &array[index].
-
- If we are successful, we replace the right hand side of USE_STMT
- with the new address computation. */
-
-static bool
-forward_propagate_addr_into_variable_array_index (tree offset,
- tree def_rhs,
- gimple_stmt_iterator *use_stmt_gsi)
-{
- tree index, tunit;
- gimple offset_def, use_stmt = gsi_stmt (*use_stmt_gsi);
- tree new_rhs, tmp;
-
- if (TREE_CODE (TREE_OPERAND (def_rhs, 0)) == ARRAY_REF)
- tunit = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (def_rhs)));
- else if (TREE_CODE (TREE_TYPE (TREE_OPERAND (def_rhs, 0))) == ARRAY_TYPE)
- tunit = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (TREE_TYPE (def_rhs))));
- else
- return false;
- if (!host_integerp (tunit, 1))
- return false;
-
- /* Get the offset's defining statement. */
- offset_def = SSA_NAME_DEF_STMT (offset);
-
- /* Try to find an expression for a proper index. This is either a
- multiplication expression by the element size or just the ssa name we came
- along in case the element size is one. In that case, however, we do not
- allow multiplications because they can be computing index to a higher
- level dimension (PR 37861). */
- if (integer_onep (tunit))
- {
- if (is_gimple_assign (offset_def)
- && gimple_assign_rhs_code (offset_def) == MULT_EXPR)
- return false;
-
- index = offset;
- }
- else
- {
- /* The statement which defines OFFSET before type conversion
- must be a simple GIMPLE_ASSIGN. */
- if (!is_gimple_assign (offset_def))
- return false;
-
- /* The RHS of the statement which defines OFFSET must be a
- multiplication of an object by the size of the array elements.
- This implicitly verifies that the size of the array elements
- is constant. */
- if (gimple_assign_rhs_code (offset_def) == MULT_EXPR
- && TREE_CODE (gimple_assign_rhs2 (offset_def)) == INTEGER_CST
- && tree_int_cst_equal (gimple_assign_rhs2 (offset_def), tunit))
- {
- /* The first operand to the MULT_EXPR is the desired index. */
- index = gimple_assign_rhs1 (offset_def);
- }
- /* If we have idx * tunit + CST * tunit re-associate that. */
- else if ((gimple_assign_rhs_code (offset_def) == PLUS_EXPR
- || gimple_assign_rhs_code (offset_def) == MINUS_EXPR)
- && TREE_CODE (gimple_assign_rhs1 (offset_def)) == SSA_NAME
- && TREE_CODE (gimple_assign_rhs2 (offset_def)) == INTEGER_CST
- && (tmp = div_if_zero_remainder (EXACT_DIV_EXPR,
- gimple_assign_rhs2 (offset_def),
- tunit)) != NULL_TREE)
- {
- gimple offset_def2 = SSA_NAME_DEF_STMT (gimple_assign_rhs1 (offset_def));
- if (is_gimple_assign (offset_def2)
- && gimple_assign_rhs_code (offset_def2) == MULT_EXPR
- && TREE_CODE (gimple_assign_rhs2 (offset_def2)) == INTEGER_CST
- && tree_int_cst_equal (gimple_assign_rhs2 (offset_def2), tunit))
- {
- index = fold_build2 (gimple_assign_rhs_code (offset_def),
- TREE_TYPE (offset),
- gimple_assign_rhs1 (offset_def2), tmp);
- }
- else
- return false;
- }
- else
- return false;
- }
-
- /* Replace the pointer addition with array indexing. */
- index = force_gimple_operand_gsi (use_stmt_gsi, index, true, NULL_TREE,
- true, GSI_SAME_STMT);
- if (TREE_CODE (TREE_OPERAND (def_rhs, 0)) == ARRAY_REF)
- {
- new_rhs = unshare_expr (def_rhs);
- TREE_OPERAND (TREE_OPERAND (new_rhs, 0), 1) = index;
- }
- else
- {
- new_rhs = build4 (ARRAY_REF, TREE_TYPE (TREE_TYPE (TREE_TYPE (def_rhs))),
- unshare_expr (TREE_OPERAND (def_rhs, 0)),
- index, integer_zero_node, NULL_TREE);
- new_rhs = build_fold_addr_expr (new_rhs);
- if (!useless_type_conversion_p (TREE_TYPE (gimple_assign_lhs (use_stmt)),
- TREE_TYPE (new_rhs)))
- {
- new_rhs = force_gimple_operand_gsi (use_stmt_gsi, new_rhs, true,
- NULL_TREE, true, GSI_SAME_STMT);
- new_rhs = fold_convert (TREE_TYPE (gimple_assign_lhs (use_stmt)),
- new_rhs);
- }
- }
- gimple_assign_set_rhs_from_tree (use_stmt_gsi, new_rhs);
- fold_stmt (use_stmt_gsi);
- tidy_after_forward_propagate_addr (gsi_stmt (*use_stmt_gsi));
- return true;
-}
-
/* NAME is a SSA_NAME representing DEF_RHS which is of the form
ADDR_EXPR <whatever>.
{
double_int off = mem_ref_offset (lhs);
tree new_ptr;
- off = double_int_add (off,
- shwi_to_double_int (def_rhs_offset));
+ off += double_int::from_shwi (def_rhs_offset);
if (TREE_CODE (def_rhs_base) == MEM_REF)
{
- off = double_int_add (off, mem_ref_offset (def_rhs_base));
+ off += mem_ref_offset (def_rhs_base);
new_ptr = TREE_OPERAND (def_rhs_base, 0);
}
else
{
double_int off = mem_ref_offset (rhs);
tree new_ptr;
- off = double_int_add (off,
- shwi_to_double_int (def_rhs_offset));
+ off += double_int::from_shwi (def_rhs_offset);
if (TREE_CODE (def_rhs_base) == MEM_REF)
{
- off = double_int_add (off, mem_ref_offset (def_rhs_base));
+ off += mem_ref_offset (def_rhs_base);
new_ptr = TREE_OPERAND (def_rhs_base, 0);
}
else
return true;
}
- /* Try to optimize &x[0] p+ OFFSET where OFFSET is defined by
- converting a multiplication of an index by the size of the
- array elements, then the result is converted into the proper
- type for the arithmetic. */
- if (TREE_CODE (rhs2) == SSA_NAME
- && (TREE_CODE (array_ref) != ARRAY_REF
- || integer_zerop (TREE_OPERAND (array_ref, 1)))
- && useless_type_conversion_p (TREE_TYPE (name), TREE_TYPE (def_rhs))
- /* Avoid problems with IVopts creating PLUS_EXPRs with a
- different type than their operands. */
- && useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (def_rhs)))
- return forward_propagate_addr_into_variable_array_index (rhs2, def_rhs,
- use_stmt_gsi);
return false;
}
static bool
forward_propagate_addr_expr (tree name, tree rhs)
{
- int stmt_loop_depth = gimple_bb (SSA_NAME_DEF_STMT (name))->loop_depth;
+ int stmt_loop_depth = bb_loop_depth (gimple_bb (SSA_NAME_DEF_STMT (name)));
imm_use_iterator iter;
gimple use_stmt;
bool all = true;
/* If the use is in a deeper loop nest, then we do not want
to propagate non-invariant ADDR_EXPRs into the loop as that
is likely adding expression evaluations into the loop. */
- if (gimple_bb (use_stmt)->loop_depth > stmt_loop_depth
+ if (bb_loop_depth (gimple_bb (use_stmt)) > stmt_loop_depth
&& !is_gimple_min_invariant (rhs))
{
all = false;
}
-/* Forward propagate the comparison defined in STMT like
+/* Forward propagate the comparison defined in *DEFGSI like
cond_1 = x CMP y to uses of the form
a_1 = (T')cond_1
a_1 = !cond_1
a_1 = cond_1 != 0
- Returns true if stmt is now unused. */
+ Returns true if stmt is now unused. Advance DEFGSI to the next
+ statement. */
static bool
-forward_propagate_comparison (gimple stmt)
+forward_propagate_comparison (gimple_stmt_iterator *defgsi)
{
+ gimple stmt = gsi_stmt (*defgsi);
tree name = gimple_assign_lhs (stmt);
gimple use_stmt;
tree tmp = NULL_TREE;
&& SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_assign_rhs1 (stmt)))
|| (TREE_CODE (gimple_assign_rhs2 (stmt)) == SSA_NAME
&& SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_assign_rhs2 (stmt))))
- return false;
+ goto bailout;
/* Do not un-cse comparisons. But propagate through copies. */
use_stmt = get_prop_dest_stmt (name, &name);
if (!use_stmt
|| !is_gimple_assign (use_stmt))
- return false;
+ goto bailout;
code = gimple_assign_rhs_code (use_stmt);
lhs = gimple_assign_lhs (use_stmt);
if (!INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
- return false;
+ goto bailout;
/* We can propagate the condition into a statement that
computes the logical negation of the comparison result. */
enum tree_code inv_code;
inv_code = invert_tree_comparison (gimple_assign_rhs_code (stmt), nans);
if (inv_code == ERROR_MARK)
- return false;
+ goto bailout;
tmp = build2 (inv_code, TREE_TYPE (lhs), gimple_assign_rhs1 (stmt),
gimple_assign_rhs2 (stmt));
}
else
- return false;
+ goto bailout;
gsi = gsi_for_stmt (use_stmt);
gimple_assign_set_rhs_from_tree (&gsi, unshare_expr (tmp));
fprintf (dump_file, "'\n");
}
+ /* When we remove stmt now the iterator defgsi goes off it's current
+ sequence, hence advance it now. */
+ gsi_next (defgsi);
+
/* Remove defining statements. */
return remove_prop_source_from_use (name);
+
+bailout:
+ gsi_next (defgsi);
+ return false;
}
return false;
}
+/* Helper function for simplify_gimple_switch. Remove case labels that
+ have values outside the range of the new type. */
+
+static void
+simplify_gimple_switch_label_vec (gimple stmt, tree index_type)
+{
+ unsigned int branch_num = gimple_switch_num_labels (stmt);
+ vec<tree> labels;
+ labels.create (branch_num);
+ unsigned int i, len;
+
+ /* Collect the existing case labels in a VEC, and preprocess it as if
+ we are gimplifying a GENERIC SWITCH_EXPR. */
+ for (i = 1; i < branch_num; i++)
+ labels.quick_push (gimple_switch_label (stmt, i));
+ preprocess_case_label_vec_for_gimple (labels, index_type, NULL);
+
+ /* If any labels were removed, replace the existing case labels
+ in the GIMPLE_SWITCH statement with the correct ones.
+ Note that the type updates were done in-place on the case labels,
+ so we only have to replace the case labels in the GIMPLE_SWITCH
+ if the number of labels changed. */
+ len = labels.length ();
+ if (len < branch_num - 1)
+ {
+ bitmap target_blocks;
+ edge_iterator ei;
+ edge e;
+
+ /* Corner case: *all* case labels have been removed as being
+ out-of-range for INDEX_TYPE. Push one label and let the
+ CFG cleanups deal with this further. */
+ if (len == 0)
+ {
+ tree label, elt;
+
+ label = CASE_LABEL (gimple_switch_default_label (stmt));
+ elt = build_case_label (build_int_cst (index_type, 0), NULL, label);
+ labels.quick_push (elt);
+ len = 1;
+ }
+
+ for (i = 0; i < labels.length (); i++)
+ gimple_switch_set_label (stmt, i + 1, labels[i]);
+ for (i++ ; i < branch_num; i++)
+ gimple_switch_set_label (stmt, i, NULL_TREE);
+ gimple_switch_set_num_labels (stmt, len + 1);
+
+ /* Cleanup any edges that are now dead. */
+ target_blocks = BITMAP_ALLOC (NULL);
+ for (i = 0; i < gimple_switch_num_labels (stmt); i++)
+ {
+ tree elt = gimple_switch_label (stmt, i);
+ basic_block target = label_to_block (CASE_LABEL (elt));
+ bitmap_set_bit (target_blocks, target->index);
+ }
+ for (ei = ei_start (gimple_bb (stmt)->succs); (e = ei_safe_edge (ei)); )
+ {
+ if (! bitmap_bit_p (target_blocks, e->dest->index))
+ {
+ remove_edge (e);
+ cfg_changed = true;
+ free_dominance_info (CDI_DOMINATORS);
+ }
+ else
+ ei_next (&ei);
+ }
+ BITMAP_FREE (target_blocks);
+ }
+
+ labels.release ();
+}
+
/* STMT is a SWITCH_EXPR for which we attempt to find equivalent forms of
the condition which we may be able to optimize better. */
def = gimple_assign_rhs1 (def_stmt);
- /* ??? Why was Jeff testing this? We are gimple... */
- gcc_checking_assert (is_gimple_val (def));
-
to = TREE_TYPE (cond);
ti = TREE_TYPE (def);
if (!fail)
{
gimple_switch_set_index (stmt, def);
+ simplify_gimple_switch_label_vec (stmt, ti);
update_stmt (stmt);
return true;
}
else
src_buf[0] = tree_low_cst (src1, 0);
memset (src_buf + tree_low_cst (diff, 1),
- tree_low_cst (val2, 1), tree_low_cst (len2, 1));
+ tree_low_cst (val2, 0), tree_low_cst (len2, 1));
src_buf[src_len] = '\0';
/* Neither builtin_strncpy_read_str nor builtin_memcpy_read_str
handle embedded '\0's. */
return NULL_TREE;
}
+/* Given a ssa_name in NAME see if it was defined by an assignment and
+ set CODE to be the code and ARG1 to the first operand on the rhs and ARG2
+ to the second operand on the rhs. */
+
+static inline void
+defcodefor_name (tree name, enum tree_code *code, tree *arg1, tree *arg2)
+{
+ gimple def;
+ enum tree_code code1;
+ tree arg11;
+ tree arg21;
+ tree arg31;
+ enum gimple_rhs_class grhs_class;
+
+ code1 = TREE_CODE (name);
+ arg11 = name;
+ arg21 = NULL_TREE;
+ grhs_class = get_gimple_rhs_class (code1);
+
+ if (code1 == SSA_NAME)
+ {
+ def = SSA_NAME_DEF_STMT (name);
+
+ if (def && is_gimple_assign (def)
+ && can_propagate_from (def))
+ {
+ code1 = gimple_assign_rhs_code (def);
+ arg11 = gimple_assign_rhs1 (def);
+ arg21 = gimple_assign_rhs2 (def);
+ arg31 = gimple_assign_rhs2 (def);
+ }
+ }
+ else if (grhs_class == GIMPLE_TERNARY_RHS
+ || GIMPLE_BINARY_RHS
+ || GIMPLE_UNARY_RHS
+ || GIMPLE_SINGLE_RHS)
+ extract_ops_from_tree_1 (name, &code1, &arg11, &arg21, &arg31);
+
+ *code = code1;
+ *arg1 = arg11;
+ if (arg2)
+ *arg2 = arg21;
+ /* Ignore arg3 currently. */
+}
+
+/* Return true if a conversion of an operand from type FROM to type TO
+ should be applied after performing the operation instead. */
+
+static bool
+hoist_conversion_for_bitop_p (tree to, tree from)
+{
+ /* That's a good idea if the conversion widens the operand, thus
+ after hoisting the conversion the operation will be narrower. */
+ if (TYPE_PRECISION (from) < TYPE_PRECISION (to))
+ return true;
+
+ /* It's also a good idea if the conversion is to a non-integer mode. */
+ if (GET_MODE_CLASS (TYPE_MODE (to)) != MODE_INT)
+ return true;
+
+ /* Or if the precision of TO is not the same as the precision
+ of its mode. */
+ if (TYPE_PRECISION (to) != GET_MODE_PRECISION (TYPE_MODE (to)))
+ return true;
+
+ return false;
+}
+
/* Simplify bitwise binary operations.
Return true if a transformation applied, otherwise return false. */
tree arg2 = gimple_assign_rhs2 (stmt);
enum tree_code code = gimple_assign_rhs_code (stmt);
tree res;
- gimple def1 = NULL, def2 = NULL;
- tree def1_arg1, def2_arg1;
+ tree def1_arg1, def1_arg2, def2_arg1, def2_arg2;
enum tree_code def1_code, def2_code;
- def1_code = TREE_CODE (arg1);
- def1_arg1 = arg1;
- if (TREE_CODE (arg1) == SSA_NAME)
- {
- def1 = SSA_NAME_DEF_STMT (arg1);
- if (is_gimple_assign (def1))
- {
- def1_code = gimple_assign_rhs_code (def1);
- def1_arg1 = gimple_assign_rhs1 (def1);
- }
- }
+ defcodefor_name (arg1, &def1_code, &def1_arg1, &def1_arg2);
+ defcodefor_name (arg2, &def2_code, &def2_arg1, &def2_arg2);
- def2_code = TREE_CODE (arg2);
- def2_arg1 = arg2;
- if (TREE_CODE (arg2) == SSA_NAME)
- {
- def2 = SSA_NAME_DEF_STMT (arg2);
- if (is_gimple_assign (def2))
- {
- def2_code = gimple_assign_rhs_code (def2);
- def2_arg1 = gimple_assign_rhs1 (def2);
- }
- }
-
- /* Try to fold (type) X op CST -> (type) (X op ((type-x) CST)). */
+ /* Try to fold (type) X op CST -> (type) (X op ((type-x) CST))
+ when profitable. */
if (TREE_CODE (arg2) == INTEGER_CST
&& CONVERT_EXPR_CODE_P (def1_code)
+ && hoist_conversion_for_bitop_p (TREE_TYPE (arg1), TREE_TYPE (def1_arg1))
&& INTEGRAL_TYPE_P (TREE_TYPE (def1_arg1))
&& int_fits_type_p (arg2, TREE_TYPE (def1_arg1)))
{
gimple newop;
- tree tem = create_tmp_reg (TREE_TYPE (def1_arg1), NULL);
+ tree tem = make_ssa_name (TREE_TYPE (def1_arg1), NULL);
newop =
gimple_build_assign_with_ops (code, tem, def1_arg1,
fold_convert_loc (gimple_location (stmt),
TREE_TYPE (def1_arg1),
arg2));
- tem = make_ssa_name (tem, newop);
- gimple_assign_set_lhs (newop, tem);
gimple_set_location (newop, gimple_location (stmt));
gsi_insert_before (gsi, newop, GSI_SAME_STMT);
gimple_assign_set_rhs_with_ops_1 (gsi, NOP_EXPR,
if (CONVERT_EXPR_CODE_P (def1_code)
&& CONVERT_EXPR_CODE_P (def2_code)
&& types_compatible_p (TREE_TYPE (def1_arg1), TREE_TYPE (def2_arg1))
- /* Make sure that the conversion widens the operands, or has same
- precision, or that it changes the operation to a bitfield
- precision. */
- && ((TYPE_PRECISION (TREE_TYPE (def1_arg1))
- <= TYPE_PRECISION (TREE_TYPE (arg1)))
- || (GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (arg1)))
- != MODE_INT)
- || (TYPE_PRECISION (TREE_TYPE (arg1))
- != GET_MODE_PRECISION (TYPE_MODE (TREE_TYPE (arg1))))))
+ && hoist_conversion_for_bitop_p (TREE_TYPE (arg1), TREE_TYPE (def1_arg1)))
{
gimple newop;
- tree tem = create_tmp_reg (TREE_TYPE (def1_arg1),
- NULL);
+ tree tem = make_ssa_name (TREE_TYPE (def1_arg1), NULL);
newop = gimple_build_assign_with_ops (code, tem, def1_arg1, def2_arg1);
- tem = make_ssa_name (tem, newop);
- gimple_assign_set_lhs (newop, tem);
gimple_set_location (newop, gimple_location (stmt));
gsi_insert_before (gsi, newop, GSI_SAME_STMT);
gimple_assign_set_rhs_with_ops_1 (gsi, NOP_EXPR,
return true;
}
+
+ /* Simplify (A & B) OP0 (C & B) to (A OP0 C) & B. */
+ if (def1_code == def2_code
+ && def1_code == BIT_AND_EXPR
+ && operand_equal_for_phi_arg_p (def1_arg2,
+ def2_arg2))
+ {
+ tree b = def1_arg2;
+ tree a = def1_arg1;
+ tree c = def2_arg1;
+ tree inner = fold_build2 (code, TREE_TYPE (arg2), a, c);
+ /* If A OP0 C (this usually means C is the same as A) is 0
+ then fold it down correctly. */
+ if (integer_zerop (inner))
+ {
+ gimple_assign_set_rhs_from_tree (gsi, inner);
+ update_stmt (stmt);
+ return true;
+ }
+ /* If A OP0 C (this usually means C is the same as A) is a ssa_name
+ then fold it down correctly. */
+ else if (TREE_CODE (inner) == SSA_NAME)
+ {
+ tree outer = fold_build2 (def1_code, TREE_TYPE (inner),
+ inner, b);
+ gimple_assign_set_rhs_from_tree (gsi, outer);
+ update_stmt (stmt);
+ return true;
+ }
+ else
+ {
+ gimple newop;
+ tree tem;
+ tem = make_ssa_name (TREE_TYPE (arg2), NULL);
+ newop = gimple_build_assign_with_ops (code, tem, a, c);
+ gimple_set_location (newop, gimple_location (stmt));
+ /* Make sure to re-process the new stmt as it's walking upwards. */
+ gsi_insert_before (gsi, newop, GSI_NEW_STMT);
+ gimple_assign_set_rhs1 (stmt, tem);
+ gimple_assign_set_rhs2 (stmt, b);
+ gimple_assign_set_rhs_code (stmt, def1_code);
+ update_stmt (stmt);
+ return true;
+ }
+ }
+
/* (a | CST1) & CST2 -> (a & CST2) | (CST1 & CST2). */
if (code == BIT_AND_EXPR
&& def1_code == BIT_IOR_EXPR
&& TREE_CODE (arg2) == INTEGER_CST
- && TREE_CODE (gimple_assign_rhs2 (def1)) == INTEGER_CST)
+ && TREE_CODE (def1_arg2) == INTEGER_CST)
{
tree cst = fold_build2 (BIT_AND_EXPR, TREE_TYPE (arg2),
- arg2, gimple_assign_rhs2 (def1));
+ arg2, def1_arg2);
tree tem;
gimple newop;
if (integer_zerop (cst))
update_stmt (stmt);
return true;
}
- tem = create_tmp_reg (TREE_TYPE (arg2), NULL);
+ tem = make_ssa_name (TREE_TYPE (arg2), NULL);
newop = gimple_build_assign_with_ops (BIT_AND_EXPR,
tem, def1_arg1, arg2);
- tem = make_ssa_name (tem, newop);
- gimple_assign_set_lhs (newop, tem);
gimple_set_location (newop, gimple_location (stmt));
/* Make sure to re-process the new stmt as it's walking upwards. */
gsi_insert_before (gsi, newop, GSI_NEW_STMT);
|| code == BIT_XOR_EXPR)
&& def1_code == code
&& TREE_CODE (arg2) == INTEGER_CST
- && TREE_CODE (gimple_assign_rhs2 (def1)) == INTEGER_CST)
+ && TREE_CODE (def1_arg2) == INTEGER_CST)
{
tree cst = fold_build2 (code, TREE_TYPE (arg2),
- arg2, gimple_assign_rhs2 (def1));
+ arg2, def1_arg2);
gimple_assign_set_rhs1 (stmt, def1_arg1);
gimple_assign_set_rhs2 (stmt, cst);
update_stmt (stmt);
return true;
}
+ if (code == BIT_AND_EXPR || code == BIT_IOR_EXPR)
+ {
+ enum tree_code ocode = code == BIT_AND_EXPR ? BIT_IOR_EXPR : BIT_AND_EXPR;
+ if (def1_code == ocode)
+ {
+ tree x = arg2;
+ enum tree_code coden;
+ tree a1, a2;
+ /* ( X | Y) & X -> X */
+ /* ( X & Y) | X -> X */
+ if (x == def1_arg1
+ || x == def1_arg2)
+ {
+ gimple_assign_set_rhs_from_tree (gsi, x);
+ update_stmt (gsi_stmt (*gsi));
+ return true;
+ }
+
+ defcodefor_name (def1_arg1, &coden, &a1, &a2);
+ /* (~X | Y) & X -> X & Y */
+ /* (~X & Y) | X -> X | Y */
+ if (coden == BIT_NOT_EXPR && a1 == x)
+ {
+ gimple_assign_set_rhs_with_ops (gsi, code,
+ x, def1_arg2);
+ gcc_assert (gsi_stmt (*gsi) == stmt);
+ update_stmt (stmt);
+ return true;
+ }
+ defcodefor_name (def1_arg2, &coden, &a1, &a2);
+ /* (Y | ~X) & X -> X & Y */
+ /* (Y & ~X) | X -> X | Y */
+ if (coden == BIT_NOT_EXPR && a1 == x)
+ {
+ gimple_assign_set_rhs_with_ops (gsi, code,
+ x, def1_arg1);
+ gcc_assert (gsi_stmt (*gsi) == stmt);
+ update_stmt (stmt);
+ return true;
+ }
+ }
+ if (def2_code == ocode)
+ {
+ enum tree_code coden;
+ tree a1;
+ tree x = arg1;
+ /* X & ( X | Y) -> X */
+ /* X | ( X & Y) -> X */
+ if (x == def2_arg1
+ || x == def2_arg2)
+ {
+ gimple_assign_set_rhs_from_tree (gsi, x);
+ update_stmt (gsi_stmt (*gsi));
+ return true;
+ }
+ defcodefor_name (def2_arg1, &coden, &a1, NULL);
+ /* (~X | Y) & X -> X & Y */
+ /* (~X & Y) | X -> X | Y */
+ if (coden == BIT_NOT_EXPR && a1 == x)
+ {
+ gimple_assign_set_rhs_with_ops (gsi, code,
+ x, def2_arg2);
+ gcc_assert (gsi_stmt (*gsi) == stmt);
+ update_stmt (stmt);
+ return true;
+ }
+ defcodefor_name (def2_arg2, &coden, &a1, NULL);
+ /* (Y | ~X) & X -> X & Y */
+ /* (Y & ~X) | X -> X | Y */
+ if (coden == BIT_NOT_EXPR && a1 == x)
+ {
+ gimple_assign_set_rhs_with_ops (gsi, code,
+ x, def2_arg1);
+ gcc_assert (gsi_stmt (*gsi) == stmt);
+ update_stmt (stmt);
+ return true;
+ }
+ }
+ }
+
return false;
}
return false;
}
+/* Associate operands of a POINTER_PLUS_EXPR assignmen at *GSI. Returns
+ true if anything changed, false otherwise. */
+
+static bool
+associate_pointerplus (gimple_stmt_iterator *gsi)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ gimple def_stmt;
+ tree ptr, rhs, algn;
+
+ /* Pattern match
+ tem = (sizetype) ptr;
+ tem = tem & algn;
+ tem = -tem;
+ ... = ptr p+ tem;
+ and produce the simpler and easier to analyze with respect to alignment
+ ... = ptr & ~algn; */
+ ptr = gimple_assign_rhs1 (stmt);
+ rhs = gimple_assign_rhs2 (stmt);
+ if (TREE_CODE (rhs) != SSA_NAME)
+ return false;
+ def_stmt = SSA_NAME_DEF_STMT (rhs);
+ if (!is_gimple_assign (def_stmt)
+ || gimple_assign_rhs_code (def_stmt) != NEGATE_EXPR)
+ return false;
+ rhs = gimple_assign_rhs1 (def_stmt);
+ if (TREE_CODE (rhs) != SSA_NAME)
+ return false;
+ def_stmt = SSA_NAME_DEF_STMT (rhs);
+ if (!is_gimple_assign (def_stmt)
+ || gimple_assign_rhs_code (def_stmt) != BIT_AND_EXPR)
+ return false;
+ rhs = gimple_assign_rhs1 (def_stmt);
+ algn = gimple_assign_rhs2 (def_stmt);
+ if (TREE_CODE (rhs) != SSA_NAME
+ || TREE_CODE (algn) != INTEGER_CST)
+ return false;
+ def_stmt = SSA_NAME_DEF_STMT (rhs);
+ if (!is_gimple_assign (def_stmt)
+ || !CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (def_stmt)))
+ return false;
+ if (gimple_assign_rhs1 (def_stmt) != ptr)
+ return false;
+
+ algn = double_int_to_tree (TREE_TYPE (ptr), ~tree_to_double_int (algn));
+ gimple_assign_set_rhs_with_ops (gsi, BIT_AND_EXPR, ptr, algn);
+ fold_stmt_inplace (gsi);
+ update_stmt (stmt);
+
+ return true;
+}
+
/* Combine two conversions in a row for the second conversion at *GSI.
Returns 1 if there were any changes made, 2 if cfg-cleanup needs to
run. Else it returns 0. */
gimple def_stmt;
tree op0, lhs;
enum tree_code code = gimple_assign_rhs_code (stmt);
+ enum tree_code code2;
gcc_checking_assert (CONVERT_EXPR_CODE_P (code)
|| code == FLOAT_EXPR
if (!is_gimple_assign (def_stmt))
return 0;
- if (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (def_stmt)))
+ code2 = gimple_assign_rhs_code (def_stmt);
+
+ if (CONVERT_EXPR_CODE_P (code2) || code2 == FLOAT_EXPR)
{
tree defop0 = gimple_assign_rhs1 (def_stmt);
tree type = TREE_TYPE (lhs);
&& inter_prec >= inside_prec
&& (inter_float || inter_vec
|| inter_unsignedp == inside_unsignedp)
- && ! (final_prec != GET_MODE_BITSIZE (TYPE_MODE (type))
+ && ! (final_prec != GET_MODE_PRECISION (TYPE_MODE (type))
&& TYPE_MODE (type) == TYPE_MODE (inter_type))
&& ! final_ptr
&& (! final_vec || inter_prec == inside_prec))
}
/* If we have a sign-extension of a zero-extended value, we can
- replace that by a single zero-extension. */
+ replace that by a single zero-extension. Likewise if the
+ final conversion does not change precision we can drop the
+ intermediate conversion. */
if (inside_int && inter_int && final_int
- && inside_prec < inter_prec && inter_prec < final_prec
- && inside_unsignedp && !inter_unsignedp)
+ && ((inside_prec < inter_prec && inter_prec < final_prec
+ && inside_unsignedp && !inter_unsignedp)
+ || final_prec == inter_prec))
{
gimple_assign_set_rhs1 (stmt, defop0);
update_stmt (stmt);
== (final_unsignedp && final_prec > inter_prec))
&& ! (inside_ptr && inter_prec != final_prec)
&& ! (final_ptr && inside_prec != inter_prec)
- && ! (final_prec != GET_MODE_BITSIZE (TYPE_MODE (type))
+ && ! (final_prec != GET_MODE_PRECISION (TYPE_MODE (type))
&& TYPE_MODE (type) == TYPE_MODE (inter_type)))
{
gimple_assign_set_rhs1 (stmt, defop0);
tem = fold_build2 (BIT_AND_EXPR, inside_type,
defop0,
double_int_to_tree
- (inside_type, double_int_mask (inter_prec)));
+ (inside_type, double_int::mask (inter_prec)));
if (!useless_type_conversion_p (type, inside_type))
{
tem = force_gimple_operand_gsi (gsi, tem, true, NULL_TREE, true,
update_stmt (gsi_stmt (*gsi));
return 1;
}
+
+ /* If we are converting an integer to a floating-point that can
+ represent it exactly and back to an integer, we can skip the
+ floating-point conversion. */
+ if (inside_int && inter_float && final_int &&
+ (unsigned) significand_size (TYPE_MODE (inter_type))
+ >= inside_prec - !inside_unsignedp)
+ {
+ if (useless_type_conversion_p (type, inside_type))
+ {
+ gimple_assign_set_rhs1 (stmt, unshare_expr (defop0));
+ gimple_assign_set_rhs_code (stmt, TREE_CODE (defop0));
+ update_stmt (stmt);
+ return remove_prop_source_from_use (op0) ? 2 : 1;
+ }
+ else
+ {
+ gimple_assign_set_rhs1 (stmt, defop0);
+ gimple_assign_set_rhs_code (stmt, CONVERT_EXPR);
+ update_stmt (stmt);
+ return remove_prop_source_from_use (op0) ? 2 : 1;
+ }
+ }
}
return 0;
}
+/* Combine an element access with a shuffle. Returns true if there were
+ any changes made, else it returns false. */
+
+static bool
+simplify_bitfield_ref (gimple_stmt_iterator *gsi)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ gimple def_stmt;
+ tree op, op0, op1, op2;
+ tree elem_type;
+ unsigned idx, n, size;
+ enum tree_code code;
+
+ op = gimple_assign_rhs1 (stmt);
+ gcc_checking_assert (TREE_CODE (op) == BIT_FIELD_REF);
+
+ op0 = TREE_OPERAND (op, 0);
+ if (TREE_CODE (op0) != SSA_NAME
+ || TREE_CODE (TREE_TYPE (op0)) != VECTOR_TYPE)
+ return false;
+
+ def_stmt = get_prop_source_stmt (op0, false, NULL);
+ if (!def_stmt || !can_propagate_from (def_stmt))
+ return false;
+
+ op1 = TREE_OPERAND (op, 1);
+ op2 = TREE_OPERAND (op, 2);
+ code = gimple_assign_rhs_code (def_stmt);
+
+ if (code == CONSTRUCTOR)
+ {
+ tree tem = fold_ternary (BIT_FIELD_REF, TREE_TYPE (op),
+ gimple_assign_rhs1 (def_stmt), op1, op2);
+ if (!tem || !valid_gimple_rhs_p (tem))
+ return false;
+ gimple_assign_set_rhs_from_tree (gsi, tem);
+ update_stmt (gsi_stmt (*gsi));
+ return true;
+ }
+
+ elem_type = TREE_TYPE (TREE_TYPE (op0));
+ if (TREE_TYPE (op) != elem_type)
+ return false;
+
+ size = TREE_INT_CST_LOW (TYPE_SIZE (elem_type));
+ n = TREE_INT_CST_LOW (op1) / size;
+ if (n != 1)
+ return false;
+ idx = TREE_INT_CST_LOW (op2) / size;
+
+ if (code == VEC_PERM_EXPR)
+ {
+ tree p, m, index, tem;
+ unsigned nelts;
+ m = gimple_assign_rhs3 (def_stmt);
+ if (TREE_CODE (m) != VECTOR_CST)
+ return false;
+ nelts = VECTOR_CST_NELTS (m);
+ idx = TREE_INT_CST_LOW (VECTOR_CST_ELT (m, idx));
+ idx %= 2 * nelts;
+ if (idx < nelts)
+ {
+ p = gimple_assign_rhs1 (def_stmt);
+ }
+ else
+ {
+ p = gimple_assign_rhs2 (def_stmt);
+ idx -= nelts;
+ }
+ index = build_int_cst (TREE_TYPE (TREE_TYPE (m)), idx * size);
+ tem = build3 (BIT_FIELD_REF, TREE_TYPE (op),
+ unshare_expr (p), op1, index);
+ gimple_assign_set_rhs1 (stmt, tem);
+ fold_stmt (gsi);
+ update_stmt (gsi_stmt (*gsi));
+ return true;
+ }
+
+ return false;
+}
+
+/* Determine whether applying the 2 permutations (mask1 then mask2)
+ gives back one of the input. */
+
+static int
+is_combined_permutation_identity (tree mask1, tree mask2)
+{
+ tree mask;
+ unsigned int nelts, i, j;
+ bool maybe_identity1 = true;
+ bool maybe_identity2 = true;
+
+ gcc_checking_assert (TREE_CODE (mask1) == VECTOR_CST
+ && TREE_CODE (mask2) == VECTOR_CST);
+ mask = fold_ternary (VEC_PERM_EXPR, TREE_TYPE (mask1), mask1, mask1, mask2);
+ gcc_assert (TREE_CODE (mask) == VECTOR_CST);
+
+ nelts = VECTOR_CST_NELTS (mask);
+ for (i = 0; i < nelts; i++)
+ {
+ tree val = VECTOR_CST_ELT (mask, i);
+ gcc_assert (TREE_CODE (val) == INTEGER_CST);
+ j = TREE_INT_CST_LOW (val) & (2 * nelts - 1);
+ if (j == i)
+ maybe_identity2 = false;
+ else if (j == i + nelts)
+ maybe_identity1 = false;
+ else
+ return 0;
+ }
+ return maybe_identity1 ? 1 : maybe_identity2 ? 2 : 0;
+}
+
+/* Combine a shuffle with its arguments. Returns 1 if there were any
+ changes made, 2 if cfg-cleanup needs to run. Else it returns 0. */
+
+static int
+simplify_permutation (gimple_stmt_iterator *gsi)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ gimple def_stmt;
+ tree op0, op1, op2, op3, arg0, arg1;
+ enum tree_code code;
+ bool single_use_op0 = false;
+
+ gcc_checking_assert (gimple_assign_rhs_code (stmt) == VEC_PERM_EXPR);
+
+ op0 = gimple_assign_rhs1 (stmt);
+ op1 = gimple_assign_rhs2 (stmt);
+ op2 = gimple_assign_rhs3 (stmt);
+
+ if (TREE_CODE (op2) != VECTOR_CST)
+ return 0;
+
+ if (TREE_CODE (op0) == VECTOR_CST)
+ {
+ code = VECTOR_CST;
+ arg0 = op0;
+ }
+ else if (TREE_CODE (op0) == SSA_NAME)
+ {
+ def_stmt = get_prop_source_stmt (op0, false, &single_use_op0);
+ if (!def_stmt || !can_propagate_from (def_stmt))
+ return 0;
+
+ code = gimple_assign_rhs_code (def_stmt);
+ arg0 = gimple_assign_rhs1 (def_stmt);
+ }
+ else
+ return 0;
+
+ /* Two consecutive shuffles. */
+ if (code == VEC_PERM_EXPR)
+ {
+ tree orig;
+ int ident;
+
+ if (op0 != op1)
+ return 0;
+ op3 = gimple_assign_rhs3 (def_stmt);
+ if (TREE_CODE (op3) != VECTOR_CST)
+ return 0;
+ ident = is_combined_permutation_identity (op3, op2);
+ if (!ident)
+ return 0;
+ orig = (ident == 1) ? gimple_assign_rhs1 (def_stmt)
+ : gimple_assign_rhs2 (def_stmt);
+ gimple_assign_set_rhs1 (stmt, unshare_expr (orig));
+ gimple_assign_set_rhs_code (stmt, TREE_CODE (orig));
+ gimple_set_num_ops (stmt, 2);
+ update_stmt (stmt);
+ return remove_prop_source_from_use (op0) ? 2 : 1;
+ }
+
+ /* Shuffle of a constructor. */
+ else if (code == CONSTRUCTOR || code == VECTOR_CST)
+ {
+ tree opt;
+ bool ret = false;
+ if (op0 != op1)
+ {
+ if (TREE_CODE (op0) == SSA_NAME && !single_use_op0)
+ return 0;
+
+ if (TREE_CODE (op1) == VECTOR_CST)
+ arg1 = op1;
+ else if (TREE_CODE (op1) == SSA_NAME)
+ {
+ enum tree_code code2;
+
+ gimple def_stmt2 = get_prop_source_stmt (op1, true, NULL);
+ if (!def_stmt2 || !can_propagate_from (def_stmt2))
+ return 0;
+
+ code2 = gimple_assign_rhs_code (def_stmt2);
+ if (code2 != CONSTRUCTOR && code2 != VECTOR_CST)
+ return 0;
+ arg1 = gimple_assign_rhs1 (def_stmt2);
+ }
+ else
+ return 0;
+ }
+ else
+ {
+ /* Already used twice in this statement. */
+ if (TREE_CODE (op0) == SSA_NAME && num_imm_uses (op0) > 2)
+ return 0;
+ arg1 = arg0;
+ }
+ opt = fold_ternary (VEC_PERM_EXPR, TREE_TYPE(op0), arg0, arg1, op2);
+ if (!opt
+ || (TREE_CODE (opt) != CONSTRUCTOR && TREE_CODE(opt) != VECTOR_CST))
+ return 0;
+ gimple_assign_set_rhs_from_tree (gsi, opt);
+ update_stmt (gsi_stmt (*gsi));
+ if (TREE_CODE (op0) == SSA_NAME)
+ ret = remove_prop_source_from_use (op0);
+ if (op0 != op1 && TREE_CODE (op1) == SSA_NAME)
+ ret |= remove_prop_source_from_use (op1);
+ return ret ? 2 : 1;
+ }
+
+ return 0;
+}
+
+/* Recognize a VEC_PERM_EXPR. Returns true if there were any changes. */
+
+static bool
+simplify_vector_constructor (gimple_stmt_iterator *gsi)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ gimple def_stmt;
+ tree op, op2, orig, type, elem_type;
+ unsigned elem_size, nelts, i;
+ enum tree_code code;
+ constructor_elt *elt;
+ unsigned char *sel;
+ bool maybe_ident;
+
+ gcc_checking_assert (gimple_assign_rhs_code (stmt) == CONSTRUCTOR);
+
+ op = gimple_assign_rhs1 (stmt);
+ type = TREE_TYPE (op);
+ gcc_checking_assert (TREE_CODE (type) == VECTOR_TYPE);
+
+ nelts = TYPE_VECTOR_SUBPARTS (type);
+ elem_type = TREE_TYPE (type);
+ elem_size = TREE_INT_CST_LOW (TYPE_SIZE (elem_type));
+
+ sel = XALLOCAVEC (unsigned char, nelts);
+ orig = NULL;
+ maybe_ident = true;
+ FOR_EACH_VEC_SAFE_ELT (CONSTRUCTOR_ELTS (op), i, elt)
+ {
+ tree ref, op1;
+
+ if (i >= nelts)
+ return false;
+
+ if (TREE_CODE (elt->value) != SSA_NAME)
+ return false;
+ def_stmt = get_prop_source_stmt (elt->value, false, NULL);
+ if (!def_stmt)
+ return false;
+ code = gimple_assign_rhs_code (def_stmt);
+ if (code != BIT_FIELD_REF)
+ return false;
+ op1 = gimple_assign_rhs1 (def_stmt);
+ ref = TREE_OPERAND (op1, 0);
+ if (orig)
+ {
+ if (ref != orig)
+ return false;
+ }
+ else
+ {
+ if (TREE_CODE (ref) != SSA_NAME)
+ return false;
+ if (!useless_type_conversion_p (type, TREE_TYPE (ref)))
+ return false;
+ orig = ref;
+ }
+ if (TREE_INT_CST_LOW (TREE_OPERAND (op1, 1)) != elem_size)
+ return false;
+ sel[i] = TREE_INT_CST_LOW (TREE_OPERAND (op1, 2)) / elem_size;
+ if (sel[i] != i) maybe_ident = false;
+ }
+ if (i < nelts)
+ return false;
+
+ if (maybe_ident)
+ gimple_assign_set_rhs_from_tree (gsi, orig);
+ else
+ {
+ tree mask_type, *mask_elts;
+
+ if (!can_vec_perm_p (TYPE_MODE (type), false, sel))
+ return false;
+ mask_type
+ = build_vector_type (build_nonstandard_integer_type (elem_size, 1),
+ nelts);
+ if (GET_MODE_CLASS (TYPE_MODE (mask_type)) != MODE_VECTOR_INT
+ || GET_MODE_SIZE (TYPE_MODE (mask_type))
+ != GET_MODE_SIZE (TYPE_MODE (type)))
+ return false;
+ mask_elts = XALLOCAVEC (tree, nelts);
+ for (i = 0; i < nelts; i++)
+ mask_elts[i] = build_int_cst (TREE_TYPE (mask_type), sel[i]);
+ op2 = build_vector (mask_type, mask_elts);
+ gimple_assign_set_rhs_with_ops_1 (gsi, VEC_PERM_EXPR, orig, orig, op2);
+ }
+ update_stmt (gsi_stmt (*gsi));
+ return true;
+}
+
/* Main entry point for the forward propagation and statement combine
optimizer. */
FOR_EACH_BB (bb)
{
- gimple_stmt_iterator gsi, prev;
- bool prev_initialized;
+ gimple_stmt_iterator gsi;
/* Apply forward propagation to all stmts in the basic-block.
Note we update GSI within the loop as necessary. */
&& forward_propagate_addr_expr (lhs, rhs))
{
release_defs (stmt);
- todoflags |= TODO_remove_unused_locals;
gsi_remove (&gsi, true);
}
else
off)))))
{
release_defs (stmt);
- todoflags |= TODO_remove_unused_locals;
gsi_remove (&gsi, true);
}
else if (is_gimple_min_invariant (rhs))
}
else if (TREE_CODE_CLASS (code) == tcc_comparison)
{
- if (forward_propagate_comparison (stmt))
+ if (forward_propagate_comparison (&gsi))
cfg_changed = true;
- gsi_next (&gsi);
}
else
gsi_next (&gsi);
/* Combine stmts with the stmts defining their operands.
Note we update GSI within the loop as necessary. */
- prev_initialized = false;
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
{
gimple stmt = gsi_stmt (gsi);
bool changed = false;
+ /* Mark stmt as potentially needing revisiting. */
+ gimple_set_plf (stmt, GF_PLF_1, false);
+
switch (gimple_code (stmt))
{
case GIMPLE_ASSIGN:
|| code == NEGATE_EXPR)
&& TREE_CODE (rhs1) == SSA_NAME)
changed = simplify_not_neg_expr (&gsi);
- else if (code == COND_EXPR)
+ else if (code == COND_EXPR
+ || code == VEC_COND_EXPR)
{
/* In this case the entire COND_EXPR is in rhs1. */
- changed |= forward_propagate_into_cond (&gsi);
- stmt = gsi_stmt (gsi);
+ if (forward_propagate_into_cond (&gsi)
+ || combine_cond_exprs (&gsi))
+ {
+ changed = true;
+ stmt = gsi_stmt (gsi);
+ }
}
else if (TREE_CODE_CLASS (code) == tcc_comparison)
{
else if (code == PLUS_EXPR
|| code == MINUS_EXPR)
changed = associate_plusminus (&gsi);
+ else if (code == POINTER_PLUS_EXPR)
+ changed = associate_pointerplus (&gsi);
else if (CONVERT_EXPR_CODE_P (code)
|| code == FLOAT_EXPR
|| code == FIX_TRUNC_EXPR)
cfg_changed = true;
changed = did_something != 0;
}
+ else if (code == VEC_PERM_EXPR)
+ {
+ int did_something = simplify_permutation (&gsi);
+ if (did_something == 2)
+ cfg_changed = true;
+ changed = did_something != 0;
+ }
+ else if (code == BIT_FIELD_REF)
+ changed = simplify_bitfield_ref (&gsi);
+ else if (code == CONSTRUCTOR
+ && TREE_CODE (TREE_TYPE (rhs1)) == VECTOR_TYPE)
+ changed = simplify_vector_constructor (&gsi);
break;
}
{
/* If the stmt changed then re-visit it and the statements
inserted before it. */
- if (!prev_initialized)
+ for (; !gsi_end_p (gsi); gsi_prev (&gsi))
+ if (gimple_plf (gsi_stmt (gsi), GF_PLF_1))
+ break;
+ if (gsi_end_p (gsi))
gsi = gsi_start_bb (bb);
else
- {
- gsi = prev;
- gsi_next (&gsi);
- }
+ gsi_next (&gsi);
}
else
{
- prev = gsi;
- prev_initialized = true;
+ /* Stmt no longer needs to be revisited. */
+ gimple_set_plf (stmt, GF_PLF_1, true);
gsi_next (&gsi);
}
}
{
GIMPLE_PASS,
"forwprop", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
gate_forwprop, /* gate */
ssa_forward_propagate_and_combine, /* execute */
NULL, /* sub */