diff options
Diffstat (limited to 'gcc/testsuite/gcc.target/sparc/struct-ret-check.c')
-rw-r--r-- | gcc/testsuite/gcc.target/sparc/struct-ret-check.c | 126 |
1 files changed, 126 insertions, 0 deletions
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 000000000..350224eb6 --- /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); +} |