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