/* 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 */ /* { dg-do run { target sparc*-*-solaris* sparc*-*-linux* sparc*-*-*bsd* } } */ /* { dg-options "-mstd-struct-return" } */ /* { dg-require-effective-target ilp32 } */ #include #include #include /* 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); }