On Wed, Oct 22, 2025 at 3:55 PM H.J. Lu <[email protected]> wrote:
>
> On Thu, Sep 18, 2025 at 9:01 PM H.J. Lu <[email protected]> wrote:
> >
> > On Mon, Aug 25, 2025 at 5:59 AM H.J. Lu <[email protected]> wrote:
> > >
> > > On Fri, Jun 27, 2025 at 05:53:57AM +0800, H.J. Lu wrote:
> > > > Here is the v3 patch.  The difference from v2 is to use
> > > >
> > > >           if (MEM_P (src)
> > > >               && MEM_EXPR (src)
> > > >               && (TREE_CODE (get_base_address (MEM_EXPR (src)))
> > > >                   == PARM_DECL))
> > > >             continue;
> > > >
> > > > to check incoming arguments on stack.
> > > >
> > > > OK for master?
> > > >
> > > > Thanks.
> > > >
> > >
> > > I forgot to include the patch.  Here it is.  OK for master?
> > >
> > > Thanks.
> > >
> > > H.J.
> > > ---
> > > For targets, like x86, which define TARGET_PROMOTE_PROTOTYPES to return
> > > true, all integer arguments smaller than int are passed as int:
> > >
> > > [hjl@gnu-tgl-3 pr14907]$ cat x.c
> > > extern int baz (char c1);
> > >
> > > int
> > > foo (char c1)
> > > {
> > >   return baz (c1);
> > > }
> > > [hjl@gnu-tgl-3 pr14907]$ gcc -S -O2 -m32 x.c
> > > [hjl@gnu-tgl-3 pr14907]$ cat x.s
> > >         .file   "x.c"
> > >         .text
> > >         .p2align 4
> > >         .globl  foo
> > >         .type   foo, @function
> > > foo:
> > > .LFB0:
> > >         .cfi_startproc
> > >         movsbl  4(%esp), %eax
> > >         movl    %eax, 4(%esp)
> > >         jmp     baz
> > >         .cfi_endproc
> > > .LFE0:
> > >         .size   foo, .-foo
> > >         .ident  "GCC: (GNU) 14.2.1 20240912 (Red Hat 14.2.1-3)"
> > >         .section        .note.GNU-stack,"",@progbits
> > > [hjl@gnu-tgl-3 pr14907]$
> > >
> > > But integer promotion:
> > >
> > >         movsbl  4(%esp), %eax
> > >         movl    %eax, 4(%esp)
> > >
> > > isn't necessary if incoming arguments are copied to outgoing arguments
> > > directly.
> > >
> > > We can use the incoming argument value as the outgoing argument as if it
> > > has been promoted if
> > >
> > > 1. Caller and callee are not nested functions.
> > > 2. Caller and callee have the same incoming argument order.  Add a new
> > > target hook, TARGET_SAME_FUNCTION_ARGUMENT_ORDER_P, which returns true
> > > if caller and callee have the same incoming argument order.  If the
> > > incoming argument order of the caller is different from the incoming
> > > argument order of the callee since the same register may be used for
> > > different incoming arguments in caller and callee.  Copying from one
> > > incoming argument register in the caller to an outgoing argument may
> > > override another incoming argument register.
> > > 3. The incoming argument is unchanged before call expansion.
> > >
> > > Otherwise, using the incoming argument as the outgoing argument may change
> > > values of other incoming arguments or the wrong outgoing argument value
> > > may be used.
> > >
> > > If callee is a global function, we always properly extend the incoming
> > > small integer arguments in callee.  If callee is a local function, since
> > > DECL_ARG_TYPE has the original small integer type, we will extend the
> > > incoming small integer arguments in callee if needed.
> > >
> > > Tested on x86-64, x32 and i686.
> > >
> > > NB: I tried to elide all incoming argument copying for all types, not
> > > just integer arguments smaller than int.  But GCC was miscompiled which
> > > is related to function inlining.  There is
> > >
> > > foo
> > >   call baz
> > >
> > > bar
> > >   call foo
> > >
> > > when foo is inlined
> > >
> > > bar
> > >    call baz
> > >
> > > the incoming arguments, which aren't integer arguments smaller than int,
> > > for baz get the wrong values sometimes.
> > >
> > > gcc/
> > >
> > >         PR middle-end/14907
> > >         * calls.cc (arg_data): Add incoming_argument_value.
> > >         (precompute_register_parameters): Set args[i].value to
> > >         args[i].incoming_argument_value if not nullptr.
> > >         (get_incoming_argument_value): New function.
> > >         (initialize_argument_information): Set
> > >         args[i].incoming_argument_value with get_incoming_argument_value.
> > >         (store_one_arg): Set arg->value to arg->incoming_argument_value
> > >         if not nullptr.
> > >         * function.h (function): Add before_first_expand_call and
> > >         no_incoming_argument_value.
> > >         * target.def (same_incoming_argument_order_p): New target hook
> > >         for calls.
> > >         * config/i386/i386.cc (ix86_same_incoming_argument_order_p): New.
> > >         (TARGET_SAME_FUNCTION_ARGUMENT_ORDER_P): Likewise.
> > >         * doc/tm.texi: Regenerated.
> > >         * doc/tm.texi.in (TARGET_SAME_FUNCTION_ARGUMENT_ORDER_P): New 
> > > hook.
> > >
> > > gcc/testsuite/
> > >
> > >         PR middle-end/14907
> > >         * gcc.dg/elide-1a.c: New test.
> > >         * gcc.dg/elide-1b.c: Likewise.
> > >         * gcc.dg/elide-2a.c: Likewise.
> > >         * gcc.dg/elide-2b.c: Likewise.
> > >         * gcc.target/i386/pr14907-1.c: Likewise.
> > >         * gcc.target/i386/pr14907-2.c: Likewise.
> > >         * gcc.target/i386/pr14907-3.c: Likewise.
> > >         * gcc.target/i386/pr14907-4.c: Likewise.
> > >         * gcc.target/i386/pr14907-5.c: Likewise.
> > >         * gcc.target/i386/pr14907-6.c: Likewise.
> > >         * gcc.target/i386/pr14907-7a.c: Likewise.
> > >         * gcc.target/i386/pr14907-7b.c: Likewise.
> > >         * gcc.target/i386/pr14907-8a.c: Likewise.
> > >         * gcc.target/i386/pr14907-8b.c: Likewise.
> > >         * gcc.target/i386/pr14907-9a.c: Likewise.
> > >         * gcc.target/i386/pr14907-9b.c: Likewise.
> > >         * gcc.target/i386/pr14907-10a.c: Likewise.
> > >         * gcc.target/i386/pr14907-10b.c: Likewise.
> > >         * gcc.target/i386/pr14907-11.c: Likewise.
> > >         * gcc.target/i386/pr14907-12.c: Likewise.
> > >         * gcc.target/i386/pr14907-13.c: Likewise.
> > >         * gcc.target/i386/pr14907-14.c: Likewise.
> > >         * gcc.target/i386/pr14907-15.c: Likewise.
> > >         * gcc.target/i386/pr14907-16.c: Likewise.
> > >         * gcc.target/i386/pr14907-17.c: Likewise.
> > >         * gcc.target/i386/pr14907-18.c: Likewise.
> > >         * gcc.target/i386/pr14907-19.c: Likewise.
> > >         * gcc.target/i386/pr14907-20a.c: Likewise.
> > >         * gcc.target/i386/pr14907-20b.c: Likewise.
> > >         * gcc.target/i386/pr14907-21.c: Likewise.
> > >         * gcc.target/i386/pr14907-22.c: Likewise.
> > >         * gcc.target/i386/pr14907-23.c: Likewise.
> > >
> > > Signed-off-by: H.J. Lu <[email protected]>
> > > ---
> > >  gcc/calls.cc                                | 212 ++++++++++++++++++--
> > >  gcc/config/i386/i386.cc                     |  16 ++
> > >  gcc/doc/tm.texi                             |   6 +
> > >  gcc/doc/tm.texi.in                          |   2 +
> > >  gcc/function.h                              |   7 +
> > >  gcc/target.def                              |   8 +
> > >  gcc/testsuite/gcc.dg/elide-1a.c             |  10 +
> > >  gcc/testsuite/gcc.dg/elide-1b.c             |  26 +++
> > >  gcc/testsuite/gcc.dg/elide-2a.c             |  12 ++
> > >  gcc/testsuite/gcc.dg/elide-2b.c             |  30 +++
> > >  gcc/testsuite/gcc.target/i386/pr14907-1.c   |  21 ++
> > >  gcc/testsuite/gcc.target/i386/pr14907-10a.c |  24 +++
> > >  gcc/testsuite/gcc.target/i386/pr14907-10b.c |  20 ++
> > >  gcc/testsuite/gcc.target/i386/pr14907-11.c  |  12 ++
> > >  gcc/testsuite/gcc.target/i386/pr14907-12.c  |  17 ++
> > >  gcc/testsuite/gcc.target/i386/pr14907-13.c  |  12 ++
> > >  gcc/testsuite/gcc.target/i386/pr14907-14.c  |  17 ++
> > >  gcc/testsuite/gcc.target/i386/pr14907-15.c  |  26 +++
> > >  gcc/testsuite/gcc.target/i386/pr14907-16.c  |  24 +++
> > >  gcc/testsuite/gcc.target/i386/pr14907-17.c  |  28 +++
> > >  gcc/testsuite/gcc.target/i386/pr14907-18.c  |  24 +++
> > >  gcc/testsuite/gcc.target/i386/pr14907-19.c  |  26 +++
> > >  gcc/testsuite/gcc.target/i386/pr14907-2.c   |  21 ++
> > >  gcc/testsuite/gcc.target/i386/pr14907-20a.c |  27 +++
> > >  gcc/testsuite/gcc.target/i386/pr14907-20b.c |  23 +++
> > >  gcc/testsuite/gcc.target/i386/pr14907-21.c  |  28 +++
> > >  gcc/testsuite/gcc.target/i386/pr14907-22.c  |  28 +++
> > >  gcc/testsuite/gcc.target/i386/pr14907-23.c  |  11 +
> > >  gcc/testsuite/gcc.target/i386/pr14907-3.c   |  21 ++
> > >  gcc/testsuite/gcc.target/i386/pr14907-4.c   |  21 ++
> > >  gcc/testsuite/gcc.target/i386/pr14907-5.c   |  21 ++
> > >  gcc/testsuite/gcc.target/i386/pr14907-6.c   |  21 ++
> > >  gcc/testsuite/gcc.target/i386/pr14907-7a.c  |  22 ++
> > >  gcc/testsuite/gcc.target/i386/pr14907-7b.c  |  17 ++
> > >  gcc/testsuite/gcc.target/i386/pr14907-8a.c  |  22 ++
> > >  gcc/testsuite/gcc.target/i386/pr14907-8b.c  |  17 ++
> > >  gcc/testsuite/gcc.target/i386/pr14907-9a.c  |  24 +++
> > >  gcc/testsuite/gcc.target/i386/pr14907-9b.c  |  19 ++
> > >  38 files changed, 906 insertions(+), 17 deletions(-)
> > >  create mode 100644 gcc/testsuite/gcc.dg/elide-1a.c
> > >  create mode 100644 gcc/testsuite/gcc.dg/elide-1b.c
> > >  create mode 100644 gcc/testsuite/gcc.dg/elide-2a.c
> > >  create mode 100644 gcc/testsuite/gcc.dg/elide-2b.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-1.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-10a.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-10b.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-11.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-12.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-13.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-14.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-15.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-16.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-17.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-18.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-19.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-2.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-20a.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-20b.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-21.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-22.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-23.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-3.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-4.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-5.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-6.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-7a.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-7b.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-8a.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-8b.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-9a.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-9b.c
> > >
> > > diff --git a/gcc/calls.cc b/gcc/calls.cc
> > > index bb8a6d09f82..c78337cf4b0 100644
> > > --- a/gcc/calls.cc
> > > +++ b/gcc/calls.cc
> > > @@ -75,6 +75,8 @@ struct arg_data
> > >    machine_mode mode;
> > >    /* Current RTL value for argument, or 0 if it isn't precomputed.  */
> > >    rtx value;
> > > +  /* RTL value from the incoming argument, or 0 if it isn't available.  
> > > */
> > > +  rtx incoming_argument_value;
> > >    /* Initially-compute RTL value for argument; only for const functions. 
> > >  */
> > >    rtx initial_value;
> > >    /* Register to pass this argument in, 0 if passed on stack, or an
> > > @@ -1019,7 +1021,10 @@ precompute_register_parameters (int num_actuals, 
> > > struct arg_data *args,
> > >         if (args[i].value == 0)
> > >           {
> > >             push_temp_slots ();
> > > -           args[i].value = expand_normal (args[i].tree_value);
> > > +           if (args[i].incoming_argument_value)
> > > +             args[i].value = args[i].incoming_argument_value;
> > > +           else
> > > +             args[i].value = expand_normal (args[i].tree_value);
> > >             preserve_temp_slots (args[i].value);
> > >             pop_temp_slots ();
> > >           }
> > > @@ -1288,6 +1293,152 @@ maybe_complain_about_tail_call (tree call_expr, 
> > > const char *reason)
> > >      }
> > >  }
> > >
> > > +/* Return the value of the incoming argument, INCOMING_ARG if ARG, which
> > > +   is an outgoing argument, is copied directly from the incoming 
> > > argument.
> > > +   Otherwise return nullptr.  */
> > > +
> > > +static rtx
> > > +get_incoming_argument_value (const_tree fndecl, tree incoming_arg,
> > > +                            tree arg)
> > > +{
> > > +  if (cfun->no_incoming_argument_value)
> > > +    return nullptr;
> > > +
> > > +  /* Skip nested callee and caller.  */
> > > +  if ((fndecl && DECL_STATIC_CHAIN (fndecl))
> > > +      || DECL_STATIC_CHAIN (current_function_decl))
> > > +    {
> > > +no_incoming_argument_value:
> > > +      cfun->no_incoming_argument_value = 1;
> > > +      return nullptr;
> > > +    }
> > > +
> > > +  /* Skip if the incoming argument order of the caller is different from
> > > +     the incoming argument order of the callee since the same register
> > > +     may be used for different incoming arguments in caller and callee.
> > > +     Copying from one incoming argument in the caller to an outgoing
> > > +     argument may override another incoming argument.  */
> > > +  if (!targetm.calls.same_incoming_argument_order_p (fndecl))
> > > +    goto no_incoming_argument_value;
> > > +
> > > +  rtx_insn *before_call_expand = get_last_insn ();
> > > +  if (cfun->before_first_expand_call == nullptr)
> > > +    {
> > > +      cfun->before_first_expand_call = before_call_expand;
> > > +
> > > +      /* Scan from the function start.  */
> > > +      rtx_insn *insn, *start = get_insns ();
> > > +      for (insn = start; insn; insn = NEXT_INSN (insn))
> > > +       {
> > > +         /* Skip argument assignments before NOTE_INSN_FUNCTION_BEG.  */
> > > +         if (NOTE_P (insn)
> > > +             && NOTE_KIND (insn) == NOTE_INSN_FUNCTION_BEG)
> > > +           {
> > > +             insn = NEXT_INSN (insn);
> > > +             break;
> > > +           }
> > > +
> > > +         if (!NONDEBUG_INSN_P (insn))
> > > +           continue;
> > > +
> > > +         rtx set = single_set (insn);
> > > +         /* Don't know if the incoming arguments are changed.  */
> > > +         if (!set)
> > > +           goto no_incoming_argument_value;
> > > +
> > > +         rtx dest = SET_DEST (set);
> > > +         /* Don't know if the incoming arguments are changed.  */
> > > +         if (!REG_P (dest))
> > > +           goto no_incoming_argument_value;
> > > +
> > > +         rtx src = SET_SRC (set);
> > > +         if (REG_P (src)
> > > +             && REG_ATTRS (dest) == REG_ATTRS (src))
> > > +           continue;
> > > +
> > > +         /* Function argument source is OK.  */
> > > +         if (MEM_P (src)
> > > +             && MEM_EXPR (src)
> > > +             && (TREE_CODE (get_base_address (MEM_EXPR (src)))
> > > +                 == PARM_DECL))
> > > +           continue;
> > > +
> > > +
> > > +         /* Don't know if the incoming arguments are changed.  */
> > > +         goto no_incoming_argument_value;
> > > +       }
> > > +
> > > +      /* Check if the incoming argument may be changed after
> > > +        NOTE_INSN_FUNCTION_BEG.  */
> > > +      for (; insn; insn = NEXT_INSN (insn))
> > > +       {
> > > +         if (!NONDEBUG_INSN_P (insn))
> > > +           continue;
> > > +
> > > +         rtx set = single_set (insn);
> > > +         if (set)
> > > +           {
> > > +             rtx dest = SET_DEST (set);
> > > +             if (REG_P (dest) && !HARD_REGISTER_P (dest))
> > > +               {
> > > +                 /* Skip assignment from the hidden argument of the
> > > +                    return value.  */
> > > +                 tree result = DECL_RESULT (current_function_decl);
> > > +                 if (DECL_RTL_SET_P (result))
> > > +                   {
> > > +                     rtx result_rtl = DECL_RTL (result);
> > > +                     if (result_rtl && MEM_P (result_rtl))
> > > +                       {
> > > +                         rtx src = SET_SRC (set);
> > > +                         result_rtl = XEXP (result_rtl, 0);
> > > +                         if (rtx_equal_p (src, result_rtl))
> > > +                           continue;
> > > +                       }
> > > +                   }
> > > +               }
> > > +           }
> > > +
> > > +         /* Don't use the incoming argument if there are any
> > > +            instructions which may change incoming arguments.  */
> > > +         goto no_incoming_argument_value;
> > > +       }
> > > +    }
> > > +  /* Incoming arguments aren't preserved after the first call.  */
> > > +  else if (cfun->before_first_expand_call != before_call_expand)
> > > +    goto no_incoming_argument_value;
> > > +
> > > +  if (TREE_CODE (arg) != SSA_NAME)
> > > +    return nullptr;
> > > +
> > > +  if (!SSA_NAME_IS_DEFAULT_DEF (arg))
> > > +    return nullptr;
> > > +
> > > +  tree var = SSA_NAME_VAR (arg);
> > > +  if (TREE_CODE (var) != PARM_DECL)
> > > +    return nullptr;
> > > +  tree arg_type = TREE_TYPE (arg);
> > > +  if (TYPE_MODE (arg_type) != TYPE_MODE (DECL_ARG_TYPE (var)))
> > > +    return nullptr;
> > > +
> > > +  /* Return nullptr if the outgoing argument isn't copied directly from
> > > +     the corresponding incoming argument.  In the case, the incoming
> > > +     argument order of the caller is different from the incoming argument
> > > +     of the callee and the same argument slot maps to different arguments
> > > +     in caller and callee.  */
> > > +  if (var != incoming_arg)
> > > +    return nullptr;
> > > +
> > > +  /* Return the small integer incoming argument as int for the outgoing
> > > +     argument without extension.  If callee is a global function, we
> > > +     always properly extend the incoming small integer arguments in
> > > +     callee.  If callee is a local function, since DECL_ARG_TYPE has
> > > +     the original small integer type, we will extend the incoming small
> > > +     integer arguments in callee if needed.  */
> > > +  rtx incoming_rtl = shallow_copy_rtx (DECL_INCOMING_RTL (var));
> > > +  PUT_MODE (incoming_rtl, SImode);
> > > +  return incoming_rtl;
> > > +}
> > > +
> > >  /* Fill in ARGS_SIZE and ARGS array based on the parameters found in
> > >     CALL_EXPR EXP.
> > >
> > > @@ -1349,6 +1500,7 @@ initialize_argument_information (int num_actuals 
> > > ATTRIBUTE_UNUSED,
> > >    /* In this loop, we consider args in the order they are written.
> > >       We fill up ARGS from the back.  */
> > >
> > > +  int implicit_argument = 0;
> > >    i = num_actuals - 1;
> > >    {
> > >      int j = i;
> > > @@ -1359,6 +1511,7 @@ initialize_argument_information (int num_actuals 
> > > ATTRIBUTE_UNUSED,
> > >        {
> > >         args[j].tree_value = struct_value_addr_value;
> > >         j--;
> > > +       implicit_argument++;
> > >        }
> > >      argpos = 0;
> > >      FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
> > > @@ -1387,19 +1540,38 @@ initialize_argument_information (int num_actuals 
> > > ATTRIBUTE_UNUSED,
> > >                                         ? TREE_TYPE (fndecl)
> > >                                         : fntype);
> > >
> > > +  tree incoming_arg = nullptr;
> > > +
> > >    /* I counts args in order (to be) pushed; ARGPOS counts in order 
> > > written.  */
> > >    for (argpos = 0; argpos < num_actuals; i--, argpos++)
> > >      {
> > >        tree type = TREE_TYPE (args[i].tree_value);
> > >        int unsignedp;
> > >
> > > +      if (argpos >= implicit_argument)
> > > +       {
> > > +         if (incoming_arg == nullptr)
> > > +           incoming_arg = DECL_ARGUMENTS (current_function_decl);
> > > +         else
> > > +           incoming_arg = DECL_CHAIN (incoming_arg);
> > > +       }
> > > +
> > >        /* Replace erroneous argument with constant zero.  */
> > >        if (type == error_mark_node || !COMPLETE_TYPE_P (type))
> > >         args[i].tree_value = integer_zero_node, type = integer_type_node;
> > > -      else if (promote_p
> > > -              && INTEGRAL_TYPE_P (type)
> > > -              && TYPE_PRECISION (type) < TYPE_PRECISION 
> > > (integer_type_node))
> > > -       type = integer_type_node;
> > > +      else
> > > +       {
> > > +         if (promote_p
> > > +             && INTEGRAL_TYPE_P (type)
> > > +             && (TYPE_PRECISION (type)
> > > +                 < TYPE_PRECISION (integer_type_node)))
> > > +           {
> > > +             type = integer_type_node;
> > > +             args[i].incoming_argument_value
> > > +               = get_incoming_argument_value (fndecl, incoming_arg,
> > > +                                              args[i].tree_value);
> > > +           }
> > > +       }
> > >
> > >        /* If TYPE is a transparent union or record, pass things the way
> > >          we would pass the first field of the union or record.  We have
> > > @@ -5073,18 +5245,24 @@ store_one_arg (struct arg_data *arg, rtx 
> > > argblock, int flags,
> > >        if (arg->pass_on_stack)
> > >         stack_arg_under_construction++;
> > >
> > > -      arg->value = expand_expr (pval,
> > > -                               (partial
> > > -                                || TYPE_MODE (TREE_TYPE (pval)) != 
> > > arg->mode)
> > > -                               ? NULL_RTX : arg->stack,
> > > -                               VOIDmode, EXPAND_STACK_PARM);
> > > -
> > > -      /* If we are promoting object (or for any other reason) the mode
> > > -        doesn't agree, convert the mode.  */
> > > -
> > > -      if (arg->mode != TYPE_MODE (TREE_TYPE (pval)))
> > > -       arg->value = convert_modes (arg->mode, TYPE_MODE (TREE_TYPE 
> > > (pval)),
> > > -                                   arg->value, arg->unsignedp);
> > > +      if (arg->incoming_argument_value)
> > > +       arg->value = arg->incoming_argument_value;
> > > +      else
> > > +       {
> > > +         arg->value = expand_expr (pval,
> > > +                                   (partial
> > > +                                    || TYPE_MODE (TREE_TYPE (pval)) != 
> > > arg->mode)
> > > +                                   ? NULL_RTX : arg->stack,
> > > +                                   VOIDmode, EXPAND_STACK_PARM);
> > > +
> > > +         /* If we are promoting object (or for any other reason) the mode
> > > +            doesn't agree, convert the mode.  */
> > > +
> > > +         if (arg->mode != TYPE_MODE (TREE_TYPE (pval)))
> > > +           arg->value = convert_modes (arg->mode,
> > > +                                       TYPE_MODE (TREE_TYPE (pval)),
> > > +                                       arg->value, arg->unsignedp);
> > > +       }
> > >
> > >        if (arg->pass_on_stack)
> > >         stack_arg_under_construction--;
> > > diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
> > > index 55c9b16dd38..d0f1a7ef80e 100644
> > > --- a/gcc/config/i386/i386.cc
> > > +++ b/gcc/config/i386/i386.cc
> > > @@ -4497,6 +4497,19 @@ ix86_return_in_memory (const_tree type, const_tree 
> > > fntype ATTRIBUTE_UNUSED)
> > >      }
> > >  }
> > >
> > > +/* Implement TARGET_SAME_INCOMING_ARGUMENT_ORDER_P.  */
> > > +
> > > +static bool
> > > +ix86_same_incoming_argument_order_p (const_tree fndecl)
> > > +{
> > > +  /* 64-bit SYSV ABI and 64-bit MS ABI have different argument orders.
> > > +     Copying one incoming argument register to another outgoing argument
> > > +     register may override the other incoming argument register.  */
> > > +  return (!TARGET_64BIT
> > > +         || (ix86_function_abi (current_function_decl)
> > > +             == ix86_function_abi (fndecl)));
> > > +}
> > > +
> > >  /* Implement TARGET_PUSH_ARGUMENT.  */
> > >
> > >  static bool
> > > @@ -27819,6 +27832,9 @@ static const scoped_attribute_specs *const 
> > > ix86_attribute_table[] =
> > >  #define TARGET_CXX_ADJUST_CDTOR_CALLABI_FNTYPE 
> > > ix86_cxx_adjust_cdtor_callabi_fntype
> > >  #undef TARGET_PROMOTE_PROTOTYPES
> > >  #define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
> > > +#undef TARGET_SAME_INCOMING_ARGUMENT_ORDER_P
> > > +#define TARGET_SAME_INCOMING_ARGUMENT_ORDER_P \
> > > +  ix86_same_incoming_argument_order_p
> > >  #undef TARGET_PUSH_ARGUMENT
> > >  #define TARGET_PUSH_ARGUMENT ix86_push_argument
> > >  #undef TARGET_SETUP_INCOMING_VARARGS
> > > diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
> > > index 4c338c382ad..0038b718ab9 100644
> > > --- a/gcc/doc/tm.texi
> > > +++ b/gcc/doc/tm.texi
> > > @@ -4057,6 +4057,12 @@ cases of mismatch, it also makes for better code 
> > > on certain machines.
> > >  The default is to not promote prototypes.
> > >  @end deftypefn
> > >
> > > +@deftypefn {Target Hook} bool TARGET_SAME_INCOMING_ARGUMENT_ORDER_P 
> > > (const_tree @var{fndecl})
> > > +This target hook returns @code{true} if the incoming argument order of
> > > +the caller is the same as the incoming argument order of the calee
> > > +@var{fndecl}.  The default is to return @code{true}.
> > > +@end deftypefn
> > > +
> > >  @deftypefn {Target Hook} bool TARGET_PUSH_ARGUMENT (unsigned int 
> > > @var{npush})
> > >  This target hook returns @code{true} if push instructions will be
> > >  used to pass outgoing arguments.  When the push instruction usage is
> > > diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
> > > index 12b8ed660a0..5f4e78ed177 100644
> > > --- a/gcc/doc/tm.texi.in
> > > +++ b/gcc/doc/tm.texi.in
> > > @@ -3210,6 +3210,8 @@ control passing certain arguments in registers.
> > >
> > >  @hook TARGET_PROMOTE_PROTOTYPES
> > >
> > > +@hook TARGET_SAME_INCOMING_ARGUMENT_ORDER_P
> > > +
> > >  @hook TARGET_PUSH_ARGUMENT
> > >
> > >  @defmac PUSH_ARGS_REVERSED
> > > diff --git a/gcc/function.h b/gcc/function.h
> > > index 370629f4de2..c7e5c171f64 100644
> > > --- a/gcc/function.h
> > > +++ b/gcc/function.h
> > > @@ -346,6 +346,9 @@ struct GTY(()) function {
> > >       a string describing the reason for failure.  */
> > >    const char * GTY((skip)) cannot_be_copied_reason;
> > >
> > > +  /* The instruction before the first call expansion.  */
> > > +  rtx_insn *before_first_expand_call;
> > > +
> > >    /* Last assigned dependence info clique.  */
> > >    unsigned short last_clique;
> > >
> > > @@ -451,6 +454,10 @@ struct GTY(()) function {
> > >
> > >    /* Nonzero if reload will have to split basic blocks.  */
> > >    unsigned int split_basic_blocks_after_reload : 1;
> > > +
> > > +  /* Nonzero if the incoming argument can't be used as the outgoing
> > > +     argument.  */
> > > +  unsigned int no_incoming_argument_value : 1;
> > >  };
> > >
> > >  /* Add the decl D to the local_decls list of FUN.  */
> > > diff --git a/gcc/target.def b/gcc/target.def
> > > index 5dd8f253ef6..e0e87bed79e 100644
> > > --- a/gcc/target.def
> > > +++ b/gcc/target.def
> > > @@ -4857,6 +4857,14 @@ The default is to not promote prototypes.",
> > >   bool, (const_tree fntype),
> > >   hook_bool_const_tree_false)
> > >
> > > +DEFHOOK
> > > +(same_incoming_argument_order_p,
> > > + "This target hook returns @code{true} if the incoming argument order 
> > > of\n\
> > > +the caller is the same as the incoming argument order of the calee\n\
> > > +@var{fndecl}.  The default is to return @code{true}.",
> > > + bool, (const_tree fndecl),
> > > + hook_bool_const_tree_true)
> > > +
> > >  DEFHOOK
> > >  (struct_value_rtx,
> > >   "This target hook should return the location of the structure value\n\
> > > diff --git a/gcc/testsuite/gcc.dg/elide-1a.c 
> > > b/gcc/testsuite/gcc.dg/elide-1a.c
> > > new file mode 100644
> > > index 00000000000..282ac98d956
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.dg/elide-1a.c
> > > @@ -0,0 +1,10 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2" } */
> > > +
> > > +extern int baz (char, char);
> > > +
> > > +int
> > > +foo (char c1, char c2)
> > > +{
> > > +  return baz (c2, c1) + 1;
> > > +}
> > > diff --git a/gcc/testsuite/gcc.dg/elide-1b.c 
> > > b/gcc/testsuite/gcc.dg/elide-1b.c
> > > new file mode 100644
> > > index 00000000000..034071974d1
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.dg/elide-1b.c
> > > @@ -0,0 +1,26 @@
> > > +/* { dg-do run } */
> > > +/* { dg-options "-O2" } */
> > > +/* { dg-additional-sources elide-1a.c } */
> > > +
> > > +extern int foo (int, int);
> > > +
> > > +/* Verify that arguments aren't elided.  */
> > > +
> > > +int
> > > +baz (int c1, int c2)
> > > +{
> > > +  if (c1 != 3)
> > > +    __builtin_abort ();
> > > +  if (c2 != -1)
> > > +    __builtin_abort ();
> > > +
> > > +  return c1 - c2;
> > > +}
> > > +
> > > +int
> > > +main (void)
> > > +{
> > > +  if (foo (-1, 3) != 5)
> > > +    __builtin_abort ();
> > > +  return 0;
> > > +}
> > > diff --git a/gcc/testsuite/gcc.dg/elide-2a.c 
> > > b/gcc/testsuite/gcc.dg/elide-2a.c
> > > new file mode 100644
> > > index 00000000000..b2b63d1199e
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.dg/elide-2a.c
> > > @@ -0,0 +1,12 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2" } */
> > > +
> > > +extern int baz1 (char, char);
> > > +extern void baz2 (char, char);
> > > +
> > > +int
> > > +foo (char c1, char c2)
> > > +{
> > > +  baz2 (c1, c2);
> > > +  return baz1 (c1, c2);
> > > +}
> > > diff --git a/gcc/testsuite/gcc.dg/elide-2b.c 
> > > b/gcc/testsuite/gcc.dg/elide-2b.c
> > > new file mode 100644
> > > index 00000000000..fc411863f30
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.dg/elide-2b.c
> > > @@ -0,0 +1,30 @@
> > > +/* { dg-do run } */
> > > +/* { dg-options "-O2" } */
> > > +/* { dg-additional-sources elide-2a.c } */
> > > +
> > > +extern int foo (int, int);
> > > +
> > > +int
> > > +baz1 (int c1, int c2)
> > > +{
> > > +  return c2 + c1;
> > > +}
> > > +
> > > +/* Verify that arguments aren't elided.  */
> > > +
> > > +void
> > > +baz2 (int c1, int c2)
> > > +{
> > > +  if (c1 != -1)
> > > +    __builtin_abort ();
> > > +  if (c2 != 3)
> > > +    __builtin_abort ();
> > > +}
> > > +
> > > +int
> > > +main (void)
> > > +{
> > > +  if (foo (-1, 3) != 2)
> > > +    __builtin_abort ();
> > > +  return 0;
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-1.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-1.c
> > > new file mode 100644
> > > index 00000000000..231819ed675
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-1.c
> > > @@ -0,0 +1,21 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2 -g0" } */
> > > +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
> > > +/* { dg-final { check-function-bodies "x86*" "" "" { target *-*-linux* 
> > > *-*-gnu* } {^\t?\.} } } */
> > > +
> > > +/*
> > > +x86*foo:
> > > +x86*.LFB0:
> > > +x86*   .cfi_startproc
> > > +x86*   jmp     baz
> > > +x86*   .cfi_endproc
> > > +x86*...
> > > +*/
> > > +
> > > +extern int baz (char);
> > > +
> > > +int
> > > +foo (char c1)
> > > +{
> > > +  return baz (c1);
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-10a.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-10a.c
> > > new file mode 100644
> > > index 00000000000..dfe401bf1ef
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-10a.c
> > > @@ -0,0 +1,24 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2 -g0" } */
> > > +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
> > > +/* { dg-final { check-function-bodies "x64*" "" "" { target { { 
> > > *-*-linux* *-*-gnu* } && { ! ia32 } } } {^\t?\.} } } */
> > > +
> > > +/*
> > > +x64*foo:
> > > +x64*.LFB0:
> > > +x64*...
> > > +x64*   movsbl  %dil, %eax
> > > +x64*...
> > > +x64*   movsbl  %sil, %edi
> > > +x64*   movl    %eax, %esi
> > > +x64*   call    baz
> > > +x64*...
> > > +*/
> > > +
> > > +extern int baz (char, char);
> > > +
> > > +int
> > > +foo (char c1, char c2)
> > > +{
> > > +  return baz (c2, c1) + 1;
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-10b.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-10b.c
> > > new file mode 100644
> > > index 00000000000..d2c5fbd7f19
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-10b.c
> > > @@ -0,0 +1,20 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2 -g0" } */
> > > +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
> > > +/* { dg-final { check-function-bodies "ia32*" "" "" { target { { 
> > > *-*-linux* *-*-gnu* } && ia32 } } {^\t?\.} } } */
> > > +
> > > +/*
> > > +ia32*foo:
> > > +ia32*.LFB0:
> > > +ia32*...
> > > +ia32*  movsbl  24\(%esp\), %eax
> > > +ia32*  pushl   %eax
> > > +ia32*...
> > > +ia32*  movsbl  32\(%esp\), %eax
> > > +ia32*  pushl   %eax
> > > +ia32*...
> > > +ia32*  call    baz
> > > +ia32*...
> > > +*/
> > > +
> > > +#include "pr14907-10a.c"
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-11.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-11.c
> > > new file mode 100644
> > > index 00000000000..12ac165c298
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-11.c
> > > @@ -0,0 +1,12 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2" } */
> > > +
> > > +extern int baz (char, char);
> > > +
> > > +int
> > > +foo (char c1, char c2)
> > > +{
> > > +  return baz (c1, c2) + 1;
> > > +}
> > > +
> > > +/* { dg-final { scan-assembler-not "movsbl" } } */
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-12.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-12.c
> > > new file mode 100644
> > > index 00000000000..6cda72ef3a2
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-12.c
> > > @@ -0,0 +1,17 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2" } */
> > > +
> > > +struct s
> > > +{
> > > +  char c[20];
> > > +};
> > > +
> > > +extern struct s baz (char, char);
> > > +
> > > +struct s
> > > +foo (char c1, char c2)
> > > +{
> > > +  return baz (c1, c2);
> > > +}
> > > +
> > > +/* { dg-final { scan-assembler-not "movsbl" } } */
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-13.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-13.c
> > > new file mode 100644
> > > index 00000000000..b4130fdcb57
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-13.c
> > > @@ -0,0 +1,12 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2" } */
> > > +
> > > +extern int baz (char, char, ...);
> > > +
> > > +int
> > > +foo (char c1, char c2)
> > > +{
> > > +  return baz (c1, c2, 0, 0, 0, 1);
> > > +}
> > > +
> > > +/* { dg-final { scan-assembler-not "movsbl" } } */
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-14.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-14.c
> > > new file mode 100644
> > > index 00000000000..9b8d7a7607d
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-14.c
> > > @@ -0,0 +1,17 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2" } */
> > > +
> > > +struct s
> > > +{
> > > +  char c[20];
> > > +};
> > > +
> > > +extern struct s baz (char, char, ...);
> > > +
> > > +struct s
> > > +foo (char c1, char c2)
> > > +{
> > > +  return baz (c1, c2, 0, 1);
> > > +}
> > > +
> > > +/* { dg-final { scan-assembler-not "movsbl" } } */
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-15.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-15.c
> > > new file mode 100644
> > > index 00000000000..08bc4ea9ac8
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-15.c
> > > @@ -0,0 +1,26 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2 -g0" } */
> > > +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
> > > +/* { dg-final { check-function-bodies "x64*" "" "" { target { { 
> > > *-*-linux* *-*-gnu* } && { ! ia32 } } } {^\t?\.} } } */
> > > +
> > > +/*
> > > +x64*foo:
> > > +x64*.LFB1:
> > > +x64*   .cfi_startproc
> > > +x64*   jmp     baz
> > > +x64*   .cfi_endproc
> > > +x64*...
> > > +*/
> > > +
> > > + __attribute__ ((noinline))
> > > +static int
> > > +baz (char c1)
> > > +{
> > > +  return c1;
> > > +}
> > > +
> > > +int
> > > +foo (char c1)
> > > +{
> > > +  return baz (c1);
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-16.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-16.c
> > > new file mode 100644
> > > index 00000000000..48c255ffb20
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-16.c
> > > @@ -0,0 +1,24 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2 -g0" } */
> > > +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
> > > +/* { dg-final { check-function-bodies "x64*" "" "" { target { { 
> > > *-*-linux* *-*-gnu* } && { ! ia32 } } } {^\t?\.} } } */
> > > +
> > > +/*
> > > +x64*foo:
> > > +x64*.LFB0:
> > > +x64*   .cfi_startproc
> > > +x64*   andl    \$1, %edi
> > > +x64*   jmp     baz
> > > +x64*   .cfi_endproc
> > > +x64*...
> > > +*/
> > > +
> > > +#include <stdbool.h>
> > > +
> > > +extern int baz (bool);
> > > +
> > > +int
> > > +foo (int c1)
> > > +{
> > > +  return baz (c1 & 0x1);
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-17.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-17.c
> > > new file mode 100644
> > > index 00000000000..079cb4475a2
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-17.c
> > > @@ -0,0 +1,28 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2 -g0" } */
> > > +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
> > > +/* { dg-final { check-function-bodies "x64*" "" "" { target { { 
> > > *-*-linux* *-*-gnu* } && { ! ia32 } } } {^\t?\.} } } */
> > > +
> > > +/*
> > > +x64*foo:
> > > +x64*.LFB0:
> > > +x64*   .cfi_startproc
> > > +x64*   movl    %edi, %eax
> > > +x64*   movl    base\(%rip\), %edi
> > > +x64*   movsbl  %sil, %esi
> > > +x64*   movsbl  %al, %edi
> > > +x64*   jmp     baz
> > > +x64*   .cfi_endproc
> > > +x64*...
> > > +*/
> > > +
> > > +extern int baz (char, char);
> > > +
> > > +extern int base;
> > > +
> > > +int
> > > +foo (char c1, char c2)
> > > +{
> > > +  asm volatile ("": : "D" (base));
> > > +  return baz (c1, c2);
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-18.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-18.c
> > > new file mode 100644
> > > index 00000000000..5d8eadfce2c
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-18.c
> > > @@ -0,0 +1,24 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2 -g0" } */
> > > +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
> > > +/* { dg-final { check-function-bodies "x86*" "" "" { target { *-*-linux* 
> > > *-*-gnu* } } {^\t?\.} } } */
> > > +
> > > +/*
> > > +x86*foo:
> > > +x86*.LFB0:
> > > +x86*...
> > > +x86*   call    baz2
> > > +x86*...
> > > +x86*   jmp     baz1
> > > +x86*...
> > > +*/
> > > +
> > > +extern int baz1 (char, char);
> > > +extern void baz2 (char, char);
> > > +
> > > +int
> > > +foo (char c1, char c2)
> > > +{
> > > +  baz2 (c1, c2);
> > > +  return baz1 (c1, c2);
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-19.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-19.c
> > > new file mode 100644
> > > index 00000000000..07712e5da20
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-19.c
> > > @@ -0,0 +1,26 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2 -g0" } */
> > > +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
> > > +/* { dg-final { check-function-bodies "ia32*" "" "" { target { { 
> > > *-*-linux* *-*-gnu* } && { ia32 } } } {^\t?\.} } } */
> > > +
> > > +/*
> > > +ia32*foo:
> > > +ia32*.LFB0:
> > > +ia32*  .cfi_startproc
> > > +ia32*  movl    8\(%esp\), %edx
> > > +ia32*  movl    4\(%esp\), %eax
> > > +ia32*  jmp     baz
> > > +ia32*  .cfi_endproc
> > > +ia32*...
> > > +*/
> > > +
> > > +__attribute__ ((regparm (2)))
> > > +extern int baz (char, char);
> > > +
> > > +int
> > > +foo (char c1, char c2)
> > > +{
> > > +  return baz (c1, c2);
> > > +}
> > > +
> > > +/* { dg-final { scan-assembler-not "movsbl" } } */
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-2.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-2.c
> > > new file mode 100644
> > > index 00000000000..5da7b029279
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-2.c
> > > @@ -0,0 +1,21 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2 -g0" } */
> > > +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
> > > +/* { dg-final { check-function-bodies "x86*" "" "" { target *-*-linux* 
> > > *-*-gnu* } {^\t?\.} } } */
> > > +
> > > +/*
> > > +x86*foo:
> > > +x86*.LFB0:
> > > +x86*   .cfi_startproc
> > > +x86*   jmp     baz
> > > +x86*   .cfi_endproc
> > > +x86*...
> > > +*/
> > > +
> > > +extern int baz (int, int, int, int, int, int, char, char);
> > > +
> > > +int
> > > +foo (int a1, int a2, int a3, int a4, int a5, int a6, char c1, char c2)
> > > +{
> > > +  return baz (a1, a2, a3, a4, a5, a6, c1, c2);
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-20a.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-20a.c
> > > new file mode 100644
> > > index 00000000000..1d65185b021
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-20a.c
> > > @@ -0,0 +1,27 @@
> > > +/* { dg-do compile { target ia32 } } */
> > > +/* { dg-options "-O2 -g0" } */
> > > +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
> > > +/* { dg-final { check-function-bodies "ia32*" "" "" { target { { 
> > > *-*-linux* *-*-gnu* } && { ia32 } } } {^\t?\.} } } */
> > > +
> > > +/*
> > > +ia32*foo:
> > > +ia32*.LFB0:
> > > +ia32*...
> > > +ia32*  pushl   %edx
> > > +ia32*...
> > > +ia32*  pushl   %eax
> > > +ia32*...
> > > +ia32*  call    baz
> > > +ia32*...
> > > +*/
> > > +
> > > +extern int baz (char, char);
> > > +
> > > +__attribute__ ((regparm (2)))
> > > +int
> > > +foo (char c1, char c2)
> > > +{
> > > +  return baz (c1, c2);
> > > +}
> > > +
> > > +/* { dg-final { scan-assembler-not "movsbl" } } */
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-20b.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-20b.c
> > > new file mode 100644
> > > index 00000000000..2dcd8a94c81
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-20b.c
> > > @@ -0,0 +1,23 @@
> > > +/* { dg-do run { target ia32 } } */
> > > +/* { dg-options "-O2" } */
> > > +/* { dg-additional-sources pr14907-20a.c } */
> > > +
> > > +extern int foo (int, int) __attribute__ ((regparm (2)));
> > > +
> > > +int
> > > +baz (int c1, int c2)
> > > +{
> > > +  if (c1 != -1)
> > > +    __builtin_abort ();
> > > +  if (c2 != 3)
> > > +    __builtin_abort ();
> > > +  return c1 + c2;
> > > +}
> > > +
> > > +int
> > > +main (void)
> > > +{
> > > +  if (foo (-1, 3) != 2)
> > > +    __builtin_abort ();
> > > +  return 0;
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-21.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-21.c
> > > new file mode 100644
> > > index 00000000000..1e6cd18349c
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-21.c
> > > @@ -0,0 +1,28 @@
> > > +/* { dg-do run { target { ! x32 } } } */
> > > +/* { dg-options "-O2" } */
> > > +
> > > +#include <stdint.h>
> > > +
> > > +__attribute__ ((sysv_abi, noipa))
> > > +uint8_t
> > > +foo (uint8_t a, uint8_t b, uint8_t c, uint8_t d,
> > > +     uint8_t e, uint8_t f, uint8_t g)
> > > +{
> > > +  return a + b + c + d + e + f + g;
> > > +}
> > > +
> > > +__attribute__((ms_abi, noipa))
> > > +uint8_t
> > > +bar (uint8_t a, uint8_t b, uint8_t c, uint8_t d,
> > > +     uint8_t e, uint8_t f, uint8_t g)
> > > +{
> > > +  return foo (a, b, c, d, e, f, g);
> > > +}
> > > +
> > > +int
> > > +main (void)
> > > +{
> > > +  if (bar (0, 1, 2, 3, 4, 5, 6) != 21)
> > > +    __builtin_abort ();
> > > +  return 0;
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-22.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-22.c
> > > new file mode 100644
> > > index 00000000000..591c8efd438
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-22.c
> > > @@ -0,0 +1,28 @@
> > > +/* { dg-do run { target { ! x32 } } } */
> > > +/* { dg-options "-O2" } */
> > > +
> > > +#include <stdint.h>
> > > +
> > > +__attribute__((ms_abi, noipa))
> > > +uint8_t
> > > +foo (uint8_t a, uint8_t b, uint8_t c, uint8_t d,
> > > +     uint8_t e, uint8_t f, uint8_t g)
> > > +{
> > > +  return a + b + c + d + e + f + g;
> > > +}
> > > +
> > > +__attribute__ ((sysv_abi, noipa))
> > > +uint8_t
> > > +bar (uint8_t a, uint8_t b, uint8_t c, uint8_t d,
> > > +     uint8_t e, uint8_t f, uint8_t g)
> > > +{
> > > +  return foo (a, b, c, d, e, f, g);
> > > +}
> > > +
> > > +int
> > > +main (void)
> > > +{
> > > +  if (bar (0, 1, 2, 3, 4, 5, 6) != 21)
> > > +    __builtin_abort ();
> > > +  return 0;
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-23.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-23.c
> > > new file mode 100644
> > > index 00000000000..5082b4de589
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-23.c
> > > @@ -0,0 +1,11 @@
> > > +/* { dg-do compile { target maybe_x32 } } */
> > > +/* { dg-require-effective-target maybe_x32 } */
> > > +/* { dg-options "-mx32 -O2" } */
> > > +
> > > +extern int baz (int a1, int a2, int a3, int a4, int a5, int a6, int *a7);
> > > +
> > > +int
> > > +foo (int a1, int a2, int a3, int a4, int a5, int a6, int *a7)
> > > +{
> > > +  return baz (a1, a2, a3, a4, a5, a6, a7);
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-3.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-3.c
> > > new file mode 100644
> > > index 00000000000..a8fb13f28f8
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-3.c
> > > @@ -0,0 +1,21 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2 -g0" } */
> > > +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
> > > +/* { dg-final { check-function-bodies "x86*" "" "" { target *-*-linux* 
> > > *-*-gnu* } {^\t?\.} } } */
> > > +
> > > +/*
> > > +x86*c1:
> > > +x86*.LFB0:
> > > +x86*   .cfi_startproc
> > > +x86*   jmp     c2
> > > +x86*   .cfi_endproc
> > > +x86*...
> > > +*/
> > > +
> > > +extern char c2 (char);
> > > +
> > > +char
> > > +c1 (char c)
> > > +{
> > > +  return c2 (c);
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-4.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-4.c
> > > new file mode 100644
> > > index 00000000000..b5fb92fefcc
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-4.c
> > > @@ -0,0 +1,21 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2 -g0" } */
> > > +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
> > > +/* { dg-final { check-function-bodies "x86*" "" "" { target *-*-linux* 
> > > *-*-gnu* } {^\t?\.} } } */
> > > +
> > > +/*
> > > +x86*foo:
> > > +x86*.LFB0:
> > > +x86*   .cfi_startproc
> > > +x86*   jmp     baz
> > > +x86*   .cfi_endproc
> > > +x86*...
> > > +*/
> > > +
> > > +extern int baz (short);
> > > +
> > > +int
> > > +foo (short c1)
> > > +{
> > > +  return baz (c1);
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-5.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-5.c
> > > new file mode 100644
> > > index 00000000000..d9abb5c8cfb
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-5.c
> > > @@ -0,0 +1,21 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2 -g0" } */
> > > +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
> > > +/* { dg-final { check-function-bodies "x86*" "" "" { target *-*-linux* 
> > > *-*-gnu* } {^\t?\.} } } */
> > > +
> > > +/*
> > > +x86*foo:
> > > +x86*.LFB0:
> > > +x86*   .cfi_startproc
> > > +x86*   jmp     baz
> > > +x86*   .cfi_endproc
> > > +x86*...
> > > +*/
> > > +
> > > +extern int baz (int, int, int, int, int, int, short, short);
> > > +
> > > +int
> > > +foo (int a1, int a2, int a3, int a4, int a5, int a6, short c1, short c2)
> > > +{
> > > +  return baz (a1, a2, a3, a4, a5, a6, c1, c2);
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-6.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-6.c
> > > new file mode 100644
> > > index 00000000000..b6d0183656a
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-6.c
> > > @@ -0,0 +1,21 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2 -g0" } */
> > > +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
> > > +/* { dg-final { check-function-bodies "x86*" "" "" { target *-*-linux* 
> > > *-*-gnu* } {^\t?\.} } } */
> > > +
> > > +/*
> > > +x86*c1:
> > > +x86*.LFB0:
> > > +x86*   .cfi_startproc
> > > +x86*   jmp     c2
> > > +x86*   .cfi_endproc
> > > +x86*...
> > > +*/
> > > +
> > > +extern short c2 (short);
> > > +
> > > +short
> > > +c1 (short c)
> > > +{
> > > +  return c2 (c);
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-7a.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-7a.c
> > > new file mode 100644
> > > index 00000000000..fbf511f691e
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-7a.c
> > > @@ -0,0 +1,22 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2 -g0" } */
> > > +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
> > > +/* { dg-final { check-function-bodies "x64*" "" "" { target { { 
> > > *-*-linux* *-*-gnu* } && { ! ia32 } } } {^\t?\.} } } */
> > > +
> > > +/*
> > > +x64*foo:
> > > +x64*.LFB0:
> > > +x64*   .cfi_startproc
> > > +x64*   movsbl  %dil, %edi
> > > +x64*   jmp     baz
> > > +x64*   .cfi_endproc
> > > +x64*...
> > > +*/
> > > +
> > > +extern int baz (int);
> > > +
> > > +int
> > > +foo (char c1)
> > > +{
> > > +  return baz (c1);
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-7b.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-7b.c
> > > new file mode 100644
> > > index 00000000000..56596e080e2
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-7b.c
> > > @@ -0,0 +1,17 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2 -g0" } */
> > > +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
> > > +/* { dg-final { check-function-bodies "ia32*" "" "" { target { { 
> > > *-*-linux* *-*-gnu* } && ia32 } } {^\t?\.} } } */
> > > +
> > > +/*
> > > +ia32*foo:
> > > +ia32*.LFB0:
> > > +ia32*  .cfi_startproc
> > > +ia32*  movsbl  4\(%esp\), %eax
> > > +ia32*  movl    %eax, 4\(%esp\)
> > > +ia32*  jmp     baz
> > > +ia32*  .cfi_endproc
> > > +ia32*...
> > > +*/
> > > +
> > > +#include "pr14907-7a.c"
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-8a.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-8a.c
> > > new file mode 100644
> > > index 00000000000..a22383694bf
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-8a.c
> > > @@ -0,0 +1,22 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2 -g0" } */
> > > +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
> > > +/* { dg-final { check-function-bodies "x64*" "" "" { target { { 
> > > *-*-linux* *-*-gnu* } && { ! ia32 } } } {^\t?\.} } } */
> > > +
> > > +/*
> > > +x64*foo:
> > > +x64*.LFB0:
> > > +x64*   .cfi_startproc
> > > +x64*   movsbl  %dil, %edi
> > > +x64*   jmp     baz
> > > +x64*   .cfi_endproc
> > > +x64*...
> > > +*/
> > > +
> > > +extern int baz (short);
> > > +
> > > +int
> > > +foo (char c1)
> > > +{
> > > +  return baz (c1);
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-8b.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-8b.c
> > > new file mode 100644
> > > index 00000000000..4e30f323e04
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-8b.c
> > > @@ -0,0 +1,17 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2 -g0" } */
> > > +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
> > > +/* { dg-final { check-function-bodies "ia32*" "" "" { target { { 
> > > *-*-linux* *-*-gnu* } && ia32 } } {^\t?\.} } } */
> > > +
> > > +/*
> > > +ia32*foo:
> > > +ia32*.LFB0:
> > > +ia32*  .cfi_startproc
> > > +ia32*  movsbl  4\(%esp\), %eax
> > > +ia32*  movl    %eax, 4\(%esp\)
> > > +ia32*  jmp     baz
> > > +ia32*  .cfi_endproc
> > > +ia32*...
> > > +*/
> > > +
> > > +#include "pr14907-8a.c"
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-9a.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-9a.c
> > > new file mode 100644
> > > index 00000000000..ee8d0b0805f
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-9a.c
> > > @@ -0,0 +1,24 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2 -g0" } */
> > > +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
> > > +/* { dg-final { check-function-bodies "x64*" "" "" { target { { 
> > > *-*-linux* *-*-gnu* } && { ! ia32 } } } {^\t?\.} } } */
> > > +
> > > +/*
> > > +x64*foo:
> > > +x64*.LFB0:
> > > +x64*   .cfi_startproc
> > > +x64*   movsbl  %dil, %eax
> > > +x64*   movsbl  %sil, %edi
> > > +x64*   movl    %eax, %esi
> > > +x64*   jmp     baz
> > > +x64*   .cfi_endproc
> > > +x64*...
> > > +*/
> > > +
> > > +extern int baz (char, char);
> > > +
> > > +int
> > > +foo (char c1, char c2)
> > > +{
> > > +  return baz (c2, c1);
> > > +}
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr14907-9b.c 
> > > b/gcc/testsuite/gcc.target/i386/pr14907-9b.c
> > > new file mode 100644
> > > index 00000000000..d094e2f4ce7
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr14907-9b.c
> > > @@ -0,0 +1,19 @@
> > > +/* { dg-do compile } */
> > > +/* { dg-options "-O2 -g0" } */
> > > +/* Keep labels and directives ('.cfi_startproc', '.cfi_endproc').  */
> > > +/* { dg-final { check-function-bodies "ia32*" "" "" { target { { 
> > > *-*-linux* *-*-gnu* } && ia32 } } {^\t?\.} } } */
> > > +
> > > +/*
> > > +ia32*foo:
> > > +ia32*.LFB0:
> > > +ia32*  .cfi_startproc
> > > +ia32*  movsbl  8\(%esp\), %eax
> > > +ia32*  movsbl  4\(%esp\), %edx
> > > +ia32*  movl    %eax, 4\(%esp\)
> > > +ia32*  movl    %edx, 8\(%esp\)
> > > +ia32*  jmp     baz
> > > +ia32*  .cfi_endproc
> > > +ia32*...
> > > +*/
> > > +
> > > +#include "pr14907-9a.c"
> > > --
> > > 2.51.0
> > >
> >
> > PING.
> >
> > --
> > H.J.
>
> PING^2.
>
> --
> H.J.

PING^3.

-- 
H.J.

Reply via email to