127 lines
3.2 KiB
C
127 lines
3.2 KiB
C
/* 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 -fno-pie" } */
|
|
/* { 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 as well. 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);
|
|
}
|