This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH] 4th try: Add sseregparm function attribute for x86
Another round - this time with some testcases verifying we appropriately
error out on invalid calling convention attribute combinations and some
verifying allowed combinations actually work. Behavior wise this patch
shouldn't change anything but add the sseregparm attribute.
Bootstrapped and regtested on i686-pc-linux-gnu and
x86_64-unknown-linux-gnu for the C language.
Ok for mainline?
Thanks,
Richard.
2005-06-15 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.target/i386/attributes-error.c: New testcase.
* gcc.target/i386/fastcall-sseregparm.c: Likewise.
* gcc.target/i386/regparm-stdcall.c: Likewise.
* gcc.target/i386/sseregparm-1.c: Likewise.
* gcc.target/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 15 Jun 2005 17:52:57 -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.829
diff -c -3 -p -r1.829 i386.c
*** config/i386/i386.c 10 Jun 2005 21:45:12 -0000 1.829
--- config/i386/i386.c 15 Jun 2005 19:40:08 -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
*** 1660,1674 ****
/* { 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 },
--- 1659,1676 ----
/* { 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,
*** 1743,1801 ****
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
- && 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
- {
- 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)
- {
- 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
--- 1745,1759 ----
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_regparm_attribute (tree *nod
*** 1805,1815 ****
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)
{
--- 1763,1781 ----
warning (OPT_Wattributes, "%qs attribute only applies to functions",
IDENTIFIER_POINTER (name));
*no_add_attrs = true;
+ return NULL_TREE;
}
!
! /* Can combine regparm with all attributes but fastcall. */
! 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
*** 1825,1836 ****
*no_add_attrs = true;
}
! if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (*node)))
! {
error ("fastcall and regparm attributes are not compatible");
}
}
return NULL_TREE;
}
--- 1791,1853 ----
*no_add_attrs = true;
}
! return NULL_TREE;
! }
!
! if (TARGET_64BIT)
! {
! 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 ("stdcall", TYPE_ATTRIBUTES (*node)))
! {
! error ("fastcall and stdcall 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");
+ }
+ if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (*node)))
+ {
+ error ("stdcall and fastcall 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. */
+
return NULL_TREE;
}
*************** ix86_comp_type_attributes (tree type1, t
*** 1847,1864 ****
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;
}
--- 1864,1886 ----
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
*** 1907,1912 ****
--- 1929,1975 ----
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
*** 2041,2050 ****
*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)
--- 2104,2110 ----
*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
*** 2053,2059 ****
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)))
--- 2113,2120 ----
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
*** 2061,2066 ****
--- 2122,2137 ----
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
*** 2084,2089 ****
--- 2155,2161 ----
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
*** 2093,2113 ****
|| (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);
--- 2165,2170 ----
*************** function_arg_advance (CUMULATIVE_ARGS *c
*** 2785,2792 ****
break;
case DFmode:
- if (!TARGET_SSE2)
- break;
case SFmode:
if (!cum->float_in_sse)
break;
--- 2842,2847 ----
*************** function_arg (CUMULATIVE_ARGS *cum, enum
*** 2914,2921 ****
}
break;
case DFmode:
- if (!TARGET_SSE2)
- break;
case SFmode:
if (!cum->float_in_sse)
break;
--- 2969,2974 ----
*************** ix86_value_regno (enum machine_mode mode
*** 3274,3287 ****
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;
}
--- 3327,3336 ----
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-*-* } } */
void foo1(int i, int j) __attribute__((fastcall, cdecl)); /* { dg-error "not compatible" } */
void foo2(int i, int j) __attribute__((fastcall, stdcall)); /* { dg-error "not compatible" } */
void foo3(int i, int j) __attribute__((fastcall, regparm(2))); /* { dg-error "not compatible" } */
void foo4(int i, int j) __attribute__((stdcall, cdecl)); /* { dg-error "not compatible" } */
void foo5(int i, int j) __attribute__((stdcall, fastcall)); /* { dg-error "not compatible" } */
void foo6(int i, int j) __attribute__((cdecl, fastcall)); /* { dg-error "not compatible" } */
void foo7(int i, int j) __attribute__((cdecl, stdcall)); /* { dg-error "not compatible" } */
void foo8(int i, int j) __attribute__((regparm(2), fastcall)); /* { dg-error "not compatible" } */
/* { dg-do run } */
/* { dg-options "-mpreferred-stack-boundary=4 -msse" } */
extern void abort(void);
void __attribute__((fastcall, sseregparm)) foo(int i, int j, float x)
{
static int last_align = -1;
int dummy, align = (int)&dummy & 15;
if (last_align < 0)
last_align = align;
else if (align != last_align)
abort ();
}
int main()
{
foo(0,0,0.0);
foo(0,0,0.0);
return 0;
}
/* { dg-do run } */
/* { dg-options -mpreferred-stack-boundary=4 } */
extern void abort(void);
void __attribute__((regparm(2), stdcall)) foo(int i, int j, float x)
{
static int last_align = -1;
int dummy, align = (int)&dummy & 15;
if (last_align < 0)
last_align = align;
else if (align != last_align)
abort ();
}
int main()
{
foo(0,0,0.0);
foo(0,0,0.0);
return 0;
}
/* { dg-do compile } */
/* { 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 } */
/* { 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" } */
}