This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [PATCH] Fix points-to / call-clobber solution in the face of pointer-arithmetic
- From: "Daniel Berlin" <dberlin at dberlin dot org>
- To: "Richard Guenther" <rguenther at suse dot de>
- Cc: gcc-patches at gcc dot gnu dot org, "Diego Novillo" <dnovillo at google dot com>
- Date: Fri, 4 Jul 2008 12:26:20 -0400
- Subject: Re: [PATCH] Fix points-to / call-clobber solution in the face of pointer-arithmetic
- References: <alpine.LNX.1.10.0807041607390.8853@zhemvz.fhfr.qr>
On Fri, Jul 4, 2008 at 10:14 AM, Richard Guenther <rguenther@suse.de> wrote:
>
> This (again) fixes how we handle pointer-arithmetic in (field-sensitive)
> PTA.
>
> First, as shown in the single testcase included, we have to be able
> to compute the reachability set of a pointer that points after
> an object - which we cannot do right now because the points-to set
> we create at the moment is empty in that case.
>
> Second, repeated increments smaller than a sub-field size do not
> cause the solution to change, so the solution for p + 2 + 2 may
> be different of that for p + 4. This problem is a real problem
> if we have a sub-field which includes multiple "real" fields
> (which happens for structs with variable sized fields, or of course
> with the patch globbing non-pointer fields).
>
> The solution to the above problems is for 1) use the last sub-field
> as the solution for off-variable pointers
Err, no.
This will do Bad Things (TM) for indirect function calls, where the
last sub field is equivalent to the last argument ;)
Unless you are going to mark which varinfos are indirect function
calls, and not do this for them.
>, 2) include the next
> sub-field in the solution iff the sub-field doesn't start at the
> offset.
I'm okay with this.
>
> An alternative solution to 1) is to drop to anything.
>
I'd much prefer dropping to anything for things like variably sized
fields than messing around in a way that will break indirect function
calls.
> We still only handle positive offsets in arithmetic (we can handle
> them if we drop to anything in case 1), or if we have an explicit
> sub-field for one-after-the-object pointers, in which case we
> would have to drop to anything if we go off that one).
>
> Bootstrapped and tested on x86_64-unknown-linux-gnu, ok for trunk?
>
> Thanks,
> Richard.
>
> 2008-07-04 Richard Guenther <rguenther@suse.de>
>
> * tree-ssa-structalias.c (solution_set_add): Correctly handle
> pointers outside a var and inside a field.
> (process_constraint): Remove zeroing offset for !use_field_sensitive.
> (get_constraint_for_ptr_offset): New function.
> (get_constraint_for_component_ref): For addresses at least include
> the last field of the variable.
> (get_constraint_for_1): Factor common code, handle POINTER_PLUS_EXPR.
> (handle_ptr_arith): Remove.
> (find_func_aliases): Simplify assignment handling.
> (init_alias_vars): Initialize use_field_sensitive from
> max-fields-for-field-sensitive parameter.
>
> * gcc.dg/torture/pta-ptrarith-1.c: New testcase.
>
> Index: trunk/gcc/tree-ssa-structalias.c
> ===================================================================
> *** trunk.orig/gcc/tree-ssa-structalias.c 2008-07-04
> 11:36:20.000000000 +0200
> --- trunk/gcc/tree-ssa-structalias.c 2008-07-04 13:52:19.000000000 +0200
> *************** solution_set_add (bitmap set, unsigned H
> *** 756,778 ****
>
> EXECUTE_IF_SET_IN_BITMAP (set, 0, i, bi)
> {
> ! /* If this is a properly sized variable, only add offset if it's
> ! less than end. Otherwise, it is globbed to a single
> ! variable. */
> ! ! if ((get_varinfo (i)->offset + offset) < get_varinfo
> (i)->fullsize)
> {
> unsigned HOST_WIDE_INT fieldoffset = get_varinfo (i)->offset +
> offset;
> varinfo_t v = first_vi_for_offset (get_varinfo (i), fieldoffset);
> if (!v)
> ! continue;
> bitmap_set_bit (result, v->id);
> ! }
> ! else if (get_varinfo (i)->is_artificial_var
> ! || get_varinfo (i)->has_union
> ! || get_varinfo (i)->is_unknown_size_var)
> ! {
> ! bitmap_set_bit (result, i);
> }
> }
>
> --- 756,784 ----
>
> EXECUTE_IF_SET_IN_BITMAP (set, 0, i, bi)
> {
> ! /* Artificial and unknown-size variables are not split. */
> ! if (get_varinfo (i)->is_artificial_var
> ! || get_varinfo (i)->has_union
> ! || get_varinfo (i)->is_unknown_size_var)
> ! bitmap_set_bit (result, i);
> ! else
> {
> unsigned HOST_WIDE_INT fieldoffset = get_varinfo (i)->offset +
> offset;
> varinfo_t v = first_vi_for_offset (get_varinfo (i), fieldoffset);
> + /* If the result is outside of the variable use the last field.
> */
> if (!v)
> ! {
> ! v = get_varinfo (i);
> ! while (v->next != NULL)
> ! v = v->next;
> ! }
> bitmap_set_bit (result, v->id);
> ! /* If the result is not exactly at fieldoffset include the next
> ! field as well. See get_constraint_for_ptr_offset for more
> ! rationale. */
> ! if (v->offset != fieldoffset
> ! && v->next != NULL)
> ! bitmap_set_bit (result, v->next->id);
> }
> }
>
> *************** process_constraint (constraint_t t)
> *** 2602,2613 ****
> gcc_assert (rhs.var < VEC_length (varinfo_t, varmap));
> gcc_assert (lhs.var < VEC_length (varinfo_t, varmap));
>
> - if (!use_field_sensitive)
> - {
> - t->rhs.offset = 0;
> - t->lhs.offset = 0;
> - }
> -
> /* ANYTHING == ANYTHING is pointless. */
> if (lhs.var == anything_id && rhs.var == anything_id)
> return;
> --- 2608,2613 ----
> *************** bitpos_of_field (const tree fdecl)
> *** 2683,2688 ****
> --- 2683,2809 ----
> }
>
>
> + /* Handle pointer arithmetic EXPR when creating aliasing constraints.
> + Expressions of the type PTR + CST can be handled in two ways:
> + + 1- If the constraint for PTR is ADDRESSOF for a non-structure
> + variable, then we can use it directly because adding or
> + subtracting a constant may not alter the original ADDRESSOF
> + constraint (i.e., pointer arithmetic may not legally go outside
> + an object's boundaries).
> + + 2- If the constraint for PTR is ADDRESSOF for a structure variable,
> + then if CST is a compile-time constant that can be used as an
> + offset, we can determine which sub-variable will be pointed-to
> + by the expression.
> + + Return true if the expression is handled. For any other kind of
> + expression, return false so that each operand can be added as a
> + separate constraint by the caller. */
> + + static void
> + get_constraint_for_ptr_offset (tree ptr, tree offset,
> + VEC (ce_s, heap) **results)
> + {
> + struct constraint_expr *c;
> + unsigned int j, n;
> + unsigned HOST_WIDE_INT rhsunitoffset, rhsoffset;
> + + /* If we do not do field-sensitive PTA adding offsets to pointers
> + does not change the points-to solution. */
> + if (!use_field_sensitive)
> + {
> + get_constraint_for (ptr, results);
> + return;
> + }
> + + /* If the offset is not a non-negative integer constant that fits
> + in a HOST_WIDE_INT, we have to fall back to a conservative
> + solution which includes all sub-fields of all pointed-to
> + variables of ptr.
> + ??? As we do not have the ability to express this, fall back
> + to anything. */
> + if (!host_integerp (offset, 1))
> + {
> + struct constraint_expr temp;
> + temp.var = anything_id;
> + temp.type = SCALAR;
> + temp.offset = 0;
> + VEC_safe_push (ce_s, heap, *results, &temp);
> + return;
> + }
> + + /* Make sure the bit-offset also fits. */
> + rhsunitoffset = TREE_INT_CST_LOW (offset);
> + rhsoffset = rhsunitoffset * BITS_PER_UNIT;
> + if (rhsunitoffset != rhsoffset / BITS_PER_UNIT)
> + {
> + struct constraint_expr temp;
> + temp.var = anything_id;
> + temp.type = SCALAR;
> + temp.offset = 0;
> + VEC_safe_push (ce_s, heap, *results, &temp);
> + return;
> + }
> + + get_constraint_for (ptr, results);
> + if (rhsoffset == 0)
> + return;
> + + /* As we are eventually appending to the solution do not use
> + VEC_iterate here. */
> + n = VEC_length (ce_s, *results);
> + for (j = 0; j < n; j++)
> + {
> + c = VEC_index (ce_s, *results, j);
> + + if (c->type == ADDRESSOF)
> + {
> + varinfo_t temp, curr = get_varinfo (c->var);
> + + /* Search the sub-field which overlaps with the
> + pointed-to offset. As we deal with positive offsets
> + only, we can start the search from the current variable. */
> + temp = first_vi_for_offset (curr, curr->offset + rhsoffset);
> + + /* If the result is outside of the variable we have to provide
> + a conservative result, as the variable is still reachable
> + from the resulting pointer (even though it technically
> + cannot point to anything). The last sub-field is such
> + a conservative result.
> + ??? If we always had a sub-field for &object + 1 then
> + we could represent this in a more precise way. */
> + if (temp == NULL)
> + {
> + temp = curr;
> + while (temp->next != NULL)
> + temp = temp->next;
> + continue;
> + }
> + + /* If the found variable is not exactly at the pointed to
> + result, we have to include the next variable in the
> + solution as well. Otherwise two increments by offset / 2
> + do not result in the same or a conservative superset
> + solution. */
> + if (temp->offset != curr->offset + rhsoffset
> + && temp->next != NULL)
> + {
> + struct constraint_expr c2;
> + c2.var = temp->next->id;
> + c2.type = ADDRESSOF;
> + c2.offset = 0;
> + VEC_safe_push (ce_s, heap, *results, &c2);
> + }
> + c->var = temp->id;
> + c->offset = 0;
> + }
> + else
> + c->offset = rhsoffset;
> + }
> + }
> + +
> /* Given a COMPONENT_REF T, return the constraint_expr vector for it.
> If address_p is true the result will be taken its address of. */
>
> *************** get_constraint_for_component_ref (tree t
> *** 2719,2727 ****
> /* Pretend to take the address of the base, we'll take care of
> adding the required subset of sub-fields below. */
> get_constraint_for_1 (t, results, true);
> - result = VEC_last (ce_s, *results);
> -
> gcc_assert (VEC_length (ce_s, *results) == 1);
>
> /* This can also happen due to weird offsetof type macros. */
> if (TREE_CODE (t) != ADDR_EXPR && result->type == ADDRESSOF)
> --- 2840,2847 ----
> /* Pretend to take the address of the base, we'll take care of
> adding the required subset of sub-fields below. */
> get_constraint_for_1 (t, results, true);
> gcc_assert (VEC_length (ce_s, *results) == 1);
> + result = VEC_last (ce_s, *results);
>
> /* This can also happen due to weird offsetof type macros. */
> if (TREE_CODE (t) != ADDR_EXPR && result->type == ADDRESSOF)
> *************** get_constraint_for_component_ref (tree t
> *** 2756,2767 ****
> break;
> }
> }
> ! /* assert that we found *some* field there. The user couldn't be
> ! accessing *only* padding. */
> ! /* Still the user could access one past the end of an array
> ! embedded in a struct resulting in accessing *only* padding. */
> ! gcc_assert (VEC_length (ce_s, *results) >= 1
> ! || ref_contains_array_ref (orig_t));
> }
> else if (bitmaxsize == 0)
> {
> --- 2876,2900 ----
> break;
> }
> }
> ! /* If we are going to take the address of this field then
> ! to be able to compute reachability correctly add at least
> ! the last field of the variable. */
> ! if (address_p
> ! && VEC_length (ce_s, *results) == 0)
> ! {
> ! curr = get_varinfo (cexpr.var);
> ! while (curr->next != NULL)
> ! curr = curr->next;
> ! cexpr.var = curr->id;
> ! VEC_safe_push (ce_s, heap, *results, &cexpr);
> ! }
> ! else
> ! /* Assert that we found *some* field there. The user couldn't be
> ! accessing *only* padding. */
> ! /* Still the user could access one past the end of an array
> ! embedded in a struct resulting in accessing *only* padding.
> */
> ! gcc_assert (VEC_length (ce_s, *results) >= 1
> ! || ref_contains_array_ref (orig_t));
> }
> else if (bitmaxsize == 0)
> {
> *************** get_constraint_for_1 (tree t, VEC (ce_s,
> *** 2905,2928 ****
> VEC_safe_push (ce_s, heap, *results, &temp);
> return;
> }
> - else
> - {
> - temp.var = anything_id;
> - temp.type = SCALAR;
> - temp.offset = 0;
> - VEC_safe_push (ce_s, heap, *results, &temp);
> - return;
> - }
> break;
> ! default:
> ! {
> ! temp.type = ADDRESSOF;
> ! temp.var = anything_id;
> ! temp.offset = 0;
> ! VEC_safe_push (ce_s, heap, *results, &temp);
> ! return;
> ! }
> }
> }
> case tcc_reference:
> {
> --- 3038,3047 ----
> VEC_safe_push (ce_s, heap, *results, &temp);
> return;
> }
> break;
> ! default:;
> }
> + break;
> }
> case tcc_reference:
> {
> *************** get_constraint_for_1 (tree t, VEC (ce_s,
> *** 2939,2953 ****
> case COMPONENT_REF:
> get_constraint_for_component_ref (t, results, address_p);
> return;
> ! default:
> ! {
> ! temp.type = ADDRESSOF;
> ! temp.var = anything_id;
> ! temp.offset = 0;
> ! VEC_safe_push (ce_s, heap, *results, &temp);
> ! return;
> ! }
> }
> }
> case tcc_unary:
> {
> --- 3058,3066 ----
> case COMPONENT_REF:
> get_constraint_for_component_ref (t, results, address_p);
> return;
> ! default:;
> }
> + break;
> }
> case tcc_unary:
> {
> *************** get_constraint_for_1 (tree t, VEC (ce_s,
> *** 2968,2982 ****
>
> /* FALLTHRU */
> }
> ! default:
> ! {
> ! temp.type = ADDRESSOF;
> ! temp.var = anything_id;
> ! temp.offset = 0;
> ! VEC_safe_push (ce_s, heap, *results, &temp);
> ! return;
> ! }
> }
> }
> case tcc_exceptional:
> {
> --- 3081,3099 ----
>
> /* FALLTHRU */
> }
> ! default:;
> }
> + break;
> + }
> + case tcc_binary:
> + {
> + if (TREE_CODE (t) == POINTER_PLUS_EXPR)
> + {
> + get_constraint_for_ptr_offset (TREE_OPERAND (t, 0),
> + TREE_OPERAND (t, 1), results);
> + return;
> + }
> + break;
> }
> case tcc_exceptional:
> {
> *************** get_constraint_for_1 (tree t, VEC (ce_s,
> *** 2987,3023 ****
> get_constraint_for_1 (PHI_RESULT (t), results, address_p);
> return;
> }
> - break;
> case SSA_NAME:
> {
> get_constraint_for_ssa_var (t, results, address_p);
> return;
> }
> ! break;
> ! default:
> ! {
> ! temp.type = ADDRESSOF;
> ! temp.var = anything_id;
> ! temp.offset = 0;
> ! VEC_safe_push (ce_s, heap, *results, &temp);
> ! return;
> ! }
> }
> }
> case tcc_declaration:
> {
> get_constraint_for_ssa_var (t, results, address_p);
> return;
> }
> ! default:
> ! {
> ! temp.type = ADDRESSOF;
> ! temp.var = anything_id;
> ! temp.offset = 0;
> ! VEC_safe_push (ce_s, heap, *results, &temp);
> ! return;
> ! }
> }
> }
>
> /* Given a gimple tree T, return the constraint expression vector for it.
> */
> --- 3104,3131 ----
> get_constraint_for_1 (PHI_RESULT (t), results, address_p);
> return;
> }
> case SSA_NAME:
> {
> get_constraint_for_ssa_var (t, results, address_p);
> return;
> }
> ! default:;
> }
> + break;
> }
> case tcc_declaration:
> {
> get_constraint_for_ssa_var (t, results, address_p);
> return;
> }
> ! default:;
> }
> + + /* The default fallback is a constraint from anything. */
> + temp.type = ADDRESSOF;
> + temp.var = anything_id;
> + temp.offset = 0;
> + VEC_safe_push (ce_s, heap, *results, &temp);
> }
>
> /* Given a gimple tree T, return the constraint expression vector for it.
> */
> *************** do_structure_copy (tree lhsop, tree rhso
> *** 3303,3382 ****
> }
> }
>
> - - /* Handle pointer arithmetic EXPR when creating aliasing constraints.
> - Expressions of the type PTR + CST can be handled in two ways:
> - - 1- If the constraint for PTR is ADDRESSOF for a non-structure
> - variable, then we can use it directly because adding or
> - subtracting a constant may not alter the original ADDRESSOF
> - constraint (i.e., pointer arithmetic may not legally go outside
> - an object's boundaries).
> - - 2- If the constraint for PTR is ADDRESSOF for a structure variable,
> - then if CST is a compile-time constant that can be used as an
> - offset, we can determine which sub-variable will be pointed-to
> - by the expression.
> - - Return true if the expression is handled. For any other kind of
> - expression, return false so that each operand can be added as a
> - separate constraint by the caller. */
> - - static bool
> - handle_ptr_arith (VEC (ce_s, heap) *lhsc, tree expr)
> - {
> - tree op0, op1;
> - struct constraint_expr *c, *c2;
> - unsigned int i = 0;
> - unsigned int j = 0;
> - VEC (ce_s, heap) *temp = NULL;
> - unsigned HOST_WIDE_INT rhsunitoffset, rhsoffset;
> - - if (TREE_CODE (expr) != POINTER_PLUS_EXPR)
> - return false;
> - - op0 = TREE_OPERAND (expr, 0);
> - op1 = TREE_OPERAND (expr, 1);
> - gcc_assert (POINTER_TYPE_P (TREE_TYPE (op0)));
> - - /* If the offset is not a non-negative integer constant that fits
> - in a HOST_WIDE_INT, we cannot handle it here. */
> - if (!host_integerp (op1, 1))
> - return false;
> - - /* Make sure the bit-offset also fits. */
> - rhsunitoffset = TREE_INT_CST_LOW (op1);
> - rhsoffset = rhsunitoffset * BITS_PER_UNIT;
> - if (rhsunitoffset != rhsoffset / BITS_PER_UNIT)
> - return false;
> - - get_constraint_for (op0, &temp);
> - - for (i = 0; VEC_iterate (ce_s, lhsc, i, c); i++)
> - for (j = 0; VEC_iterate (ce_s, temp, j, c2); j++)
> - {
> - if (c2->type == ADDRESSOF && rhsoffset != 0)
> - {
> - varinfo_t temp = get_varinfo (c2->var);
> - - /* An access one after the end of an array is valid,
> - so simply punt on accesses we cannot resolve. */
> - temp = first_vi_for_offset (temp, rhsoffset);
> - if (temp == NULL)
> - continue;
> - c2->var = temp->id;
> - c2->offset = 0;
> - }
> - else
> - c2->offset = rhsoffset;
> - process_constraint (new_constraint (*c, *c2));
> - }
> - - VEC_free (ce_s, heap, temp);
> - - return true;
> - }
> -
> /* Create a constraint ID = OP. */
>
> static void
> --- 3411,3416 ----
> *************** find_func_aliases (tree origt)
> *** 3776,3864 ****
> }
> }
> }
> ! /* Otherwise, just a regular assignment statement. */
> ! else if (TREE_CODE (t) == GIMPLE_MODIFY_STMT)
> {
> tree lhsop = GIMPLE_STMT_OPERAND (t, 0);
> tree rhsop = GIMPLE_STMT_OPERAND (t, 1);
> - int i;
>
> ! if (AGGREGATE_TYPE_P (TREE_TYPE (lhsop))
> ! && AGGREGATE_TYPE_P (TREE_TYPE (rhsop)))
> ! {
> ! do_structure_copy (lhsop, rhsop);
> ! }
> else
> {
> ! /* Only care about operations with pointers, structures
> ! containing pointers, dereferences, and call expressions. */
> ! if (could_have_pointers (lhsop)
> ! || TREE_CODE (rhsop) == CALL_EXPR)
> {
> ! get_constraint_for (lhsop, &lhsc);
> ! switch (TREE_CODE_CLASS (TREE_CODE (rhsop)))
> ! {
> ! /* RHS that consist of unary operations,
> ! exceptional types, or bare decls/constants, get
> ! handled directly by get_constraint_for. */
> ! case tcc_reference:
> ! case tcc_declaration:
> ! case tcc_constant:
> ! case tcc_exceptional:
> ! case tcc_expression:
> ! case tcc_vl_exp:
> ! case tcc_unary:
> ! {
> ! unsigned int j;
> ! ! get_constraint_for (rhsop, &rhsc);
> ! for (j = 0; VEC_iterate (ce_s, lhsc, j, c); j++)
> ! {
> ! struct constraint_expr *c2;
> ! unsigned int k;
> ! ! for (k = 0; VEC_iterate (ce_s, rhsc, k, c2);
> k++)
> ! process_constraint (new_constraint (*c, *c2));
> ! }
> ! ! }
> ! break;
>
> ! case tcc_binary:
> ! {
> ! /* For pointer arithmetic of the form
> ! PTR + CST, we can simply use PTR's
> ! constraint because pointer arithmetic is
> ! not allowed to go out of bounds. */
> ! if (handle_ptr_arith (lhsc, rhsop))
> ! break;
> ! }
> ! /* FALLTHRU */
> ! ! /* Otherwise, walk each operand. Notice that we
> ! can't use the operand interface because we need
> ! to process expressions other than simple operands
> ! (e.g. INDIRECT_REF, ADDR_EXPR, CALL_EXPR). */
> ! default:
> ! for (i = 0; i < TREE_OPERAND_LENGTH (rhsop); i++)
> ! {
> ! tree op = TREE_OPERAND (rhsop, i);
> ! unsigned int j;
> ! ! gcc_assert (VEC_length (ce_s, rhsc) == 0);
> ! get_constraint_for (op, &rhsc);
> ! for (j = 0; VEC_iterate (ce_s, lhsc, j, c); j++)
> ! {
> ! struct constraint_expr *c2;
> ! while (VEC_length (ce_s, rhsc) > 0)
> ! {
> ! c2 = VEC_last (ce_s, rhsc);
> ! process_constraint (new_constraint (*c,
> *c2));
> ! VEC_pop (ce_s, rhsc);
> ! }
> ! }
> ! }
> ! }
> }
> }
> }
> --- 3810,3838 ----
> }
> }
> }
> ! /* Otherwise, just a regular assignment statement. Only care about
> ! operations with pointer result, others are dealt with as escape
> ! points if they have pointer operands. */
> ! else if (TREE_CODE (t) == GIMPLE_MODIFY_STMT
> ! && could_have_pointers (GIMPLE_STMT_OPERAND (t, 0)))
> {
> tree lhsop = GIMPLE_STMT_OPERAND (t, 0);
> tree rhsop = GIMPLE_STMT_OPERAND (t, 1);
>
> ! if (AGGREGATE_TYPE_P (TREE_TYPE (lhsop)))
> ! do_structure_copy (lhsop, rhsop);
> else
> {
> ! unsigned int j;
> ! get_constraint_for (lhsop, &lhsc);
> ! get_constraint_for (rhsop, &rhsc);
> ! for (j = 0; VEC_iterate (ce_s, lhsc, j, c); j++)
> {
> ! struct constraint_expr *c2;
> ! unsigned int k;
>
> ! for (k = 0; VEC_iterate (ce_s, rhsc, k, c2); k++)
> ! process_constraint (new_constraint (*c, *c2));
> }
> }
> }
> *************** init_base_vars (void)
> *** 5209,5214 ****
> --- 5183,5190 ----
> static void
> init_alias_vars (void)
> {
> + use_field_sensitive = (MAX_FIELDS_FOR_FIELD_SENSITIVE > 1);
> +
> bitmap_obstack_initialize (&pta_obstack);
> bitmap_obstack_initialize (&oldpta_obstack);
> bitmap_obstack_initialize (&predbitmap_obstack);
> Index: trunk/gcc/testsuite/gcc.dg/torture/pta-ptrarith-1.c
> ===================================================================
> *** /dev/null 1970-01-01 00:00:00.000000000 +0000
> --- trunk/gcc/testsuite/gcc.dg/torture/pta-ptrarith-1.c 2008-07-04
> 14:43:54.000000000 +0200
> ***************
> *** 0 ****
> --- 1,33 ----
> + /* { dg-do run } */
> + /* { dg-options "-fdump-tree-alias" } */
> + /* { dg-skip-if "" { *-*-* } { "-O0" } { "" } } */
> + + struct Foo {
> + int *p;
> + };
> + + void __attribute__((noinline))
> + foo (void *p)
> + {
> + struct Foo *f = (struct Foo *)p - 1;
> + *f->p = 0;
> + }
> + + int bar (void)
> + {
> + struct Foo f;
> + int i = 1;
> + f.p = &i;
> + foo (&f + 1);
> + return i;
> + }
> + extern void abort (void);
> + int main()
> + {
> + if (bar () != 0)
> + abort ();
> + return 0;
> + }
> + + /* { dg-final { scan-tree-dump "ESCAPED = { ESCAPED NONLOCAL f i }"
> "alias" } } */
> + /* { dg-final { cleanup-tree-dump "alias" } } */
>