summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcarlos <carlos@138bc75d-0d04-0410-961f-82ee72b054a4>2006-04-04 16:19:59 +0000
committercarlos <carlos@138bc75d-0d04-0410-961f-82ee72b054a4>2006-04-04 16:19:59 +0000
commitd8c09ceb51453b4bf62d7507b613ea5e423dc9ab (patch)
tree320de322098272075404c4d8bf933e1c2b3bfa03
parent169cdb47e4f165e6716feefad48321205444342b (diff)
downloadgcc-d8c09ceb51453b4bf62d7507b613ea5e423dc9ab.tar.gz
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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@112672 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/ChangeLog13
-rw-r--r--gcc/config/sparc/sparc.c49
-rw-r--r--gcc/config/sparc/sparc.md9
-rw-r--r--gcc/config/sparc/sparc.opt3
-rw-r--r--gcc/doc/tm.texi11
-rw-r--r--gcc/function.c2
-rw-r--r--gcc/testsuite/ChangeLog4
-rw-r--r--gcc/testsuite/gcc.target/sparc/struct-ret-check.c126
8 files changed, 208 insertions, 9 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 192d6f79509..a9ad12d75f7 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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
diff --git a/gcc/config/sparc/sparc.c b/gcc/config/sparc/sparc.c
index 709a92b518e..e880226412e 100644
--- a/gcc/config/sparc/sparc.c
+++ b/gcc/config/sparc/sparc.c
@@ -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))))
diff --git a/gcc/config/sparc/sparc.md b/gcc/config/sparc/sparc.md
index b6ebb8287a1..ed68f1e8a1e 100644
--- a/gcc/config/sparc/sparc.md
+++ b/gcc/config/sparc/sparc.md
@@ -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")
diff --git a/gcc/config/sparc/sparc.opt b/gcc/config/sparc/sparc.opt
index da50e1a15ec..8cdf11cf735 100644
--- a/gcc/config/sparc/sparc.opt
+++ b/gcc/config/sparc/sparc.opt
@@ -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
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index 2e7632e5a3a..69514e244ab 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -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
diff --git a/gcc/function.c b/gcc/function.c
index 2e9aec05c82..3becef3183d 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -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. */
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index f626bd795af..1872b3d7eea 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -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
diff --git a/gcc/testsuite/gcc.target/sparc/struct-ret-check.c b/gcc/testsuite/gcc.target/sparc/struct-ret-check.c
new file mode 100644
index 00000000000..350224eb667
--- /dev/null
+++ b/gcc/testsuite/gcc.target/sparc/struct-ret-check.c
@@ -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);
+}