diff mbox

[PR,49886] Prevent fnsplit from changing signature when there are type attributes

Message ID 20110729205530.GA24476@virgil.arch.suse.de
State New
Headers show

Commit Message

Martin Jambor July 29, 2011, 8:55 p.m. UTC
Hi,

On Thu, Jul 28, 2011 at 06:52:05PM +0200, Martin Jambor wrote:
> pass_split_functions is happy to split functions which have type
> attributes but cannot update them if the new clone has in any way
> different parameters than the original.  This can lead to
> miscompilations in cases like the testcase.
> 
> This patch solves it by 1) making the inliner set the
> can_change_signature flag to false for them because their signature
> cannot be changed (this step is also necessary to make IPA-CP operate
> on them and handle them correctly), and 2) make the splitting pass
> keep all parameters if the flag is set.  The second step might involve
> inventing some default definitions if the parameters did not really
> have any.
> 
> I spoke about this with Honza and he claimed that the new function is
> really an entirely different thing and that the parameters may
> correspond only very loosely and thus the type attributes should be
> cleared.  I'm not sure I agree, but in any case I needed this to work
> to allow me continue with promised IPA-CP polishing and so I decided
> to do this because it was easier.  (My own opinion is that the current
> representation of parameter-describing function type attributes is
> evil and will cause harm no matter hat we do.)
> 

Actually, I'd like to commit the patch below which also clears
can_change_signature for BUILT_IN_VA_START.  It is not really
necessary for this fix but fixes some problems in a followup patch and
is also the correct thing to do because if we clone a function calling
it and pass non-NULL for args_to_skip, the new clone would not have a
stdarg_p type and fold_builtin_next_arg could error when dealing with
it.

Also bootstrapped and tested on x86_64-linux.  OK for trunk?

Thanks,

Martin


2011-07-29  Martin Jambor  <mjambor@suse.cz>

	PR middle-end/49886
	* ipa-inline-analysis.c (compute_inline_parameters): Set
	can_change_signature of noes with typde attributes.
	* ipa-split.c (split_function): Do not skip any arguments if
	can_change_signature is set.

	* testsuite/gcc.c-torture/execute/pr49886.c: New testcase.

Comments

Martin Jambor Aug. 30, 2011, 4:50 p.m. UTC | #1
Ping.  Re-bootstrapped and re-tested yesterday on x86_64-linux.

THanks,

Martin


On Fri, Jul 29, 2011 at 10:55:31PM +0200, Martin Jambor wrote:
> Hi,
> 
> On Thu, Jul 28, 2011 at 06:52:05PM +0200, Martin Jambor wrote:
> > pass_split_functions is happy to split functions which have type
> > attributes but cannot update them if the new clone has in any way
> > different parameters than the original.  This can lead to
> > miscompilations in cases like the testcase.
> > 
> > This patch solves it by 1) making the inliner set the
> > can_change_signature flag to false for them because their signature
> > cannot be changed (this step is also necessary to make IPA-CP operate
> > on them and handle them correctly), and 2) make the splitting pass
> > keep all parameters if the flag is set.  The second step might involve
> > inventing some default definitions if the parameters did not really
> > have any.
> > 
> > I spoke about this with Honza and he claimed that the new function is
> > really an entirely different thing and that the parameters may
> > correspond only very loosely and thus the type attributes should be
> > cleared.  I'm not sure I agree, but in any case I needed this to work
> > to allow me continue with promised IPA-CP polishing and so I decided
> > to do this because it was easier.  (My own opinion is that the current
> > representation of parameter-describing function type attributes is
> > evil and will cause harm no matter hat we do.)
> > 
> 
> Actually, I'd like to commit the patch below which also clears
> can_change_signature for BUILT_IN_VA_START.  It is not really
> necessary for this fix but fixes some problems in a followup patch and
> is also the correct thing to do because if we clone a function calling
> it and pass non-NULL for args_to_skip, the new clone would not have a
> stdarg_p type and fold_builtin_next_arg could error when dealing with
> it.
> 
> Also bootstrapped and tested on x86_64-linux.  OK for trunk?
> 
> Thanks,
> 
> Martin
> 
> 
> 2011-07-29  Martin Jambor  <mjambor@suse.cz>
> 
> 	PR middle-end/49886
> 	* ipa-inline-analysis.c (compute_inline_parameters): Set
> 	can_change_signature of noes with typde attributes.
> 	* ipa-split.c (split_function): Do not skip any arguments if
> 	can_change_signature is set.
> 
> 	* testsuite/gcc.c-torture/execute/pr49886.c: New testcase.
> 
> Index: src/gcc/ipa-inline-analysis.c
> ===================================================================
> --- src.orig/gcc/ipa-inline-analysis.c
> +++ src/gcc/ipa-inline-analysis.c
> @@ -1658,18 +1658,28 @@ compute_inline_parameters (struct cgraph
>    /* Can this function be inlined at all?  */
>    info->inlinable = tree_inlinable_function_p (node->decl);
>  
> -  /* Inlinable functions always can change signature.  */
> -  if (info->inlinable)
> -    node->local.can_change_signature = true;
> +  /* Type attributes can use parameter indices to describe them.  */
> +  if (TYPE_ATTRIBUTES (TREE_TYPE (node->decl)))
> +    node->local.can_change_signature = false;
>    else
>      {
> -      /* Functions calling builtin_apply can not change signature.  */
> -      for (e = node->callees; e; e = e->next_callee)
> -	if (DECL_BUILT_IN (e->callee->decl)
> -	    && DECL_BUILT_IN_CLASS (e->callee->decl) == BUILT_IN_NORMAL
> -	    && DECL_FUNCTION_CODE (e->callee->decl) == BUILT_IN_APPLY_ARGS)
> -	  break;
> -      node->local.can_change_signature = !e;
> +      /* Otherwise, inlinable functions always can change signature.  */
> +      if (info->inlinable)
> +	node->local.can_change_signature = true;
> +      else
> +	{
> +	  /* Functions calling builtin_apply can not change signature.  */
> +	  for (e = node->callees; e; e = e->next_callee)
> +	    {
> +	      tree cdecl = e->callee->decl;
> +	      if (DECL_BUILT_IN (cdecl)
> +		  && DECL_BUILT_IN_CLASS (cdecl) == BUILT_IN_NORMAL
> +		  && (DECL_FUNCTION_CODE (cdecl) == BUILT_IN_APPLY_ARGS
> +		      || DECL_FUNCTION_CODE (cdecl) == BUILT_IN_VA_START))
> +		break;
> +	    }
> +	  node->local.can_change_signature = !e;
> +	}
>      }
>    estimate_function_body_sizes (node, early);
>  
> Index: src/gcc/ipa-split.c
> ===================================================================
> --- src.orig/gcc/ipa-split.c
> +++ src/gcc/ipa-split.c
> @@ -945,10 +945,10 @@ static void
>  split_function (struct split_point *split_point)
>  {
>    VEC (tree, heap) *args_to_pass = NULL;
> -  bitmap args_to_skip = BITMAP_ALLOC (NULL);
> +  bitmap args_to_skip;
>    tree parm;
>    int num = 0;
> -  struct cgraph_node *node;
> +  struct cgraph_node *node, *cur_node = cgraph_get_node (current_function_decl);
>    basic_block return_bb = find_return_bb ();
>    basic_block call_bb;
>    gimple_stmt_iterator gsi;
> @@ -968,17 +968,30 @@ split_function (struct split_point *spli
>        dump_split_point (dump_file, split_point);
>      }
>  
> +  if (cur_node->local.can_change_signature)
> +    args_to_skip = BITMAP_ALLOC (NULL);
> +  else
> +    args_to_skip = NULL;
> +
>    /* Collect the parameters of new function and args_to_skip bitmap.  */
>    for (parm = DECL_ARGUMENTS (current_function_decl);
>         parm; parm = DECL_CHAIN (parm), num++)
> -    if (!is_gimple_reg (parm)
> -	|| !gimple_default_def (cfun, parm)
> -	|| !bitmap_bit_p (split_point->ssa_names_to_pass,
> -			  SSA_NAME_VERSION (gimple_default_def (cfun, parm))))
> +    if (args_to_skip
> +	&& (!is_gimple_reg (parm)
> +	    || !gimple_default_def (cfun, parm)
> +	    || !bitmap_bit_p (split_point->ssa_names_to_pass,
> +			      SSA_NAME_VERSION (gimple_default_def (cfun,
> +								    parm)))))
>        bitmap_set_bit (args_to_skip, num);
>      else
>        {
>  	arg = gimple_default_def (cfun, parm);
> +	if (!arg)
> +	  {
> +	    arg = make_ssa_name (parm, gimple_build_nop ());
> +	    set_default_def (parm, arg);
> +	  }
> +
>  	if (TYPE_MAIN_VARIANT (DECL_ARG_TYPE (parm))
>  	    != TYPE_MAIN_VARIANT (TREE_TYPE (arg)))
>  	  {
> @@ -1081,9 +1094,7 @@ split_function (struct split_point *spli
>  
>    /* Now create the actual clone.  */
>    rebuild_cgraph_edges ();
> -  node = cgraph_function_versioning (cgraph_get_node (current_function_decl),
> -				     NULL, NULL,
> -				     args_to_skip,
> +  node = cgraph_function_versioning (cur_node, NULL, NULL, args_to_skip,
>  				     split_point->split_bbs,
>  				     split_point->entry_bb, "part");
>    /* For usual cloning it is enough to clear builtin only when signature
> @@ -1094,7 +1105,7 @@ split_function (struct split_point *spli
>        DECL_BUILT_IN_CLASS (node->decl) = NOT_BUILT_IN;
>        DECL_FUNCTION_CODE (node->decl) = (enum built_in_function) 0;
>      }
> -  cgraph_node_remove_callees (cgraph_get_node (current_function_decl));
> +  cgraph_node_remove_callees (cur_node);
>    if (!split_part_return_p)
>      TREE_THIS_VOLATILE (node->decl) = 1;
>    if (dump_file)
> Index: src/gcc/testsuite/gcc.c-torture/execute/pr49886.c
> ===================================================================
> --- /dev/null
> +++ src/gcc/testsuite/gcc.c-torture/execute/pr49886.c
> @@ -0,0 +1,100 @@
> +struct PMC {
> +    unsigned flags;
> +};
> +
> +typedef struct Pcc_cell
> +{
> +    struct PMC *p;
> +    long bla;
> +    long type;
> +} Pcc_cell;
> +
> +int gi;
> +int cond;
> +
> +extern void abort ();
> +extern void never_ever(int interp, struct PMC *pmc)
> +  __attribute__((noinline,noclone));
> +
> +void never_ever (int interp, struct PMC *pmc)
> +{
> +  abort ();
> +}
> +
> +static void mark_cell(int * interp, Pcc_cell *c)
> +  __attribute__((__nonnull__(1)));
> +
> +static void
> +mark_cell(int * interp, Pcc_cell *c)
> +{
> +  if (!cond)
> +    return;
> +
> +  if (c && c->type == 4 && c->p
> +      && !(c->p->flags & (1<<18)))
> +    never_ever(gi + 1, c->p);
> +  if (c && c->type == 4 && c->p
> +      && !(c->p->flags & (1<<17)))
> +    never_ever(gi + 2, c->p);
> +  if (c && c->type == 4 && c->p
> +      && !(c->p->flags & (1<<16)))
> +    never_ever(gi + 3, c->p);
> +  if (c && c->type == 4 && c->p
> +      && !(c->p->flags & (1<<15)))
> +    never_ever(gi + 4, c->p);
> +  if (c && c->type == 4 && c->p
> +      && !(c->p->flags & (1<<14)))
> +    never_ever(gi + 5, c->p);
> +  if (c && c->type == 4 && c->p
> +      && !(c->p->flags & (1<<13)))
> +    never_ever(gi + 6, c->p);
> +  if (c && c->type == 4 && c->p
> +      && !(c->p->flags & (1<<12)))
> +    never_ever(gi + 7, c->p);
> +  if (c && c->type == 4 && c->p
> +      && !(c->p->flags & (1<<11)))
> +    never_ever(gi + 8, c->p);
> +  if (c && c->type == 4 && c->p
> +      && !(c->p->flags & (1<<10)))
> +    never_ever(gi + 9, c->p);
> +}
> +
> +static void
> +foo(int * interp, Pcc_cell *c)
> +{
> +  mark_cell(interp, c);
> +}
> +
> +static struct Pcc_cell *
> +__attribute__((noinline,noclone))
> +getnull(void)
> +{
> +  return (struct Pcc_cell *) 0;
> +}
> +
> +
> +int main()
> +{
> +  int i;
> +
> +  cond = 1;
> +  for (i = 0; i < 100; i++)
> +    foo (&gi, getnull ());
> +  return 0;
> +}
> +
> +
> +void
> +bar_1 (int * interp, Pcc_cell *c)
> +{
> +  c->bla += 1;
> +  mark_cell(interp, c);
> +}
> +
> +void
> +bar_2 (int * interp, Pcc_cell *c)
> +{
> +  c->bla += 2;
> +  mark_cell(interp, c);
> +}
> +
Richard Biener Aug. 31, 2011, 7:33 a.m. UTC | #2
On Tue, Aug 30, 2011 at 6:50 PM, Martin Jambor <mjambor@suse.cz> wrote:
> Ping.  Re-bootstrapped and re-tested yesterday on x86_64-linux.

Ok.  Does this also apply (maybe in modifed form) to the 4.6 branch?

Thanks,
Richard.

> THanks,
>
> Martin
>
>
> On Fri, Jul 29, 2011 at 10:55:31PM +0200, Martin Jambor wrote:
>> Hi,
>>
>> On Thu, Jul 28, 2011 at 06:52:05PM +0200, Martin Jambor wrote:
>> > pass_split_functions is happy to split functions which have type
>> > attributes but cannot update them if the new clone has in any way
>> > different parameters than the original.  This can lead to
>> > miscompilations in cases like the testcase.
>> >
>> > This patch solves it by 1) making the inliner set the
>> > can_change_signature flag to false for them because their signature
>> > cannot be changed (this step is also necessary to make IPA-CP operate
>> > on them and handle them correctly), and 2) make the splitting pass
>> > keep all parameters if the flag is set.  The second step might involve
>> > inventing some default definitions if the parameters did not really
>> > have any.
>> >
>> > I spoke about this with Honza and he claimed that the new function is
>> > really an entirely different thing and that the parameters may
>> > correspond only very loosely and thus the type attributes should be
>> > cleared.  I'm not sure I agree, but in any case I needed this to work
>> > to allow me continue with promised IPA-CP polishing and so I decided
>> > to do this because it was easier.  (My own opinion is that the current
>> > representation of parameter-describing function type attributes is
>> > evil and will cause harm no matter hat we do.)
>> >
>>
>> Actually, I'd like to commit the patch below which also clears
>> can_change_signature for BUILT_IN_VA_START.  It is not really
>> necessary for this fix but fixes some problems in a followup patch and
>> is also the correct thing to do because if we clone a function calling
>> it and pass non-NULL for args_to_skip, the new clone would not have a
>> stdarg_p type and fold_builtin_next_arg could error when dealing with
>> it.
>>
>> Also bootstrapped and tested on x86_64-linux.  OK for trunk?
>>
>> Thanks,
>>
>> Martin
>>
>>
>> 2011-07-29  Martin Jambor  <mjambor@suse.cz>
>>
>>       PR middle-end/49886
>>       * ipa-inline-analysis.c (compute_inline_parameters): Set
>>       can_change_signature of noes with typde attributes.
>>       * ipa-split.c (split_function): Do not skip any arguments if
>>       can_change_signature is set.
>>
>>       * testsuite/gcc.c-torture/execute/pr49886.c: New testcase.
>>
>> Index: src/gcc/ipa-inline-analysis.c
>> ===================================================================
>> --- src.orig/gcc/ipa-inline-analysis.c
>> +++ src/gcc/ipa-inline-analysis.c
>> @@ -1658,18 +1658,28 @@ compute_inline_parameters (struct cgraph
>>    /* Can this function be inlined at all?  */
>>    info->inlinable = tree_inlinable_function_p (node->decl);
>>
>> -  /* Inlinable functions always can change signature.  */
>> -  if (info->inlinable)
>> -    node->local.can_change_signature = true;
>> +  /* Type attributes can use parameter indices to describe them.  */
>> +  if (TYPE_ATTRIBUTES (TREE_TYPE (node->decl)))
>> +    node->local.can_change_signature = false;
>>    else
>>      {
>> -      /* Functions calling builtin_apply can not change signature.  */
>> -      for (e = node->callees; e; e = e->next_callee)
>> -     if (DECL_BUILT_IN (e->callee->decl)
>> -         && DECL_BUILT_IN_CLASS (e->callee->decl) == BUILT_IN_NORMAL
>> -         && DECL_FUNCTION_CODE (e->callee->decl) == BUILT_IN_APPLY_ARGS)
>> -       break;
>> -      node->local.can_change_signature = !e;
>> +      /* Otherwise, inlinable functions always can change signature.  */
>> +      if (info->inlinable)
>> +     node->local.can_change_signature = true;
>> +      else
>> +     {
>> +       /* Functions calling builtin_apply can not change signature.  */
>> +       for (e = node->callees; e; e = e->next_callee)
>> +         {
>> +           tree cdecl = e->callee->decl;
>> +           if (DECL_BUILT_IN (cdecl)
>> +               && DECL_BUILT_IN_CLASS (cdecl) == BUILT_IN_NORMAL
>> +               && (DECL_FUNCTION_CODE (cdecl) == BUILT_IN_APPLY_ARGS
>> +                   || DECL_FUNCTION_CODE (cdecl) == BUILT_IN_VA_START))
>> +             break;
>> +         }
>> +       node->local.can_change_signature = !e;
>> +     }
>>      }
>>    estimate_function_body_sizes (node, early);
>>
>> Index: src/gcc/ipa-split.c
>> ===================================================================
>> --- src.orig/gcc/ipa-split.c
>> +++ src/gcc/ipa-split.c
>> @@ -945,10 +945,10 @@ static void
>>  split_function (struct split_point *split_point)
>>  {
>>    VEC (tree, heap) *args_to_pass = NULL;
>> -  bitmap args_to_skip = BITMAP_ALLOC (NULL);
>> +  bitmap args_to_skip;
>>    tree parm;
>>    int num = 0;
>> -  struct cgraph_node *node;
>> +  struct cgraph_node *node, *cur_node = cgraph_get_node (current_function_decl);
>>    basic_block return_bb = find_return_bb ();
>>    basic_block call_bb;
>>    gimple_stmt_iterator gsi;
>> @@ -968,17 +968,30 @@ split_function (struct split_point *spli
>>        dump_split_point (dump_file, split_point);
>>      }
>>
>> +  if (cur_node->local.can_change_signature)
>> +    args_to_skip = BITMAP_ALLOC (NULL);
>> +  else
>> +    args_to_skip = NULL;
>> +
>>    /* Collect the parameters of new function and args_to_skip bitmap.  */
>>    for (parm = DECL_ARGUMENTS (current_function_decl);
>>         parm; parm = DECL_CHAIN (parm), num++)
>> -    if (!is_gimple_reg (parm)
>> -     || !gimple_default_def (cfun, parm)
>> -     || !bitmap_bit_p (split_point->ssa_names_to_pass,
>> -                       SSA_NAME_VERSION (gimple_default_def (cfun, parm))))
>> +    if (args_to_skip
>> +     && (!is_gimple_reg (parm)
>> +         || !gimple_default_def (cfun, parm)
>> +         || !bitmap_bit_p (split_point->ssa_names_to_pass,
>> +                           SSA_NAME_VERSION (gimple_default_def (cfun,
>> +                                                                 parm)))))
>>        bitmap_set_bit (args_to_skip, num);
>>      else
>>        {
>>       arg = gimple_default_def (cfun, parm);
>> +     if (!arg)
>> +       {
>> +         arg = make_ssa_name (parm, gimple_build_nop ());
>> +         set_default_def (parm, arg);
>> +       }
>> +
>>       if (TYPE_MAIN_VARIANT (DECL_ARG_TYPE (parm))
>>           != TYPE_MAIN_VARIANT (TREE_TYPE (arg)))
>>         {
>> @@ -1081,9 +1094,7 @@ split_function (struct split_point *spli
>>
>>    /* Now create the actual clone.  */
>>    rebuild_cgraph_edges ();
>> -  node = cgraph_function_versioning (cgraph_get_node (current_function_decl),
>> -                                  NULL, NULL,
>> -                                  args_to_skip,
>> +  node = cgraph_function_versioning (cur_node, NULL, NULL, args_to_skip,
>>                                    split_point->split_bbs,
>>                                    split_point->entry_bb, "part");
>>    /* For usual cloning it is enough to clear builtin only when signature
>> @@ -1094,7 +1105,7 @@ split_function (struct split_point *spli
>>        DECL_BUILT_IN_CLASS (node->decl) = NOT_BUILT_IN;
>>        DECL_FUNCTION_CODE (node->decl) = (enum built_in_function) 0;
>>      }
>> -  cgraph_node_remove_callees (cgraph_get_node (current_function_decl));
>> +  cgraph_node_remove_callees (cur_node);
>>    if (!split_part_return_p)
>>      TREE_THIS_VOLATILE (node->decl) = 1;
>>    if (dump_file)
>> Index: src/gcc/testsuite/gcc.c-torture/execute/pr49886.c
>> ===================================================================
>> --- /dev/null
>> +++ src/gcc/testsuite/gcc.c-torture/execute/pr49886.c
>> @@ -0,0 +1,100 @@
>> +struct PMC {
>> +    unsigned flags;
>> +};
>> +
>> +typedef struct Pcc_cell
>> +{
>> +    struct PMC *p;
>> +    long bla;
>> +    long type;
>> +} Pcc_cell;
>> +
>> +int gi;
>> +int cond;
>> +
>> +extern void abort ();
>> +extern void never_ever(int interp, struct PMC *pmc)
>> +  __attribute__((noinline,noclone));
>> +
>> +void never_ever (int interp, struct PMC *pmc)
>> +{
>> +  abort ();
>> +}
>> +
>> +static void mark_cell(int * interp, Pcc_cell *c)
>> +  __attribute__((__nonnull__(1)));
>> +
>> +static void
>> +mark_cell(int * interp, Pcc_cell *c)
>> +{
>> +  if (!cond)
>> +    return;
>> +
>> +  if (c && c->type == 4 && c->p
>> +      && !(c->p->flags & (1<<18)))
>> +    never_ever(gi + 1, c->p);
>> +  if (c && c->type == 4 && c->p
>> +      && !(c->p->flags & (1<<17)))
>> +    never_ever(gi + 2, c->p);
>> +  if (c && c->type == 4 && c->p
>> +      && !(c->p->flags & (1<<16)))
>> +    never_ever(gi + 3, c->p);
>> +  if (c && c->type == 4 && c->p
>> +      && !(c->p->flags & (1<<15)))
>> +    never_ever(gi + 4, c->p);
>> +  if (c && c->type == 4 && c->p
>> +      && !(c->p->flags & (1<<14)))
>> +    never_ever(gi + 5, c->p);
>> +  if (c && c->type == 4 && c->p
>> +      && !(c->p->flags & (1<<13)))
>> +    never_ever(gi + 6, c->p);
>> +  if (c && c->type == 4 && c->p
>> +      && !(c->p->flags & (1<<12)))
>> +    never_ever(gi + 7, c->p);
>> +  if (c && c->type == 4 && c->p
>> +      && !(c->p->flags & (1<<11)))
>> +    never_ever(gi + 8, c->p);
>> +  if (c && c->type == 4 && c->p
>> +      && !(c->p->flags & (1<<10)))
>> +    never_ever(gi + 9, c->p);
>> +}
>> +
>> +static void
>> +foo(int * interp, Pcc_cell *c)
>> +{
>> +  mark_cell(interp, c);
>> +}
>> +
>> +static struct Pcc_cell *
>> +__attribute__((noinline,noclone))
>> +getnull(void)
>> +{
>> +  return (struct Pcc_cell *) 0;
>> +}
>> +
>> +
>> +int main()
>> +{
>> +  int i;
>> +
>> +  cond = 1;
>> +  for (i = 0; i < 100; i++)
>> +    foo (&gi, getnull ());
>> +  return 0;
>> +}
>> +
>> +
>> +void
>> +bar_1 (int * interp, Pcc_cell *c)
>> +{
>> +  c->bla += 1;
>> +  mark_cell(interp, c);
>> +}
>> +
>> +void
>> +bar_2 (int * interp, Pcc_cell *c)
>> +{
>> +  c->bla += 2;
>> +  mark_cell(interp, c);
>> +}
>> +
>
diff mbox

Patch

Index: src/gcc/ipa-inline-analysis.c
===================================================================
--- src.orig/gcc/ipa-inline-analysis.c
+++ src/gcc/ipa-inline-analysis.c
@@ -1658,18 +1658,28 @@  compute_inline_parameters (struct cgraph
   /* Can this function be inlined at all?  */
   info->inlinable = tree_inlinable_function_p (node->decl);
 
-  /* Inlinable functions always can change signature.  */
-  if (info->inlinable)
-    node->local.can_change_signature = true;
+  /* Type attributes can use parameter indices to describe them.  */
+  if (TYPE_ATTRIBUTES (TREE_TYPE (node->decl)))
+    node->local.can_change_signature = false;
   else
     {
-      /* Functions calling builtin_apply can not change signature.  */
-      for (e = node->callees; e; e = e->next_callee)
-	if (DECL_BUILT_IN (e->callee->decl)
-	    && DECL_BUILT_IN_CLASS (e->callee->decl) == BUILT_IN_NORMAL
-	    && DECL_FUNCTION_CODE (e->callee->decl) == BUILT_IN_APPLY_ARGS)
-	  break;
-      node->local.can_change_signature = !e;
+      /* Otherwise, inlinable functions always can change signature.  */
+      if (info->inlinable)
+	node->local.can_change_signature = true;
+      else
+	{
+	  /* Functions calling builtin_apply can not change signature.  */
+	  for (e = node->callees; e; e = e->next_callee)
+	    {
+	      tree cdecl = e->callee->decl;
+	      if (DECL_BUILT_IN (cdecl)
+		  && DECL_BUILT_IN_CLASS (cdecl) == BUILT_IN_NORMAL
+		  && (DECL_FUNCTION_CODE (cdecl) == BUILT_IN_APPLY_ARGS
+		      || DECL_FUNCTION_CODE (cdecl) == BUILT_IN_VA_START))
+		break;
+	    }
+	  node->local.can_change_signature = !e;
+	}
     }
   estimate_function_body_sizes (node, early);
 
Index: src/gcc/ipa-split.c
===================================================================
--- src.orig/gcc/ipa-split.c
+++ src/gcc/ipa-split.c
@@ -945,10 +945,10 @@  static void
 split_function (struct split_point *split_point)
 {
   VEC (tree, heap) *args_to_pass = NULL;
-  bitmap args_to_skip = BITMAP_ALLOC (NULL);
+  bitmap args_to_skip;
   tree parm;
   int num = 0;
-  struct cgraph_node *node;
+  struct cgraph_node *node, *cur_node = cgraph_get_node (current_function_decl);
   basic_block return_bb = find_return_bb ();
   basic_block call_bb;
   gimple_stmt_iterator gsi;
@@ -968,17 +968,30 @@  split_function (struct split_point *spli
       dump_split_point (dump_file, split_point);
     }
 
+  if (cur_node->local.can_change_signature)
+    args_to_skip = BITMAP_ALLOC (NULL);
+  else
+    args_to_skip = NULL;
+
   /* Collect the parameters of new function and args_to_skip bitmap.  */
   for (parm = DECL_ARGUMENTS (current_function_decl);
        parm; parm = DECL_CHAIN (parm), num++)
-    if (!is_gimple_reg (parm)
-	|| !gimple_default_def (cfun, parm)
-	|| !bitmap_bit_p (split_point->ssa_names_to_pass,
-			  SSA_NAME_VERSION (gimple_default_def (cfun, parm))))
+    if (args_to_skip
+	&& (!is_gimple_reg (parm)
+	    || !gimple_default_def (cfun, parm)
+	    || !bitmap_bit_p (split_point->ssa_names_to_pass,
+			      SSA_NAME_VERSION (gimple_default_def (cfun,
+								    parm)))))
       bitmap_set_bit (args_to_skip, num);
     else
       {
 	arg = gimple_default_def (cfun, parm);
+	if (!arg)
+	  {
+	    arg = make_ssa_name (parm, gimple_build_nop ());
+	    set_default_def (parm, arg);
+	  }
+
 	if (TYPE_MAIN_VARIANT (DECL_ARG_TYPE (parm))
 	    != TYPE_MAIN_VARIANT (TREE_TYPE (arg)))
 	  {
@@ -1081,9 +1094,7 @@  split_function (struct split_point *spli
 
   /* Now create the actual clone.  */
   rebuild_cgraph_edges ();
-  node = cgraph_function_versioning (cgraph_get_node (current_function_decl),
-				     NULL, NULL,
-				     args_to_skip,
+  node = cgraph_function_versioning (cur_node, NULL, NULL, args_to_skip,
 				     split_point->split_bbs,
 				     split_point->entry_bb, "part");
   /* For usual cloning it is enough to clear builtin only when signature
@@ -1094,7 +1105,7 @@  split_function (struct split_point *spli
       DECL_BUILT_IN_CLASS (node->decl) = NOT_BUILT_IN;
       DECL_FUNCTION_CODE (node->decl) = (enum built_in_function) 0;
     }
-  cgraph_node_remove_callees (cgraph_get_node (current_function_decl));
+  cgraph_node_remove_callees (cur_node);
   if (!split_part_return_p)
     TREE_THIS_VOLATILE (node->decl) = 1;
   if (dump_file)
Index: src/gcc/testsuite/gcc.c-torture/execute/pr49886.c
===================================================================
--- /dev/null
+++ src/gcc/testsuite/gcc.c-torture/execute/pr49886.c
@@ -0,0 +1,100 @@ 
+struct PMC {
+    unsigned flags;
+};
+
+typedef struct Pcc_cell
+{
+    struct PMC *p;
+    long bla;
+    long type;
+} Pcc_cell;
+
+int gi;
+int cond;
+
+extern void abort ();
+extern void never_ever(int interp, struct PMC *pmc)
+  __attribute__((noinline,noclone));
+
+void never_ever (int interp, struct PMC *pmc)
+{
+  abort ();
+}
+
+static void mark_cell(int * interp, Pcc_cell *c)
+  __attribute__((__nonnull__(1)));
+
+static void
+mark_cell(int * interp, Pcc_cell *c)
+{
+  if (!cond)
+    return;
+
+  if (c && c->type == 4 && c->p
+      && !(c->p->flags & (1<<18)))
+    never_ever(gi + 1, c->p);
+  if (c && c->type == 4 && c->p
+      && !(c->p->flags & (1<<17)))
+    never_ever(gi + 2, c->p);
+  if (c && c->type == 4 && c->p
+      && !(c->p->flags & (1<<16)))
+    never_ever(gi + 3, c->p);
+  if (c && c->type == 4 && c->p
+      && !(c->p->flags & (1<<15)))
+    never_ever(gi + 4, c->p);
+  if (c && c->type == 4 && c->p
+      && !(c->p->flags & (1<<14)))
+    never_ever(gi + 5, c->p);
+  if (c && c->type == 4 && c->p
+      && !(c->p->flags & (1<<13)))
+    never_ever(gi + 6, c->p);
+  if (c && c->type == 4 && c->p
+      && !(c->p->flags & (1<<12)))
+    never_ever(gi + 7, c->p);
+  if (c && c->type == 4 && c->p
+      && !(c->p->flags & (1<<11)))
+    never_ever(gi + 8, c->p);
+  if (c && c->type == 4 && c->p
+      && !(c->p->flags & (1<<10)))
+    never_ever(gi + 9, c->p);
+}
+
+static void
+foo(int * interp, Pcc_cell *c)
+{
+  mark_cell(interp, c);
+}
+
+static struct Pcc_cell *
+__attribute__((noinline,noclone))
+getnull(void)
+{
+  return (struct Pcc_cell *) 0;
+}
+
+
+int main()
+{
+  int i;
+
+  cond = 1;
+  for (i = 0; i < 100; i++)
+    foo (&gi, getnull ());
+  return 0;
+}
+
+
+void
+bar_1 (int * interp, Pcc_cell *c)
+{
+  c->bla += 1;
+  mark_cell(interp, c);
+}
+
+void
+bar_2 (int * interp, Pcc_cell *c)
+{
+  c->bla += 2;
+  mark_cell(interp, c);
+}
+