mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-03-28 16:01:00 +08:00
tm.texi (TARGET_STRUCT_VALUE_RTX): Document new value 2 for incoming.
gcc/ 2006-04-04 Carlos O'Donell <carlos@codesourcery.com> * doc/tm.texi (TARGET_STRUCT_VALUE_RTX): Document new value 2 for incoming. * function.c (expand_function_start): Call struct_value_rtx with incoming as 2. * config/sparc/sparc.md: Comment updated_return. * config/sparc/sparc.opt: Add -mstd-struct-return option. * config/sparc/sparc.c (sparc_struct_value_rtx): Use standard struct return if sparc_std_struct_return and incoming is 2. (print_operand): Do not adjust return if sparc_std_struct_return. gcc/testsuite/ 2006-04-04 Carlos O'Donell <carlos@codesourcery.com> * gcc.target/sparc/struct-ret-check.c: New test. From-SVN: r112672
This commit is contained in:
parent
41ed243ff0
commit
2225b57c28
@ -1,3 +1,16 @@
|
||||
2006-04-04 Carlos O'Donell <carlos@codesourcery.com>
|
||||
|
||||
* doc/tm.texi (TARGET_STRUCT_VALUE_RTX): Document
|
||||
new value 2 for incoming.
|
||||
* function.c (expand_function_start): Call struct_value_rtx
|
||||
with incoming as 2.
|
||||
* config/sparc/sparc.md: Comment updated_return.
|
||||
* config/sparc/sparc.opt: Add -mstd-struct-return option.
|
||||
* config/sparc/sparc.c (sparc_struct_value_rtx): Use standard
|
||||
struct return if sparc_std_struct_return and incoming is 2.
|
||||
(print_operand): Do not adjust return if
|
||||
sparc_std_struct_return.
|
||||
|
||||
2006-04-04 Roger Sayle <roger@eyesopen.com>
|
||||
|
||||
* builtins.c (fold_builtin_sprintf): Use fold_convert instead of
|
||||
|
@ -5425,7 +5425,7 @@ sparc_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
|
||||
Return where to find the structure return value address. */
|
||||
|
||||
static rtx
|
||||
sparc_struct_value_rtx (tree fndecl ATTRIBUTE_UNUSED, int incoming)
|
||||
sparc_struct_value_rtx (tree fndecl, int incoming)
|
||||
{
|
||||
if (TARGET_ARCH64)
|
||||
return 0;
|
||||
@ -5440,6 +5440,46 @@ sparc_struct_value_rtx (tree fndecl ATTRIBUTE_UNUSED, int incoming)
|
||||
mem = gen_rtx_MEM (Pmode, plus_constant (stack_pointer_rtx,
|
||||
STRUCT_VALUE_OFFSET));
|
||||
|
||||
/* Only follow the SPARC ABI for fixed-size structure returns.
|
||||
Variable size structure returns are handled per the normal
|
||||
procedures in GCC. This is enabled by -mstd-struct-return */
|
||||
if (incoming == 2
|
||||
&& sparc_std_struct_return
|
||||
&& TYPE_SIZE_UNIT (TREE_TYPE (fndecl))
|
||||
&& TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (fndecl))) == INTEGER_CST)
|
||||
{
|
||||
/* We must check and adjust the return address, as it is
|
||||
optional as to whether the return object is really
|
||||
provided. */
|
||||
rtx ret_rtx = gen_rtx_REG (Pmode, 31);
|
||||
rtx scratch = gen_reg_rtx (SImode);
|
||||
rtx endlab = gen_label_rtx ();
|
||||
|
||||
/* Calculate the return object size */
|
||||
tree size = TYPE_SIZE_UNIT (TREE_TYPE (fndecl));
|
||||
rtx size_rtx = GEN_INT (TREE_INT_CST_LOW (size) & 0xfff);
|
||||
/* Construct a temporary return value */
|
||||
rtx temp_val = assign_stack_local (Pmode, TREE_INT_CST_LOW (size), 0);
|
||||
|
||||
/* Implement SPARC 32-bit psABI callee returns struck checking
|
||||
requirements:
|
||||
|
||||
Fetch the instruction where we will return to and see if
|
||||
it's an unimp instruction (the most significant 10 bits
|
||||
will be zero). */
|
||||
emit_move_insn (scratch, gen_rtx_MEM (SImode,
|
||||
plus_constant (ret_rtx, 8)));
|
||||
/* Assume the size is valid and pre-adjust */
|
||||
emit_insn (gen_add3_insn (ret_rtx, ret_rtx, GEN_INT (4)));
|
||||
emit_cmp_and_jump_insns (scratch, size_rtx, EQ, const0_rtx, SImode, 0, endlab);
|
||||
emit_insn (gen_sub3_insn (ret_rtx, ret_rtx, GEN_INT (4)));
|
||||
/* Assign stack temp:
|
||||
Write the address of the memory pointed to by temp_val into
|
||||
the memory pointed to by mem */
|
||||
emit_move_insn (mem, XEXP (temp_val, 0));
|
||||
emit_label (endlab);
|
||||
}
|
||||
|
||||
set_mem_alias_set (mem, struct_value_alias_set);
|
||||
return mem;
|
||||
}
|
||||
@ -6639,9 +6679,14 @@ print_operand (FILE *file, rtx x, int code)
|
||||
so we have to account for it. This insn is used in the 32-bit ABI
|
||||
when calling a function that returns a non zero-sized structure. The
|
||||
64-bit ABI doesn't have it. Be careful to have this test be the same
|
||||
as that used on the call. */
|
||||
as that used on the call. The exception here is that when
|
||||
sparc_std_struct_return is enabled, the psABI is followed exactly
|
||||
and the adjustment is made by the code in sparc_struct_value_rtx.
|
||||
The call emitted is the same when sparc_std_struct_return is
|
||||
present. */
|
||||
if (! TARGET_ARCH64
|
||||
&& current_function_returns_struct
|
||||
&& ! sparc_std_struct_return
|
||||
&& (TREE_CODE (DECL_SIZE (DECL_RESULT (current_function_decl)))
|
||||
== INTEGER_CST)
|
||||
&& ! integer_zerop (DECL_SIZE (DECL_RESULT (current_function_decl))))
|
||||
|
@ -7094,8 +7094,13 @@
|
||||
DONE;
|
||||
})
|
||||
|
||||
;; This is a bit of a hack. We're incrementing a fixed register (%i7),
|
||||
;; and parts of the compiler don't want to believe that the add is needed.
|
||||
;; Adjust the return address conditionally. If the value of op1 is equal
|
||||
;; to all zero then adjust the return address i.e. op0 = op0 + 4.
|
||||
;; This is technically *half* the check required by the 32-bit SPARC
|
||||
;; psABI. This check only ensures that an "unimp" insn was written by
|
||||
;; the caller, but doesn't check to see if the expected size matches
|
||||
;; (this is encoded in the 12 lower bits). This check is obsolete and
|
||||
;; only used by the above code "untyped_return".
|
||||
|
||||
(define_insn "update_return"
|
||||
[(unspec:SI [(match_operand:SI 0 "register_operand" "r")
|
||||
|
@ -99,6 +99,9 @@ mcmodel=
|
||||
Target RejectNegative Joined Var(sparc_cmodel_string)
|
||||
Use given SPARC-V9 code model
|
||||
|
||||
mstd-struct-return
|
||||
Target Report RejectNegative Var(sparc_std_struct_return)
|
||||
Enable strict 32-bit psABI struct return checking.
|
||||
|
||||
Mask(LITTLE_ENDIAN)
|
||||
;; Generate code for little-endian
|
||||
|
@ -4187,12 +4187,15 @@ On some architectures the place where the structure value address
|
||||
is found by the called function is not the same place that the
|
||||
caller put it. This can be due to register windows, or it could
|
||||
be because the function prologue moves it to a different place.
|
||||
@var{incoming} is @code{true} when the location is needed in
|
||||
the context of the called function, and @code{false} in the context of
|
||||
@var{incoming} is @code{1} or @code{2} when the location is needed in
|
||||
the context of the called function, and @code{0} in the context of
|
||||
the caller.
|
||||
|
||||
If @var{incoming} is @code{true} and the address is to be found on the
|
||||
stack, return a @code{mem} which refers to the frame pointer.
|
||||
If @var{incoming} is non-zero and the address is to be found on the
|
||||
stack, return a @code{mem} which refers to the frame pointer. If
|
||||
@var{incoming} is @code{2}, the result is being used to fetch the
|
||||
structure value address at the beginning of a function. If you need
|
||||
to emit adjusting code, you should do it at this point.
|
||||
@end deftypefn
|
||||
|
||||
@defmac PCC_STATIC_STRUCT_RETURN
|
||||
|
@ -4120,7 +4120,7 @@ expand_function_start (tree subr)
|
||||
else
|
||||
#endif
|
||||
{
|
||||
rtx sv = targetm.calls.struct_value_rtx (TREE_TYPE (subr), 1);
|
||||
rtx sv = targetm.calls.struct_value_rtx (TREE_TYPE (subr), 2);
|
||||
/* Expect to be passed the address of a place to store the value.
|
||||
If it is passed as an argument, assign_parms will take care of
|
||||
it. */
|
||||
|
@ -1,3 +1,7 @@
|
||||
2006-04-04 Carlos O'Donell <carlos@codesourcery.com>
|
||||
|
||||
* gcc.target/sparc/struct-ret-check.c: New test.
|
||||
|
||||
2006-04-03 Jerry DeLisle <jvdelisle@gcc.gnu.org>
|
||||
|
||||
* gfortran.dg/fmt_zero_digits.f90: New test for no error when
|
||||
|
126
gcc/testsuite/gcc.target/sparc/struct-ret-check.c
Normal file
126
gcc/testsuite/gcc.target/sparc/struct-ret-check.c
Normal file
@ -0,0 +1,126 @@
|
||||
/* Copyright (C) 2006 Free Software Foundation, Inc. */
|
||||
/* Contributed by Carlos O'Donell on 2006-03-14 */
|
||||
|
||||
/* Test that GCC follows the SPARC 32-bit psABI with regards to
|
||||
structure return checking in a callee. When -mstd-struct-return
|
||||
is specificed then gcc will emit code to skip the unimp insn. */
|
||||
|
||||
/* Origin: Carlos O'Donell <carlos@codesourcery.com> */
|
||||
/* { dg-do run { target sparc*-*-solaris* sparc*-*-linux* sparc*-*-*bsd* } } */
|
||||
/* { dg-options "-mstd-struct-return" } */
|
||||
/* { dg-require-effective-target ilp32 } */
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
|
||||
/* Local declaration of div_t structure */
|
||||
struct mydiv_t {
|
||||
int rem;
|
||||
int quot;
|
||||
};
|
||||
|
||||
/* Global check variable used by signal handler */
|
||||
int check = 1;
|
||||
struct mydiv_t dcheck;
|
||||
|
||||
struct mydiv_t foo (void)
|
||||
{
|
||||
struct mydiv_t bar;
|
||||
bar.rem = 3;
|
||||
bar.quot = 4;
|
||||
return bar;
|
||||
}
|
||||
|
||||
void handle_sigill (int signum)
|
||||
{
|
||||
if (signum == SIGILL && check == 2)
|
||||
{
|
||||
/* We expected a SIGILL due to a mismatch in unimp size
|
||||
and struct mydiv_t size */
|
||||
exit (0);
|
||||
}
|
||||
else
|
||||
abort ();
|
||||
}
|
||||
|
||||
/* Implement 3 checks to validate SPARC 32-bit psABI callee
|
||||
returns struct
|
||||
|
||||
Test1: Save area is valid. unimp size is valid.
|
||||
Success: Save area modified correctly.
|
||||
Failure: Save area unmodified.
|
||||
|
||||
Test2: Save area is valid. unimp size is invalid (invalid insn).
|
||||
Success: Save area unmodified. check == 2.
|
||||
Failure: Save area modified or check == 1.
|
||||
|
||||
Test3: Save area is invalid. unimp size is invalid (invalid size).
|
||||
Success: Will raise a SIGILL.
|
||||
Failure: SIGSEGV caused by write to invalid save area. */
|
||||
|
||||
int main (void)
|
||||
{
|
||||
dcheck.rem = 1;
|
||||
dcheck.quot = 2;
|
||||
|
||||
/*** Test1 ***/
|
||||
/* Insert a call, insert unimp by hand */
|
||||
__asm__ ("st %1, [ %%sp + 0x40 ]\n\t"
|
||||
"call foo\n\t"
|
||||
" nop\n\t"
|
||||
"unimp %2\n\t"
|
||||
: "=m" (dcheck)
|
||||
: "r" (&dcheck), "i" (sizeof(struct mydiv_t))
|
||||
: "memory");
|
||||
|
||||
/* If the caller doesn't adjust the return, then it crashes.
|
||||
Check the result too. */
|
||||
|
||||
if ((dcheck.rem != 3) || (dcheck.quot !=4))
|
||||
abort ();
|
||||
|
||||
|
||||
/*** Test 2 ***/
|
||||
dcheck.rem = 1;
|
||||
dcheck.quot = 2;
|
||||
|
||||
/* Ignore the return of the function */
|
||||
__asm__ ("st %3, [ %%sp + 0x40 ]\n\t"
|
||||
"call foo\n\t"
|
||||
" nop\n\t"
|
||||
"mov %2, %0\n\t"
|
||||
: "+r" (check), "=m" (dcheck)
|
||||
: "i" (0x2), "r" (&dcheck)
|
||||
: "memory");
|
||||
|
||||
/* If the caller does an unconditional adjustment it will skip
|
||||
the mov, and then we can fail the test based on check's value
|
||||
We pass a valid pointer to a save area in order to check if
|
||||
caller incorrectly wrote to the save area aswell. There may
|
||||
be a case where the unimp check and skip is correct, but the
|
||||
write to the save area still occurs. */
|
||||
|
||||
if (check != 2)
|
||||
abort ();
|
||||
|
||||
if ((dcheck.rem != 1) || (dcheck.quot != 2))
|
||||
abort ();
|
||||
|
||||
/*** Test 3 ***/
|
||||
/* Prepare a test that must SIGILL. According to the spec
|
||||
if the sizes of the save area and return don't match then
|
||||
the copy is ignored and we return to the unimp. */
|
||||
|
||||
signal (SIGILL, handle_sigill);
|
||||
|
||||
__asm__ ("st %%g0, [ %%sp + 0x40 ]\n\t"
|
||||
"call foo\n\t"
|
||||
" nop\n\t"
|
||||
"unimp %0\n\t"
|
||||
: /* No outputs */
|
||||
: "i" (sizeof(struct mydiv_t)-1)
|
||||
: "memory");
|
||||
|
||||
/* NEVER REACHED */
|
||||
exit (0);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user