mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-04-13 11:51:22 +08:00
re PR tree-optimization/46562 (CCP currently needs iteration for &a[i])
2011-03-24 Richard Guenther <rguenther@suse.de> PR tree-optimization/46562 * tree.c (build_invariant_address): New function. * tree.h (build_invariant_address): Declare. * tree-dfa.c (get_addr_base_and_unit_offset): Wrap around a renamed function moved ... * tree-flow-inline.h (get_addr_base_and_unit_offset_1): ... here. Take valueization callback parameter. * tree-flow.h (gimple_fold_stmt_to_constant): Declare. * gimple-fold.h: New file. * tree-ssa-ccp.c (ccp_fold): Use gimple_fold_stmt_to_constant_1. (ccp_fold, fold_const_aggregate_ref, fold_ctor_reference, fold_nonarray_ctor_reference, fold_array_ctor_reference, fold_string_cst_ctor_reference, get_base_constructor): Move ... * gimple-fold.c: ... here. (gimple_fold_stmt_to_constant_1): New function split out from ccp_fold. Take a valueization callback parameter. Valueize all operands. (gimple_fold_stmt_to_constant): New wrapper function. (fold_const_aggregate_ref_1): New function split out from fold_const_aggregate_ref. Take a valueization callback parameter. (fold_const_aggregate_ref): Wrap fold_const_aggregate_ref_1. * tree-ssa-sccvn.c (simplify_binary_expression): Simplify invariant POINTER_PLUS_EXPRs to invariant form. (vn_valueize): New function. (try_to_simplify): Simplify by using gimple_fold_stmt_to_constant. * tree-vrp.c (vrp_valueize): New function. (vrp_visit_assignment_or_call): Use gimple_fold_stmt_to_constant to fold statements to constants. * tree-ssa-pre.c (eliminate): Properly guard propagation of function declarations. * Makefile.in (tree-ssa-sccvn.o, tree-vrp.o, gimple-fold.o, tree-ssa-ccp.o): Add gimple-fold.h dependencies. * c-c++-common/pr46562-2.c: New testcase. * c-c++-common/pr46562.c: Likewise. From-SVN: r171386
This commit is contained in:
parent
f3e8ab1973
commit
cfef45c809
@ -1,3 +1,39 @@
|
||||
2011-03-24 Richard Guenther <rguenther@suse.de>
|
||||
|
||||
PR tree-optimization/46562
|
||||
* tree.c (build_invariant_address): New function.
|
||||
* tree.h (build_invariant_address): Declare.
|
||||
* tree-dfa.c (get_addr_base_and_unit_offset): Wrap around
|
||||
a renamed function moved ...
|
||||
* tree-flow-inline.h (get_addr_base_and_unit_offset_1): ... here.
|
||||
Take valueization callback parameter.
|
||||
* tree-flow.h (gimple_fold_stmt_to_constant): Declare.
|
||||
* gimple-fold.h: New file.
|
||||
* tree-ssa-ccp.c (ccp_fold): Use gimple_fold_stmt_to_constant_1.
|
||||
(ccp_fold, fold_const_aggregate_ref,
|
||||
fold_ctor_reference, fold_nonarray_ctor_reference,
|
||||
fold_array_ctor_reference, fold_string_cst_ctor_reference,
|
||||
get_base_constructor): Move ...
|
||||
* gimple-fold.c: ... here.
|
||||
(gimple_fold_stmt_to_constant_1): New function
|
||||
split out from ccp_fold. Take a valueization callback parameter.
|
||||
Valueize all operands.
|
||||
(gimple_fold_stmt_to_constant): New wrapper function.
|
||||
(fold_const_aggregate_ref_1): New function split out from
|
||||
fold_const_aggregate_ref. Take a valueization callback parameter.
|
||||
(fold_const_aggregate_ref): Wrap fold_const_aggregate_ref_1.
|
||||
* tree-ssa-sccvn.c (simplify_binary_expression): Simplify
|
||||
invariant POINTER_PLUS_EXPRs to invariant form.
|
||||
(vn_valueize): New function.
|
||||
(try_to_simplify): Simplify by using gimple_fold_stmt_to_constant.
|
||||
* tree-vrp.c (vrp_valueize): New function.
|
||||
(vrp_visit_assignment_or_call): Use gimple_fold_stmt_to_constant
|
||||
to fold statements to constants.
|
||||
* tree-ssa-pre.c (eliminate): Properly guard propagation of
|
||||
function declarations.
|
||||
* Makefile.in (tree-ssa-sccvn.o, tree-vrp.o, gimple-fold.o,
|
||||
tree-ssa-ccp.o): Add gimple-fold.h dependencies.
|
||||
|
||||
2011-03-24 Richard Sandiford <richard.sandiford@linaro.org>
|
||||
|
||||
* config/h8300/predicates.md (jump_address_operand): Fix register
|
||||
|
@ -2485,12 +2485,12 @@ tree-ssa-sccvn.o : tree-ssa-sccvn.c $(TREE_FLOW_H) $(CONFIG_H) \
|
||||
$(TM_H) coretypes.h $(TREE_DUMP_H) $(TREE_PASS_H) $(FLAGS_H) $(CFGLOOP_H) \
|
||||
alloc-pool.h $(BASIC_BLOCK_H) $(BITMAP_H) langhooks.h $(HASHTAB_H) $(GIMPLE_H) \
|
||||
$(TREE_INLINE_H) tree-iterator.h tree-ssa-propagate.h tree-ssa-sccvn.h \
|
||||
$(PARAMS_H) tree-pretty-print.h gimple-pretty-print.h
|
||||
$(PARAMS_H) tree-pretty-print.h gimple-pretty-print.h gimple-fold.h
|
||||
tree-vrp.o : tree-vrp.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TM_H) $(TREE_H) \
|
||||
$(TREE_FLOW_H) $(TREE_PASS_H) $(TREE_DUMP_H) $(DIAGNOSTIC_H) $(GGC_H) \
|
||||
$(BASIC_BLOCK_H) tree-ssa-propagate.h $(FLAGS_H) $(TREE_DUMP_H) \
|
||||
$(CFGLOOP_H) $(SCEV_H) $(TIMEVAR_H) intl.h tree-pretty-print.h \
|
||||
gimple-pretty-print.h
|
||||
gimple-pretty-print.h gimple-fold.h
|
||||
tree-cfg.o : tree-cfg.c $(TREE_FLOW_H) $(CONFIG_H) $(SYSTEM_H) \
|
||||
$(TREE_H) $(TM_P_H) $(EXPR_H) $(GGC_H) $(FLAGS_H) output.h \
|
||||
$(DIAGNOSTIC_H) $(FUNCTION_H) $(TIMEVAR_H) $(TM_H) coretypes.h \
|
||||
@ -2638,7 +2638,7 @@ gimple-fold.o : gimple-fold.c $(TREE_FLOW_H) $(CONFIG_H) \
|
||||
$(SYSTEM_H) $(RTL_H) $(TREE_H) $(TM_P_H) $(EXPR_H) $(GGC_H) output.h \
|
||||
$(DIAGNOSTIC_H) $(FUNCTION_H) $(TIMEVAR_H) $(TM_H) coretypes.h \
|
||||
$(TREE_DUMP_H) $(BASIC_BLOCK_H) $(TREE_PASS_H) langhooks.h \
|
||||
tree-ssa-propagate.h value-prof.h $(FLAGS_H) $(TARGET_H)
|
||||
tree-ssa-propagate.h value-prof.h $(FLAGS_H) $(TARGET_H) gimple-fold.h
|
||||
gimple-low.o : gimple-low.c $(CONFIG_H) $(SYSTEM_H) $(TREE_H) \
|
||||
$(DIAGNOSTIC_H) $(GIMPLE_H) $(TREE_INLINE_H) langhooks.h \
|
||||
$(LANGHOOKS_DEF_H) $(TREE_FLOW_H) $(TIMEVAR_H) $(TM_H) coretypes.h \
|
||||
@ -3103,7 +3103,7 @@ tree-ssa-ccp.o : tree-ssa-ccp.c $(TREE_FLOW_H) $(CONFIG_H) \
|
||||
$(DIAGNOSTIC_H) $(FUNCTION_H) $(TIMEVAR_H) $(TM_H) coretypes.h \
|
||||
$(TREE_DUMP_H) $(BASIC_BLOCK_H) $(TREE_PASS_H) langhooks.h \
|
||||
tree-ssa-propagate.h value-prof.h $(FLAGS_H) $(TARGET_H) $(DIAGNOSTIC_CORE_H) \
|
||||
$(DBGCNT_H) tree-pretty-print.h gimple-pretty-print.h
|
||||
$(DBGCNT_H) tree-pretty-print.h gimple-pretty-print.h gimple-fold.h
|
||||
tree-sra.o : tree-sra.c $(CONFIG_H) $(SYSTEM_H) coretypes.h alloc-pool.h \
|
||||
$(TM_H) $(TREE_H) $(GIMPLE_H) $(CGRAPH_H) $(TREE_FLOW_H) \
|
||||
$(IPA_PROP_H) $(DIAGNOSTIC_H) statistics.h $(TREE_DUMP_H) $(TIMEVAR_H) \
|
||||
|
@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "tree-pass.h"
|
||||
#include "tree-ssa-propagate.h"
|
||||
#include "target.h"
|
||||
#include "gimple-fold.h"
|
||||
|
||||
/* Return true when DECL can be referenced from current unit.
|
||||
We can get declarations that are not possible to reference for
|
||||
@ -2665,3 +2666,639 @@ maybe_fold_or_comparisons (enum tree_code code1, tree op1a, tree op1b,
|
||||
else
|
||||
return or_comparisons_1 (code2, op2a, op2b, code1, op1a, op1b);
|
||||
}
|
||||
|
||||
|
||||
/* Fold STMT to a constant using VALUEIZE to valueize SSA names.
|
||||
|
||||
Either NULL_TREE, a simplified but non-constant or a constant
|
||||
is returned.
|
||||
|
||||
??? This should go into a gimple-fold-inline.h file to be eventually
|
||||
privatized with the single valueize function used in the various TUs
|
||||
to avoid the indirect function call overhead. */
|
||||
|
||||
tree
|
||||
gimple_fold_stmt_to_constant_1 (gimple stmt, tree (*valueize) (tree))
|
||||
{
|
||||
location_t loc = gimple_location (stmt);
|
||||
switch (gimple_code (stmt))
|
||||
{
|
||||
case GIMPLE_ASSIGN:
|
||||
{
|
||||
enum tree_code subcode = gimple_assign_rhs_code (stmt);
|
||||
|
||||
switch (get_gimple_rhs_class (subcode))
|
||||
{
|
||||
case GIMPLE_SINGLE_RHS:
|
||||
{
|
||||
tree rhs = gimple_assign_rhs1 (stmt);
|
||||
enum tree_code_class kind = TREE_CODE_CLASS (subcode);
|
||||
|
||||
if (TREE_CODE (rhs) == SSA_NAME)
|
||||
{
|
||||
/* If the RHS is an SSA_NAME, return its known constant value,
|
||||
if any. */
|
||||
return (*valueize) (rhs);
|
||||
}
|
||||
/* Handle propagating invariant addresses into address
|
||||
operations. */
|
||||
else if (TREE_CODE (rhs) == ADDR_EXPR
|
||||
&& !is_gimple_min_invariant (rhs))
|
||||
{
|
||||
HOST_WIDE_INT offset;
|
||||
tree base;
|
||||
base = get_addr_base_and_unit_offset_1 (TREE_OPERAND (rhs, 0),
|
||||
&offset,
|
||||
valueize);
|
||||
if (base
|
||||
&& (CONSTANT_CLASS_P (base)
|
||||
|| decl_address_invariant_p (base)))
|
||||
return build_invariant_address (TREE_TYPE (rhs),
|
||||
base, offset);
|
||||
}
|
||||
else if (TREE_CODE (rhs) == CONSTRUCTOR
|
||||
&& TREE_CODE (TREE_TYPE (rhs)) == VECTOR_TYPE
|
||||
&& (CONSTRUCTOR_NELTS (rhs)
|
||||
== TYPE_VECTOR_SUBPARTS (TREE_TYPE (rhs))))
|
||||
{
|
||||
unsigned i;
|
||||
tree val, list;
|
||||
|
||||
list = NULL_TREE;
|
||||
FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (rhs), i, val)
|
||||
{
|
||||
val = (*valueize) (val);
|
||||
if (TREE_CODE (val) == INTEGER_CST
|
||||
|| TREE_CODE (val) == REAL_CST
|
||||
|| TREE_CODE (val) == FIXED_CST)
|
||||
list = tree_cons (NULL_TREE, val, list);
|
||||
else
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
return build_vector (TREE_TYPE (rhs), nreverse (list));
|
||||
}
|
||||
|
||||
if (kind == tcc_reference)
|
||||
{
|
||||
if ((TREE_CODE (rhs) == VIEW_CONVERT_EXPR
|
||||
|| TREE_CODE (rhs) == REALPART_EXPR
|
||||
|| TREE_CODE (rhs) == IMAGPART_EXPR)
|
||||
&& TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME)
|
||||
{
|
||||
tree val = (*valueize) (TREE_OPERAND (rhs, 0));
|
||||
return fold_unary_loc (EXPR_LOCATION (rhs),
|
||||
TREE_CODE (rhs),
|
||||
TREE_TYPE (rhs), val);
|
||||
}
|
||||
else if (TREE_CODE (rhs) == BIT_FIELD_REF
|
||||
&& TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME)
|
||||
{
|
||||
tree val = (*valueize) (TREE_OPERAND (rhs, 0));
|
||||
return fold_ternary_loc (EXPR_LOCATION (rhs),
|
||||
TREE_CODE (rhs),
|
||||
TREE_TYPE (rhs), val,
|
||||
TREE_OPERAND (rhs, 1),
|
||||
TREE_OPERAND (rhs, 2));
|
||||
}
|
||||
else if (TREE_CODE (rhs) == MEM_REF
|
||||
&& TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME)
|
||||
{
|
||||
tree val = (*valueize) (TREE_OPERAND (rhs, 0));
|
||||
if (TREE_CODE (val) == ADDR_EXPR
|
||||
&& is_gimple_min_invariant (val))
|
||||
{
|
||||
tree tem = fold_build2 (MEM_REF, TREE_TYPE (rhs),
|
||||
unshare_expr (val),
|
||||
TREE_OPERAND (rhs, 1));
|
||||
if (tem)
|
||||
rhs = tem;
|
||||
}
|
||||
}
|
||||
return fold_const_aggregate_ref_1 (rhs, valueize);
|
||||
}
|
||||
else if (kind == tcc_declaration)
|
||||
return get_symbol_constant_value (rhs);
|
||||
return rhs;
|
||||
}
|
||||
|
||||
case GIMPLE_UNARY_RHS:
|
||||
{
|
||||
/* Handle unary operators that can appear in GIMPLE form.
|
||||
Note that we know the single operand must be a constant,
|
||||
so this should almost always return a simplified RHS. */
|
||||
tree lhs = gimple_assign_lhs (stmt);
|
||||
tree op0 = (*valueize) (gimple_assign_rhs1 (stmt));
|
||||
|
||||
/* Conversions are useless for CCP purposes if they are
|
||||
value-preserving. Thus the restrictions that
|
||||
useless_type_conversion_p places for pointer type conversions
|
||||
do not apply here. Substitution later will only substitute to
|
||||
allowed places. */
|
||||
if (CONVERT_EXPR_CODE_P (subcode)
|
||||
&& POINTER_TYPE_P (TREE_TYPE (lhs))
|
||||
&& POINTER_TYPE_P (TREE_TYPE (op0)))
|
||||
{
|
||||
tree tem;
|
||||
/* Try to re-construct array references on-the-fly. */
|
||||
if (!useless_type_conversion_p (TREE_TYPE (lhs),
|
||||
TREE_TYPE (op0))
|
||||
&& ((tem = maybe_fold_offset_to_address
|
||||
(loc,
|
||||
op0, integer_zero_node, TREE_TYPE (lhs)))
|
||||
!= NULL_TREE))
|
||||
return tem;
|
||||
return op0;
|
||||
}
|
||||
|
||||
return
|
||||
fold_unary_ignore_overflow_loc (loc, subcode,
|
||||
gimple_expr_type (stmt), op0);
|
||||
}
|
||||
|
||||
case GIMPLE_BINARY_RHS:
|
||||
{
|
||||
/* Handle binary operators that can appear in GIMPLE form. */
|
||||
tree op0 = (*valueize) (gimple_assign_rhs1 (stmt));
|
||||
tree op1 = (*valueize) (gimple_assign_rhs2 (stmt));
|
||||
|
||||
/* Translate &x + CST into an invariant form suitable for
|
||||
further propagation. */
|
||||
if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR
|
||||
&& TREE_CODE (op0) == ADDR_EXPR
|
||||
&& TREE_CODE (op1) == INTEGER_CST)
|
||||
{
|
||||
tree off = fold_convert (ptr_type_node, op1);
|
||||
return build_fold_addr_expr
|
||||
(fold_build2 (MEM_REF,
|
||||
TREE_TYPE (TREE_TYPE (op0)),
|
||||
unshare_expr (op0), off));
|
||||
}
|
||||
|
||||
return fold_binary_loc (loc, subcode,
|
||||
gimple_expr_type (stmt), op0, op1);
|
||||
}
|
||||
|
||||
case GIMPLE_TERNARY_RHS:
|
||||
{
|
||||
/* Handle ternary operators that can appear in GIMPLE form. */
|
||||
tree op0 = (*valueize) (gimple_assign_rhs1 (stmt));
|
||||
tree op1 = (*valueize) (gimple_assign_rhs2 (stmt));
|
||||
tree op2 = (*valueize) (gimple_assign_rhs3 (stmt));
|
||||
|
||||
return fold_ternary_loc (loc, subcode,
|
||||
gimple_expr_type (stmt), op0, op1, op2);
|
||||
}
|
||||
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
}
|
||||
|
||||
case GIMPLE_CALL:
|
||||
{
|
||||
tree fn = (*valueize) (gimple_call_fn (stmt));
|
||||
if (TREE_CODE (fn) == ADDR_EXPR
|
||||
&& TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL
|
||||
&& DECL_BUILT_IN (TREE_OPERAND (fn, 0)))
|
||||
{
|
||||
tree *args = XALLOCAVEC (tree, gimple_call_num_args (stmt));
|
||||
tree call, retval;
|
||||
unsigned i;
|
||||
for (i = 0; i < gimple_call_num_args (stmt); ++i)
|
||||
args[i] = (*valueize) (gimple_call_arg (stmt, i));
|
||||
call = build_call_array_loc (loc,
|
||||
gimple_call_return_type (stmt),
|
||||
fn, gimple_call_num_args (stmt), args);
|
||||
retval = fold_call_expr (EXPR_LOCATION (call), call, false);
|
||||
if (retval)
|
||||
/* fold_call_expr wraps the result inside a NOP_EXPR. */
|
||||
STRIP_NOPS (retval);
|
||||
return retval;
|
||||
}
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
default:
|
||||
return NULL_TREE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fold STMT to a constant using VALUEIZE to valueize SSA names.
|
||||
Returns NULL_TREE if folding to a constant is not possible, otherwise
|
||||
returns a constant according to is_gimple_min_invariant. */
|
||||
|
||||
tree
|
||||
gimple_fold_stmt_to_constant (gimple stmt, tree (*valueize) (tree))
|
||||
{
|
||||
tree res = gimple_fold_stmt_to_constant_1 (stmt, valueize);
|
||||
if (res && is_gimple_min_invariant (res))
|
||||
return res;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
|
||||
/* The following set of functions are supposed to fold references using
|
||||
their constant initializers. */
|
||||
|
||||
static tree fold_ctor_reference (tree type, tree ctor,
|
||||
unsigned HOST_WIDE_INT offset,
|
||||
unsigned HOST_WIDE_INT size);
|
||||
|
||||
/* See if we can find constructor defining value of BASE.
|
||||
When we know the consructor with constant offset (such as
|
||||
base is array[40] and we do know constructor of array), then
|
||||
BIT_OFFSET is adjusted accordingly.
|
||||
|
||||
As a special case, return error_mark_node when constructor
|
||||
is not explicitly available, but it is known to be zero
|
||||
such as 'static const int a;'. */
|
||||
static tree
|
||||
get_base_constructor (tree base, HOST_WIDE_INT *bit_offset,
|
||||
tree (*valueize)(tree))
|
||||
{
|
||||
HOST_WIDE_INT bit_offset2, size, max_size;
|
||||
if (TREE_CODE (base) == MEM_REF)
|
||||
{
|
||||
if (!integer_zerop (TREE_OPERAND (base, 1)))
|
||||
{
|
||||
if (!host_integerp (TREE_OPERAND (base, 1), 0))
|
||||
return NULL_TREE;
|
||||
*bit_offset += (mem_ref_offset (base).low
|
||||
* BITS_PER_UNIT);
|
||||
}
|
||||
|
||||
if (valueize
|
||||
&& TREE_CODE (TREE_OPERAND (base, 0)) == SSA_NAME)
|
||||
base = valueize (TREE_OPERAND (base, 0));
|
||||
if (!base || TREE_CODE (base) != ADDR_EXPR)
|
||||
return NULL_TREE;
|
||||
base = TREE_OPERAND (base, 0);
|
||||
}
|
||||
|
||||
/* Get a CONSTRUCTOR. If BASE is a VAR_DECL, get its
|
||||
DECL_INITIAL. If BASE is a nested reference into another
|
||||
ARRAY_REF or COMPONENT_REF, make a recursive call to resolve
|
||||
the inner reference. */
|
||||
switch (TREE_CODE (base))
|
||||
{
|
||||
case VAR_DECL:
|
||||
if (!const_value_known_p (base))
|
||||
return NULL_TREE;
|
||||
|
||||
/* Fallthru. */
|
||||
case CONST_DECL:
|
||||
if (!DECL_INITIAL (base)
|
||||
&& (TREE_STATIC (base) || DECL_EXTERNAL (base)))
|
||||
return error_mark_node;
|
||||
return DECL_INITIAL (base);
|
||||
|
||||
case ARRAY_REF:
|
||||
case COMPONENT_REF:
|
||||
base = get_ref_base_and_extent (base, &bit_offset2, &size, &max_size);
|
||||
if (max_size == -1 || size != max_size)
|
||||
return NULL_TREE;
|
||||
*bit_offset += bit_offset2;
|
||||
return get_base_constructor (base, bit_offset, valueize);
|
||||
|
||||
case STRING_CST:
|
||||
case CONSTRUCTOR:
|
||||
return base;
|
||||
|
||||
default:
|
||||
return NULL_TREE;
|
||||
}
|
||||
}
|
||||
|
||||
/* CTOR is STRING_CST. Fold reference of type TYPE and size SIZE
|
||||
to the memory at bit OFFSET.
|
||||
|
||||
We do only simple job of folding byte accesses. */
|
||||
|
||||
static tree
|
||||
fold_string_cst_ctor_reference (tree type, tree ctor,
|
||||
unsigned HOST_WIDE_INT offset,
|
||||
unsigned HOST_WIDE_INT size)
|
||||
{
|
||||
if (INTEGRAL_TYPE_P (type)
|
||||
&& (TYPE_MODE (type)
|
||||
== TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor))))
|
||||
&& (GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor))))
|
||||
== MODE_INT)
|
||||
&& GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) == 1
|
||||
&& size == BITS_PER_UNIT
|
||||
&& !(offset % BITS_PER_UNIT))
|
||||
{
|
||||
offset /= BITS_PER_UNIT;
|
||||
if (offset < (unsigned HOST_WIDE_INT) TREE_STRING_LENGTH (ctor))
|
||||
return build_int_cst_type (type, (TREE_STRING_POINTER (ctor)
|
||||
[offset]));
|
||||
/* Folding
|
||||
const char a[20]="hello";
|
||||
return a[10];
|
||||
|
||||
might lead to offset greater than string length. In this case we
|
||||
know value is either initialized to 0 or out of bounds. Return 0
|
||||
in both cases. */
|
||||
return build_zero_cst (type);
|
||||
}
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* CTOR is CONSTRUCTOR of an array type. Fold reference of type TYPE and size
|
||||
SIZE to the memory at bit OFFSET. */
|
||||
|
||||
static tree
|
||||
fold_array_ctor_reference (tree type, tree ctor,
|
||||
unsigned HOST_WIDE_INT offset,
|
||||
unsigned HOST_WIDE_INT size)
|
||||
{
|
||||
unsigned HOST_WIDE_INT cnt;
|
||||
tree cfield, cval;
|
||||
double_int low_bound, elt_size;
|
||||
double_int index, max_index;
|
||||
double_int access_index;
|
||||
tree domain_type = TYPE_DOMAIN (TREE_TYPE (ctor));
|
||||
HOST_WIDE_INT inner_offset;
|
||||
|
||||
/* Compute low bound and elt size. */
|
||||
if (domain_type && TYPE_MIN_VALUE (domain_type))
|
||||
{
|
||||
/* Static constructors for variably sized objects makes no sense. */
|
||||
gcc_assert (TREE_CODE (TYPE_MIN_VALUE (domain_type)) == INTEGER_CST);
|
||||
low_bound = tree_to_double_int (TYPE_MIN_VALUE (domain_type));
|
||||
}
|
||||
else
|
||||
low_bound = double_int_zero;
|
||||
/* Static constructors for variably sized objects makes no sense. */
|
||||
gcc_assert (TREE_CODE(TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (ctor))))
|
||||
== INTEGER_CST);
|
||||
elt_size =
|
||||
tree_to_double_int (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (ctor))));
|
||||
|
||||
|
||||
/* We can handle only constantly sized accesses that are known to not
|
||||
be larger than size of array element. */
|
||||
if (!TYPE_SIZE_UNIT (type)
|
||||
|| TREE_CODE (TYPE_SIZE_UNIT (type)) != INTEGER_CST
|
||||
|| double_int_cmp (elt_size,
|
||||
tree_to_double_int (TYPE_SIZE_UNIT (type)), 0) < 0)
|
||||
return NULL_TREE;
|
||||
|
||||
/* Compute the array index we look for. */
|
||||
access_index = double_int_udiv (uhwi_to_double_int (offset / BITS_PER_UNIT),
|
||||
elt_size, TRUNC_DIV_EXPR);
|
||||
access_index = double_int_add (access_index, low_bound);
|
||||
|
||||
/* And offset within the access. */
|
||||
inner_offset = offset % (double_int_to_uhwi (elt_size) * BITS_PER_UNIT);
|
||||
|
||||
/* See if the array field is large enough to span whole access. We do not
|
||||
care to fold accesses spanning multiple array indexes. */
|
||||
if (inner_offset + size > double_int_to_uhwi (elt_size) * BITS_PER_UNIT)
|
||||
return NULL_TREE;
|
||||
|
||||
index = double_int_sub (low_bound, double_int_one);
|
||||
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (ctor), cnt, cfield, cval)
|
||||
{
|
||||
/* Array constructor might explicitely set index, or specify range
|
||||
or leave index NULL meaning that it is next index after previous
|
||||
one. */
|
||||
if (cfield)
|
||||
{
|
||||
if (TREE_CODE (cfield) == INTEGER_CST)
|
||||
max_index = index = tree_to_double_int (cfield);
|
||||
else
|
||||
{
|
||||
gcc_assert (TREE_CODE (cfield) == RANGE_EXPR);
|
||||
index = tree_to_double_int (TREE_OPERAND (cfield, 0));
|
||||
max_index = tree_to_double_int (TREE_OPERAND (cfield, 1));
|
||||
}
|
||||
}
|
||||
else
|
||||
max_index = index = double_int_add (index, double_int_one);
|
||||
|
||||
/* Do we have match? */
|
||||
if (double_int_cmp (access_index, index, 1) >= 0
|
||||
&& double_int_cmp (access_index, max_index, 1) <= 0)
|
||||
return fold_ctor_reference (type, cval, inner_offset, size);
|
||||
}
|
||||
/* When memory is not explicitely mentioned in constructor,
|
||||
it is 0 (or out of range). */
|
||||
return build_zero_cst (type);
|
||||
}
|
||||
|
||||
/* CTOR is CONSTRUCTOR of an aggregate or vector.
|
||||
Fold reference of type TYPE and size SIZE to the memory at bit OFFSET. */
|
||||
|
||||
static tree
|
||||
fold_nonarray_ctor_reference (tree type, tree ctor,
|
||||
unsigned HOST_WIDE_INT offset,
|
||||
unsigned HOST_WIDE_INT size)
|
||||
{
|
||||
unsigned HOST_WIDE_INT cnt;
|
||||
tree cfield, cval;
|
||||
|
||||
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (ctor), cnt, cfield,
|
||||
cval)
|
||||
{
|
||||
tree byte_offset = DECL_FIELD_OFFSET (cfield);
|
||||
tree field_offset = DECL_FIELD_BIT_OFFSET (cfield);
|
||||
tree field_size = DECL_SIZE (cfield);
|
||||
double_int bitoffset;
|
||||
double_int byte_offset_cst = tree_to_double_int (byte_offset);
|
||||
double_int bits_per_unit_cst = uhwi_to_double_int (BITS_PER_UNIT);
|
||||
double_int bitoffset_end;
|
||||
|
||||
/* Variable sized objects in static constructors makes no sense,
|
||||
but field_size can be NULL for flexible array members. */
|
||||
gcc_assert (TREE_CODE (field_offset) == INTEGER_CST
|
||||
&& TREE_CODE (byte_offset) == INTEGER_CST
|
||||
&& (field_size != NULL_TREE
|
||||
? TREE_CODE (field_size) == INTEGER_CST
|
||||
: TREE_CODE (TREE_TYPE (cfield)) == ARRAY_TYPE));
|
||||
|
||||
/* Compute bit offset of the field. */
|
||||
bitoffset = double_int_add (tree_to_double_int (field_offset),
|
||||
double_int_mul (byte_offset_cst,
|
||||
bits_per_unit_cst));
|
||||
/* Compute bit offset where the field ends. */
|
||||
if (field_size != NULL_TREE)
|
||||
bitoffset_end = double_int_add (bitoffset,
|
||||
tree_to_double_int (field_size));
|
||||
else
|
||||
bitoffset_end = double_int_zero;
|
||||
|
||||
/* Is OFFSET in the range (BITOFFSET, BITOFFSET_END)? */
|
||||
if (double_int_cmp (uhwi_to_double_int (offset), bitoffset, 0) >= 0
|
||||
&& (field_size == NULL_TREE
|
||||
|| double_int_cmp (uhwi_to_double_int (offset),
|
||||
bitoffset_end, 0) < 0))
|
||||
{
|
||||
double_int access_end = double_int_add (uhwi_to_double_int (offset),
|
||||
uhwi_to_double_int (size));
|
||||
double_int inner_offset = double_int_sub (uhwi_to_double_int (offset),
|
||||
bitoffset);
|
||||
/* We do have overlap. Now see if field is large enough to
|
||||
cover the access. Give up for accesses spanning multiple
|
||||
fields. */
|
||||
if (double_int_cmp (access_end, bitoffset_end, 0) > 0)
|
||||
return NULL_TREE;
|
||||
return fold_ctor_reference (type, cval,
|
||||
double_int_to_uhwi (inner_offset), size);
|
||||
}
|
||||
}
|
||||
/* When memory is not explicitely mentioned in constructor, it is 0. */
|
||||
return build_zero_cst (type);
|
||||
}
|
||||
|
||||
/* CTOR is value initializing memory, fold reference of type TYPE and size SIZE
|
||||
to the memory at bit OFFSET. */
|
||||
|
||||
static tree
|
||||
fold_ctor_reference (tree type, tree ctor, unsigned HOST_WIDE_INT offset,
|
||||
unsigned HOST_WIDE_INT size)
|
||||
{
|
||||
tree ret;
|
||||
|
||||
/* We found the field with exact match. */
|
||||
if (useless_type_conversion_p (type, TREE_TYPE (ctor))
|
||||
&& !offset)
|
||||
return canonicalize_constructor_val (ctor);
|
||||
|
||||
/* We are at the end of walk, see if we can view convert the
|
||||
result. */
|
||||
if (!AGGREGATE_TYPE_P (TREE_TYPE (ctor)) && !offset
|
||||
/* VIEW_CONVERT_EXPR is defined only for matching sizes. */
|
||||
&& operand_equal_p (TYPE_SIZE (type),
|
||||
TYPE_SIZE (TREE_TYPE (ctor)), 0))
|
||||
{
|
||||
ret = canonicalize_constructor_val (ctor);
|
||||
ret = fold_unary (VIEW_CONVERT_EXPR, type, ret);
|
||||
if (ret)
|
||||
STRIP_NOPS (ret);
|
||||
return ret;
|
||||
}
|
||||
if (TREE_CODE (ctor) == STRING_CST)
|
||||
return fold_string_cst_ctor_reference (type, ctor, offset, size);
|
||||
if (TREE_CODE (ctor) == CONSTRUCTOR)
|
||||
{
|
||||
|
||||
if (TREE_CODE (TREE_TYPE (ctor)) == ARRAY_TYPE)
|
||||
return fold_array_ctor_reference (type, ctor, offset, size);
|
||||
else
|
||||
return fold_nonarray_ctor_reference (type, ctor, offset, size);
|
||||
}
|
||||
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Return the tree representing the element referenced by T if T is an
|
||||
ARRAY_REF or COMPONENT_REF into constant aggregates valuezing SSA
|
||||
names using VALUEIZE. Return NULL_TREE otherwise. */
|
||||
|
||||
tree
|
||||
fold_const_aggregate_ref_1 (tree t, tree (*valueize) (tree))
|
||||
{
|
||||
tree ctor, idx, base;
|
||||
HOST_WIDE_INT offset, size, max_size;
|
||||
tree tem;
|
||||
|
||||
if (TREE_CODE_CLASS (TREE_CODE (t)) == tcc_declaration)
|
||||
return get_symbol_constant_value (t);
|
||||
|
||||
tem = fold_read_from_constant_string (t);
|
||||
if (tem)
|
||||
return tem;
|
||||
|
||||
switch (TREE_CODE (t))
|
||||
{
|
||||
case ARRAY_REF:
|
||||
case ARRAY_RANGE_REF:
|
||||
/* Constant indexes are handled well by get_base_constructor.
|
||||
Only special case variable offsets.
|
||||
FIXME: This code can't handle nested references with variable indexes
|
||||
(they will be handled only by iteration of ccp). Perhaps we can bring
|
||||
get_ref_base_and_extent here and make it use a valueize callback. */
|
||||
if (TREE_CODE (TREE_OPERAND (t, 1)) == SSA_NAME
|
||||
&& valueize
|
||||
&& (idx = (*valueize) (TREE_OPERAND (t, 1)))
|
||||
&& host_integerp (idx, 0))
|
||||
{
|
||||
tree low_bound, unit_size;
|
||||
|
||||
/* If the resulting bit-offset is constant, track it. */
|
||||
if ((low_bound = array_ref_low_bound (t),
|
||||
host_integerp (low_bound, 0))
|
||||
&& (unit_size = array_ref_element_size (t),
|
||||
host_integerp (unit_size, 1)))
|
||||
{
|
||||
offset = TREE_INT_CST_LOW (idx);
|
||||
offset -= TREE_INT_CST_LOW (low_bound);
|
||||
offset *= TREE_INT_CST_LOW (unit_size);
|
||||
offset *= BITS_PER_UNIT;
|
||||
|
||||
base = TREE_OPERAND (t, 0);
|
||||
ctor = get_base_constructor (base, &offset, valueize);
|
||||
/* Empty constructor. Always fold to 0. */
|
||||
if (ctor == error_mark_node)
|
||||
return build_zero_cst (TREE_TYPE (t));
|
||||
/* Out of bound array access. Value is undefined,
|
||||
but don't fold. */
|
||||
if (offset < 0)
|
||||
return NULL_TREE;
|
||||
/* We can not determine ctor. */
|
||||
if (!ctor)
|
||||
return NULL_TREE;
|
||||
return fold_ctor_reference (TREE_TYPE (t), ctor, offset,
|
||||
TREE_INT_CST_LOW (unit_size)
|
||||
* BITS_PER_UNIT);
|
||||
}
|
||||
}
|
||||
/* Fallthru. */
|
||||
|
||||
case COMPONENT_REF:
|
||||
case BIT_FIELD_REF:
|
||||
case TARGET_MEM_REF:
|
||||
case MEM_REF:
|
||||
base = get_ref_base_and_extent (t, &offset, &size, &max_size);
|
||||
ctor = get_base_constructor (base, &offset, valueize);
|
||||
|
||||
/* Empty constructor. Always fold to 0. */
|
||||
if (ctor == error_mark_node)
|
||||
return build_zero_cst (TREE_TYPE (t));
|
||||
/* We do not know precise address. */
|
||||
if (max_size == -1 || max_size != size)
|
||||
return NULL_TREE;
|
||||
/* We can not determine ctor. */
|
||||
if (!ctor)
|
||||
return NULL_TREE;
|
||||
|
||||
/* Out of bound array access. Value is undefined, but don't fold. */
|
||||
if (offset < 0)
|
||||
return NULL_TREE;
|
||||
|
||||
return fold_ctor_reference (TREE_TYPE (t), ctor, offset, size);
|
||||
|
||||
case REALPART_EXPR:
|
||||
case IMAGPART_EXPR:
|
||||
{
|
||||
tree c = fold_const_aggregate_ref_1 (TREE_OPERAND (t, 0), valueize);
|
||||
if (c && TREE_CODE (c) == COMPLEX_CST)
|
||||
return fold_build1_loc (EXPR_LOCATION (t),
|
||||
TREE_CODE (t), TREE_TYPE (t), c);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
tree
|
||||
fold_const_aggregate_ref (tree t)
|
||||
{
|
||||
return fold_const_aggregate_ref_1 (t, NULL);
|
||||
}
|
||||
|
33
gcc/gimple-fold.h
Normal file
33
gcc/gimple-fold.h
Normal file
@ -0,0 +1,33 @@
|
||||
/* Gimple folding definitions.
|
||||
|
||||
Copyright 2011 Free Software Foundation, Inc.
|
||||
Contributed by Richard Guenther <rguenther@suse.de>
|
||||
|
||||
This file is part of GCC.
|
||||
|
||||
GCC is free software; you can redistribute it and/or modify it under
|
||||
the terms of the GNU General Public License as published by the Free
|
||||
Software Foundation; either version 3, or (at your option) any later
|
||||
version.
|
||||
|
||||
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with GCC; see the file COPYING3. If not see
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#ifndef GCC_GIMPLE_FOLD_H
|
||||
#define GCC_GIMPLE_FOLD_H
|
||||
|
||||
#include "coretypes.h"
|
||||
|
||||
tree fold_const_aggregate_ref_1 (tree, tree (*) (tree));
|
||||
tree fold_const_aggregate_ref (tree);
|
||||
|
||||
tree gimple_fold_stmt_to_constant_1 (gimple, tree (*) (tree));
|
||||
tree gimple_fold_stmt_to_constant (gimple, tree (*) (tree));
|
||||
|
||||
#endif /* GCC_GIMPLE_FOLD_H */
|
@ -169,8 +169,9 @@ static void
|
||||
dump_split_point (FILE * file, struct split_point *current)
|
||||
{
|
||||
fprintf (file,
|
||||
"Split point at BB %i header time:%i header size: %i"
|
||||
" split time: %i split size: %i\n bbs: ",
|
||||
"Split point at BB %i\n"
|
||||
" header time: %i header size: %i\n"
|
||||
" split time: %i split size: %i\n bbs: ",
|
||||
current->entry_bb->index, current->header_time,
|
||||
current->header_size, current->split_time, current->split_size);
|
||||
dump_bitmap (file, current->split_bbs);
|
||||
@ -1036,12 +1037,13 @@ split_function (struct split_point *split_point)
|
||||
|
||||
/* If RETURN_BB has virtual operand PHIs, they must be removed and the
|
||||
virtual operand marked for renaming as we change the CFG in a way that
|
||||
tree-inline is not able to compensate for.
|
||||
tree-inline is not able to compensate for.
|
||||
|
||||
Note this can happen whether or not we have a return value. If we have
|
||||
a return value, then RETURN_BB may have PHIs for real operands too. */
|
||||
if (return_bb != EXIT_BLOCK_PTR)
|
||||
{
|
||||
bool phi_p = false;
|
||||
for (gsi = gsi_start_phis (return_bb); !gsi_end_p (gsi);)
|
||||
{
|
||||
gimple stmt = gsi_stmt (gsi);
|
||||
@ -1052,7 +1054,28 @@ split_function (struct split_point *split_point)
|
||||
}
|
||||
mark_virtual_phi_result_for_renaming (stmt);
|
||||
remove_phi_node (&gsi, true);
|
||||
phi_p = true;
|
||||
}
|
||||
/* In reality we have to rename the reaching definition of the
|
||||
virtual operand at return_bb as we will eventually release it
|
||||
when we remove the code region we outlined.
|
||||
So we have to rename all immediate virtual uses of that region
|
||||
if we didn't see a PHI definition yet. */
|
||||
/* ??? In real reality we want to set the reaching vdef of the
|
||||
entry of the SESE region as the vuse of the call and the reaching
|
||||
vdef of the exit of the SESE region as the vdef of the call. */
|
||||
if (!phi_p)
|
||||
for (gsi = gsi_start_bb (return_bb); !gsi_end_p (gsi); gsi_next (&gsi))
|
||||
{
|
||||
gimple stmt = gsi_stmt (gsi);
|
||||
if (gimple_vuse (stmt))
|
||||
{
|
||||
gimple_set_vuse (stmt, NULL_TREE);
|
||||
update_stmt (stmt);
|
||||
}
|
||||
if (gimple_vdef (stmt))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now create the actual clone. */
|
||||
|
@ -1,3 +1,9 @@
|
||||
2011-03-24 Richard Guenther <rguenther@suse.de>
|
||||
|
||||
PR tree-optimization/46562
|
||||
* c-c++-common/pr46562-2.c: New testcase.
|
||||
* c-c++-common/pr46562.c: Likewise.
|
||||
|
||||
2011-03-24 Ira Rosen <ira.rosen@linaro.org>
|
||||
|
||||
* gcc.dg/vect/vect-cselim-1.c: New test.
|
||||
|
13
gcc/testsuite/c-c++-common/pr46562-2.c
Normal file
13
gcc/testsuite/c-c++-common/pr46562-2.c
Normal file
@ -0,0 +1,13 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O -fno-tree-ccp -fno-tree-forwprop -fdump-tree-fre" } */
|
||||
|
||||
static const int a[4] = {};
|
||||
int foo(void)
|
||||
{
|
||||
int i = 1;
|
||||
const int *p = &a[i];
|
||||
return *p;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-tree-dump "= 0;" "fre" } } */
|
||||
/* { dg-final { cleanup-tree-dump "fre" } } */
|
13
gcc/testsuite/c-c++-common/pr46562.c
Normal file
13
gcc/testsuite/c-c++-common/pr46562.c
Normal file
@ -0,0 +1,13 @@
|
||||
/* { dg-do compile } */
|
||||
/* { dg-options "-O -fdump-tree-ccp1" } */
|
||||
|
||||
static const int a[4] = {};
|
||||
int foo(void)
|
||||
{
|
||||
int i = 1;
|
||||
const int *p = &a[i];
|
||||
return *p;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-tree-dump "return 0;" "ccp1" } } */
|
||||
/* { dg-final { cleanup-tree-dump "ccp1" } } */
|
105
gcc/tree-dfa.c
105
gcc/tree-dfa.c
@ -963,110 +963,7 @@ get_ref_base_and_extent (tree exp, HOST_WIDE_INT *poffset,
|
||||
tree
|
||||
get_addr_base_and_unit_offset (tree exp, HOST_WIDE_INT *poffset)
|
||||
{
|
||||
HOST_WIDE_INT byte_offset = 0;
|
||||
|
||||
/* Compute cumulative byte-offset for nested component-refs and array-refs,
|
||||
and find the ultimate containing object. */
|
||||
while (1)
|
||||
{
|
||||
switch (TREE_CODE (exp))
|
||||
{
|
||||
case BIT_FIELD_REF:
|
||||
return NULL_TREE;
|
||||
|
||||
case COMPONENT_REF:
|
||||
{
|
||||
tree field = TREE_OPERAND (exp, 1);
|
||||
tree this_offset = component_ref_field_offset (exp);
|
||||
HOST_WIDE_INT hthis_offset;
|
||||
|
||||
if (!this_offset
|
||||
|| TREE_CODE (this_offset) != INTEGER_CST
|
||||
|| (TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (field))
|
||||
% BITS_PER_UNIT))
|
||||
return NULL_TREE;
|
||||
|
||||
hthis_offset = TREE_INT_CST_LOW (this_offset);
|
||||
hthis_offset += (TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (field))
|
||||
/ BITS_PER_UNIT);
|
||||
byte_offset += hthis_offset;
|
||||
}
|
||||
break;
|
||||
|
||||
case ARRAY_REF:
|
||||
case ARRAY_RANGE_REF:
|
||||
{
|
||||
tree index = TREE_OPERAND (exp, 1);
|
||||
tree low_bound, unit_size;
|
||||
|
||||
/* If the resulting bit-offset is constant, track it. */
|
||||
if (TREE_CODE (index) == INTEGER_CST
|
||||
&& (low_bound = array_ref_low_bound (exp),
|
||||
TREE_CODE (low_bound) == INTEGER_CST)
|
||||
&& (unit_size = array_ref_element_size (exp),
|
||||
TREE_CODE (unit_size) == INTEGER_CST))
|
||||
{
|
||||
HOST_WIDE_INT hindex = TREE_INT_CST_LOW (index);
|
||||
|
||||
hindex -= TREE_INT_CST_LOW (low_bound);
|
||||
hindex *= TREE_INT_CST_LOW (unit_size);
|
||||
byte_offset += hindex;
|
||||
}
|
||||
else
|
||||
return NULL_TREE;
|
||||
}
|
||||
break;
|
||||
|
||||
case REALPART_EXPR:
|
||||
break;
|
||||
|
||||
case IMAGPART_EXPR:
|
||||
byte_offset += TREE_INT_CST_LOW (TYPE_SIZE_UNIT (TREE_TYPE (exp)));
|
||||
break;
|
||||
|
||||
case VIEW_CONVERT_EXPR:
|
||||
break;
|
||||
|
||||
case MEM_REF:
|
||||
/* Hand back the decl for MEM[&decl, off]. */
|
||||
if (TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR)
|
||||
{
|
||||
if (!integer_zerop (TREE_OPERAND (exp, 1)))
|
||||
{
|
||||
double_int off = mem_ref_offset (exp);
|
||||
gcc_assert (off.high == -1 || off.high == 0);
|
||||
byte_offset += double_int_to_shwi (off);
|
||||
}
|
||||
exp = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
|
||||
}
|
||||
goto done;
|
||||
|
||||
case TARGET_MEM_REF:
|
||||
/* Hand back the decl for MEM[&decl, off]. */
|
||||
if (TREE_CODE (TMR_BASE (exp)) == ADDR_EXPR)
|
||||
{
|
||||
if (TMR_INDEX (exp) || TMR_INDEX2 (exp))
|
||||
return NULL_TREE;
|
||||
if (!integer_zerop (TMR_OFFSET (exp)))
|
||||
{
|
||||
double_int off = mem_ref_offset (exp);
|
||||
gcc_assert (off.high == -1 || off.high == 0);
|
||||
byte_offset += double_int_to_shwi (off);
|
||||
}
|
||||
exp = TREE_OPERAND (TMR_BASE (exp), 0);
|
||||
}
|
||||
goto done;
|
||||
|
||||
default:
|
||||
goto done;
|
||||
}
|
||||
|
||||
exp = TREE_OPERAND (exp, 0);
|
||||
}
|
||||
done:
|
||||
|
||||
*poffset = byte_offset;
|
||||
return exp;
|
||||
return get_addr_base_and_unit_offset_1 (exp, poffset, NULL);
|
||||
}
|
||||
|
||||
/* Returns true if STMT references an SSA_NAME that has
|
||||
|
@ -1227,4 +1227,139 @@ make_ssa_name (tree var, gimple stmt)
|
||||
return make_ssa_name_fn (cfun, var, stmt);
|
||||
}
|
||||
|
||||
/* Returns the base object and a constant BITS_PER_UNIT offset in *POFFSET that
|
||||
denotes the starting address of the memory access EXP.
|
||||
Returns NULL_TREE if the offset is not constant or any component
|
||||
is not BITS_PER_UNIT-aligned.
|
||||
VALUEIZE if non-NULL is used to valueize SSA names. It should return
|
||||
its argument or a constant if the argument is known to be constant. */
|
||||
|
||||
static inline tree
|
||||
get_addr_base_and_unit_offset_1 (tree exp, HOST_WIDE_INT *poffset,
|
||||
tree (*valueize) (tree))
|
||||
{
|
||||
HOST_WIDE_INT byte_offset = 0;
|
||||
|
||||
/* Compute cumulative byte-offset for nested component-refs and array-refs,
|
||||
and find the ultimate containing object. */
|
||||
while (1)
|
||||
{
|
||||
switch (TREE_CODE (exp))
|
||||
{
|
||||
case BIT_FIELD_REF:
|
||||
return NULL_TREE;
|
||||
|
||||
case COMPONENT_REF:
|
||||
{
|
||||
tree field = TREE_OPERAND (exp, 1);
|
||||
tree this_offset = component_ref_field_offset (exp);
|
||||
HOST_WIDE_INT hthis_offset;
|
||||
|
||||
if (!this_offset
|
||||
|| TREE_CODE (this_offset) != INTEGER_CST
|
||||
|| (TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (field))
|
||||
% BITS_PER_UNIT))
|
||||
return NULL_TREE;
|
||||
|
||||
hthis_offset = TREE_INT_CST_LOW (this_offset);
|
||||
hthis_offset += (TREE_INT_CST_LOW (DECL_FIELD_BIT_OFFSET (field))
|
||||
/ BITS_PER_UNIT);
|
||||
byte_offset += hthis_offset;
|
||||
}
|
||||
break;
|
||||
|
||||
case ARRAY_REF:
|
||||
case ARRAY_RANGE_REF:
|
||||
{
|
||||
tree index = TREE_OPERAND (exp, 1);
|
||||
tree low_bound, unit_size;
|
||||
|
||||
if (valueize
|
||||
&& TREE_CODE (index) == SSA_NAME)
|
||||
index = (*valueize) (index);
|
||||
|
||||
/* If the resulting bit-offset is constant, track it. */
|
||||
if (TREE_CODE (index) == INTEGER_CST
|
||||
&& (low_bound = array_ref_low_bound (exp),
|
||||
TREE_CODE (low_bound) == INTEGER_CST)
|
||||
&& (unit_size = array_ref_element_size (exp),
|
||||
TREE_CODE (unit_size) == INTEGER_CST))
|
||||
{
|
||||
HOST_WIDE_INT hindex = TREE_INT_CST_LOW (index);
|
||||
|
||||
hindex -= TREE_INT_CST_LOW (low_bound);
|
||||
hindex *= TREE_INT_CST_LOW (unit_size);
|
||||
byte_offset += hindex;
|
||||
}
|
||||
else
|
||||
return NULL_TREE;
|
||||
}
|
||||
break;
|
||||
|
||||
case REALPART_EXPR:
|
||||
break;
|
||||
|
||||
case IMAGPART_EXPR:
|
||||
byte_offset += TREE_INT_CST_LOW (TYPE_SIZE_UNIT (TREE_TYPE (exp)));
|
||||
break;
|
||||
|
||||
case VIEW_CONVERT_EXPR:
|
||||
break;
|
||||
|
||||
case MEM_REF:
|
||||
{
|
||||
tree base = TREE_OPERAND (exp, 0);
|
||||
if (valueize
|
||||
&& TREE_CODE (base) == SSA_NAME)
|
||||
base = (*valueize) (base);
|
||||
|
||||
/* Hand back the decl for MEM[&decl, off]. */
|
||||
if (TREE_CODE (base) == ADDR_EXPR)
|
||||
{
|
||||
if (!integer_zerop (TREE_OPERAND (exp, 1)))
|
||||
{
|
||||
double_int off = mem_ref_offset (exp);
|
||||
gcc_assert (off.high == -1 || off.high == 0);
|
||||
byte_offset += double_int_to_shwi (off);
|
||||
}
|
||||
exp = TREE_OPERAND (base, 0);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
case TARGET_MEM_REF:
|
||||
{
|
||||
tree base = TREE_OPERAND (exp, 0);
|
||||
if (valueize
|
||||
&& TREE_CODE (base) == SSA_NAME)
|
||||
base = (*valueize) (base);
|
||||
|
||||
/* Hand back the decl for MEM[&decl, off]. */
|
||||
if (TREE_CODE (base) == ADDR_EXPR)
|
||||
{
|
||||
if (TMR_INDEX (exp) || TMR_INDEX2 (exp))
|
||||
return NULL_TREE;
|
||||
if (!integer_zerop (TMR_OFFSET (exp)))
|
||||
{
|
||||
double_int off = mem_ref_offset (exp);
|
||||
gcc_assert (off.high == -1 || off.high == 0);
|
||||
byte_offset += double_int_to_shwi (off);
|
||||
}
|
||||
exp = TREE_OPERAND (base, 0);
|
||||
}
|
||||
goto done;
|
||||
}
|
||||
|
||||
default:
|
||||
goto done;
|
||||
}
|
||||
|
||||
exp = TREE_OPERAND (exp, 0);
|
||||
}
|
||||
done:
|
||||
|
||||
*poffset = byte_offset;
|
||||
return exp;
|
||||
}
|
||||
|
||||
#endif /* _TREE_FLOW_INLINE_H */
|
||||
|
@ -594,6 +594,7 @@ extern void ssanames_print_statistics (void);
|
||||
|
||||
/* In tree-ssa-ccp.c */
|
||||
tree fold_const_aggregate_ref (tree);
|
||||
tree gimple_fold_stmt_to_constant (gimple, tree (*)(tree));
|
||||
|
||||
/* In tree-ssa-dom.c */
|
||||
extern void dump_dominator_optimization_stats (FILE *);
|
||||
|
@ -132,6 +132,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "target.h"
|
||||
#include "diagnostic-core.h"
|
||||
#include "dbgcnt.h"
|
||||
#include "gimple-fold.h"
|
||||
|
||||
|
||||
/* Possible lattice values. */
|
||||
@ -167,9 +168,6 @@ static prop_value_t *const_val;
|
||||
|
||||
static void canonicalize_float_value (prop_value_t *);
|
||||
static bool ccp_fold_stmt (gimple_stmt_iterator *);
|
||||
static tree fold_ctor_reference (tree type, tree ctor,
|
||||
unsigned HOST_WIDE_INT offset,
|
||||
unsigned HOST_WIDE_INT size);
|
||||
|
||||
/* Dump constant propagation value VAL to file OUTF prefixed by PREFIX. */
|
||||
|
||||
@ -1098,220 +1096,6 @@ ccp_fold (gimple stmt)
|
||||
location_t loc = gimple_location (stmt);
|
||||
switch (gimple_code (stmt))
|
||||
{
|
||||
case GIMPLE_ASSIGN:
|
||||
{
|
||||
enum tree_code subcode = gimple_assign_rhs_code (stmt);
|
||||
|
||||
switch (get_gimple_rhs_class (subcode))
|
||||
{
|
||||
case GIMPLE_SINGLE_RHS:
|
||||
{
|
||||
tree rhs = gimple_assign_rhs1 (stmt);
|
||||
enum tree_code_class kind = TREE_CODE_CLASS (subcode);
|
||||
|
||||
if (TREE_CODE (rhs) == SSA_NAME)
|
||||
{
|
||||
/* If the RHS is an SSA_NAME, return its known constant value,
|
||||
if any. */
|
||||
return get_constant_value (rhs);
|
||||
}
|
||||
/* Handle propagating invariant addresses into address operations.
|
||||
The folding we do here matches that in tree-ssa-forwprop.c. */
|
||||
else if (TREE_CODE (rhs) == ADDR_EXPR)
|
||||
{
|
||||
tree *base;
|
||||
base = &TREE_OPERAND (rhs, 0);
|
||||
while (handled_component_p (*base))
|
||||
base = &TREE_OPERAND (*base, 0);
|
||||
if (TREE_CODE (*base) == MEM_REF
|
||||
&& TREE_CODE (TREE_OPERAND (*base, 0)) == SSA_NAME)
|
||||
{
|
||||
tree val = get_constant_value (TREE_OPERAND (*base, 0));
|
||||
if (val
|
||||
&& TREE_CODE (val) == ADDR_EXPR)
|
||||
{
|
||||
tree ret, save = *base;
|
||||
tree new_base;
|
||||
new_base = fold_build2 (MEM_REF, TREE_TYPE (*base),
|
||||
unshare_expr (val),
|
||||
TREE_OPERAND (*base, 1));
|
||||
/* We need to return a new tree, not modify the IL
|
||||
or share parts of it. So play some tricks to
|
||||
avoid manually building it. */
|
||||
*base = new_base;
|
||||
ret = unshare_expr (rhs);
|
||||
recompute_tree_invariant_for_addr_expr (ret);
|
||||
*base = save;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (TREE_CODE (rhs) == CONSTRUCTOR
|
||||
&& TREE_CODE (TREE_TYPE (rhs)) == VECTOR_TYPE
|
||||
&& (CONSTRUCTOR_NELTS (rhs)
|
||||
== TYPE_VECTOR_SUBPARTS (TREE_TYPE (rhs))))
|
||||
{
|
||||
unsigned i;
|
||||
tree val, list;
|
||||
|
||||
list = NULL_TREE;
|
||||
FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (rhs), i, val)
|
||||
{
|
||||
val = valueize_op (val);
|
||||
if (TREE_CODE (val) == INTEGER_CST
|
||||
|| TREE_CODE (val) == REAL_CST
|
||||
|| TREE_CODE (val) == FIXED_CST)
|
||||
list = tree_cons (NULL_TREE, val, list);
|
||||
else
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
return build_vector (TREE_TYPE (rhs), nreverse (list));
|
||||
}
|
||||
|
||||
if (kind == tcc_reference)
|
||||
{
|
||||
if ((TREE_CODE (rhs) == VIEW_CONVERT_EXPR
|
||||
|| TREE_CODE (rhs) == REALPART_EXPR
|
||||
|| TREE_CODE (rhs) == IMAGPART_EXPR)
|
||||
&& TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME)
|
||||
{
|
||||
tree val = get_constant_value (TREE_OPERAND (rhs, 0));
|
||||
if (val)
|
||||
return fold_unary_loc (EXPR_LOCATION (rhs),
|
||||
TREE_CODE (rhs),
|
||||
TREE_TYPE (rhs), val);
|
||||
}
|
||||
else if (TREE_CODE (rhs) == BIT_FIELD_REF
|
||||
&& TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME)
|
||||
{
|
||||
tree val = get_constant_value (TREE_OPERAND (rhs, 0));
|
||||
if (val)
|
||||
return fold_ternary_loc (EXPR_LOCATION (rhs),
|
||||
TREE_CODE (rhs),
|
||||
TREE_TYPE (rhs), val,
|
||||
TREE_OPERAND (rhs, 1),
|
||||
TREE_OPERAND (rhs, 2));
|
||||
}
|
||||
else if (TREE_CODE (rhs) == MEM_REF
|
||||
&& TREE_CODE (TREE_OPERAND (rhs, 0)) == SSA_NAME)
|
||||
{
|
||||
tree val = get_constant_value (TREE_OPERAND (rhs, 0));
|
||||
if (val
|
||||
&& TREE_CODE (val) == ADDR_EXPR)
|
||||
{
|
||||
tree tem = fold_build2 (MEM_REF, TREE_TYPE (rhs),
|
||||
unshare_expr (val),
|
||||
TREE_OPERAND (rhs, 1));
|
||||
if (tem)
|
||||
rhs = tem;
|
||||
}
|
||||
}
|
||||
return fold_const_aggregate_ref (rhs);
|
||||
}
|
||||
else if (kind == tcc_declaration)
|
||||
return get_symbol_constant_value (rhs);
|
||||
return rhs;
|
||||
}
|
||||
|
||||
case GIMPLE_UNARY_RHS:
|
||||
{
|
||||
/* Handle unary operators that can appear in GIMPLE form.
|
||||
Note that we know the single operand must be a constant,
|
||||
so this should almost always return a simplified RHS. */
|
||||
tree lhs = gimple_assign_lhs (stmt);
|
||||
tree op0 = valueize_op (gimple_assign_rhs1 (stmt));
|
||||
|
||||
/* Conversions are useless for CCP purposes if they are
|
||||
value-preserving. Thus the restrictions that
|
||||
useless_type_conversion_p places for pointer type conversions
|
||||
do not apply here. Substitution later will only substitute to
|
||||
allowed places. */
|
||||
if (CONVERT_EXPR_CODE_P (subcode)
|
||||
&& POINTER_TYPE_P (TREE_TYPE (lhs))
|
||||
&& POINTER_TYPE_P (TREE_TYPE (op0)))
|
||||
{
|
||||
tree tem;
|
||||
/* Try to re-construct array references on-the-fly. */
|
||||
if (!useless_type_conversion_p (TREE_TYPE (lhs),
|
||||
TREE_TYPE (op0))
|
||||
&& ((tem = maybe_fold_offset_to_address
|
||||
(loc,
|
||||
op0, integer_zero_node, TREE_TYPE (lhs)))
|
||||
!= NULL_TREE))
|
||||
return tem;
|
||||
return op0;
|
||||
}
|
||||
|
||||
return
|
||||
fold_unary_ignore_overflow_loc (loc, subcode,
|
||||
gimple_expr_type (stmt), op0);
|
||||
}
|
||||
|
||||
case GIMPLE_BINARY_RHS:
|
||||
{
|
||||
/* Handle binary operators that can appear in GIMPLE form. */
|
||||
tree op0 = valueize_op (gimple_assign_rhs1 (stmt));
|
||||
tree op1 = valueize_op (gimple_assign_rhs2 (stmt));
|
||||
|
||||
/* Translate &x + CST into an invariant form suitable for
|
||||
further propagation. */
|
||||
if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR
|
||||
&& TREE_CODE (op0) == ADDR_EXPR
|
||||
&& TREE_CODE (op1) == INTEGER_CST)
|
||||
{
|
||||
tree off = fold_convert (ptr_type_node, op1);
|
||||
return build_fold_addr_expr
|
||||
(fold_build2 (MEM_REF,
|
||||
TREE_TYPE (TREE_TYPE (op0)),
|
||||
unshare_expr (op0), off));
|
||||
}
|
||||
|
||||
return fold_binary_loc (loc, subcode,
|
||||
gimple_expr_type (stmt), op0, op1);
|
||||
}
|
||||
|
||||
case GIMPLE_TERNARY_RHS:
|
||||
{
|
||||
/* Handle ternary operators that can appear in GIMPLE form. */
|
||||
tree op0 = valueize_op (gimple_assign_rhs1 (stmt));
|
||||
tree op1 = valueize_op (gimple_assign_rhs2 (stmt));
|
||||
tree op2 = valueize_op (gimple_assign_rhs3 (stmt));
|
||||
|
||||
return fold_ternary_loc (loc, subcode,
|
||||
gimple_expr_type (stmt), op0, op1, op2);
|
||||
}
|
||||
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GIMPLE_CALL:
|
||||
{
|
||||
tree fn = valueize_op (gimple_call_fn (stmt));
|
||||
if (TREE_CODE (fn) == ADDR_EXPR
|
||||
&& TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL
|
||||
&& DECL_BUILT_IN (TREE_OPERAND (fn, 0)))
|
||||
{
|
||||
tree *args = XALLOCAVEC (tree, gimple_call_num_args (stmt));
|
||||
tree call, retval;
|
||||
unsigned i;
|
||||
for (i = 0; i < gimple_call_num_args (stmt); ++i)
|
||||
args[i] = valueize_op (gimple_call_arg (stmt, i));
|
||||
call = build_call_array_loc (loc,
|
||||
gimple_call_return_type (stmt),
|
||||
fn, gimple_call_num_args (stmt), args);
|
||||
retval = fold_call_expr (EXPR_LOCATION (call), call, false);
|
||||
if (retval)
|
||||
/* fold_call_expr wraps the result inside a NOP_EXPR. */
|
||||
STRIP_NOPS (retval);
|
||||
return retval;
|
||||
}
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
case GIMPLE_COND:
|
||||
{
|
||||
/* Handle comparison operators that can appear in GIMPLE form. */
|
||||
@ -1327,397 +1111,15 @@ ccp_fold (gimple stmt)
|
||||
return valueize_op (gimple_switch_index (stmt));
|
||||
}
|
||||
|
||||
case GIMPLE_ASSIGN:
|
||||
case GIMPLE_CALL:
|
||||
return gimple_fold_stmt_to_constant_1 (stmt, valueize_op);
|
||||
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
}
|
||||
|
||||
/* See if we can find constructor defining value of BASE.
|
||||
When we know the consructor with constant offset (such as
|
||||
base is array[40] and we do know constructor of array), then
|
||||
BIT_OFFSET is adjusted accordingly.
|
||||
|
||||
As a special case, return error_mark_node when constructor
|
||||
is not explicitly available, but it is known to be zero
|
||||
such as 'static const int a;'. */
|
||||
static tree
|
||||
get_base_constructor (tree base, HOST_WIDE_INT *bit_offset)
|
||||
{
|
||||
HOST_WIDE_INT bit_offset2, size, max_size;
|
||||
if (TREE_CODE (base) == MEM_REF)
|
||||
{
|
||||
if (!integer_zerop (TREE_OPERAND (base, 1)))
|
||||
{
|
||||
if (!host_integerp (TREE_OPERAND (base, 1), 0))
|
||||
return NULL_TREE;
|
||||
*bit_offset += (mem_ref_offset (base).low
|
||||
* BITS_PER_UNIT);
|
||||
}
|
||||
|
||||
base = get_constant_value (TREE_OPERAND (base, 0));
|
||||
if (!base || TREE_CODE (base) != ADDR_EXPR)
|
||||
return NULL_TREE;
|
||||
base = TREE_OPERAND (base, 0);
|
||||
}
|
||||
|
||||
/* Get a CONSTRUCTOR. If BASE is a VAR_DECL, get its
|
||||
DECL_INITIAL. If BASE is a nested reference into another
|
||||
ARRAY_REF or COMPONENT_REF, make a recursive call to resolve
|
||||
the inner reference. */
|
||||
switch (TREE_CODE (base))
|
||||
{
|
||||
case VAR_DECL:
|
||||
if (!const_value_known_p (base))
|
||||
return NULL_TREE;
|
||||
|
||||
/* Fallthru. */
|
||||
case CONST_DECL:
|
||||
if (!DECL_INITIAL (base)
|
||||
&& (TREE_STATIC (base) || DECL_EXTERNAL (base)))
|
||||
return error_mark_node;
|
||||
return DECL_INITIAL (base);
|
||||
|
||||
case ARRAY_REF:
|
||||
case COMPONENT_REF:
|
||||
base = get_ref_base_and_extent (base, &bit_offset2, &size, &max_size);
|
||||
if (max_size == -1 || size != max_size)
|
||||
return NULL_TREE;
|
||||
*bit_offset += bit_offset2;
|
||||
return get_base_constructor (base, bit_offset);
|
||||
|
||||
case STRING_CST:
|
||||
case CONSTRUCTOR:
|
||||
return base;
|
||||
|
||||
default:
|
||||
return NULL_TREE;
|
||||
}
|
||||
}
|
||||
|
||||
/* CTOR is STRING_CST. Fold reference of type TYPE and size SIZE
|
||||
to the memory at bit OFFSET.
|
||||
|
||||
We do only simple job of folding byte accesses. */
|
||||
|
||||
static tree
|
||||
fold_string_cst_ctor_reference (tree type, tree ctor, unsigned HOST_WIDE_INT offset,
|
||||
unsigned HOST_WIDE_INT size)
|
||||
{
|
||||
if (INTEGRAL_TYPE_P (type)
|
||||
&& (TYPE_MODE (type)
|
||||
== TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor))))
|
||||
&& (GET_MODE_CLASS (TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor))))
|
||||
== MODE_INT)
|
||||
&& GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (TREE_TYPE (ctor)))) == 1
|
||||
&& size == BITS_PER_UNIT
|
||||
&& !(offset % BITS_PER_UNIT))
|
||||
{
|
||||
offset /= BITS_PER_UNIT;
|
||||
if (offset < (unsigned HOST_WIDE_INT) TREE_STRING_LENGTH (ctor))
|
||||
return build_int_cst_type (type, (TREE_STRING_POINTER (ctor)
|
||||
[offset]));
|
||||
/* Folding
|
||||
const char a[20]="hello";
|
||||
return a[10];
|
||||
|
||||
might lead to offset greater than string length. In this case we
|
||||
know value is either initialized to 0 or out of bounds. Return 0
|
||||
in both cases. */
|
||||
return build_zero_cst (type);
|
||||
}
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* CTOR is CONSTRUCTOR of an array type. Fold reference of type TYPE and size
|
||||
SIZE to the memory at bit OFFSET. */
|
||||
|
||||
static tree
|
||||
fold_array_ctor_reference (tree type, tree ctor,
|
||||
unsigned HOST_WIDE_INT offset,
|
||||
unsigned HOST_WIDE_INT size)
|
||||
{
|
||||
unsigned HOST_WIDE_INT cnt;
|
||||
tree cfield, cval;
|
||||
double_int low_bound, elt_size;
|
||||
double_int index, max_index;
|
||||
double_int access_index;
|
||||
tree domain_type = TYPE_DOMAIN (TREE_TYPE (ctor));
|
||||
HOST_WIDE_INT inner_offset;
|
||||
|
||||
/* Compute low bound and elt size. */
|
||||
if (domain_type && TYPE_MIN_VALUE (domain_type))
|
||||
{
|
||||
/* Static constructors for variably sized objects makes no sense. */
|
||||
gcc_assert (TREE_CODE (TYPE_MIN_VALUE (domain_type)) == INTEGER_CST);
|
||||
low_bound = tree_to_double_int (TYPE_MIN_VALUE (domain_type));
|
||||
}
|
||||
else
|
||||
low_bound = double_int_zero;
|
||||
/* Static constructors for variably sized objects makes no sense. */
|
||||
gcc_assert (TREE_CODE(TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (ctor))))
|
||||
== INTEGER_CST);
|
||||
elt_size =
|
||||
tree_to_double_int (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (ctor))));
|
||||
|
||||
|
||||
/* We can handle only constantly sized accesses that are known to not
|
||||
be larger than size of array element. */
|
||||
if (!TYPE_SIZE_UNIT (type)
|
||||
|| TREE_CODE (TYPE_SIZE_UNIT (type)) != INTEGER_CST
|
||||
|| double_int_cmp (elt_size,
|
||||
tree_to_double_int (TYPE_SIZE_UNIT (type)), 0) < 0)
|
||||
return NULL_TREE;
|
||||
|
||||
/* Compute the array index we look for. */
|
||||
access_index = double_int_udiv (uhwi_to_double_int (offset / BITS_PER_UNIT),
|
||||
elt_size, TRUNC_DIV_EXPR);
|
||||
access_index = double_int_add (access_index, low_bound);
|
||||
|
||||
/* And offset within the access. */
|
||||
inner_offset = offset % (double_int_to_uhwi (elt_size) * BITS_PER_UNIT);
|
||||
|
||||
/* See if the array field is large enough to span whole access. We do not
|
||||
care to fold accesses spanning multiple array indexes. */
|
||||
if (inner_offset + size > double_int_to_uhwi (elt_size) * BITS_PER_UNIT)
|
||||
return NULL_TREE;
|
||||
|
||||
index = double_int_sub (low_bound, double_int_one);
|
||||
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (ctor), cnt, cfield, cval)
|
||||
{
|
||||
/* Array constructor might explicitely set index, or specify range
|
||||
or leave index NULL meaning that it is next index after previous
|
||||
one. */
|
||||
if (cfield)
|
||||
{
|
||||
if (TREE_CODE (cfield) == INTEGER_CST)
|
||||
max_index = index = tree_to_double_int (cfield);
|
||||
else
|
||||
{
|
||||
gcc_assert (TREE_CODE (cfield) == RANGE_EXPR);
|
||||
index = tree_to_double_int (TREE_OPERAND (cfield, 0));
|
||||
max_index = tree_to_double_int (TREE_OPERAND (cfield, 1));
|
||||
}
|
||||
}
|
||||
else
|
||||
max_index = index = double_int_add (index, double_int_one);
|
||||
|
||||
/* Do we have match? */
|
||||
if (double_int_cmp (access_index, index, 1) >= 0
|
||||
&& double_int_cmp (access_index, max_index, 1) <= 0)
|
||||
return fold_ctor_reference (type, cval, inner_offset, size);
|
||||
}
|
||||
/* When memory is not explicitely mentioned in constructor,
|
||||
it is 0 (or out of range). */
|
||||
return build_zero_cst (type);
|
||||
}
|
||||
|
||||
/* CTOR is CONSTRUCTOR of an aggregate or vector.
|
||||
Fold reference of type TYPE and size SIZE to the memory at bit OFFSET. */
|
||||
|
||||
static tree
|
||||
fold_nonarray_ctor_reference (tree type, tree ctor,
|
||||
unsigned HOST_WIDE_INT offset,
|
||||
unsigned HOST_WIDE_INT size)
|
||||
{
|
||||
unsigned HOST_WIDE_INT cnt;
|
||||
tree cfield, cval;
|
||||
|
||||
FOR_EACH_CONSTRUCTOR_ELT (CONSTRUCTOR_ELTS (ctor), cnt, cfield,
|
||||
cval)
|
||||
{
|
||||
tree byte_offset = DECL_FIELD_OFFSET (cfield);
|
||||
tree field_offset = DECL_FIELD_BIT_OFFSET (cfield);
|
||||
tree field_size = DECL_SIZE (cfield);
|
||||
double_int bitoffset;
|
||||
double_int byte_offset_cst = tree_to_double_int (byte_offset);
|
||||
double_int bits_per_unit_cst = uhwi_to_double_int (BITS_PER_UNIT);
|
||||
double_int bitoffset_end;
|
||||
|
||||
/* Variable sized objects in static constructors makes no sense,
|
||||
but field_size can be NULL for flexible array members. */
|
||||
gcc_assert (TREE_CODE (field_offset) == INTEGER_CST
|
||||
&& TREE_CODE (byte_offset) == INTEGER_CST
|
||||
&& (field_size != NULL_TREE
|
||||
? TREE_CODE (field_size) == INTEGER_CST
|
||||
: TREE_CODE (TREE_TYPE (cfield)) == ARRAY_TYPE));
|
||||
|
||||
/* Compute bit offset of the field. */
|
||||
bitoffset = double_int_add (tree_to_double_int (field_offset),
|
||||
double_int_mul (byte_offset_cst,
|
||||
bits_per_unit_cst));
|
||||
/* Compute bit offset where the field ends. */
|
||||
if (field_size != NULL_TREE)
|
||||
bitoffset_end = double_int_add (bitoffset,
|
||||
tree_to_double_int (field_size));
|
||||
else
|
||||
bitoffset_end = double_int_zero;
|
||||
|
||||
/* Is OFFSET in the range (BITOFFSET, BITOFFSET_END)? */
|
||||
if (double_int_cmp (uhwi_to_double_int (offset), bitoffset, 0) >= 0
|
||||
&& (field_size == NULL_TREE
|
||||
|| double_int_cmp (uhwi_to_double_int (offset),
|
||||
bitoffset_end, 0) < 0))
|
||||
{
|
||||
double_int access_end = double_int_add (uhwi_to_double_int (offset),
|
||||
uhwi_to_double_int (size));
|
||||
double_int inner_offset = double_int_sub (uhwi_to_double_int (offset),
|
||||
bitoffset);
|
||||
/* We do have overlap. Now see if field is large enough to
|
||||
cover the access. Give up for accesses spanning multiple
|
||||
fields. */
|
||||
if (double_int_cmp (access_end, bitoffset_end, 0) > 0)
|
||||
return NULL_TREE;
|
||||
return fold_ctor_reference (type, cval,
|
||||
double_int_to_uhwi (inner_offset), size);
|
||||
}
|
||||
}
|
||||
/* When memory is not explicitely mentioned in constructor, it is 0. */
|
||||
return build_zero_cst (type);
|
||||
}
|
||||
|
||||
/* CTOR is value initializing memory, fold reference of type TYPE and size SIZE
|
||||
to the memory at bit OFFSET. */
|
||||
|
||||
static tree
|
||||
fold_ctor_reference (tree type, tree ctor, unsigned HOST_WIDE_INT offset,
|
||||
unsigned HOST_WIDE_INT size)
|
||||
{
|
||||
tree ret;
|
||||
|
||||
/* We found the field with exact match. */
|
||||
if (useless_type_conversion_p (type, TREE_TYPE (ctor))
|
||||
&& !offset)
|
||||
return canonicalize_constructor_val (ctor);
|
||||
|
||||
/* We are at the end of walk, see if we can view convert the
|
||||
result. */
|
||||
if (!AGGREGATE_TYPE_P (TREE_TYPE (ctor)) && !offset
|
||||
/* VIEW_CONVERT_EXPR is defined only for matching sizes. */
|
||||
&& operand_equal_p (TYPE_SIZE (type),
|
||||
TYPE_SIZE (TREE_TYPE (ctor)), 0))
|
||||
{
|
||||
ret = canonicalize_constructor_val (ctor);
|
||||
ret = fold_unary (VIEW_CONVERT_EXPR, type, ret);
|
||||
if (ret)
|
||||
STRIP_NOPS (ret);
|
||||
return ret;
|
||||
}
|
||||
if (TREE_CODE (ctor) == STRING_CST)
|
||||
return fold_string_cst_ctor_reference (type, ctor, offset, size);
|
||||
if (TREE_CODE (ctor) == CONSTRUCTOR)
|
||||
{
|
||||
|
||||
if (TREE_CODE (TREE_TYPE (ctor)) == ARRAY_TYPE)
|
||||
return fold_array_ctor_reference (type, ctor, offset, size);
|
||||
else
|
||||
return fold_nonarray_ctor_reference (type, ctor, offset, size);
|
||||
}
|
||||
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Return the tree representing the element referenced by T if T is an
|
||||
ARRAY_REF or COMPONENT_REF into constant aggregates. Return
|
||||
NULL_TREE otherwise. */
|
||||
|
||||
tree
|
||||
fold_const_aggregate_ref (tree t)
|
||||
{
|
||||
tree ctor, idx, base;
|
||||
HOST_WIDE_INT offset, size, max_size;
|
||||
tree tem;
|
||||
|
||||
if (TREE_CODE_CLASS (TREE_CODE (t)) == tcc_declaration)
|
||||
return get_symbol_constant_value (t);
|
||||
|
||||
tem = fold_read_from_constant_string (t);
|
||||
if (tem)
|
||||
return tem;
|
||||
|
||||
switch (TREE_CODE (t))
|
||||
{
|
||||
case ARRAY_REF:
|
||||
case ARRAY_RANGE_REF:
|
||||
/* Constant indexes are handled well by get_base_constructor.
|
||||
Only special case variable offsets.
|
||||
FIXME: This code can't handle nested references with variable indexes
|
||||
(they will be handled only by iteration of ccp). Perhaps we can bring
|
||||
get_ref_base_and_extent here and make it use get_constant_value. */
|
||||
if (TREE_CODE (TREE_OPERAND (t, 1)) == SSA_NAME
|
||||
&& (idx = get_constant_value (TREE_OPERAND (t, 1)))
|
||||
&& host_integerp (idx, 0))
|
||||
{
|
||||
tree low_bound, unit_size;
|
||||
|
||||
/* If the resulting bit-offset is constant, track it. */
|
||||
if ((low_bound = array_ref_low_bound (t),
|
||||
host_integerp (low_bound, 0))
|
||||
&& (unit_size = array_ref_element_size (t),
|
||||
host_integerp (unit_size, 1)))
|
||||
{
|
||||
offset = TREE_INT_CST_LOW (idx);
|
||||
offset -= TREE_INT_CST_LOW (low_bound);
|
||||
offset *= TREE_INT_CST_LOW (unit_size);
|
||||
offset *= BITS_PER_UNIT;
|
||||
|
||||
base = TREE_OPERAND (t, 0);
|
||||
ctor = get_base_constructor (base, &offset);
|
||||
/* Empty constructor. Always fold to 0. */
|
||||
if (ctor == error_mark_node)
|
||||
return build_zero_cst (TREE_TYPE (t));
|
||||
/* Out of bound array access. Value is undefined, but don't fold. */
|
||||
if (offset < 0)
|
||||
return NULL_TREE;
|
||||
/* We can not determine ctor. */
|
||||
if (!ctor)
|
||||
return NULL_TREE;
|
||||
return fold_ctor_reference (TREE_TYPE (t), ctor, offset,
|
||||
TREE_INT_CST_LOW (unit_size)
|
||||
* BITS_PER_UNIT);
|
||||
}
|
||||
}
|
||||
/* Fallthru. */
|
||||
|
||||
case COMPONENT_REF:
|
||||
case BIT_FIELD_REF:
|
||||
case TARGET_MEM_REF:
|
||||
case MEM_REF:
|
||||
base = get_ref_base_and_extent (t, &offset, &size, &max_size);
|
||||
ctor = get_base_constructor (base, &offset);
|
||||
|
||||
/* Empty constructor. Always fold to 0. */
|
||||
if (ctor == error_mark_node)
|
||||
return build_zero_cst (TREE_TYPE (t));
|
||||
/* We do not know precise address. */
|
||||
if (max_size == -1 || max_size != size)
|
||||
return NULL_TREE;
|
||||
/* We can not determine ctor. */
|
||||
if (!ctor)
|
||||
return NULL_TREE;
|
||||
|
||||
/* Out of bound array access. Value is undefined, but don't fold. */
|
||||
if (offset < 0)
|
||||
return NULL_TREE;
|
||||
|
||||
return fold_ctor_reference (TREE_TYPE (t), ctor, offset, size);
|
||||
|
||||
case REALPART_EXPR:
|
||||
case IMAGPART_EXPR:
|
||||
{
|
||||
tree c = fold_const_aggregate_ref (TREE_OPERAND (t, 0));
|
||||
if (c && TREE_CODE (c) == COMPLEX_CST)
|
||||
return fold_build1_loc (EXPR_LOCATION (t),
|
||||
TREE_CODE (t), TREE_TYPE (t), c);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Apply the operation CODE in type TYPE to the value, mask pair
|
||||
RVAL and RMASK representing a value of type RTYPE and set
|
||||
the value, mask pair *VAL and *MASK to the result. */
|
||||
|
@ -4380,9 +4380,12 @@ eliminate (void)
|
||||
if (is_gimple_call (stmt)
|
||||
&& TREE_CODE (gimple_call_fn (stmt)) == SSA_NAME)
|
||||
{
|
||||
tree fn = VN_INFO (gimple_call_fn (stmt))->valnum;
|
||||
tree orig_fn = gimple_call_fn (stmt);
|
||||
tree fn = VN_INFO (orig_fn)->valnum;
|
||||
if (TREE_CODE (fn) == ADDR_EXPR
|
||||
&& TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL)
|
||||
&& TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL
|
||||
&& useless_type_conversion_p (TREE_TYPE (orig_fn),
|
||||
TREE_TYPE (fn)))
|
||||
{
|
||||
bool can_make_abnormal_goto
|
||||
= stmt_can_make_abnormal_goto (stmt);
|
||||
|
@ -44,6 +44,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "params.h"
|
||||
#include "tree-ssa-propagate.h"
|
||||
#include "tree-ssa-sccvn.h"
|
||||
#include "gimple-fold.h"
|
||||
|
||||
/* This algorithm is based on the SCC algorithm presented by Keith
|
||||
Cooper and L. Taylor Simpson in "SCC-Based Value numbering"
|
||||
@ -2770,6 +2771,16 @@ simplify_binary_expression (gimple stmt)
|
||||
op1 = SSA_VAL (op1);
|
||||
}
|
||||
|
||||
/* Pointer plus constant can be represented as invariant address.
|
||||
Do so to allow further propatation, see also tree forwprop. */
|
||||
if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR
|
||||
&& host_integerp (op1, 1)
|
||||
&& TREE_CODE (op0) == ADDR_EXPR
|
||||
&& is_gimple_min_invariant (op0))
|
||||
return build_invariant_address (TREE_TYPE (op0),
|
||||
TREE_OPERAND (op0, 0),
|
||||
TREE_INT_CST_LOW (op1));
|
||||
|
||||
/* Avoid folding if nothing changed. */
|
||||
if (op0 == gimple_assign_rhs1 (stmt)
|
||||
&& op1 == gimple_assign_rhs2 (stmt))
|
||||
@ -2849,6 +2860,19 @@ simplify_unary_expression (gimple stmt)
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Valueize NAME if it is an SSA name, otherwise just return it. */
|
||||
|
||||
static inline tree
|
||||
vn_valueize (tree name)
|
||||
{
|
||||
if (TREE_CODE (name) == SSA_NAME)
|
||||
{
|
||||
tree tem = SSA_VAL (name);
|
||||
return tem == VN_TOP ? name : tem;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/* Try to simplify RHS using equivalences and constant folding. */
|
||||
|
||||
static tree
|
||||
@ -2862,21 +2886,15 @@ try_to_simplify (gimple stmt)
|
||||
&& TREE_CODE (gimple_assign_rhs1 (stmt)) == SSA_NAME)
|
||||
return NULL_TREE;
|
||||
|
||||
/* First try constant folding based on our current lattice. */
|
||||
tem = gimple_fold_stmt_to_constant (stmt, vn_valueize);
|
||||
if (tem)
|
||||
return tem;
|
||||
|
||||
/* If that didn't work try combining multiple statements. */
|
||||
switch (TREE_CODE_CLASS (gimple_assign_rhs_code (stmt)))
|
||||
{
|
||||
case tcc_declaration:
|
||||
tem = get_symbol_constant_value (gimple_assign_rhs1 (stmt));
|
||||
if (tem)
|
||||
return tem;
|
||||
break;
|
||||
|
||||
case tcc_reference:
|
||||
/* Do not do full-blown reference lookup here, but simplify
|
||||
reads from constant aggregates. */
|
||||
tem = fold_const_aggregate_ref (gimple_assign_rhs1 (stmt));
|
||||
if (tem)
|
||||
return tem;
|
||||
|
||||
/* Fallthrough for some codes that can operate on registers. */
|
||||
if (!(TREE_CODE (gimple_assign_rhs1 (stmt)) == REALPART_EXPR
|
||||
|| TREE_CODE (gimple_assign_rhs1 (stmt)) == IMAGPART_EXPR
|
||||
@ -2886,11 +2904,11 @@ try_to_simplify (gimple stmt)
|
||||
into binary ops, but it's debatable whether it is worth it. */
|
||||
case tcc_unary:
|
||||
return simplify_unary_expression (stmt);
|
||||
break;
|
||||
|
||||
case tcc_comparison:
|
||||
case tcc_binary:
|
||||
return simplify_binary_expression (stmt);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "tree-scalar-evolution.h"
|
||||
#include "tree-ssa-propagate.h"
|
||||
#include "tree-chrec.h"
|
||||
#include "gimple-fold.h"
|
||||
|
||||
|
||||
/* Type of value ranges. See value_range_d for a description of these
|
||||
@ -5614,6 +5615,21 @@ vrp_initialize (void)
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the singleton value-range for NAME or NAME. */
|
||||
|
||||
static inline tree
|
||||
vrp_valueize (tree name)
|
||||
{
|
||||
if (TREE_CODE (name) == SSA_NAME)
|
||||
{
|
||||
value_range_t *vr = get_value_range (name);
|
||||
if (vr->type == VR_RANGE
|
||||
&& (vr->min == vr->max
|
||||
|| operand_equal_p (vr->min, vr->max, 0)))
|
||||
return vr->min;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
/* Visit assignment STMT. If it produces an interesting range, record
|
||||
the SSA name in *OUTPUT_P. */
|
||||
@ -5637,7 +5653,12 @@ vrp_visit_assignment_or_call (gimple stmt, tree *output_p)
|
||||
{
|
||||
value_range_t new_vr = { VR_UNDEFINED, NULL_TREE, NULL_TREE, NULL };
|
||||
|
||||
if (code == GIMPLE_CALL)
|
||||
/* Try folding the statement to a constant first. */
|
||||
tree tem = gimple_fold_stmt_to_constant (stmt, vrp_valueize);
|
||||
if (tem && !is_overflow_infinity (tem))
|
||||
set_value_range (&new_vr, VR_RANGE, tem, tem, NULL);
|
||||
/* Then dispatch to value-range extracting functions. */
|
||||
else if (code == GIMPLE_CALL)
|
||||
extract_range_basic (&new_vr, stmt);
|
||||
else
|
||||
extract_range_from_assignment (&new_vr, stmt);
|
||||
@ -6366,7 +6387,6 @@ vrp_visit_stmt (gimple stmt, edge *taken_edge_p, tree *output_p)
|
||||
/* In general, assignments with virtual operands are not useful
|
||||
for deriving ranges, with the obvious exception of calls to
|
||||
builtin functions. */
|
||||
|
||||
if ((is_gimple_call (stmt)
|
||||
&& gimple_call_fndecl (stmt) != NULL_TREE
|
||||
&& DECL_IS_BUILTIN (gimple_call_fndecl (stmt)))
|
||||
|
14
gcc/tree.c
14
gcc/tree.c
@ -4013,6 +4013,20 @@ reference_alias_ptr_type (const_tree t)
|
||||
return build_pointer_type (TYPE_MAIN_VARIANT (TREE_TYPE (base)));
|
||||
}
|
||||
|
||||
/* Return an invariant ADDR_EXPR of type TYPE taking the address of BASE
|
||||
offsetted by OFFSET units. */
|
||||
|
||||
tree
|
||||
build_invariant_address (tree type, tree base, HOST_WIDE_INT offset)
|
||||
{
|
||||
tree ref = fold_build2 (MEM_REF, TREE_TYPE (type),
|
||||
build_fold_addr_expr (base),
|
||||
build_int_cst (ptr_type_node, offset));
|
||||
tree addr = build1 (ADDR_EXPR, type, ref);
|
||||
recompute_tree_invariant_for_addr_expr (addr);
|
||||
return addr;
|
||||
}
|
||||
|
||||
/* Similar except don't specify the TREE_TYPE
|
||||
and leave the TREE_SIDE_EFFECTS as 0.
|
||||
It is permissible for arguments to be null,
|
||||
|
@ -5096,6 +5096,7 @@ extern tree build_simple_mem_ref_loc (location_t, tree);
|
||||
build_simple_mem_ref_loc (UNKNOWN_LOCATION, T)
|
||||
extern double_int mem_ref_offset (const_tree);
|
||||
extern tree reference_alias_ptr_type (const_tree);
|
||||
extern tree build_invariant_address (tree, tree, HOST_WIDE_INT);
|
||||
extern tree constant_boolean_node (int, tree);
|
||||
extern tree div_if_zero_remainder (enum tree_code, const_tree, const_tree);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user