- /* The statement produced a nonconstant value. If the statement
- had UNDEFINED operands, then the result of the statement
- should be UNDEFINED. Otherwise, the statement is VARYING. */
- if (likelyvalue == UNDEFINED)
- val.lattice_val = likelyvalue;
- else
- val.lattice_val = VARYING;
-
- val.value = NULL_TREE;
- }
-
- return val;
-}
-
-/* Visit the assignment statement STMT. Set the value of its LHS to the
- value computed by the RHS and store LHS in *OUTPUT_P. If STMT
- creates virtual definitions, set the value of each new name to that
- of the RHS (if we can derive a constant out of the RHS).
- Value-returning call statements also perform an assignment, and
- are handled here. */
-
-static enum ssa_prop_result
-visit_assignment (gimple stmt, tree *output_p)
-{
- prop_value_t val;
- enum ssa_prop_result retval;
-
- tree lhs = gimple_get_lhs (stmt);
-
- gcc_assert (gimple_code (stmt) != GIMPLE_CALL
- || gimple_call_lhs (stmt) != NULL_TREE);
-
- if (gimple_assign_copy_p (stmt))
- {
- tree rhs = gimple_assign_rhs1 (stmt);
-
- if (TREE_CODE (rhs) == SSA_NAME)
- {
- /* For a simple copy operation, we copy the lattice values. */
- prop_value_t *nval = get_value (rhs);
- val = *nval;
- }
- else
- val = evaluate_stmt (stmt);
- }
- else
- /* Evaluate the statement, which could be
- either a GIMPLE_ASSIGN or a GIMPLE_CALL. */
- val = evaluate_stmt (stmt);
-
- retval = SSA_PROP_NOT_INTERESTING;
-
- /* Set the lattice value of the statement's output. */
- if (TREE_CODE (lhs) == SSA_NAME)
- {
- /* If STMT is an assignment to an SSA_NAME, we only have one
- value to set. */
- if (set_lattice_value (lhs, val))
- {
- *output_p = lhs;
- if (val.lattice_val == VARYING)
- retval = SSA_PROP_VARYING;
- else
- retval = SSA_PROP_INTERESTING;
- }
- }
-
- return retval;
-}
-
-
-/* Visit the conditional statement STMT. Return SSA_PROP_INTERESTING
- if it can determine which edge will be taken. Otherwise, return
- SSA_PROP_VARYING. */
-
-static enum ssa_prop_result
-visit_cond_stmt (gimple stmt, edge *taken_edge_p)
-{
- prop_value_t val;
- basic_block block;
-
- block = gimple_bb (stmt);
- val = evaluate_stmt (stmt);
-
- /* Find which edge out of the conditional block will be taken and add it
- to the worklist. If no single edge can be determined statically,
- return SSA_PROP_VARYING to feed all the outgoing edges to the
- propagation engine. */
- *taken_edge_p = val.value ? find_taken_edge (block, val.value) : 0;
- if (*taken_edge_p)
- return SSA_PROP_INTERESTING;
- else
- return SSA_PROP_VARYING;
-}
-
-
-/* Evaluate statement STMT. If the statement produces an output value and
- its evaluation changes the lattice value of its output, return
- SSA_PROP_INTERESTING and set *OUTPUT_P to the SSA_NAME holding the
- output value.
-
- If STMT is a conditional branch and we can determine its truth
- value, set *TAKEN_EDGE_P accordingly. If STMT produces a varying
- value, return SSA_PROP_VARYING. */
-
-static enum ssa_prop_result
-ccp_visit_stmt (gimple stmt, edge *taken_edge_p, tree *output_p)
-{
- tree def;
- ssa_op_iter iter;
-
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "\nVisiting statement:\n");
- print_gimple_stmt (dump_file, stmt, 0, dump_flags);
- }
-
- switch (gimple_code (stmt))
- {
- case GIMPLE_ASSIGN:
- /* If the statement is an assignment that produces a single
- output value, evaluate its RHS to see if the lattice value of
- its output has changed. */
- return visit_assignment (stmt, output_p);
-
- case GIMPLE_CALL:
- /* A value-returning call also performs an assignment. */
- if (gimple_call_lhs (stmt) != NULL_TREE)
- return visit_assignment (stmt, output_p);
- break;
-
- case GIMPLE_COND:
- case GIMPLE_SWITCH:
- /* If STMT is a conditional branch, see if we can determine
- which branch will be taken. */
- /* FIXME. It appears that we should be able to optimize
- computed GOTOs here as well. */
- return visit_cond_stmt (stmt, taken_edge_p);
-
- default:
- break;
- }
-
- /* Any other kind of statement is not interesting for constant
- propagation and, therefore, not worth simulating. */
- if (dump_file && (dump_flags & TDF_DETAILS))
- fprintf (dump_file, "No interesting values produced. Marked VARYING.\n");
-
- /* Definitions made by statements other than assignments to
- SSA_NAMEs represent unknown modifications to their outputs.
- Mark them VARYING. */
- FOR_EACH_SSA_TREE_OPERAND (def, stmt, iter, SSA_OP_ALL_DEFS)
- {
- prop_value_t v = { VARYING, NULL_TREE };
- set_lattice_value (def, v);
- }
-
- return SSA_PROP_VARYING;
-}
-
-
-/* Main entry point for SSA Conditional Constant Propagation. */
-
-static unsigned int
-do_ssa_ccp (void)
-{
- ccp_initialize ();
- ssa_propagate (ccp_visit_stmt, ccp_visit_phi_node);
- if (ccp_finalize ())
- return (TODO_cleanup_cfg | TODO_update_ssa | TODO_remove_unused_locals);
- else
- return 0;
-}
-
-
-static bool
-gate_ccp (void)
-{
- return flag_tree_ccp != 0;
-}
-
-
-struct gimple_opt_pass pass_ccp =
-{
- {
- GIMPLE_PASS,
- "ccp", /* name */
- gate_ccp, /* gate */
- do_ssa_ccp, /* execute */
- NULL, /* sub */
- NULL, /* next */
- 0, /* static_pass_number */
- TV_TREE_CCP, /* 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_verify_stmts | TODO_ggc_collect/* todo_flags_finish */
- }
-};
-
-
-/* A subroutine of fold_stmt. Attempts to fold *(A+O) to A[X].
- BASE is an array type. OFFSET is a byte displacement. ORIG_TYPE
- is the desired result type. */
-
-static tree
-maybe_fold_offset_to_array_ref (tree base, tree offset, tree orig_type,
- bool allow_negative_idx)
-{
- tree min_idx, idx, idx_type, elt_offset = integer_zero_node;
- tree array_type, elt_type, elt_size;
- tree domain_type;
-
- /* If BASE is an ARRAY_REF, we can pick up another offset (this time
- measured in units of the size of elements type) from that ARRAY_REF).
- We can't do anything if either is variable.
-
- The case we handle here is *(&A[N]+O). */
- if (TREE_CODE (base) == ARRAY_REF)
- {
- tree low_bound = array_ref_low_bound (base);
-
- elt_offset = TREE_OPERAND (base, 1);
- if (TREE_CODE (low_bound) != INTEGER_CST
- || TREE_CODE (elt_offset) != INTEGER_CST)
- return NULL_TREE;
-
- elt_offset = int_const_binop (MINUS_EXPR, elt_offset, low_bound, 0);
- base = TREE_OPERAND (base, 0);
- }
-
- /* Ignore stupid user tricks of indexing non-array variables. */
- array_type = TREE_TYPE (base);
- if (TREE_CODE (array_type) != ARRAY_TYPE)
- return NULL_TREE;
- elt_type = TREE_TYPE (array_type);
- if (!useless_type_conversion_p (orig_type, elt_type))
- return NULL_TREE;
-
- /* Use signed size type for intermediate computation on the index. */
- idx_type = signed_type_for (size_type_node);
-
- /* If OFFSET and ELT_OFFSET are zero, we don't care about the size of the
- element type (so we can use the alignment if it's not constant).
- Otherwise, compute the offset as an index by using a division. If the
- division isn't exact, then don't do anything. */
- elt_size = TYPE_SIZE_UNIT (elt_type);
- if (!elt_size)
- return NULL;
- if (integer_zerop (offset))
- {
- if (TREE_CODE (elt_size) != INTEGER_CST)
- elt_size = size_int (TYPE_ALIGN (elt_type));
-
- idx = build_int_cst (idx_type, 0);
- }
- else
- {
- unsigned HOST_WIDE_INT lquo, lrem;
- HOST_WIDE_INT hquo, hrem;
- double_int soffset;
-
- /* The final array offset should be signed, so we need
- to sign-extend the (possibly pointer) offset here
- and use signed division. */
- soffset = double_int_sext (tree_to_double_int (offset),
- TYPE_PRECISION (TREE_TYPE (offset)));
- if (TREE_CODE (elt_size) != INTEGER_CST
- || div_and_round_double (TRUNC_DIV_EXPR, 0,
- soffset.low, soffset.high,
- TREE_INT_CST_LOW (elt_size),
- TREE_INT_CST_HIGH (elt_size),
- &lquo, &hquo, &lrem, &hrem)
- || lrem || hrem)
- return NULL_TREE;
-
- idx = build_int_cst_wide (idx_type, lquo, hquo);
- }
-
- /* Assume the low bound is zero. If there is a domain type, get the
- low bound, if any, convert the index into that type, and add the
- low bound. */
- min_idx = build_int_cst (idx_type, 0);
- domain_type = TYPE_DOMAIN (array_type);
- if (domain_type)
- {
- idx_type = domain_type;
- if (TYPE_MIN_VALUE (idx_type))
- min_idx = TYPE_MIN_VALUE (idx_type);
- else
- min_idx = fold_convert (idx_type, min_idx);
-
- if (TREE_CODE (min_idx) != INTEGER_CST)
- return NULL_TREE;
-
- elt_offset = fold_convert (idx_type, elt_offset);
- }
-
- if (!integer_zerop (min_idx))
- idx = int_const_binop (PLUS_EXPR, idx, min_idx, 0);
- if (!integer_zerop (elt_offset))
- idx = int_const_binop (PLUS_EXPR, idx, elt_offset, 0);
-
- /* Make sure to possibly truncate late after offsetting. */
- idx = fold_convert (idx_type, idx);
-
- /* We don't want to construct access past array bounds. For example
- char *(c[4]);
- c[3][2];
- should not be simplified into (*c)[14] or tree-vrp will
- give false warnings. The same is true for
- struct A { long x; char d[0]; } *a;
- (char *)a - 4;
- which should be not folded to &a->d[-8]. */
- if (domain_type
- && TYPE_MAX_VALUE (domain_type)
- && TREE_CODE (TYPE_MAX_VALUE (domain_type)) == INTEGER_CST)
- {
- tree up_bound = TYPE_MAX_VALUE (domain_type);
-
- if (tree_int_cst_lt (up_bound, idx)
- /* Accesses after the end of arrays of size 0 (gcc
- extension) and 1 are likely intentional ("struct
- hack"). */
- && compare_tree_int (up_bound, 1) > 0)
- return NULL_TREE;
- }
- if (domain_type
- && TYPE_MIN_VALUE (domain_type))
- {
- if (!allow_negative_idx
- && TREE_CODE (TYPE_MIN_VALUE (domain_type)) == INTEGER_CST
- && tree_int_cst_lt (idx, TYPE_MIN_VALUE (domain_type)))
- return NULL_TREE;
- }
- else if (!allow_negative_idx
- && compare_tree_int (idx, 0) < 0)
- return NULL_TREE;
-
- return build4 (ARRAY_REF, elt_type, base, idx, NULL_TREE, NULL_TREE);
-}
-
-
-/* Attempt to fold *(S+O) to S.X.
- BASE is a record type. OFFSET is a byte displacement. ORIG_TYPE
- is the desired result type. */
-
-static tree
-maybe_fold_offset_to_component_ref (tree record_type, tree base, tree offset,
- tree orig_type, bool base_is_ptr)
-{
- tree f, t, field_type, tail_array_field, field_offset;
- tree ret;
- tree new_base;
-
- if (TREE_CODE (record_type) != RECORD_TYPE
- && TREE_CODE (record_type) != UNION_TYPE
- && TREE_CODE (record_type) != QUAL_UNION_TYPE)
- return NULL_TREE;
-
- /* Short-circuit silly cases. */
- if (useless_type_conversion_p (record_type, orig_type))
- return NULL_TREE;
-
- tail_array_field = NULL_TREE;
- for (f = TYPE_FIELDS (record_type); f ; f = TREE_CHAIN (f))
- {
- int cmp;
-
- if (TREE_CODE (f) != FIELD_DECL)
- continue;
- if (DECL_BIT_FIELD (f))
- continue;
-
- if (!DECL_FIELD_OFFSET (f))
- continue;
- field_offset = byte_position (f);
- if (TREE_CODE (field_offset) != INTEGER_CST)
- continue;
-
- /* ??? Java creates "interesting" fields for representing base classes.
- They have no name, and have no context. With no context, we get into
- trouble with nonoverlapping_component_refs_p. Skip them. */
- if (!DECL_FIELD_CONTEXT (f))
- continue;
-
- /* The previous array field isn't at the end. */
- tail_array_field = NULL_TREE;
-
- /* Check to see if this offset overlaps with the field. */
- cmp = tree_int_cst_compare (field_offset, offset);
- if (cmp > 0)
- continue;
-
- field_type = TREE_TYPE (f);
-
- /* Here we exactly match the offset being checked. If the types match,
- then we can return that field. */
- if (cmp == 0
- && useless_type_conversion_p (orig_type, field_type))
- {
- if (base_is_ptr)
- base = build1 (INDIRECT_REF, record_type, base);
- t = build3 (COMPONENT_REF, field_type, base, f, NULL_TREE);
- return t;
- }
-
- /* Don't care about offsets into the middle of scalars. */
- if (!AGGREGATE_TYPE_P (field_type))
- continue;
-
- /* Check for array at the end of the struct. This is often
- used as for flexible array members. We should be able to
- turn this into an array access anyway. */
- if (TREE_CODE (field_type) == ARRAY_TYPE)
- tail_array_field = f;
-
- /* Check the end of the field against the offset. */
- if (!DECL_SIZE_UNIT (f)
- || TREE_CODE (DECL_SIZE_UNIT (f)) != INTEGER_CST)
- continue;
- t = int_const_binop (MINUS_EXPR, offset, field_offset, 1);
- if (!tree_int_cst_lt (t, DECL_SIZE_UNIT (f)))
- continue;
-
- /* If we matched, then set offset to the displacement into
- this field. */
- if (base_is_ptr)
- new_base = build1 (INDIRECT_REF, record_type, base);
- else
- new_base = base;
- new_base = build3 (COMPONENT_REF, field_type, new_base, f, NULL_TREE);
-
- /* Recurse to possibly find the match. */
- ret = maybe_fold_offset_to_array_ref (new_base, t, orig_type,
- f == TYPE_FIELDS (record_type));
- if (ret)
- return ret;
- ret = maybe_fold_offset_to_component_ref (field_type, new_base, t,
- orig_type, false);
- if (ret)
- return ret;
- }
-
- if (!tail_array_field)
- return NULL_TREE;
-
- f = tail_array_field;
- field_type = TREE_TYPE (f);
- offset = int_const_binop (MINUS_EXPR, offset, byte_position (f), 1);
-
- /* If we get here, we've got an aggregate field, and a possibly
- nonzero offset into them. Recurse and hope for a valid match. */
- if (base_is_ptr)
- base = build1 (INDIRECT_REF, record_type, base);
- base = build3 (COMPONENT_REF, field_type, base, f, NULL_TREE);
-
- t = maybe_fold_offset_to_array_ref (base, offset, orig_type,
- f == TYPE_FIELDS (record_type));
- if (t)
- return t;
- return maybe_fold_offset_to_component_ref (field_type, base, offset,
- orig_type, false);
-}
-
-/* Attempt to express (ORIG_TYPE)BASE+OFFSET as BASE->field_of_orig_type
- or BASE[index] or by combination of those.
-
- Before attempting the conversion strip off existing ADDR_EXPRs and
- handled component refs. */
-
-tree
-maybe_fold_offset_to_reference (tree base, tree offset, tree orig_type)
-{
- tree ret;
- tree type;
- bool base_is_ptr = true;
-
- STRIP_NOPS (base);
- if (TREE_CODE (base) == ADDR_EXPR)
- {
- base_is_ptr = false;
-
- base = TREE_OPERAND (base, 0);
-
- /* Handle case where existing COMPONENT_REF pick e.g. wrong field of union,
- so it needs to be removed and new COMPONENT_REF constructed.
- The wrong COMPONENT_REF are often constructed by folding the
- (type *)&object within the expression (type *)&object+offset */
- if (handled_component_p (base))