diff options
Diffstat (limited to 'gcc/tree-ssa-ccp.c')
-rw-r--r-- | gcc/tree-ssa-ccp.c | 111 |
1 files changed, 90 insertions, 21 deletions
diff --git a/gcc/tree-ssa-ccp.c b/gcc/tree-ssa-ccp.c index 9c9d847eed6..6c9475540fe 100644 --- a/gcc/tree-ssa-ccp.c +++ b/gcc/tree-ssa-ccp.c @@ -1587,6 +1587,8 @@ maybe_fold_offset_to_array_ref (tree base, tree offset, tree orig_type) 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) @@ -1647,12 +1649,11 @@ maybe_fold_offset_to_array_ref (tree base, tree offset, tree orig_type) } -/* A subroutine of fold_stmt_r. Attempts to fold *(S+O) to S.X. +/* 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. */ -/* ??? This doesn't handle class inheritance. */ -tree +static tree maybe_fold_offset_to_component_ref (tree record_type, tree base, tree offset, tree orig_type, bool base_is_ptr) { @@ -1679,6 +1680,8 @@ maybe_fold_offset_to_component_ref (tree record_type, tree base, tree offset, 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; @@ -1766,6 +1769,69 @@ maybe_fold_offset_to_component_ref (tree record_type, tree base, tree 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) && 0) + { + HOST_WIDE_INT sub_offset, size, maxsize; + tree newbase; + newbase = get_ref_base_and_extent (base, &sub_offset, + &size, &maxsize); + gcc_assert (newbase); + gcc_assert (!(sub_offset & (BITS_PER_UNIT - 1))); + if (size == maxsize) + { + base = newbase; + if (sub_offset) + offset = int_const_binop (PLUS_EXPR, offset, + build_int_cst (TREE_TYPE (offset), + sub_offset / BITS_PER_UNIT), 1); + } + } + if (lang_hooks.types_compatible_p (orig_type, TREE_TYPE (base)) + && integer_zerop (offset)) + return base; + type = TREE_TYPE (base); + } + else + { + base_is_ptr = true; + if (!POINTER_TYPE_P (TREE_TYPE (base))) + return NULL_TREE; + type = TREE_TYPE (TREE_TYPE (base)); + } + ret = maybe_fold_offset_to_component_ref (type, base, offset, + orig_type, base_is_ptr); + if (!ret) + { + if (base_is_ptr) + base = build1 (INDIRECT_REF, type, base); + ret = maybe_fold_offset_to_array_ref (base, offset, orig_type); + } + return ret; +} /* A subroutine of fold_stmt_r. Attempt to simplify *(BASE+OFFSET). Return the simplified expression, or NULL if nothing could be done. */ @@ -1802,6 +1868,8 @@ maybe_fold_stmt_indirect (tree expr, tree base, tree offset) if (TREE_CODE (base) == ADDR_EXPR) { + tree base_addr = base; + /* Strip the ADDR_EXPR. */ base = TREE_OPERAND (base, 0); @@ -1810,24 +1878,11 @@ maybe_fold_stmt_indirect (tree expr, tree base, tree offset) && ccp_decl_initial_min_invariant (DECL_INITIAL (base))) return DECL_INITIAL (base); - /* Try folding *(&B+O) to B[X]. */ - t = maybe_fold_offset_to_array_ref (base, offset, TREE_TYPE (expr)); - if (t) - return t; - /* Try folding *(&B+O) to B.X. */ - t = maybe_fold_offset_to_component_ref (TREE_TYPE (base), base, offset, - TREE_TYPE (expr), false); + t = maybe_fold_offset_to_reference (base_addr, offset, + TREE_TYPE (expr)); if (t) return t; - - /* Fold *&B to B. We can only do this if EXPR is the same type - as BASE. We can't do this if EXPR is the element type of an array - and BASE is the array. */ - if (integer_zerop (offset) - && lang_hooks.types_compatible_p (TREE_TYPE (base), - TREE_TYPE (expr))) - return base; } else { @@ -1856,9 +1911,8 @@ maybe_fold_stmt_indirect (tree expr, tree base, tree offset) /* Try folding *(B+O) to B->X. Still an improvement. */ if (POINTER_TYPE_P (TREE_TYPE (base))) { - t = maybe_fold_offset_to_component_ref (TREE_TYPE (TREE_TYPE (base)), - base, offset, - TREE_TYPE (expr), true); + t = maybe_fold_offset_to_reference (base, offset, + TREE_TYPE (expr)); if (t) return t; } @@ -2020,6 +2074,21 @@ fold_stmt_r (tree *expr_p, int *walk_subtrees, void *data) integer_zero_node); break; + case NOP_EXPR: + t = walk_tree (&TREE_OPERAND (expr, 0), fold_stmt_r, data, NULL); + if (t) + return t; + *walk_subtrees = 0; + + if (POINTER_TYPE_P (TREE_TYPE (expr)) + && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (expr, 0))) + && (t = maybe_fold_offset_to_reference + (TREE_OPERAND (expr, 0), + integer_zero_node, + TREE_TYPE (TREE_TYPE (expr))))) + t = build_fold_addr_expr_with_type (t, TREE_TYPE (expr)); + break; + /* ??? Could handle more ARRAY_REFs here, as a variant of INDIRECT_REF. We'd only want to bother decomposing an existing ARRAY_REF if the base array is found to have another offset contained within. |