| /* This header file should be included for the purpose of function return |
| value testing. */ |
| |
| #include "abitest-common.h" |
| #include "validate_memory.h" |
| |
| void (*testfunc_ptr)(char* stack); |
| |
| /* Helper macros to generate function name. Example of the function name: |
| func_return_val_1. */ |
| #define FUNC_BASE_NAME func_return_val_ |
| #define FUNC_NAME_COMBINE(base,suffix) base ## suffix |
| #define FUNC_NAME_1(base,suffix) FUNC_NAME_COMBINE(base,suffix) |
| #define FUNC_NAME(suffix) FUNC_NAME_1(FUNC_BASE_NAME,suffix) |
| #define TEST_FUNC_BASE_NAME testfunc_ |
| #define TEST_FUNC_NAME(suffix) FUNC_NAME_1(TEST_FUNC_BASE_NAME,suffix) |
| |
| #undef DUMP_STATUS |
| #ifdef DUMP_ENABLED |
| #define DUMP_STATUS(type,val) printf ("### Checking "#type" "#val"\n"); |
| #else |
| #define DUMP_STATUS(type,val) |
| #endif |
| |
| /* Generate code to do memcmp to check if the returned value is in the |
| correct location and has the expected value. |
| Note that for value that is returned in the caller-allocated memory |
| block, we get the address from the saved x8 register. x8 is saved |
| just after the callee is returned; we assume that x8 has not been |
| clobbered at then, although there is no requirement for the callee |
| preserve the value stored in x8. Luckily, all test cases here are |
| simple enough that x8 doesn't normally get clobbered (although not |
| guaranteed). */ |
| #undef FUNC_VAL_CHECK |
| #define FUNC_VAL_CHECK(id, type, val, offset, layout) \ |
| void TEST_FUNC_NAME(id)(char* stack) \ |
| { \ |
| type __x = val; \ |
| char* addr; \ |
| DUMP_STATUS(type,val) \ |
| if (offset != X8) \ |
| addr = stack + offset; \ |
| else \ |
| addr = *(char **)(stack + X8); \ |
| if (validate_memory (&__x, addr, sizeof (type), layout) != 0) \ |
| abort(); \ |
| } |
| |
| /* Composite larger than 16 bytes is replaced by a pointer to a copy prepared |
| by the caller, so here we extrat the pointer, deref it and compare the |
| content with that of the original one. */ |
| #define PTR(type, val, offset, ...) { \ |
| type * ptr; \ |
| DUMP_ARG(type,val) \ |
| ptr = *(type **)(stack + offset); \ |
| if (memcmp (ptr, &val, sizeof (type)) != 0) abort (); \ |
| } |
| |
| #include TESTFILE |
| |
| MYFUNCTYPE myfunc () PCSATTR; |
| |
| /* Define the function to return VAL of type TYPE. I and D in the |
| parameter list are two dummy parameters to help improve the detection |
| of bugs like a short vector being returned in X0 after copied from V0. */ |
| #undef FUNC_VAL_CHECK |
| #define FUNC_VAL_CHECK(id, type, var, offset, layout) \ |
| __attribute__ ((noinline)) type FUNC_NAME (id) (int i, double d, type t) \ |
| { \ |
| asm (""::"r" (i),"r" (d)); /* asm prevents function from getting \ |
| optimized away. Using i and d prevents \ |
| warnings about unused parameters. \ |
| */ \ |
| return t; \ |
| } |
| #include TESTFILE |
| |
| |
| /* Call the function to return value and call the checking function |
| to validate. See the comment above for the reason of having 0 and 0.0 |
| in the function argument list. */ |
| #undef FUNC_VAL_CHECK |
| #define FUNC_VAL_CHECK(id, type, var, offset, layout) \ |
| { \ |
| testfunc_ptr = TEST_FUNC_NAME(id); \ |
| FUNC_NAME(id) (0, 0.0, var); \ |
| myfunc (); \ |
| } |
| |
| int main() |
| { |
| which_kind_of_test = TK_RETURN; |
| |
| #ifdef HAS_DATA_INIT_FUNC |
| init_data (); |
| #endif |
| |
| #include TESTFILE |
| |
| return 0; |
| } |