Index: doc/extend.texi =================================================================== --- doc/extend.texi (revision 118846) +++ doc/extend.texi (working copy) @@ -2221,6 +2221,14 @@ disabled with the linker or the loader if desired, to avoid the problem.) +@item x87regparm +@cindex @code{x87regparm} attribute +On the Intel x86 with 80387 @code{x87regparm} attribute causes the +compiler to pass up to 3 floating point arguments in 80387 registers +instead of on the stack. Functions that take a variable number of +arguments will continue to pass all of their floating point arguments +on the stack. + @item sseregparm @cindex @code{sseregparm} attribute On the Intel 386 with SSE support, the @code{sseregparm} attribute Index: doc/invoke.texi =================================================================== --- doc/invoke.texi (revision 118846) +++ doc/invoke.texi (working copy) @@ -535,8 +535,8 @@ -mmmx -msse -msse2 -msse3 -mssse3 -m3dnow @gol -mthreads -mno-align-stringops -minline-all-stringops @gol -mpush-args -maccumulate-outgoing-args -m128bit-long-double @gol --m96bit-long-double -mregparm=@var{num} -msseregparm @gol --mstackrealign @gol +-m96bit-long-double -mregparm=@var{num} -mx87regparm @gol +-msseregparm @gol -mstackrealign @gol -momit-leaf-frame-pointer -mno-red-zone -mno-tls-direct-seg-refs @gol -mcmodel=@var{code-model} @gol -m32 -m64 -mlarge-data-threshold=@var{num}} @@ -9542,6 +9542,17 @@ value, including any libraries. This includes the system libraries and startup modules. +@item -mx87regparm +@opindex mx87regparm +Use 80387 register passing conventions for floating point arguments. +You can control this behavior for a specific function by using the +function attribute @samp{x87regparm}. +@xref{Function Attributes}. + +@strong{Warning:} if you use this switch then you must build all +modules with the same value, including any libraries. This includes +the system libraries and startup modules. + @item -msseregparm @opindex msseregparm Use SSE register passing conventions for float and double arguments Index: testsuite/gcc.target/i386/x87regparm-2.c =================================================================== --- testsuite/gcc.target/i386/x87regparm-2.c (revision 0) +++ testsuite/gcc.target/i386/x87regparm-2.c (revision 0) @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fomit-frame-pointer -mx87regparm" } */ +/* { dg-require-effective-target ilp32 } */ + +float efoo_f(float); +double efoo_d(double); +long double efoo_ld(long double); + +volatile float f; +volatile double d; +volatile long double ld; + +void test() +{ + f = efoo_f(f); + d = efoo_d(d); + ld = efoo_ld(ld); +} + +/* Check that no memory is used to pass arguments. */ + +/* { dg-final { scan-assembler-not "\\(%esp\\)" } } */ Index: testsuite/gcc.target/i386/x87regparm-3.c =================================================================== --- testsuite/gcc.target/i386/x87regparm-3.c (revision 0) +++ testsuite/gcc.target/i386/x87regparm-3.c (revision 0) @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fomit-frame-pointer" } */ +/* { dg-require-effective-target ilp32 } */ + +static float __attribute__((noinline)) foo_f(float f) { return f; } +static double __attribute__((noinline)) foo_d(double d) { return d; } +static long double __attribute__((noinline)) foo_ld(long double ld) { return ld; } + +volatile float f; +volatile double d; +volatile long double ld; + +void test() +{ + f = foo_f(f); + d = foo_d(d); + ld = foo_ld(ld); +} + +/* Check that float and double arguments are passed through memory. */ + +/* { dg-final { scan-assembler-times "\\(%esp\\)" 4 } } */ Index: testsuite/gcc.target/i386/x87regparm-4.c =================================================================== --- testsuite/gcc.target/i386/x87regparm-4.c (revision 0) +++ testsuite/gcc.target/i386/x87regparm-4.c (revision 0) @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fomit-frame-pointer -ffast-math" } */ +/* { dg-require-effective-target ilp32 } */ + +static float __attribute__((noinline)) foo_f(float f) { return f; } +static double __attribute__((noinline)) foo_d(double d) { return d; } +static long double __attribute__((noinline)) foo_ld(long double ld) { return ld; } + +volatile float f; +volatile double d; +volatile long double ld; + +void test() +{ + f = foo_f(f); + d = foo_d(d); + ld = foo_ld(ld); +} + +/* Check that no memory is used to pass arguments. */ + +/* { dg-final { scan-assembler-not "\\(%esp\\)" } } */ Index: testsuite/gcc.target/i386/x87regparm-1.c =================================================================== --- testsuite/gcc.target/i386/x87regparm-1.c (revision 0) +++ testsuite/gcc.target/i386/x87regparm-1.c (revision 0) @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fomit-frame-pointer" } */ +/* { dg-require-effective-target ilp32 } */ + +float foo_f(float) __attribute__((x87regparm)); +double foo_d(double) __attribute__((x87regparm)); +long double foo_ld(long double) __attribute__((x87regparm)); + +volatile float f; +volatile double d; +volatile long double ld; + +void test() +{ + f = foo_f(f); + d = foo_d(d); + ld = foo_ld(ld); +} + +/* Check that no memory is used to pass arguments. */ + +/* { dg-final { scan-assembler-not "\\(%esp\\)" } } */ Index: reg-stack.c =================================================================== --- reg-stack.c (revision 118846) +++ reg-stack.c (working copy) @@ -2553,11 +2553,28 @@ static int convert_regs_entry (void) { + tree params = DECL_ARGUMENTS (current_function_decl); + tree p; + HARD_REG_SET incoming_regs; + rtx inc_rtx; + int inserted = 0; edge e; edge_iterator ei; - /* Load something into each stack register live at function entry. + /* Find out which registers were used as argument passing registers. */ + + CLEAR_HARD_REG_SET (incoming_regs); + for (p = params; p; p = TREE_CHAIN (p)) + { + inc_rtx = DECL_INCOMING_RTL (p); + + if (REG_P (inc_rtx) + && IN_RANGE (REGNO (inc_rtx), FIRST_STACK_REG, LAST_STACK_REG)) + SET_HARD_REG_BIT (incoming_regs, REGNO (inc_rtx)); + } + + /* Load something into remaining stack register live at function entry. Such live registers can be caused by uninitialized variables or functions not returning values on all paths. In order to keep the push/pop code happy, and to not scrog the register stack, we @@ -2579,6 +2596,10 @@ bi->stack_in.reg[++top] = reg; + /* Skip argument passing registers. */ + if (TEST_HARD_REG_BIT (incoming_regs, reg)) + continue; + init = gen_rtx_SET (VOIDmode, FP_MODE_REG (FIRST_STACK_REG, SFmode), not_a_num); Index: config/i386/i386.h =================================================================== --- config/i386/i386.h (revision 118846) +++ config/i386/i386.h (working copy) @@ -1428,19 +1428,21 @@ such as FUNCTION_ARG to determine where the next arg should go. */ typedef struct ix86_args { - int words; /* # words passed so far */ int nregs; /* # registers available for passing */ int regno; /* next available register number */ + int words; /* # words passed so far */ int fastcall; /* fastcall calling convention is used */ - int sse_words; /* # sse words passed so far */ + int x87_nregs; /* # x87 registers available for passing */ + int x87_regno; /* # next available x87 register number */ int sse_nregs; /* # sse registers available for passing */ + int sse_regno; /* next available sse register number */ int warn_sse; /* True when we want to warn about SSE ABI. */ - int warn_mmx; /* True when we want to warn about MMX ABI. */ - int sse_regno; /* next available sse register number */ - int mmx_words; /* # mmx words passed so far */ int mmx_nregs; /* # mmx registers available for passing */ int mmx_regno; /* next available mmx register number */ + int warn_mmx; /* True when we want to warn about MMX ABI. */ int maybe_vaarg; /* true for calls to possibly vardic fncts. */ + int float_in_x87; /* 1 if floating point arguments should + be passed in 80387 registere. */ int float_in_sse; /* 1 if in 32-bit mode SFmode (2 for DFmode) should be passed in SSE registers. Otherwise 0. */ } CUMULATIVE_ARGS; @@ -1727,6 +1729,8 @@ #define REGPARM_MAX (TARGET_64BIT ? 6 : 3) +#define X87_REGPARM_MAX 3 + #define SSE_REGPARM_MAX (TARGET_64BIT ? 8 : (TARGET_SSE ? 3 : 0)) #define MMX_REGPARM_MAX (TARGET_64BIT ? 0 : (TARGET_MMX ? 3 : 0)) Index: config/i386/i386.opt =================================================================== --- config/i386/i386.opt (revision 118846) +++ config/i386/i386.opt (working copy) @@ -201,6 +201,10 @@ Target Report Mask(SSSE3) Support MMX, SSE, SSE2, SSE3 and SSSE3 built-in functions and code generation +mx87regparm +Target RejectNegative Mask(X87REGPARM) +Use x87 register passing conventions to pass floating point arguments + msseregparm Target RejectNegative Mask(SSEREGPARM) Use SSE register passing conventions for SF and DF mode Index: config/i386/i386.c =================================================================== --- config/i386/i386.c (revision 118846) +++ config/i386/i386.c (working copy) @@ -1991,6 +1991,11 @@ ix86_preferred_stack_boundary = (1 << i) * BITS_PER_UNIT; } + /* Accept -mx87regparm only if 80387 support is enabled. */ + if (TARGET_X87REGPARM + && ! TARGET_80387) + error ("-mx87regparm used without 80387 enabled"); + /* Accept -msseregparm only if at least SSE support is enabled. */ if (TARGET_SSEREGPARM && ! TARGET_SSE) @@ -2298,6 +2303,9 @@ /* Regparm attribute specifies how many integer arguments are to be passed in registers. */ { "regparm", 1, 1, false, true, true, ix86_handle_cconv_attribute }, + /* X87regparm attribute says we are passing floating point arguments + in 80387 registers. */ + { "x87regparm", 0, 0, false, true, true, ix86_handle_cconv_attribute }, /* Sseregparm attribute says we are using x86_64 calling conventions for FP arguments. */ { "sseregparm", 0, 0, false, true, true, ix86_handle_cconv_attribute }, @@ -2400,8 +2408,8 @@ return true; } -/* Handle "cdecl", "stdcall", "fastcall", "regparm" and "sseregparm" - calling convention attributes; +/* Handle "cdecl", "stdcall", "fastcall", "regparm", "x87regparm" + and "sseregparm" calling convention attributes; arguments as in struct attribute_spec.handler. */ static tree @@ -2466,7 +2474,8 @@ return NULL_TREE; } - /* Can combine fastcall with stdcall (redundant) and sseregparm. */ + /* Can combine fastcall with stdcall (redundant), x87regparm + and sseregparm. */ if (is_attribute_p ("fastcall", name)) { if (lookup_attribute ("cdecl", TYPE_ATTRIBUTES (*node))) @@ -2483,8 +2492,8 @@ } } - /* Can combine stdcall with fastcall (redundant), regparm and - sseregparm. */ + /* Can combine stdcall with fastcall (redundant), regparm, + x87regparm and sseregparm. */ else if (is_attribute_p ("stdcall", name)) { if (lookup_attribute ("cdecl", TYPE_ATTRIBUTES (*node))) @@ -2497,7 +2506,7 @@ } } - /* Can combine cdecl with regparm and sseregparm. */ + /* Can combine cdecl with regparm, x87regparm and sseregparm. */ else if (is_attribute_p ("cdecl", name)) { if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (*node))) @@ -2510,7 +2519,7 @@ } } - /* Can combine sseregparm with all attributes. */ + /* Can combine x87regparm or sseregparm with all attributes. */ return NULL_TREE; } @@ -2535,6 +2544,11 @@ != ix86_function_regparm (type2, NULL))) return 0; + /* Check for mismatched x87regparm types. */ + if (!lookup_attribute ("x87regparm", TYPE_ATTRIBUTES (type1)) + != !lookup_attribute ("x87regparm", TYPE_ATTRIBUTES (type2))) + return 0; + /* Check for mismatched sseregparm types. */ if (!lookup_attribute ("sseregparm", TYPE_ATTRIBUTES (type1)) != !lookup_attribute ("sseregparm", TYPE_ATTRIBUTES (type2))) @@ -2623,6 +2637,48 @@ return regparm; } +/* Return 1 if we can pass up to X87_REGPARM_MAX floating point + arguments in x87 registers for a function with the indicated + TYPE and DECL. DECL may be NULL when calling function indirectly + or considering a libcall. For local functions, return 2. + Otherwise return 0. */ + +static int +ix86_function_x87regparm (tree type, tree decl) +{ + /* Use x87 registers to pass floating point arguments if requested + by the x87regparm attribute. */ + if (TARGET_X87REGPARM + || (type + && lookup_attribute ("x87regparm", TYPE_ATTRIBUTES (type)))) + { + if (!TARGET_80387) + { + if (decl) + error ("Calling %qD with attribute x87regparm without " + "80387 enabled", decl); + else + error ("Calling %qT with attribute x87regparm without " + "80387 enabled", type); + return 0; + } + + return 1; + } + + /* For local functions, pass up to X87_REGPARM_MAX floating point + arguments in x87 registers. */ + if (!TARGET_64BIT && decl + && flag_unit_at_a_time && !profile_flag) + { + struct cgraph_local_info *i = cgraph_local_info (decl); + if (i && i->local) + return 2; + } + + return 0; +} + /* Return 1 or 2, if we can pass up to 8 SFmode (1) and DFmode (2) arguments in SSE registers for a function with the indicated TYPE and DECL. DECL may be NULL when calling function indirectly @@ -2742,6 +2798,8 @@ int i; if (!TARGET_64BIT) return (regno < REGPARM_MAX + || (TARGET_80387 && FP_REGNO_P (regno) + && (regno < FIRST_FLOAT_REG + X87_REGPARM_MAX)) || (TARGET_MMX && MMX_REGNO_P (regno) && (regno < FIRST_MMX_REG + MMX_REGPARM_MAX)) || (TARGET_SSE && SSE_REGNO_P (regno) @@ -2805,6 +2863,8 @@ /* Set up the number of registers to use for passing arguments. */ cum->nregs = ix86_regparm; + if (TARGET_80387) + cum->x87_nregs = X87_REGPARM_MAX; if (TARGET_SSE) cum->sse_nregs = SSE_REGPARM_MAX; if (TARGET_MMX) @@ -2826,6 +2886,10 @@ cum->nregs = ix86_function_regparm (fntype, fndecl); } + /* Set up the number of 80387 registers used for passing + floating point arguments. Warn for mismatching ABI. */ + cum->float_in_x87 = ix86_function_x87regparm (fntype, fndecl); + /* Set up the number of SSE registers used for passing SFmode and DFmode arguments. Warn for mismatching ABI. */ cum->float_in_sse = ix86_function_sseregparm (fntype, fndecl); @@ -2835,7 +2899,8 @@ are no variable arguments. If there are variable arguments, then we won't pass anything in registers in 32-bit mode. */ - if (cum->nregs || cum->mmx_nregs || cum->sse_nregs) + if (cum->nregs || cum->mmx_nregs + || cum->x87_nregs || cum->sse_nregs) { for (param = (fntype) ? TYPE_ARG_TYPES (fntype) : 0; param != 0; param = next_param) @@ -2846,11 +2911,13 @@ if (!TARGET_64BIT) { cum->nregs = 0; + cum->x87_nregs = 0; cum->sse_nregs = 0; cum->mmx_nregs = 0; cum->warn_sse = 0; cum->warn_mmx = 0; cum->fastcall = 0; + cum->float_in_x87 = 0; cum->float_in_sse = 0; } cum->maybe_vaarg = true; @@ -3547,14 +3614,41 @@ } break; + case SFmode: + if (cum->float_in_sse > 0) + goto skip_80387; + case DFmode: - if (cum->float_in_sse < 2) + if (cum->float_in_sse > 1) + goto skip_80387; + + /* Because no inherent XFmode->DFmode and XFmode->SFmode + rounding takes place when values are passed in x87 + registers, pass DFmode and SFmode types to local functions + only when flag_unsafe_math_optimizations is set. */ + if (!cum->float_in_x87 + || (cum->float_in_x87 == 2 + && !flag_unsafe_math_optimizations)) break; - case SFmode: - if (cum->float_in_sse < 1) + + case XFmode: + if (!cum->float_in_x87) break; - /* FALLTHRU */ + if (!type || !AGGREGATE_TYPE_P (type)) + { + cum->x87_nregs -= 1; + cum->x87_regno += 1; + if (cum->x87_nregs <= 0) + { + cum->x87_nregs = 0; + cum->x87_regno = 0; + } + } + break; + + skip_80387: + case TImode: case V16QImode: case V8HImode: @@ -3564,7 +3658,6 @@ case V2DFmode: if (!type || !AGGREGATE_TYPE_P (type)) { - cum->sse_words += words; cum->sse_nregs -= 1; cum->sse_regno += 1; if (cum->sse_nregs <= 0) @@ -3581,7 +3674,6 @@ case V2SFmode: if (!type || !AGGREGATE_TYPE_P (type)) { - cum->mmx_words += words; cum->mmx_nregs -= 1; cum->mmx_regno += 1; if (cum->mmx_nregs <= 0) @@ -3646,7 +3738,6 @@ else switch (mode) { - /* For now, pass fp/complex values on the stack. */ default: break; @@ -3676,13 +3767,35 @@ ret = gen_rtx_REG (mode, regno); } break; - case DFmode: - if (cum->float_in_sse < 2) + + case SFmode: + if (cum->float_in_sse > 0) + goto skip_80387; + + case DFmode: + if (cum->float_in_sse > 1) + goto skip_80387; + + /* Because no inherent XFmode->DFmode and XFmode->SFmode + rounding takes place when values are passed in x87 + registers, pass DFmode and SFmode types to local functions + only when flag_unsafe_math_optimizations is set. */ + if (!cum->float_in_x87 + || (cum->float_in_x87 == 2 + && !flag_unsafe_math_optimizations)) + break; + + case XFmode: + if (!cum->float_in_x87) + break; + + if (!type || !AGGREGATE_TYPE_P (type)) + if (cum->x87_nregs) + ret = gen_rtx_REG (mode, cum->x87_regno + FIRST_FLOAT_REG); break; - case SFmode: - if (cum->float_in_sse < 1) - break; - /* FALLTHRU */ + + skip_80387: + case TImode: case V16QImode: case V8HImode: