extend.texi: Document sseregparm target attribute.

2005-06-16  Richard Guenther  <rguenth@gcc.gnu.org>

	* doc/extend.texi: Document sseregparm target attribute.
	Clarify fastcall and regparm documentation.
	* config/i386/i386.h: Adjust float_in_sse 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.

From-SVN: r101085
This commit is contained in:
Richard Guenther 2005-06-16 16:39:51 +00:00 committed by Richard Biener
parent 9f18db39e6
commit 2f84b963e9
10 changed files with 279 additions and 103 deletions

View file

@ -1,3 +1,22 @@
2005-06-16 Richard Guenther <rguenth@gcc.gnu.org>
* doc/extend.texi: Document sseregparm target attribute.
Clarify fastcall and regparm documentation.
* config/i386/i386.h: Adjust float_in_sse 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.
2005-06-16 Paolo Bonzini <bonzini@gnu.org>
Daniel Jacobowitz <dan@codesourcery.com>
Alan Modra <amodra.bigpond.net.au>

View file

@ -890,8 +890,7 @@ static int ix86_comp_type_attributes (tree, tree);
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 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);
@ -1660,15 +1659,18 @@ const struct attribute_spec ix86_attribute_table[] =
/* { 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 },
{ "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_cdecl_attribute },
{ "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_cdecl_attribute },
{ "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_regparm_attribute },
{ "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 },
@ -1743,12 +1745,15 @@ ix86_function_ok_for_sibcall (tree decl, tree exp)
return true;
}
/* Handle a "cdecl", "stdcall", or "fastcall" attribute;
/* Handle "cdecl", "stdcall", "fastcall", "regparm" and "sseregparm"
calling convention attributes;
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)
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
@ -1758,58 +1763,19 @@ ix86_handle_cdecl_attribute (tree *node, tree name,
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");
}
}
return NULL_TREE;
}
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
&& 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
/* 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)
{
@ -1825,12 +1791,63 @@ ix86_handle_regparm_attribute (tree *node, tree name, tree args,
*no_add_attrs = true;
}
if (lookup_attribute ("fastcall", TYPE_ATTRIBUTES (*node)))
{
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;
}
@ -1847,18 +1864,23 @@ ix86_comp_type_attributes (tree type1, tree type2)
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)))
/* 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;
if (ix86_function_regparm (type1, NULL)
!= ix86_function_regparm (type2, NULL))
return 0;
return 1;
}
@ -1907,6 +1929,47 @@ ix86_function_regparm (tree type, tree decl)
return regparm;
}
/* 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
or considering a libcall. Otherwise return 0. */
static int
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 0;
}
return 2;
}
/* 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 TARGET_SSE2 ? 2 : 1;
}
return 0;
}
/* 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. */
@ -2041,10 +2104,7 @@ init_cumulative_args (CUMULATIVE_ARGS *cum, /* Argument info to initialize */
*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;
cum->nregs = ix86_regparm;
if (TARGET_SSE)
cum->sse_nregs = SSE_REGPARM_MAX;
if (TARGET_MMX)
@ -2053,7 +2113,8 @@ init_cumulative_args (CUMULATIVE_ARGS *cum, /* Argument info to initialize */
cum->warn_mmx = true;
cum->maybe_vaarg = false;
/* Use ecx and edx registers if function has fastcall attribute */
/* 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)))
@ -2061,8 +2122,14 @@ init_cumulative_args (CUMULATIVE_ARGS *cum, /* Argument info to initialize */
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. */
cum->float_in_sse = ix86_function_sseregparm (fntype, fndecl);
/* Determine if this function has variable arguments. This is
indicated by the last argument being 'void_type_mode' if there
are no variable arguments. If there are variable arguments, then
@ -2084,6 +2151,7 @@ init_cumulative_args (CUMULATIVE_ARGS *cum, /* Argument info to initialize */
cum->warn_sse = 0;
cum->warn_mmx = 0;
cum->fastcall = 0;
cum->float_in_sse = 0;
}
cum->maybe_vaarg = true;
}
@ -2093,21 +2161,6 @@ init_cumulative_args (CUMULATIVE_ARGS *cum, /* Argument info to initialize */
|| (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);
@ -2785,10 +2838,10 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
break;
case DFmode:
if (!TARGET_SSE2)
if (cum->float_in_sse < 2)
break;
case SFmode:
if (!cum->float_in_sse)
if (cum->float_in_sse < 1)
break;
/* FALLTHRU */
@ -2914,10 +2967,10 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode orig_mode,
}
break;
case DFmode:
if (!TARGET_SSE2)
if (cum->float_in_sse < 2)
break;
case SFmode:
if (!cum->float_in_sse)
if (cum->float_in_sse < 1)
break;
/* FALLTHRU */
case TImode:
@ -3274,13 +3327,13 @@ ix86_value_regno (enum machine_mode mode, tree func)
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)
SSE math is enabled or for functions with sseregparm attribute. */
if (func && (mode == SFmode || mode == DFmode))
{
struct cgraph_local_info *i = cgraph_local_info (func);
if (i && i->local)
return FIRST_SSE_REG;
int sse_level = ix86_function_sseregparm (TREE_TYPE (func), func);
if ((sse_level >= 1 && mode == SFmode)
|| (sse_level == 2 && mode == DFmode))
return FIRST_SSE_REG;
}
return FIRST_FLOAT_REG;

View file

@ -1478,8 +1478,8 @@ typedef struct ix86_args {
int mmx_nregs; /* # mmx registers available for passing */
int mmx_regno; /* next available mmx register number */
int maybe_vaarg; /* true for calls to possibly vardic fncts. */
int float_in_sse; /* true if in 32-bit mode SFmode/DFmode should
be passed in SSE registers. */
int float_in_sse; /* 1 if in 32-bit mode SFmode (2 for DFmode) should
be passed in SSE registers. Otherwise 0. */
} CUMULATIVE_ARGS;
/* Initialize a variable CUM of type CUMULATIVE_ARGS

View file

@ -1752,9 +1752,10 @@ the @code{rtc}.
@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
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})
@ -2126,9 +2127,9 @@ than 2.96.
@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
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
@ -2141,6 +2142,14 @@ safe since the loaders there save all registers. (Lazy binding can be
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

View file

@ -1,3 +1,11 @@
2005-06-16 Richard Guenther <rguenth@gcc.gnu.org>
* 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.
2005-06-16 Nathan Sidwell <nathan@codesourcery.com>
* g++.dg/rtti/crash2.C: New.

View file

@ -0,0 +1,11 @@
/* { 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" } */

View file

@ -0,0 +1,21 @@
/* { 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;
}

View file

@ -0,0 +1,21 @@
/* { 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;
}

View file

@ -0,0 +1,18 @@
/* { 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" } } */

View file

@ -0,0 +1,16 @@
/* { 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" } */
}