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.

Add a new target hook, TARGET_GET_SMALL_INTEGER_ARGUMENT_VALUE, defaulting
to return nullptr.  If the new target hook returns non-nullptr, use it to
get the outgoing small integer argument.  The x86 target hook returns the
value of the corresponding incoming argument as int if it can be used as
the outgoing argument.  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.
It is safe only if

1. Caller and callee are not nested functions.
2. Caller and callee use the same ABI.
3. The incoming argument and the outgoing argument are in the same
location.
4. 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.

gcc/

PR middle-end/14907
* calls.cc (arg_data): Add small_integer_argument_value.
(precompute_register_parameters): Set args[i].value to
args[i].small_integer_argument_value if not nullptr.
(initialize_argument_information): Set
args[i].small_integer_argument_value to
TARGET_GET_SMALL_INTEGER_ARGUMENT_VALUE.
(store_one_arg): Set arg->value to arg->small_integer_argument_value
if not nullptr.
* target.def (get_small_integer_argument_value): New for calls.
* targhooks.cc (default_get_small_integer_argument_value): New.
* targhooks.h (default_get_small_integer_argument_value): Likewise.
* config/i386/i386.cc (ix86_get_small_integer_argument_value): New.
(TARGET_GET_SMALL_INTEGER_ARGUMENT_VALUE): Likewise.
* config/i386/i386.h (machine_function): Add
no_small_integer_argument_value and before_first_expand_call.
* doc/tm.texi: Regenerated.
* doc/tm.texi.in (TARGET_GET_SMALL_INTEGER_ARGUMENT_VALUE): New
hook.

gcc/testsuite/

PR middle-end/14907
* gcc.target/i386/pr14907-1.c: New test.
* 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-10c.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-18a.c: Likewise.
* gcc.target/i386/pr14907-18b.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.


-- 
H.J.
From a093f7cff03796cf7e72e850a722d079db3c72af Mon Sep 17 00:00:00 2001
From: "H.J. Lu" <hjl.to...@gmail.com>
Date: Thu, 21 Nov 2024 09:22:40 +0800
Subject: [PATCH] Use incoming small integer argument value if possible

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.

Add a new target hook, TARGET_GET_SMALL_INTEGER_ARGUMENT_VALUE, defaulting
to return nullptr.  If the new target hook returns non-nullptr, use it to
get the outgoing small integer argument.  The x86 target hook returns the
value of the corresponding incoming argument as int if it can be used as
the outgoing argument.  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.
It is safe only if

1. Caller and callee are not nested functions.
2. Caller and callee use the same ABI.
3. The incoming argument and the outgoing argument are in the same
location.
4. 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.

gcc/

	PR middle-end/14907
	* calls.cc (arg_data): Add small_integer_argument_value.
	(precompute_register_parameters): Set args[i].value to
	args[i].small_integer_argument_value if not nullptr.
	(initialize_argument_information): Set
	args[i].small_integer_argument_value to
	TARGET_GET_SMALL_INTEGER_ARGUMENT_VALUE.
	(store_one_arg): Set arg->value to arg->small_integer_argument_value
	if not nullptr.
	* target.def (get_small_integer_argument_value): New for calls.
	* targhooks.cc (default_get_small_integer_argument_value): New.
	* targhooks.h (default_get_small_integer_argument_value): Likewise.
	* config/i386/i386.cc (ix86_get_small_integer_argument_value): New.
	(TARGET_GET_SMALL_INTEGER_ARGUMENT_VALUE): Likewise.
	* config/i386/i386.h (machine_function): Add
	no_small_integer_argument_value and before_first_expand_call.
	* doc/tm.texi: Regenerated.
	* doc/tm.texi.in (TARGET_GET_SMALL_INTEGER_ARGUMENT_VALUE): New
	hook.

gcc/testsuite/

	PR middle-end/14907
	* gcc.target/i386/pr14907-1.c: New test.
	* 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-10c.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-18a.c: Likewise.
	* gcc.target/i386/pr14907-18b.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.

Signed-off-by: H.J. Lu <hjl.to...@gmail.com>
---
 gcc/calls.cc                                |  57 ++++++---
 gcc/config/i386/i386.cc                     | 122 ++++++++++++++++++++
 gcc/config/i386/i386.h                      |   7 ++
 gcc/doc/tm.texi                             |   9 ++
 gcc/doc/tm.texi.in                          |   2 +
 gcc/target.def                              |  11 ++
 gcc/targhooks.cc                            |   9 ++
 gcc/targhooks.h                             |   2 +
 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-10c.c |  24 ++++
 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-18a.c |  24 ++++
 gcc/testsuite/gcc.target/i386/pr14907-18b.c |  28 +++++
 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-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 +++
 37 files changed, 840 insertions(+), 14 deletions(-)
 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-10c.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-18a.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr14907-18b.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-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 676f0f9229e..92a817b9e88 100644
--- a/gcc/calls.cc
+++ b/gcc/calls.cc
@@ -75,6 +75,9 @@ 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 small integer argument, or 0 if it isn't
+     available.  */
+  rtx small_integer_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 +1022,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].small_integer_argument_value)
+	      args[i].value = args[i].small_integer_argument_value;
+	    else
+	      args[i].value = expand_normal (args[i].tree_value);
 	    preserve_temp_slots (args[i].value);
 	    pop_temp_slots ();
 	  }
@@ -1349,6 +1355,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 +1366,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 +1395,34 @@ 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;
+	{
+	  args[i].small_integer_argument_value
+	    = targetm.calls.get_small_integer_argument_value
+		(fndecl, incoming_arg, args[i].tree_value);
+	  type = integer_type_node;
+	}
 
       /* 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
@@ -5087,18 +5110,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->small_integer_argument_value)
+	arg->value = arg->small_integer_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 bfd9cac215a..b940852c290 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -4438,6 +4438,125 @@ ix86_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
     }
 }
 
+/* Implement TARGET_GET_SMALL_INTEGER_ARGUMENT_VALUE.  Return the value
+   of the corresponding incoming argument, INCOMING_ARG, if ARG which is
+   an outgoing argument of an integral type smaller than int, is copied
+   directly from the incoming argument.  Otherwise return nullptr.  */
+
+static rtx
+ix86_get_small_integer_argument_value (const_tree fndecl,
+				       tree incoming_arg, tree arg)
+{
+  /* Skip nested callee and caller.  */
+  if ((fndecl && DECL_STATIC_CHAIN (fndecl))
+      || DECL_STATIC_CHAIN (current_function_decl))
+    return nullptr;
+
+  if (TARGET_64BIT
+      && (ix86_function_abi (current_function_decl)
+	  != ix86_function_abi (fndecl)))
+    {
+      /* Don't use the incoming small integer argument since the same
+	 register may be used for different incoming and outgoing
+	 arguments in different 64bit ABIs.  */
+      cfun->machine->no_small_integer_argument_value = true;
+      return nullptr;
+    }
+
+  if (cfun->machine->no_small_integer_argument_value)
+    return nullptr;
+
+  rtx_insn *before_call_expand = get_last_insn ();
+  if (cfun->machine->before_first_expand_call == nullptr)
+    {
+      cfun->machine->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 all instructions before NOTE_INSN_FUNCTION_BEG.  */
+	  if (NOTE_P (insn)
+	      && NOTE_KIND (insn) == NOTE_INSN_FUNCTION_BEG)
+	    {
+	      insn = NEXT_INSN (insn);
+	      break;
+	    }
+	}
+
+      for (; insn; insn = NEXT_INSN (insn))
+	{
+	  if (!NONDEBUG_INSN_P (insn))
+	    continue;
+
+	  if (TARGET_X32)
+	    {
+	      /* Skip the hidden argument SUBREG for x32.  */
+	      rtx set = single_set (insn);
+	      if (set)
+		{
+		  rtx dest = SET_DEST (set);
+		  if (REG_P (dest) && GET_MODE (dest) == SImode)
+		    {
+		      rtx src = SET_SRC (set);
+		      if (SUBREG_P (src))
+			{
+			  src = SUBREG_REG (src);
+			  if (REG_P (src) && GET_MODE (src) == DImode)
+			    {
+			      tree reg_expr = REG_EXPR (src);
+			      if (reg_expr
+				  && TREE_CODE (reg_expr) == PARM_DECL
+				  && DECL_ARTIFICIAL (reg_expr))
+				continue;
+			    }
+			}
+		    }
+		}
+	    }
+
+	  /* Don't use the incoming small integer argument if there are
+	     any instructions which may change incoming arguments.  */
+	  cfun->machine->no_small_integer_argument_value = true;
+	  return nullptr;
+	}
+    }
+  else if (cfun->machine->before_first_expand_call != before_call_expand)
+    {
+      /* Incoming arguments aren't preserved after the first call.  */
+      cfun->machine->no_small_integer_argument_value = true;
+      return nullptr;
+    }
+
+  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;
+  if (TYPE_MODE (TREE_TYPE (arg)) != TYPE_MODE (DECL_ARG_TYPE (var)))
+    return nullptr;
+
+  /* Return nullptr if the outgoing argument isn't copied directly from
+     the corresponding incoming argument since an outgoing argument may
+     be in the same location as a different incoming argument.  */
+  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;
+}
+
 /* Implement TARGET_PUSH_ARGUMENT.  */
 
 static bool
@@ -27326,6 +27445,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_GET_SMALL_INTEGER_ARGUMENT_VALUE
+#define TARGET_GET_SMALL_INTEGER_ARGUMENT_VALUE \
+  ix86_get_small_integer_argument_value
 #undef TARGET_PUSH_ARGUMENT
 #define TARGET_PUSH_ARGUMENT ix86_push_argument
 #undef TARGET_SETUP_INCOMING_VARARGS
diff --git a/gcc/config/i386/i386.h b/gcc/config/i386/i386.h
index 18aa42da3be..f1e2796cfa2 100644
--- a/gcc/config/i386/i386.h
+++ b/gcc/config/i386/i386.h
@@ -2910,6 +2910,13 @@ struct GTY(()) machine_function {
   /* True if inline asm with redzone clobber has been seen.  */
   BOOL_BITFIELD asm_redzone_clobber_seen : 1;
 
+  /* If true, don't use the incoming small integer argument as the
+     outgoing argument.  */
+  BOOL_BITFIELD no_small_integer_argument_value : 1;
+
+  /* The instruction before the first call expansion.  */
+  rtx_insn *before_first_expand_call;
+
   /* The largest alignment, in bytes, of stack slot actually used.  */
   unsigned int max_used_stack_alignment;
 
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index a96700c0d38..f1027c8d5ba 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -4055,6 +4055,15 @@ cases of mismatch, it also makes for better code on certain machines.
 The default is to not promote prototypes.
 @end deftypefn
 
+@deftypefn {Target Hook} rtx TARGET_GET_SMALL_INTEGER_ARGUMENT_VALUE (const_tree @var{fndecl}, tree @var{incoming_arg}, tree @var{arg})
+This target hook returns the corresponding incoming rtx of
+@var{incoming_arg}, if @var{arg}, which is an outgoing argument of an
+integral type smaller than @code{int}, is copied directly from the
+incoming argument.  Otherwise, @code{nullptr} is returned.  This hook
+is applied if @code{TARGET_PROMOTE_PROTOTYPES}
+returns @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 eccc4d88493..05bfd6d220b 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -3208,6 +3208,8 @@ control passing certain arguments in registers.
 
 @hook TARGET_PROMOTE_PROTOTYPES
 
+@hook TARGET_GET_SMALL_INTEGER_ARGUMENT_VALUE
+
 @hook TARGET_PUSH_ARGUMENT
 
 @defmac PUSH_ARGS_REVERSED
diff --git a/gcc/target.def b/gcc/target.def
index 6c7cdc8126b..dd4643cdf3f 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -4839,6 +4839,17 @@ The default is to not promote prototypes.",
  bool, (const_tree fntype),
  hook_bool_const_tree_false)
 
+DEFHOOK
+(get_small_integer_argument_value,
+ "This target hook returns the corresponding incoming rtx of\n\
+@var{incoming_arg}, if @var{arg}, which is an outgoing argument of an\n\
+integral type smaller than @code{int}, is copied directly from the\n\
+incoming argument.  Otherwise, @code{nullptr} is returned.  This hook\n\
+is applied if @code{TARGET_PROMOTE_PROTOTYPES}\n\
+returns @code{true}.",
+ rtx, (const_tree fndecl, tree incoming_arg, tree arg),
+ default_get_small_integer_argument_value)
+
 DEFHOOK
 (struct_value_rtx,
  "This target hook should return the location of the structure value\n\
diff --git a/gcc/targhooks.cc b/gcc/targhooks.cc
index c79458e374e..c9bad0d5487 100644
--- a/gcc/targhooks.cc
+++ b/gcc/targhooks.cc
@@ -162,6 +162,15 @@ default_promote_function_mode_always_promote (const_tree type,
   return promote_mode (type, mode, punsignedp);
 }
 
+/* The default implementation of TARGET_GET_SMALL_INTEGER_ARGUMENT_VALUE.
+ */
+
+tree
+default_get_small_integer_argument_value (const_tree, tree, tree)
+{
+  return nullptr;
+}
+
 machine_mode
 default_cc_modes_compatible (machine_mode m1, machine_mode m2)
 {
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index f16b58798c2..b93cc034b2c 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -33,6 +33,8 @@ extern machine_mode default_promote_function_mode (const_tree, machine_mode,
 							int *, const_tree, int);
 extern machine_mode default_promote_function_mode_always_promote
 			(const_tree, machine_mode, int *, const_tree, int);
+extern rtx default_get_small_integer_argument_value (const_tree, tree,
+						     tree);
 
 extern machine_mode default_cc_modes_compatible (machine_mode,
 						      machine_mode);
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-10c.c b/gcc/testsuite/gcc.target/i386/pr14907-10c.c
new file mode 100644
index 00000000000..eb1d1fb7310
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr14907-10c.c
@@ -0,0 +1,24 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+/* { dg-additional-sources pr14907-10a.c } */
+
+extern int foo (int, int);
+
+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.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-18a.c b/gcc/testsuite/gcc.target/i386/pr14907-18a.c
new file mode 100644
index 00000000000..5d8eadfce2c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr14907-18a.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-18b.c b/gcc/testsuite/gcc.target/i386/pr14907-18b.c
new file mode 100644
index 00000000000..0767e246c56
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr14907-18b.c
@@ -0,0 +1,28 @@
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+/* { dg-additional-sources pr14907-18a.c } */
+
+extern int foo (int, int);
+
+int
+baz1 (int c1, int c2)
+{
+  return c2 + c1;
+}
+
+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-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-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.49.0

Reply via email to