On Wed, Apr 30, 2025 at 2:43 PM Richard Biener <richard.guent...@gmail.com> wrote: > > On Tue, Apr 29, 2025 at 3:53 PM H.J. Lu <hjl.to...@gmail.com> wrote: > > > > On Tue, Apr 29, 2025 at 9:34 PM Richard Biener > > <richard.guent...@gmail.com> wrote: > > > > > > On Tue, Apr 29, 2025 at 2:33 PM H.J. Lu <hjl.to...@gmail.com> wrote: > > > > > > > > On Tue, Apr 29, 2025 at 6:46 PM Richard Biener > > > > <richard.guent...@gmail.com> wrote: > > > > > > > > > > On Tue, Apr 29, 2025 at 12:32 PM H.J. Lu <hjl.to...@gmail.com> wrote: > > > > > > > > > > > > On Tue, Apr 29, 2025 at 5:56 PM Richard Biener > > > > > > <richard.guent...@gmail.com> wrote: > > > > > > > > > > > > > > On Tue, Apr 29, 2025 at 10:48 AM H.J. Lu <hjl.to...@gmail.com> > > > > > > > wrote: > > > > > > > > > > > > > > > > On Tue, Apr 29, 2025 at 4:25 PM Richard Biener > > > > > > > > <richard.guent...@gmail.com> wrote: > > > > > > > > > > > > > > > > > > On Tue, Apr 29, 2025 at 9:39 AM H.J. Lu <hjl.to...@gmail.com> > > > > > > > > > wrote: > > > > > > > > > > > > > > > > > > > > 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. > > > > > > > > > > > > > > > > > > How do these influence the value? TARGET_PROMOTE_PROTOTYPES > > > > > > > > > should apply to all of them, no? > > > > > > > > > > > > > > > > When the arguments are passed in different registers in > > > > > > > > different ABIs, > > > > > > > > we have to copy them anyway. > > > > > > > > > > > > > > But optimization can elide copies easily, but not easily elide > > > > > > > sign-/zero-extensions. > > > > > > > > > > > > What I meant was that caller and callee have different ABIs. > > > > > > Optimizer can't elide copies since incoming arguments and outgoing > > > > > > arguments are in different registers. They have to be moved. > > > > > > > > > > > > > > > > > > > > > > > > > 3. The incoming argument and the outgoing argument are in > > > > > > > > > > the same > > > > > > > > > > location. > > > > > > > > > > > > > > > > > > Why's that? Can't we move them but still elide the > > > > > > > > > sign-/zero-extension? > > > > > > > > > > > > > > > > If they aren't in the same locations, we have to move them > > > > > > > > anyway. > > > > > > > > This patch tries to avoid necessary moves of incoming arguments > > > > > > > > to > > > > > > > > outgoing arguments. > > > > > > > > > > > > > > That's not exactly how you presented it, but you convenitently > > > > > > > used > > > > > > > x86 stack argument passing. That might be difficult to elide, > > > > > > > but is > > > > > > > also uncommon for "small integer types" - does the same issue not > > > > > > > apply to other arguments passed on the stack as well? > > > > > > > > > > > > It applies to both passing in registers and on stack. It is an > > > > > > issue only > > > > > > for small integer types due to sign-/zero-extensions at call sites. > > > > > > My > > > > > > patch elides sign-/zero-extensions when incoming arguments and > > > > > > outgoing > > > > > > arguments are unchanged in the exactly same location, in register > > > > > > or on stack. > > > > > > > > > > Is it possible to dissect this from TARGET_PROMOTE_PROTOTYPES then? > > > > > That is, this should also work for the case prototypes are not > > > > > promoted and > > > > > for modes larger than SImode, even BLKmode. > > > > > > > > > > Richard. > > > > > > > > Arguments which don't need promotion, including large arguments, are > > > > already > > > > working today. The only issue is sign-/zero-extension of small > > > > outgoing integer > > > > arguments on x86. My patch removes unnecessary sign-/zero-extensions. > > > > See: > > > > > > So we're back to square one ... why restrict this sign-/zero-extension > > > elimination > > > to the case where you can also elide the copy? > > > > There is no copy in other cases. The only copy case is > > sign-/zero-extension. > > There is no copy to eliminate except for sign-/zero-extension. > > So why not eliminate the sign-/zero-extension to a copy when the location is > not equivalent? > > It seems to me there's existing code (w/o a target hook) that can eliminate > useless copies but that fails when an extension is required. > TARGET_PROMOTE_PROTOTYPES > says incoming args are promoted for GCC controlled calls. I fail to see > where "location" or "ABI" or anything else should be relevant here. It seems
Location is the wrong word. Caller and callee must have the same argument order. > there's just a tiny bit of information missing in the generic mechanism and > thus > I fail to see why a full blown target hook is required just for the > special case of > the copy case of sign-/zero-extensions that are not necessary. > > Richard. > Here is the v2 patch. ix86_get_small_integer_argument_value was moved to calls.cc. I added a target hook, TARGET_SAME_FUNCTION_ARGUMENT_ORDER_P, to verify that caller and callee have the same incoming argument order. The default returns true. The x86 hook has /* Implement TARGET_SAME_INCOMING_ARGUMENT_ORDER_P. */ static bool ix86_same_incoming_argument_order_p (const_tree fndecl) { return (!TARGET_64BIT || (ix86_function_abi (current_function_decl) == ix86_function_abi (fndecl))); } since 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. -- H.J. --- or 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 in the caller to an outgoing argument may override another incoming argument. 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.
From 3e213144f41daf7e2e9540a7698a5ea40cae7218 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 v2] Use incoming small integer argument value as if promoted 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 | 220 ++++++++++++++++++-- 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, 914 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 676f0f9229e..827c755a144 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,160 @@ 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; + + /* Don't know if the incoming arguments are changed. */ + if (!MEM_P (src)) + goto no_incoming_argument_value; + + rtx op = XEXP (src, 0); + if (GET_CODE (op) == PLUS) + { + rtx op1 = XEXP (op, 1); + if (!CONST_INT_P (op1)) + break; + op = XEXP (op, 0); + } + if (REG_P (op) + && REGNO (op) == VIRTUAL_INCOMING_ARGS_REGNUM) + 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 +1508,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 +1519,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 +1548,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 @@ -5087,18 +5267,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 5ad47e19434..ec7803829d0 100644 --- a/gcc/config/i386/i386.cc +++ b/gcc/config/i386/i386.cc @@ -4438,6 +4438,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 @@ -27457,6 +27470,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 5e305643b3a..d53f29a47bf 100644 --- a/gcc/doc/tm.texi +++ b/gcc/doc/tm.texi @@ -4055,6 +4055,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 eccc4d88493..1a26b5e56fc 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_SAME_INCOMING_ARGUMENT_ORDER_P + @hook TARGET_PUSH_ARGUMENT @defmac PUSH_ARGS_REVERSED diff --git a/gcc/function.h b/gcc/function.h index 2260d6704ec..146ec877af0 100644 --- a/gcc/function.h +++ b/gcc/function.h @@ -347,6 +347,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; @@ -452,6 +455,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 38903eb567a..470581ebf5a 100644 --- a/gcc/target.def +++ b/gcc/target.def @@ -4839,6 +4839,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.49.0