This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] 3rd try: Add sseregparm function attribute for x86
Next try. Patch is a bit more convoluted due to cleanups in call
convention affecting attributes.
Bootstrapped and regtested on i686-pc-linux-gnu and
x86_64-unknown-linux-gnu.
Ok for mainline?
I can also split the patch into cleanup and actual sseregparm
attribute patch, if necessary.
Thanks,
Richard.
2005-06-09 Richard Guenther <rguenth@gcc.gnu.org>
* doc/extend.texi: Document sseregparm target attribute.
Clarify fastcall and regparm documentation.
* config/i386/i386.c: Add new target attribute sseregparm.
(ix86_handle_cdecl_attribute, ix86_handle_regparm_attribute):
Merge into ...
(ix86_handle_cconv_attribute): ... here. Also handle
sseregparm attribute.
(ix86_comp_type_attributes): Compare sseregparm attributes.
(ix86_function_sseregparm): New function, split out from ...
(init_cumulative_args): ... here. Use to decide use
of SSE registers and error in case of missing support.
(ix86_value_regno): Likewise.
(function_arg_advance): Do not bail out for DFmode if we need
to pass doubles in registers.
(function_arg): Likewise.
* gcc.dg/i386-sseregparm-1.c: New testcase.
* gcc.dg/i386-sseregparm-2.c: Likewise.
Index: doc/extend.texi
===================================================================
RCS file: /cvs/gcc/gcc/gcc/doc/extend.texi,v
retrieving revision 1.254
diff -c -3 -p -r1.254 extend.texi
*** doc/extend.texi 30 May 2005 20:15:46 -0000 1.254
--- doc/extend.texi 9 Jun 2005 19:40:38 -0000
*************** the @code{rtc}.
*** 1752,1760 ****
@item fastcall
@cindex functions that pop the argument stack on the 386
On the Intel 386, the @code{fastcall} attribute causes the compiler to
! pass the first two arguments in the registers ECX and EDX@. Subsequent
! arguments are passed on the stack. The called function will pop the
! arguments off the stack. If the number of arguments is variable all
arguments are pushed on the stack.
@item format (@var{archetype}, @var{string-index}, @var{first-to-check})
--- 1760,1769 ----
@item fastcall
@cindex functions that pop the argument stack on the 386
On the Intel 386, the @code{fastcall} attribute causes the compiler to
! pass the first argument (if of integral type) in the register ECX and
! the second argument (if of integral type) in the register EDX@. Subsequent
! and other typed arguments are passed on the stack. The called function will
! pop the arguments off the stack. If the number of arguments is variable all
arguments are pushed on the stack.
@item format (@var{archetype}, @var{string-index}, @var{first-to-check})
*************** than 2.96.
*** 2126,2134 ****
@cindex @code{regparm} attribute
@cindex functions that are passed arguments in registers on the 386
On the Intel 386, the @code{regparm} attribute causes the compiler to
! pass up to @var{number} integer arguments in registers EAX,
! EDX, and ECX instead of on the stack. Functions that take a
! variable number of arguments will continue to be passed all of their
arguments on the stack.
Beware that on some ELF systems this attribute is unsuitable for
--- 2135,2143 ----
@cindex @code{regparm} attribute
@cindex functions that are passed arguments in registers on the 386
On the Intel 386, the @code{regparm} attribute causes the compiler to
! pass arguments number one to @var{number} if they are of integral type
! in registers EAX, EDX, and ECX instead of on the stack. Functions that
! take a variable number of arguments will continue to be passed all of their
arguments on the stack.
Beware that on some ELF systems this attribute is unsuitable for
*************** safe since the loaders there save all re
*** 2141,2146 ****
--- 2150,2163 ----
disabled with the linker or the loader if desired, to avoid the
problem.)
+ @item sseregparm
+ @cindex @code{sseregparm} attribute
+ On the Intel 386 with SSE support, the @code{sseregparm} attribute
+ causes the compiler to pass up to 8 floating point arguments in
+ SSE 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 returns_twice
@cindex @code{returns_twice} attribute
The @code{returns_twice} attribute tells the compiler that a function may
Index: config/i386/i386.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/config/i386/i386.c,v
retrieving revision 1.827
diff -c -3 -p -r1.827 i386.c
*** config/i386/i386.c 9 Jun 2005 03:53:20 -0000 1.827
--- config/i386/i386.c 9 Jun 2005 19:41:13 -0000
*************** static int ix86_comp_type_attributes (tr
*** 890,897 ****
static int ix86_function_regparm (tree, tree);
const struct attribute_spec ix86_attribute_table[];
static bool ix86_function_ok_for_sibcall (tree, tree);
! static tree ix86_handle_cdecl_attribute (tree *, tree, tree, int, bool *);
! static tree ix86_handle_regparm_attribute (tree *, tree, tree, int, bool *);
static int ix86_value_regno (enum machine_mode, tree);
static bool contains_128bit_aligned_vector_p (tree);
static rtx ix86_struct_value_rtx (tree, int);
--- 890,896 ----
static int ix86_function_regparm (tree, tree);
const struct attribute_spec ix86_attribute_table[];
static bool ix86_function_ok_for_sibcall (tree, tree);
! static tree ix86_handle_cconv_attribute (tree *, tree, tree, int, bool *);
static int ix86_value_regno (enum machine_mode, tree);
static bool contains_128bit_aligned_vector_p (tree);
static rtx ix86_struct_value_rtx (tree, int);
*************** const struct attribute_spec ix86_attribu
*** 1655,1669 ****
/* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
/* Stdcall attribute says callee is responsible for popping arguments
if they are not variable. */
! { "stdcall", 0, 0, false, true, true, ix86_handle_cdecl_attribute },
/* Fastcall attribute says callee is responsible for popping arguments
if they are not variable. */
! { "fastcall", 0, 0, false, true, true, ix86_handle_cdecl_attribute },
/* Cdecl attribute says the callee is a normal C declaration */
! { "cdecl", 0, 0, false, true, true, ix86_handle_cdecl_attribute },
/* Regparm attribute specifies how many integer arguments are to be
passed in registers. */
! { "regparm", 1, 1, false, true, true, ix86_handle_regparm_attribute },
#if TARGET_DLLIMPORT_DECL_ATTRIBUTES
{ "dllimport", 0, 0, false, false, false, handle_dll_attribute },
{ "dllexport", 0, 0, false, false, false, handle_dll_attribute },
--- 1654,1671 ----
/* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
/* Stdcall attribute says callee is responsible for popping arguments
if they are not variable. */
! { "stdcall", 0, 0, false, true, true, ix86_handle_cconv_attribute },
/* Fastcall attribute says callee is responsible for popping arguments
if they are not variable. */
! { "fastcall", 0, 0, false, true, true, ix86_handle_cconv_attribute },
/* Cdecl attribute says the callee is a normal C declaration */
! { "cdecl", 0, 0, false, true, true, ix86_handle_cconv_attribute },
/* Regparm attribute specifies how many integer arguments are to be
passed in registers. */
! { "regparm", 1, 1, 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 },
#if TARGET_DLLIMPORT_DECL_ATTRIBUTES
{ "dllimport", 0, 0, false, false, false, handle_dll_attribute },
{ "dllexport", 0, 0, false, false, false, handle_dll_attribute },
*************** ix86_function_ok_for_sibcall (tree decl,
*** 1738,1749 ****
return true;
}
! /* Handle a "cdecl", "stdcall", or "fastcall" attribute;
arguments as in struct attribute_spec.handler. */
static tree
! ix86_handle_cdecl_attribute (tree *node, tree name,
! tree args ATTRIBUTE_UNUSED,
! int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
{
if (TREE_CODE (*node) != FUNCTION_TYPE
&& TREE_CODE (*node) != METHOD_TYPE
--- 1740,1754 ----
return true;
}
! /* Handle "cdecl", "stdcall", "fastcall", "regparm" and "sseregparm"
! calling convention attributes;
arguments as in struct attribute_spec.handler. */
+
static tree
! ix86_handle_cconv_attribute (tree *node, tree name,
! tree args,
! int flags ATTRIBUTE_UNUSED,
! bool *no_add_attrs)
{
if (TREE_CODE (*node) != FUNCTION_TYPE
&& TREE_CODE (*node) != METHOD_TYPE
*************** ix86_handle_cdecl_attribute (tree *node,
*** 1753,1779 ****
warning (OPT_Wattributes, "%qs attribute only applies to functions",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
! }
! else
! {
! if (is_attribute_p ("fastcall", name))
! {
! if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (*node)))
! {
! error ("fastcall and stdcall attributes are not compatible");
! }
! else if (lookup_attribute ("regparm", TYPE_ATTRIBUTES (*node)))
! {
! error ("fastcall and regparm attributes are not compatible");
! }
! }
! else if (is_attribute_p ("stdcall", name))
! {
! if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (*node)))
! {
! error ("fastcall and stdcall attributes are not compatible");
! }
! }
}
if (TARGET_64BIT)
--- 1758,1764 ----
warning (OPT_Wattributes, "%qs attribute only applies to functions",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
! return NULL_TREE;
}
if (TARGET_64BIT)
*************** ix86_handle_cdecl_attribute (tree *node,
*** 1781,1810 ****
warning (OPT_Wattributes, "%qs attribute ignored",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
}
! return NULL_TREE;
! }
! /* Handle a "regparm" attribute;
! arguments as in struct attribute_spec.handler. */
! static tree
! ix86_handle_regparm_attribute (tree *node, tree name, tree args,
! int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
! {
! if (TREE_CODE (*node) != FUNCTION_TYPE
! && TREE_CODE (*node) != METHOD_TYPE
! && TREE_CODE (*node) != FIELD_DECL
! && TREE_CODE (*node) != TYPE_DECL)
{
! warning (OPT_Wattributes, "%qs attribute only applies to functions",
! IDENTIFIER_POINTER (name));
! *no_add_attrs = true;
}
! else
{
tree cst;
cst = TREE_VALUE (args);
if (TREE_CODE (cst) != INTEGER_CST)
{
--- 1766,1824 ----
warning (OPT_Wattributes, "%qs attribute ignored",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
+ return NULL_TREE;
}
! /* Can combine fastcall with stdcall (redundant) and sseregparm. */
! if (is_attribute_p ("fastcall", name))
! {
! if (lookup_attribute ("cdecl", TYPE_ATTRIBUTES (*node)))
! {
! error ("fastcall and cdecl attributes are not compatible");
! }
! if (lookup_attribute ("regparm", TYPE_ATTRIBUTES (*node)))
! {
! error ("fastcall and regparm attributes are not compatible");
! }
! }
! /* Can combine stdcall with fastcall (redundant), regparm and
! sseregparm. */
! else if (is_attribute_p ("stdcall", name))
{
! if (lookup_attribute ("cdecl", TYPE_ATTRIBUTES (*node)))
! {
! error ("stdcall and cdecl attributes are not compatible");
! }
}
!
! /* Can combine cdecl with regparm and sseregparm. */
! else if (is_attribute_p ("cdecl", name))
! {
! if (lookup_attribute ("stdcall", TYPE_ATTRIBUTES (*node)))
! {
! error ("stdcall and cdecl attributes are not compatible");
! }
! if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (*node)))
! {
! error ("fastcall and cdecl attributes are not compatible");
! }
! }
!
! /* Can combine sseregparm with all attributes. */
! else if (is_attribute_p ("sseregparm", name))
! ;
!
! /* Can combine regparm with all attributes but fastcall. */
! else if (is_attribute_p ("regparm", name))
{
tree cst;
+ if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (*node)))
+ {
+ error ("fastcall and regparm attributes are not compatible");
+ }
+
cst = TREE_VALUE (args);
if (TREE_CODE (cst) != INTEGER_CST)
{
*************** ix86_handle_regparm_attribute (tree *nod
*** 1819,1829 ****
IDENTIFIER_POINTER (name), REGPARM_MAX);
*no_add_attrs = true;
}
-
- if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (*node)))
- {
- error ("fastcall and regparm attributes are not compatible");
- }
}
return NULL_TREE;
--- 1833,1838 ----
*************** ix86_comp_type_attributes (tree type1, t
*** 1842,1859 ****
if (TREE_CODE (type1) != FUNCTION_TYPE)
return 1;
! /* Check for mismatched fastcall types */
! if (!lookup_attribute ("fastcall", TYPE_ATTRIBUTES (type1))
! != !lookup_attribute ("fastcall", TYPE_ATTRIBUTES (type2)))
return 0;
/* Check for mismatched return types (cdecl vs stdcall). */
if (!lookup_attribute (rtdstr, TYPE_ATTRIBUTES (type1))
!= !lookup_attribute (rtdstr, TYPE_ATTRIBUTES (type2)))
return 0;
! if (ix86_function_regparm (type1, NULL)
! != ix86_function_regparm (type2, NULL))
! return 0;
return 1;
}
--- 1851,1873 ----
if (TREE_CODE (type1) != FUNCTION_TYPE)
return 1;
! /* Check for mismatched fastcall/regparm types. */
! if ((!lookup_attribute ("fastcall", TYPE_ATTRIBUTES (type1))
! != !lookup_attribute ("fastcall", TYPE_ATTRIBUTES (type2)))
! || (ix86_function_regparm (type1, NULL)
! != ix86_function_regparm (type2, NULL)))
! return 0;
!
! /* Check for mismatched sseregparm types. */
! if (!lookup_attribute ("sseregparm", TYPE_ATTRIBUTES (type1))
! != !lookup_attribute ("sseregparm", TYPE_ATTRIBUTES (type2)))
return 0;
/* Check for mismatched return types (cdecl vs stdcall). */
if (!lookup_attribute (rtdstr, TYPE_ATTRIBUTES (type1))
!= !lookup_attribute (rtdstr, TYPE_ATTRIBUTES (type2)))
return 0;
!
return 1;
}
*************** ix86_function_regparm (tree type, tree d
*** 1902,1907 ****
--- 1916,1962 ----
return regparm;
}
+ /* Return true, if we can pass up to 8 SFmode and DFmode arguments
+ in SSE registers for a function with the indicated TYPE and DECL.
+ DECL may be NULL when calling function indirectly
+ or considering a libcall. */
+
+ static bool
+ ix86_function_sseregparm (tree type, tree decl)
+ {
+ /* Use SSE registers to pass SFmode and DFmode arguments if requested
+ by the sseregparm attribute. */
+ if (type
+ && lookup_attribute ("sseregparm", TYPE_ATTRIBUTES (type)))
+ {
+ if (!TARGET_SSE)
+ {
+ if (decl)
+ error ("Calling %qD with attribute sseregparm without "
+ "SSE/SSE2 enabled", decl);
+ else
+ error ("Calling %qT with attribute sseregparm without "
+ "SSE/SSE2 enabled", type);
+ return false;
+ }
+
+ return true;
+ }
+
+ /* For local functions, pass SFmode (and DFmode for SSE2) arguments
+ in SSE registers even for 32-bit mode and not just 3, but up to
+ 8 SSE arguments in registers. */
+ if (!TARGET_64BIT && decl
+ && TARGET_SSE_MATH && flag_unit_at_a_time && !profile_flag)
+ {
+ struct cgraph_local_info *i = cgraph_local_info (decl);
+ if (i && i->local)
+ return true;
+ }
+
+ return false;
+ }
+
/* Return true if EAX is live at the start of the function. Used by
ix86_expand_prologue to determine if we need special help before
calling allocate_stack_worker. */
*************** init_cumulative_args (CUMULATIVE_ARGS *c
*** 2036,2045 ****
*cum = zero_cum;
/* Set up the number of registers to use for passing arguments. */
! if (fntype)
! cum->nregs = ix86_function_regparm (fntype, fndecl);
! else
! cum->nregs = ix86_regparm;
if (TARGET_SSE)
cum->sse_nregs = SSE_REGPARM_MAX;
if (TARGET_MMX)
--- 2091,2097 ----
*cum = zero_cum;
/* Set up the number of registers to use for passing arguments. */
! cum->nregs = ix86_regparm;
if (TARGET_SSE)
cum->sse_nregs = SSE_REGPARM_MAX;
if (TARGET_MMX)
*************** init_cumulative_args (CUMULATIVE_ARGS *c
*** 2048,2054 ****
cum->warn_mmx = true;
cum->maybe_vaarg = false;
! /* Use ecx and edx registers if function has fastcall attribute */
if (fntype && !TARGET_64BIT)
{
if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (fntype)))
--- 2100,2107 ----
cum->warn_mmx = true;
cum->maybe_vaarg = false;
! /* Use ecx and edx registers if function has fastcall attribute,
! else look for regparm information. */
if (fntype && !TARGET_64BIT)
{
if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (fntype)))
*************** init_cumulative_args (CUMULATIVE_ARGS *c
*** 2056,2061 ****
--- 2109,2124 ----
cum->nregs = 2;
cum->fastcall = 1;
}
+ else
+ cum->nregs = ix86_function_regparm (fntype, fndecl);
+ }
+
+ /* Set up the number of SSE registers used for passing SFmode
+ and DFmode arguments. Warn for mismatching ABI. */
+ if (ix86_function_sseregparm (fntype, fndecl))
+ {
+ cum->sse_nregs = 8;
+ cum->float_in_sse = true;
}
/* Determine if this function has variable arguments. This is
*************** init_cumulative_args (CUMULATIVE_ARGS *c
*** 2079,2084 ****
--- 2142,2148 ----
cum->warn_sse = 0;
cum->warn_mmx = 0;
cum->fastcall = 0;
+ cum->float_in_sse = false;
}
cum->maybe_vaarg = true;
}
*************** init_cumulative_args (CUMULATIVE_ARGS *c
*** 2088,2108 ****
|| (fntype && !TYPE_ARG_TYPES (fntype)))
cum->maybe_vaarg = true;
- /* For local functions, pass SFmode (and DFmode for SSE2) arguments
- in SSE registers even for 32-bit mode and not just 3, but up to
- 8 SSE arguments in registers. */
- if (!TARGET_64BIT && !cum->maybe_vaarg && !cum->fastcall
- && cum->sse_nregs == SSE_REGPARM_MAX && fndecl
- && TARGET_SSE_MATH && flag_unit_at_a_time && !profile_flag)
- {
- struct cgraph_local_info *i = cgraph_local_info (fndecl);
- if (i && i->local)
- {
- cum->sse_nregs = 8;
- cum->float_in_sse = true;
- }
- }
-
if (TARGET_DEBUG_ARG)
fprintf (stderr, ", nregs=%d )\n", cum->nregs);
--- 2152,2157 ----
*************** function_arg_advance (CUMULATIVE_ARGS *c
*** 2780,2787 ****
break;
case DFmode:
- if (!TARGET_SSE2)
- break;
case SFmode:
if (!cum->float_in_sse)
break;
--- 2829,2834 ----
*************** function_arg (CUMULATIVE_ARGS *cum, enum
*** 2909,2916 ****
}
break;
case DFmode:
- if (!TARGET_SSE2)
- break;
case SFmode:
if (!cum->float_in_sse)
break;
--- 2956,2961 ----
*************** ix86_value_regno (enum machine_mode mode
*** 3249,3262 ****
return 0;
/* Floating point return values in %st(0), except for local functions when
! SSE math is enabled. */
! if (func && SSE_FLOAT_MODE_P (mode) && TARGET_SSE_MATH
! && flag_unit_at_a_time)
! {
! struct cgraph_local_info *i = cgraph_local_info (func);
! if (i && i->local)
! return FIRST_SSE_REG;
! }
return FIRST_FLOAT_REG;
}
--- 3294,3303 ----
return 0;
/* Floating point return values in %st(0), except for local functions when
! SSE math is enabled or for functions with sseregparm attribute. */
! if (func && (mode == SFmode || mode == DFmode)
! && ix86_function_sseregparm (TREE_TYPE (func), func))
! return FIRST_SSE_REG;
return FIRST_FLOAT_REG;
}
/* { dg-do compile { target i?86-*-* } } */
/* { dg-options "-O2 -msse" } */
float essef(float) __attribute__((sseregparm));
double essed(double) __attribute__((sseregparm));
float __attribute__((sseregparm, noinline)) ssef(float f) { return f; }
double __attribute__((sseregparm, noinline)) ssed(double d) { return d; }
extern double d;
extern float f;
void test(void)
{
f = essef(f);
d = essed(d);
f = ssef(f);
d = ssed(d);
}
/* { dg-final { scan-assembler-not "fldl" } } */
/* { dg-do compile { target i?86-*-* } } */
/* { dg-options "-mno-sse" } */
float essef(float) __attribute__((sseregparm));
double essed(double) __attribute__((sseregparm));
float __attribute__((sseregparm, noinline)) ssef(float f) { return f; } /* { dg-warning "SSE" } */
double __attribute__((sseregparm, noinline)) ssed(double d) { return d; } /* { dg-warning "SSE" } */
extern double d;
extern float f;
void test(void)
{
f = essef(f); /* { dg-warning "SSE" } */
d = essed(d); /* { dg-warning "SSE" } */
f = ssef(f); /* { dg-warning "SSE" } */
d = ssed(d); /* { dg-warning "SSE" } */
}