/* Statement simplification on GIMPLE.
- Copyright (C) 2010, 2011 Free Software Foundation, Inc.
+ Copyright (C) 2010, 2011, 2012 Free Software Foundation, Inc.
Split out from tree-ssa-ccp.c.
This file is part of GCC.
tree
canonicalize_constructor_val (tree cval)
{
+ tree orig_cval = cval;
STRIP_NOPS (cval);
if (TREE_CODE (cval) == POINTER_PLUS_EXPR
&& TREE_CODE (TREE_OPERAND (cval, 1)) == INTEGER_CST)
|| TREE_CODE (base) == FUNCTION_DECL)
&& !can_refer_decl_in_current_unit_p (base))
return NULL_TREE;
- if (cfun && base && TREE_CODE (base) == VAR_DECL)
- add_referenced_var (base);
+ if (base && TREE_CODE (base) == VAR_DECL)
+ {
+ TREE_ADDRESSABLE (base) = 1;
+ if (cfun && gimple_referenced_vars (cfun))
+ add_referenced_var (base);
+ }
/* Fixup types in global initializers. */
if (TREE_TYPE (TREE_TYPE (cval)) != TREE_TYPE (TREE_OPERAND (cval, 0)))
cval = build_fold_addr_expr (TREE_OPERAND (cval, 0));
+
+ if (!useless_type_conversion_p (TREE_TYPE (orig_cval), TREE_TYPE (cval)))
+ cval = fold_convert (TREE_TYPE (orig_cval), cval);
+ return cval;
}
- return cval;
+ return orig_cval;
}
/* If SYM is a constant variable with known value, return the value.
gimplify_and_update_call_from_tree (gimple_stmt_iterator *si_p, tree expr)
{
tree lhs;
- tree tmp = NULL_TREE; /* Silence warning. */
gimple stmt, new_stmt;
gimple_stmt_iterator i;
gimple_seq stmts = gimple_seq_alloc();
struct gimplify_ctx gctx;
- gimple last = NULL;
- gimple laststore = NULL;
+ gimple last;
+ gimple laststore;
tree reaching_vuse;
stmt = gsi_stmt (*si_p);
gcc_assert (is_gimple_call (stmt));
- lhs = gimple_call_lhs (stmt);
- reaching_vuse = gimple_vuse (stmt);
-
push_gimplify_context (&gctx);
+ gctx.into_ssa = gimple_in_ssa_p (cfun);
+ lhs = gimple_call_lhs (stmt);
if (lhs == NULL_TREE)
{
gimplify_and_add (expr, &stmts);
unlink_stmt_vdef (stmt);
release_defs (stmt);
}
- gsi_remove (si_p, true);
+ gsi_replace (si_p, gimple_build_nop (), true);
return;
}
}
else
- tmp = get_initialized_tmp_var (expr, &stmts, NULL);
+ {
+ tree tmp = get_initialized_tmp_var (expr, &stmts, NULL);
+ new_stmt = gimple_build_assign (lhs, tmp);
+ i = gsi_last (stmts);
+ gsi_insert_after_without_update (&i, new_stmt,
+ GSI_CONTINUE_LINKING);
+ }
pop_gimplify_context (NULL);
if (gimple_has_location (stmt))
annotate_all_with_location (stmts, gimple_location (stmt));
- /* The replacement can expose previously unreferenced variables. */
- for (i = gsi_start (stmts); !gsi_end_p (i); gsi_next (&i))
+ /* First iterate over the replacement statements backward, assigning
+ virtual operands to their defining statements. */
+ laststore = NULL;
+ for (i = gsi_last (stmts); !gsi_end_p (i); gsi_prev (&i))
{
- if (last)
- {
- gsi_insert_before (si_p, last, GSI_NEW_STMT);
- gsi_next (si_p);
- }
new_stmt = gsi_stmt (i);
- if (gimple_in_ssa_p (cfun))
- {
- find_new_referenced_vars (new_stmt);
- mark_symbols_for_renaming (new_stmt);
- }
- /* If the new statement has a VUSE, update it with exact SSA name we
- know will reach this one. */
- if (gimple_vuse (new_stmt))
- {
- /* If we've also seen a previous store create a new VDEF for
- the latter one, and make that the new reaching VUSE. */
- if (laststore)
- {
- reaching_vuse = make_ssa_name (gimple_vop (cfun), laststore);
- gimple_set_vdef (laststore, reaching_vuse);
- update_stmt (laststore);
- laststore = NULL;
- }
- gimple_set_vuse (new_stmt, reaching_vuse);
- gimple_set_modified (new_stmt, true);
- }
- if (gimple_assign_single_p (new_stmt)
- && !is_gimple_reg (gimple_assign_lhs (new_stmt)))
+ if ((gimple_assign_single_p (new_stmt)
+ && !is_gimple_reg (gimple_assign_lhs (new_stmt)))
+ || (is_gimple_call (new_stmt)
+ && (gimple_call_flags (new_stmt)
+ & (ECF_NOVOPS | ECF_PURE | ECF_CONST | ECF_NORETURN)) == 0))
{
+ tree vdef;
+ if (!laststore)
+ vdef = gimple_vdef (stmt);
+ else
+ vdef = make_ssa_name (gimple_vop (cfun), new_stmt);
+ gimple_set_vdef (new_stmt, vdef);
+ if (vdef && TREE_CODE (vdef) == SSA_NAME)
+ SSA_NAME_DEF_STMT (vdef) = new_stmt;
laststore = new_stmt;
}
- last = new_stmt;
}
- if (lhs == NULL_TREE)
- {
- /* If we replace a call without LHS that has a VDEF and our new
- sequence ends with a store we must make that store have the same
- vdef in order not to break the sequencing. This can happen
- for instance when folding memcpy calls into assignments. */
- if (gimple_vdef (stmt) && laststore)
- {
- gimple_set_vdef (laststore, gimple_vdef (stmt));
- if (TREE_CODE (gimple_vdef (stmt)) == SSA_NAME)
- SSA_NAME_DEF_STMT (gimple_vdef (stmt)) = laststore;
- update_stmt (laststore);
- }
- else if (gimple_in_ssa_p (cfun))
- {
- unlink_stmt_vdef (stmt);
- release_defs (stmt);
- }
- new_stmt = last;
- }
- else
+ /* Second iterate over the statements forward, assigning virtual
+ operands to their uses. */
+ last = NULL;
+ reaching_vuse = gimple_vuse (stmt);
+ for (i = gsi_start (stmts); !gsi_end_p (i); gsi_next (&i))
{
+ /* Do not insert the last stmt in this loop but remember it
+ for replacing the original statement. */
if (last)
{
gsi_insert_before (si_p, last, GSI_NEW_STMT);
gsi_next (si_p);
}
- if (laststore && is_gimple_reg (lhs))
- {
- gimple_set_vdef (laststore, gimple_vdef (stmt));
- update_stmt (laststore);
- if (TREE_CODE (gimple_vdef (stmt)) == SSA_NAME)
- SSA_NAME_DEF_STMT (gimple_vdef (stmt)) = laststore;
- laststore = NULL;
- }
- else if (laststore)
- {
- reaching_vuse = make_ssa_name (gimple_vop (cfun), laststore);
- gimple_set_vdef (laststore, reaching_vuse);
- update_stmt (laststore);
- laststore = NULL;
- }
- new_stmt = gimple_build_assign (lhs, tmp);
- if (!is_gimple_reg (tmp))
+ new_stmt = gsi_stmt (i);
+ /* The replacement can expose previously unreferenced variables. */
+ if (gimple_in_ssa_p (cfun))
+ find_new_referenced_vars (new_stmt);
+ /* If the new statement possibly has a VUSE, update it with exact SSA
+ name we know will reach this one. */
+ if (gimple_has_mem_ops (new_stmt))
gimple_set_vuse (new_stmt, reaching_vuse);
- if (!is_gimple_reg (lhs))
+ gimple_set_modified (new_stmt, true);
+ if (gimple_vdef (new_stmt))
+ reaching_vuse = gimple_vdef (new_stmt);
+ last = new_stmt;
+ }
+
+ /* If the new sequence does not do a store release the virtual
+ definition of the original statement. */
+ if (reaching_vuse
+ && reaching_vuse == gimple_vuse (stmt))
+ {
+ tree vdef = gimple_vdef (stmt);
+ if (vdef
+ && TREE_CODE (vdef) == SSA_NAME)
{
- gimple_set_vdef (new_stmt, gimple_vdef (stmt));
- if (TREE_CODE (gimple_vdef (stmt)) == SSA_NAME)
- SSA_NAME_DEF_STMT (gimple_vdef (stmt)) = new_stmt;
+ unlink_stmt_vdef (stmt);
+ release_ssa_name (vdef);
}
- else if (reaching_vuse == gimple_vuse (stmt))
- unlink_stmt_vdef (stmt);
}
- gimple_set_location (new_stmt, gimple_location (stmt));
- gsi_replace (si_p, new_stmt, false);
+ /* Finally replace rhe original statement with the last. */
+ gsi_replace (si_p, last, false);
}
/* Return the string length, maximum string length or maximum value of
if (TREE_CODE (arg) != SSA_NAME)
{
- if (TREE_CODE (arg) == COND_EXPR)
- return get_maxval_strlen (COND_EXPR_THEN (arg), length, visited, type)
- && get_maxval_strlen (COND_EXPR_ELSE (arg), length, visited, type);
/* We can end up with &(*iftmp_1)[0] here as well, so handle it. */
- else if (TREE_CODE (arg) == ADDR_EXPR
- && TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF
- && integer_zerop (TREE_OPERAND (TREE_OPERAND (arg, 0), 1)))
+ if (TREE_CODE (arg) == ADDR_EXPR
+ && TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF
+ && integer_zerop (TREE_OPERAND (TREE_OPERAND (arg, 0), 1)))
{
tree aop0 = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
if (TREE_CODE (aop0) == INDIRECT_REF
tree rhs = gimple_assign_rhs1 (def_stmt);
return get_maxval_strlen (rhs, length, visited, type);
}
+ else if (gimple_assign_rhs_code (def_stmt) == COND_EXPR)
+ {
+ tree op2 = gimple_assign_rhs2 (def_stmt);
+ tree op3 = gimple_assign_rhs3 (def_stmt);
+ return get_maxval_strlen (op2, length, visited, type)
+ && get_maxval_strlen (op3, length, visited, type);
+ }
return false;
case GIMPLE_PHI:
if (DECL_BUILT_IN_CLASS (callee) == BUILT_IN_MD)
return NULL_TREE;
+ /* Give up for always_inline inline builtins until they are
+ inlined. */
+ if (avoid_folding_inline_builtin (callee))
+ return NULL_TREE;
+
/* If the builtin could not be folded, and it has no argument list,
we're done. */
nargs = gimple_call_num_args (stmt);
case BUILT_IN_MEMMOVE_CHK:
case BUILT_IN_MEMSET_CHK:
case BUILT_IN_STRNCPY_CHK:
+ case BUILT_IN_STPNCPY_CHK:
arg_idx = 2;
type = 2;
break;
break;
case BUILT_IN_STRNCPY_CHK:
+ case BUILT_IN_STPNCPY_CHK:
if (val[2] && is_gimple_val (val[2]) && nargs == 4)
- result = fold_builtin_strncpy_chk (loc, gimple_call_arg (stmt, 0),
+ result = fold_builtin_stxncpy_chk (loc, gimple_call_arg (stmt, 0),
gimple_call_arg (stmt, 1),
gimple_call_arg (stmt, 2),
gimple_call_arg (stmt, 3),
- val[2]);
+ val[2], ignore,
+ DECL_FUNCTION_CODE (callee));
break;
case BUILT_IN_SNPRINTF_CHK:
simplifies to a constant value. Return true if any changes were made.
It is assumed that the operands have been previously folded. */
-bool
+static bool
gimple_fold_call (gimple_stmt_iterator *gsi, bool inplace)
{
gimple stmt = gsi_stmt (*gsi);
tree callee;
+ bool changed = false;
+ unsigned i;
- /* Check for builtins that CCP can handle using information not
- available in the generic fold routines. */
- callee = gimple_call_fndecl (stmt);
- if (!inplace && callee && DECL_BUILT_IN (callee))
- {
- tree result = gimple_fold_builtin (stmt);
-
- if (result)
- {
- if (!update_call_from_tree (gsi, result))
- gimplify_and_update_call_from_tree (gsi, result);
- return true;
- }
- }
+ /* Fold *& in call arguments. */
+ for (i = 0; i < gimple_call_num_args (stmt); ++i)
+ if (REFERENCE_CLASS_P (gimple_call_arg (stmt, i)))
+ {
+ tree tmp = maybe_fold_reference (gimple_call_arg (stmt, i), false);
+ if (tmp)
+ {
+ gimple_call_set_arg (stmt, i, tmp);
+ changed = true;
+ }
+ }
/* Check for virtual calls that became direct calls. */
callee = gimple_call_fn (stmt);
if (callee && TREE_CODE (callee) == OBJ_TYPE_REF)
{
- tree binfo, fndecl, obj;
- HOST_WIDE_INT token;
-
if (gimple_call_addr_fndecl (OBJ_TYPE_REF_EXPR (callee)) != NULL_TREE)
{
gimple_call_set_fn (stmt, OBJ_TYPE_REF_EXPR (callee));
- return true;
+ changed = true;
+ }
+ else
+ {
+ tree obj = OBJ_TYPE_REF_OBJECT (callee);
+ tree binfo = gimple_extract_devirt_binfo_from_cst (obj);
+ if (binfo)
+ {
+ HOST_WIDE_INT token
+ = TREE_INT_CST_LOW (OBJ_TYPE_REF_TOKEN (callee));
+ tree fndecl = gimple_get_virt_method_for_binfo (token, binfo);
+ if (fndecl)
+ {
+ gimple_call_set_fndecl (stmt, fndecl);
+ changed = true;
+ }
+ }
}
+ }
- obj = OBJ_TYPE_REF_OBJECT (callee);
- binfo = gimple_extract_devirt_binfo_from_cst (obj);
- if (!binfo)
- return false;
- token = TREE_INT_CST_LOW (OBJ_TYPE_REF_TOKEN (callee));
- fndecl = gimple_get_virt_method_for_binfo (token, binfo);
- if (!fndecl)
- return false;
- gimple_call_set_fndecl (stmt, fndecl);
- return true;
+ if (inplace)
+ return changed;
+
+ /* Check for builtins that CCP can handle using information not
+ available in the generic fold routines. */
+ callee = gimple_call_fndecl (stmt);
+ if (callee && DECL_BUILT_IN (callee))
+ {
+ tree result = gimple_fold_builtin (stmt);
+ if (result)
+ {
+ if (!update_call_from_tree (gsi, result))
+ gimplify_and_update_call_from_tree (gsi, result);
+ changed = true;
+ }
}
- return false;
+ return changed;
}
/* Worker for both fold_stmt and fold_stmt_inplace. The INPLACE argument
break;
case GIMPLE_CALL:
- /* Fold *& in call arguments. */
- for (i = 0; i < gimple_call_num_args (stmt); ++i)
- if (REFERENCE_CLASS_P (gimple_call_arg (stmt, i)))
- {
- tree tmp = maybe_fold_reference (gimple_call_arg (stmt, i), false);
- if (tmp)
- {
- gimple_call_set_arg (stmt, i, tmp);
- changed = true;
- }
- }
changed |= gimple_fold_call (gsi, inplace);
break;
case GIMPLE_ASM:
/* Fold *& in asm operands. */
- for (i = 0; i < gimple_asm_noutputs (stmt); ++i)
- {
- tree link = gimple_asm_output_op (stmt, i);
- tree op = TREE_VALUE (link);
- if (REFERENCE_CLASS_P (op)
- && (op = maybe_fold_reference (op, true)) != NULL_TREE)
- {
- TREE_VALUE (link) = op;
- changed = true;
- }
- }
- for (i = 0; i < gimple_asm_ninputs (stmt); ++i)
- {
- tree link = gimple_asm_input_op (stmt, i);
- tree op = TREE_VALUE (link);
- if (REFERENCE_CLASS_P (op)
- && (op = maybe_fold_reference (op, false)) != NULL_TREE)
- {
- TREE_VALUE (link) = op;
- changed = true;
- }
- }
+ {
+ size_t noutputs;
+ const char **oconstraints;
+ const char *constraint;
+ bool allows_mem, allows_reg;
+
+ noutputs = gimple_asm_noutputs (stmt);
+ oconstraints = XALLOCAVEC (const char *, noutputs);
+
+ for (i = 0; i < gimple_asm_noutputs (stmt); ++i)
+ {
+ tree link = gimple_asm_output_op (stmt, i);
+ tree op = TREE_VALUE (link);
+ oconstraints[i]
+ = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
+ if (REFERENCE_CLASS_P (op)
+ && (op = maybe_fold_reference (op, true)) != NULL_TREE)
+ {
+ TREE_VALUE (link) = op;
+ changed = true;
+ }
+ }
+ for (i = 0; i < gimple_asm_ninputs (stmt); ++i)
+ {
+ tree link = gimple_asm_input_op (stmt, i);
+ tree op = TREE_VALUE (link);
+ constraint
+ = TREE_STRING_POINTER (TREE_VALUE (TREE_PURPOSE (link)));
+ parse_input_constraint (&constraint, 0, 0, noutputs, 0,
+ oconstraints, &allows_mem, &allows_reg);
+ if (REFERENCE_CLASS_P (op)
+ && (op = maybe_fold_reference (op, !allows_reg && allows_mem))
+ != NULL_TREE)
+ {
+ TREE_VALUE (link) = op;
+ changed = true;
+ }
+ }
+ }
break;
case GIMPLE_DEBUG:
changed = true;
}
}
+ else if (val
+ && TREE_CODE (val) == ADDR_EXPR)
+ {
+ tree ref = TREE_OPERAND (val, 0);
+ tree tem = maybe_fold_reference (ref, false);
+ if (tem)
+ {
+ tem = build_fold_addr_expr_with_type (tem, TREE_TYPE (val));
+ gimple_debug_bind_set_value (stmt, tem);
+ changed = true;
+ }
+ }
}
break;
if (CONVERT_EXPR_CODE_P (subcode)
&& POINTER_TYPE_P (TREE_TYPE (lhs))
&& POINTER_TYPE_P (TREE_TYPE (op0))
- && (TYPE_ADDR_SPACE (TREE_TYPE (lhs))
- == TYPE_ADDR_SPACE (TREE_TYPE (op0))))
+ && TYPE_ADDR_SPACE (TREE_TYPE (lhs))
+ == TYPE_ADDR_SPACE (TREE_TYPE (op0))
+ && TYPE_MODE (TREE_TYPE (lhs))
+ == TYPE_MODE (TREE_TYPE (op0)))
return op0;
return
tree op1 = (*valueize) (gimple_assign_rhs2 (stmt));
tree op2 = (*valueize) (gimple_assign_rhs3 (stmt));
+ /* Fold embedded expressions in ternary codes. */
+ if ((subcode == COND_EXPR
+ || subcode == VEC_COND_EXPR)
+ && COMPARISON_CLASS_P (op0))
+ {
+ tree op00 = (*valueize) (TREE_OPERAND (op0, 0));
+ tree op01 = (*valueize) (TREE_OPERAND (op0, 1));
+ tree tem = fold_binary_loc (loc, TREE_CODE (op0),
+ TREE_TYPE (op0), op00, op01);
+ if (tem)
+ op0 = tem;
+ }
+
return fold_ternary_loc (loc, subcode,
gimple_expr_type (stmt), op0, op1, op2);
}
if (!DECL_INITIAL (base)
&& (TREE_STATIC (base) || DECL_EXTERNAL (base)))
return error_mark_node;
+ /* Do not return an error_mark_node DECL_INITIAL. LTO uses this
+ as special marker (_not_ zero ...) for its own purposes. */
+ if (DECL_INITIAL (base) == error_mark_node)
+ return NULL_TREE;
return DECL_INITIAL (base);
case ARRAY_REF:
double_int low_bound, elt_size;
double_int index, max_index;
double_int access_index;
- tree domain_type = TYPE_DOMAIN (TREE_TYPE (ctor));
+ tree domain_type = NULL_TREE;
HOST_WIDE_INT inner_offset;
/* Compute low bound and elt size. */
+ if (TREE_CODE (TREE_TYPE (ctor)) == ARRAY_TYPE)
+ domain_type = TYPE_DOMAIN (TREE_TYPE (ctor));
if (domain_type && TYPE_MIN_VALUE (domain_type))
{
/* Static constructors for variably sized objects makes no sense. */
if (TREE_CODE (ctor) == CONSTRUCTOR)
{
- if (TREE_CODE (TREE_TYPE (ctor)) == ARRAY_TYPE)
+ if (TREE_CODE (TREE_TYPE (ctor)) == ARRAY_TYPE
+ || TREE_CODE (TREE_TYPE (ctor)) == VECTOR_TYPE)
return fold_array_ctor_reference (type, ctor, offset, size);
else
return fold_nonarray_ctor_reference (type, ctor, offset, size);
offset += token * size;
fn = fold_ctor_reference (TREE_TYPE (TREE_TYPE (v)), DECL_INITIAL (v),
offset, size);
- if (!fn)
+ if (!fn || integer_zerop (fn))
return NULL_TREE;
gcc_assert (TREE_CODE (fn) == ADDR_EXPR
|| TREE_CODE (fn) == FDESC_EXPR);