On Mon, Aug 25, 2025 at 5:59 AM H.J. Lu <hjl.to...@gmail.com> 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 <hjl.to...@gmail.com>
> ---
>  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.

Reply via email to