2013-04-18 00:51:51 +04:00
# include <libtcc.h>
# include <stdlib.h>
# include <stdio.h>
# include <string.h>
2013-04-19 14:08:12 +04:00
# include <stdarg.h>
2013-04-18 00:51:51 +04:00
2013-04-20 02:21:33 +04:00
// MinGW has 80-bit rather than 64-bit long double which isn't compatible with TCC or MSVC
# if defined(_WIN32) && defined(__GNUC__)
# define LONG_DOUBLE double
# define LONG_DOUBLE_LITERAL(x) x
# else
# define LONG_DOUBLE long double
# define LONG_DOUBLE_LITERAL(x) x ## L
# endif
tccpp: fix issues, add tests
* fix some macro expansion issues
* add some pp tests in tests/pp
* improved tcc -E output for better diff'ability
* remove -dD feature (quirky code, exotic feature,
didn't work well)
Based partially on ideas / researches from PipCet
Some issues remain with VA_ARGS macros (if used in a
rather tricky way).
Also, to keep it simple, the pp doesn't automtically
add any extra spaces to separate tokens which otherwise
would form wrong tokens if re-read from tcc -E output
(such as '+' '=') GCC does that, other compilers don't.
* cleanups
- #line 01 "file" / # 01 "file" processing
- #pragma comment(lib,"foo")
- tcc -E: forward some pragmas to output (pack, comment(lib))
- fix macro parameter list parsing mess from
a3fc54345949535524d01319e1ca6378b7c2c201
a715d7143d9d17da17e67fec6af1c01409a71a31
(some coffee might help, next time ;)
- introduce TOK_PPSTR - to have character constants as
written in the file (similar to TOK_PPNUM)
- allow '\' appear in macros
- new functions begin/end_macro to:
- fix switching macro levels during expansion
- allow unget_tok to unget more than one tok
- slight speedup by using bitflags in isidnum_table
Also:
- x86_64.c : fix decl after statements
- i386-gen,c : fix a vstack leak with VLA on windows
- configure/Makefile : build on windows (MSYS) was broken
- tcc_warning: fflush stderr to keep output order (win32)
2015-05-09 15:29:39 +03:00
static int g_argc ;
static char * * g_argv ;
static void set_options ( TCCState * s , int argc , char * * argv )
{
int i ;
for ( i = 1 ; i < argc ; + + i ) {
char * a = argv [ i ] ;
if ( a [ 0 ] = = ' - ' ) {
if ( a [ 1 ] = = ' B ' )
tcc_set_lib_path ( s , a + 2 ) ;
else if ( a [ 1 ] = = ' I ' )
tcc_add_include_path ( s , a + 2 ) ;
else if ( a [ 1 ] = = ' L ' )
tcc_add_library_path ( s , a + 2 ) ;
}
}
}
2013-04-18 00:51:51 +04:00
typedef int ( * callback_type ) ( void * ) ;
/*
* Compile source code and call a callback with a pointer to the symbol " f " .
*/
static int run_callback ( const char * src , callback_type callback ) {
TCCState * s ;
int result ;
void * ptr ;
2015-07-29 23:53:57 +03:00
2013-04-18 00:51:51 +04:00
s = tcc_new ( ) ;
if ( ! s )
return - 1 ;
tccpp: fix issues, add tests
* fix some macro expansion issues
* add some pp tests in tests/pp
* improved tcc -E output for better diff'ability
* remove -dD feature (quirky code, exotic feature,
didn't work well)
Based partially on ideas / researches from PipCet
Some issues remain with VA_ARGS macros (if used in a
rather tricky way).
Also, to keep it simple, the pp doesn't automtically
add any extra spaces to separate tokens which otherwise
would form wrong tokens if re-read from tcc -E output
(such as '+' '=') GCC does that, other compilers don't.
* cleanups
- #line 01 "file" / # 01 "file" processing
- #pragma comment(lib,"foo")
- tcc -E: forward some pragmas to output (pack, comment(lib))
- fix macro parameter list parsing mess from
a3fc54345949535524d01319e1ca6378b7c2c201
a715d7143d9d17da17e67fec6af1c01409a71a31
(some coffee might help, next time ;)
- introduce TOK_PPSTR - to have character constants as
written in the file (similar to TOK_PPNUM)
- allow '\' appear in macros
- new functions begin/end_macro to:
- fix switching macro levels during expansion
- allow unget_tok to unget more than one tok
- slight speedup by using bitflags in isidnum_table
Also:
- x86_64.c : fix decl after statements
- i386-gen,c : fix a vstack leak with VLA on windows
- configure/Makefile : build on windows (MSYS) was broken
- tcc_warning: fflush stderr to keep output order (win32)
2015-05-09 15:29:39 +03:00
set_options ( s , g_argc , g_argv ) ;
2013-04-18 00:51:51 +04:00
if ( tcc_set_output_type ( s , TCC_OUTPUT_MEMORY ) = = - 1 )
return - 1 ;
if ( tcc_compile_string ( s , src ) = = - 1 )
return - 1 ;
if ( tcc_relocate ( s , TCC_RELOCATE_AUTO ) = = - 1 )
return - 1 ;
2015-07-29 23:53:57 +03:00
2013-04-18 00:51:51 +04:00
ptr = tcc_get_symbol ( s , " f " ) ;
if ( ! ptr )
return - 1 ;
result = callback ( ptr ) ;
2015-07-29 23:53:57 +03:00
2013-04-18 00:51:51 +04:00
tcc_delete ( s ) ;
2015-07-29 23:53:57 +03:00
2013-04-18 00:51:51 +04:00
return result ;
}
2013-04-20 02:21:33 +04:00
# define STR2(x) #x
# define STR(x) STR2(x)
2013-04-20 01:55:09 +04:00
# define RET_PRIMITIVE_TEST(name, type, val) \
2013-04-18 20:55:00 +04:00
static int ret_ # # name # # _test_callback ( void * ptr ) { \
type ( * callback ) ( type ) = ( type ( * ) ( type ) ) ptr ; \
2013-04-20 01:55:09 +04:00
type x = val ; \
2013-04-18 20:55:00 +04:00
type y = callback ( x ) ; \
return ( y = = x + x ) ? 0 : - 1 ; \
} \
\
static int ret_ # # name # # _test ( void ) { \
2013-04-20 02:21:33 +04:00
const char * src = STR ( type ) " f( " STR ( type ) " x) {return x+x;} " ; \
2013-04-18 20:55:00 +04:00
return run_callback ( src , ret_ # # name # # _test_callback ) ; \
}
2013-04-20 01:55:09 +04:00
RET_PRIMITIVE_TEST ( int , int , 70000 )
RET_PRIMITIVE_TEST ( longlong , long long , 4333369356528LL )
RET_PRIMITIVE_TEST ( float , float , 63.0 )
RET_PRIMITIVE_TEST ( double , double , 14789798.0 )
2013-04-20 02:21:33 +04:00
RET_PRIMITIVE_TEST ( longdouble , LONG_DOUBLE , LONG_DOUBLE_LITERAL ( 378943892.0 ) )
2013-04-18 20:55:00 +04:00
2013-04-19 14:08:12 +04:00
/*
* ret_2float_test :
2015-07-29 23:53:57 +03:00
*
2013-04-19 14:08:12 +04:00
* On x86 - 64 , a struct with 2 floats should be packed into a single
* SSE register ( VT_DOUBLE is used for this purpose ) .
*/
2013-04-18 20:55:00 +04:00
typedef struct ret_2float_test_type_s { float x , y ; } ret_2float_test_type ;
typedef ret_2float_test_type ( * ret_2float_test_function_type ) ( ret_2float_test_type ) ;
static int ret_2float_test_callback ( void * ptr ) {
ret_2float_test_function_type f = ( ret_2float_test_function_type ) ptr ;
ret_2float_test_type a = { 10 , 35 } ;
ret_2float_test_type r ;
r = f ( a ) ;
return ( ( r . x = = a . x * 5 ) & & ( r . y = = a . y * 3 ) ) ? 0 : - 1 ;
}
static int ret_2float_test ( void ) {
const char * src =
" typedef struct ret_2float_test_type_s {float x, y;} ret_2float_test_type; "
" ret_2float_test_type f(ret_2float_test_type a) { \n "
" ret_2float_test_type r = {a.x*5, a.y*3}; \n "
" return r; \n "
" } \n " ;
return run_callback ( src , ret_2float_test_callback ) ;
}
2013-04-19 14:08:12 +04:00
/*
* ret_2double_test :
2015-07-29 23:53:57 +03:00
*
2013-04-19 18:33:16 +04:00
* On x86 - 64 , a struct with 2 doubles should be passed in two SSE
* registers .
2013-04-19 14:08:12 +04:00
*/
typedef struct ret_2double_test_type_s { double x , y ; } ret_2double_test_type ;
typedef ret_2double_test_type ( * ret_2double_test_function_type ) ( ret_2double_test_type ) ;
static int ret_2double_test_callback ( void * ptr ) {
ret_2double_test_function_type f = ( ret_2double_test_function_type ) ptr ;
ret_2double_test_type a = { 10 , 35 } ;
ret_2double_test_type r ;
r = f ( a ) ;
return ( ( r . x = = a . x * 5 ) & & ( r . y = = a . y * 3 ) ) ? 0 : - 1 ;
}
static int ret_2double_test ( void ) {
const char * src =
" typedef struct ret_2double_test_type_s {double x, y;} ret_2double_test_type; "
" ret_2double_test_type f(ret_2double_test_type a) { \n "
" ret_2double_test_type r = {a.x*5, a.y*3}; \n "
" return r; \n "
" } \n " ;
return run_callback ( src , ret_2double_test_callback ) ;
}
2015-04-26 20:31:39 +03:00
/*
* ret_8plus2double_test :
*
* This catches a corner case in the x86_64 ABI code : the first 7
* arguments fit into registers , the 8 th doesn ' t , but the 9 th argument
* fits into the 8 th XMM register .
*
* Note that the purpose of the 10 th argument is to avoid a situation
* in which gcc would accidentally put the double at the right
* address , thus causing a success message even though TCC actually
* generated incorrect code .
*/
typedef ret_2double_test_type ( * ret_8plus2double_test_function_type ) ( double , double , double , double , double , double , double , ret_2double_test_type , double , double ) ;
static int ret_8plus2double_test_callback ( void * ptr ) {
ret_8plus2double_test_function_type f = ( ret_8plus2double_test_function_type ) ptr ;
ret_2double_test_type a = { 10 , 35 } ;
ret_2double_test_type r ;
r = f ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , a , 37 , 38 ) ;
return ( ( r . x = = 37 ) & & ( r . y = = 37 ) ) ? 0 : - 1 ;
}
static int ret_8plus2double_test ( void ) {
const char * src =
" typedef struct ret_2double_test_type_s {double x, y;} ret_2double_test_type; "
" ret_2double_test_type f(double x1, double x2, double x3, double x4, double x5, double x6, double x7, ret_2double_test_type a, double x8, double x9) { \n "
" ret_2double_test_type r = { x8, x8 }; \n "
" return r; \n "
" } \n " ;
return run_callback ( src , ret_8plus2double_test_callback ) ;
}
2015-04-25 13:49:55 +03:00
/*
* ret_mixed_test :
*
* On x86 - 64 , a struct with a double and a 64 - bit integer should be
* passed in one SSE register and one integer register .
*/
typedef struct ret_mixed_test_type_s { double x ; long long y ; } ret_mixed_test_type ;
typedef ret_mixed_test_type ( * ret_mixed_test_function_type ) ( ret_mixed_test_type ) ;
static int ret_mixed_test_callback ( void * ptr ) {
ret_mixed_test_function_type f = ( ret_mixed_test_function_type ) ptr ;
ret_mixed_test_type a = { 10 , 35 } ;
ret_mixed_test_type r ;
r = f ( a ) ;
return ( ( r . x = = a . x * 5 ) & & ( r . y = = a . y * 3 ) ) ? 0 : - 1 ;
}
static int ret_mixed_test ( void ) {
const char * src =
" typedef struct ret_mixed_test_type_s {double x; long long y;} ret_mixed_test_type; "
" ret_mixed_test_type f(ret_mixed_test_type a) { \n "
" ret_mixed_test_type r = {a.x*5, a.y*3}; \n "
" return r; \n "
" } \n " ;
return run_callback ( src , ret_mixed_test_callback ) ;
}
/*
* ret_mixed2_test :
*
* On x86 - 64 , a struct with two floats and two 32 - bit integers should
* be passed in one SSE register and one integer register .
*/
typedef struct ret_mixed2_test_type_s { float x , x2 ; int y , y2 ; } ret_mixed2_test_type ;
typedef ret_mixed2_test_type ( * ret_mixed2_test_function_type ) ( ret_mixed2_test_type ) ;
static int ret_mixed2_test_callback ( void * ptr ) {
ret_mixed2_test_function_type f = ( ret_mixed2_test_function_type ) ptr ;
ret_mixed2_test_type a = { 10 , 5 , 35 , 7 } ;
ret_mixed2_test_type r ;
r = f ( a ) ;
return ( ( r . x = = a . x * 5 ) & & ( r . y = = a . y * 3 ) ) ? 0 : - 1 ;
}
static int ret_mixed2_test ( void ) {
const char * src =
" typedef struct ret_mixed2_test_type_s {float x, x2; int y,y2;} ret_mixed2_test_type; "
" ret_mixed2_test_type f(ret_mixed2_test_type a) { \n "
" ret_mixed2_test_type r = {a.x*5, 0, a.y*3, 0}; \n "
" return r; \n "
" } \n " ;
return run_callback ( src , ret_mixed2_test_callback ) ;
}
/*
* ret_mixed3_test :
*
* On x86 - 64 , this struct should be passed in two integer registers .
*/
typedef struct ret_mixed3_test_type_s { float x ; int y ; float x2 ; int y2 ; } ret_mixed3_test_type ;
typedef ret_mixed3_test_type ( * ret_mixed3_test_function_type ) ( ret_mixed3_test_type ) ;
static int ret_mixed3_test_callback ( void * ptr ) {
ret_mixed3_test_function_type f = ( ret_mixed3_test_function_type ) ptr ;
ret_mixed3_test_type a = { 10 , 5 , 35 , 7 } ;
ret_mixed3_test_type r ;
r = f ( a ) ;
return ( ( r . x = = a . x * 5 ) & & ( r . y2 = = a . y * 3 ) ) ? 0 : - 1 ;
}
static int ret_mixed3_test ( void ) {
const char * src =
" typedef struct ret_mixed3_test_type_s {float x; int y; float x2; int y2;} ret_mixed3_test_type; "
" ret_mixed3_test_type f(ret_mixed3_test_type a) { \n "
" ret_mixed3_test_type r = {a.x*5, 0, 0, a.y*3}; \n "
" return r; \n "
" } \n " ;
return run_callback ( src , ret_mixed3_test_callback ) ;
}
2013-04-18 20:27:34 +04:00
/*
* reg_pack_test : return a small struct which should be packed into
* registers ( Win32 ) during return .
*/
typedef struct reg_pack_test_type_s { int x , y ; } reg_pack_test_type ;
typedef reg_pack_test_type ( * reg_pack_test_function_type ) ( reg_pack_test_type ) ;
static int reg_pack_test_callback ( void * ptr ) {
reg_pack_test_function_type f = ( reg_pack_test_function_type ) ptr ;
reg_pack_test_type a = { 10 , 35 } ;
reg_pack_test_type r ;
r = f ( a ) ;
return ( ( r . x = = a . x * 5 ) & & ( r . y = = a . y * 3 ) ) ? 0 : - 1 ;
}
static int reg_pack_test ( void ) {
const char * src =
" typedef struct reg_pack_test_type_s {int x, y;} reg_pack_test_type; "
" reg_pack_test_type f(reg_pack_test_type a) { \n "
" reg_pack_test_type r = {a.x*5, a.y*3}; \n "
" return r; \n "
" } \n " ;
2015-07-29 23:53:57 +03:00
2013-04-18 20:27:34 +04:00
return run_callback ( src , reg_pack_test_callback ) ;
}
2013-04-19 14:08:12 +04:00
/*
* reg_pack_longlong_test : return a small struct which should be packed into
* registers ( x86 - 64 ) during return .
*/
typedef struct reg_pack_longlong_test_type_s { long long x , y ; } reg_pack_longlong_test_type ;
typedef reg_pack_longlong_test_type ( * reg_pack_longlong_test_function_type ) ( reg_pack_longlong_test_type ) ;
static int reg_pack_longlong_test_callback ( void * ptr ) {
reg_pack_longlong_test_function_type f = ( reg_pack_longlong_test_function_type ) ptr ;
reg_pack_longlong_test_type a = { 10 , 35 } ;
reg_pack_longlong_test_type r ;
r = f ( a ) ;
return ( ( r . x = = a . x * 5 ) & & ( r . y = = a . y * 3 ) ) ? 0 : - 1 ;
}
static int reg_pack_longlong_test ( void ) {
const char * src =
" typedef struct reg_pack_longlong_test_type_s {long long x, y;} reg_pack_longlong_test_type; "
" reg_pack_longlong_test_type f(reg_pack_longlong_test_type a) { \n "
" reg_pack_longlong_test_type r = {a.x*5, a.y*3}; \n "
" return r; \n "
" } \n " ;
2015-07-29 23:53:57 +03:00
2013-04-19 14:08:12 +04:00
return run_callback ( src , reg_pack_longlong_test_callback ) ;
}
2015-04-26 20:31:39 +03:00
/*
* ret_6plus2longlong_test :
*
* This catches a corner case in the x86_64 ABI code : the first 5
* arguments fit into registers , the 6 th doesn ' t , but the 7 th argument
* fits into the 6 th argument integer register , % r9 .
*
* Note that the purpose of the 10 th argument is to avoid a situation
* in which gcc would accidentally put the longlong at the right
* address , thus causing a success message even though TCC actually
* generated incorrect code .
*/
typedef reg_pack_longlong_test_type ( * ret_6plus2longlong_test_function_type ) ( long long , long long , long long , long long , long long , reg_pack_longlong_test_type , long long , long long ) ;
static int ret_6plus2longlong_test_callback ( void * ptr ) {
ret_6plus2longlong_test_function_type f = ( ret_6plus2longlong_test_function_type ) ptr ;
reg_pack_longlong_test_type a = { 10 , 35 } ;
reg_pack_longlong_test_type r ;
r = f ( 0 , 0 , 0 , 0 , 0 , a , 37 , 38 ) ;
return ( ( r . x = = 37 ) & & ( r . y = = 37 ) ) ? 0 : - 1 ;
}
static int ret_6plus2longlong_test ( void ) {
const char * src =
" typedef struct reg_pack_longlong_test_type_s {long long x, y;} reg_pack_longlong_test_type; "
" reg_pack_longlong_test_type f(long long x1, long long x2, long long x3, long long x4, long long x5, reg_pack_longlong_test_type a, long long x8, long long x9) { \n "
" reg_pack_longlong_test_type r = { x8, x8 }; \n "
" return r; \n "
" } \n " ;
return run_callback ( src , ret_6plus2longlong_test_callback ) ;
}
2013-04-18 20:27:34 +04:00
/*
* sret_test : Create a struct large enough to be returned via sret
* ( hidden pointer as first function argument )
*/
2013-04-18 20:55:00 +04:00
typedef struct sret_test_type_s { long long a , b , c ; } sret_test_type ;
2013-04-18 20:27:34 +04:00
typedef sret_test_type ( * sret_test_function_type ) ( sret_test_type ) ;
2013-04-18 00:51:51 +04:00
2013-04-18 20:27:34 +04:00
static int sret_test_callback ( void * ptr ) {
sret_test_function_type f = ( sret_test_function_type ) ( ptr ) ;
2013-04-18 20:55:00 +04:00
sret_test_type x = { 5436LL , 658277698LL , 43878957LL } ;
2013-04-18 20:27:34 +04:00
sret_test_type r = f ( x ) ;
2013-04-18 20:55:00 +04:00
return ( ( r . a = = x . a * 35 ) & & ( r . b = = x . b * 19 ) & & ( r . c = = x . c * 21 ) ) ? 0 : - 1 ;
2013-04-18 00:51:51 +04:00
}
2013-04-18 20:27:34 +04:00
static int sret_test ( void ) {
2013-04-18 00:51:51 +04:00
const char * src =
2013-04-18 20:55:00 +04:00
" typedef struct sret_test_type_s {long long a, b, c;} sret_test_type; \n "
2013-04-18 20:27:34 +04:00
" sret_test_type f(sret_test_type x) { \n "
2013-04-18 20:55:00 +04:00
" sret_test_type r = {x.a*35, x.b*19, x.c*21}; \n "
2013-04-18 00:51:51 +04:00
" return r; \n "
" } \n " ;
2015-07-29 23:53:57 +03:00
2013-04-18 20:27:34 +04:00
return run_callback ( src , sret_test_callback ) ;
2013-04-18 00:51:51 +04:00
}
2013-04-19 14:08:12 +04:00
/*
* one_member_union_test :
2015-07-29 23:53:57 +03:00
*
2013-04-19 14:08:12 +04:00
* In the x86 - 64 ABI a union should always be passed on the stack . However
* it appears that a single member union is treated by GCC as its member .
*/
2013-04-19 03:40:48 +04:00
typedef union one_member_union_test_type_u { int x ; } one_member_union_test_type ;
typedef one_member_union_test_type ( * one_member_union_test_function_type ) ( one_member_union_test_type ) ;
static int one_member_union_test_callback ( void * ptr ) {
one_member_union_test_function_type f = ( one_member_union_test_function_type ) ptr ;
one_member_union_test_type a , b ;
a . x = 34 ;
b = f ( a ) ;
return ( b . x = = a . x * 2 ) ? 0 : - 1 ;
}
static int one_member_union_test ( void ) {
const char * src =
" typedef union one_member_union_test_type_u {int x;} one_member_union_test_type; \n "
" one_member_union_test_type f(one_member_union_test_type a) { \n "
" one_member_union_test_type b; \n "
" b.x = a.x * 2; \n "
" return b; \n "
" } \n " ;
return run_callback ( src , one_member_union_test_callback ) ;
}
2013-04-19 14:08:12 +04:00
/*
* two_member_union_test :
2015-07-29 23:53:57 +03:00
*
2013-04-19 14:08:12 +04:00
* In the x86 - 64 ABI a union should always be passed on the stack .
*/
2013-04-19 03:40:48 +04:00
typedef union two_member_union_test_type_u { int x ; long y ; } two_member_union_test_type ;
typedef two_member_union_test_type ( * two_member_union_test_function_type ) ( two_member_union_test_type ) ;
static int two_member_union_test_callback ( void * ptr ) {
two_member_union_test_function_type f = ( two_member_union_test_function_type ) ptr ;
two_member_union_test_type a , b ;
a . x = 34 ;
b = f ( a ) ;
return ( b . x = = a . x * 2 ) ? 0 : - 1 ;
}
static int two_member_union_test ( void ) {
const char * src =
" typedef union two_member_union_test_type_u {int x; long y;} two_member_union_test_type; \n "
" two_member_union_test_type f(two_member_union_test_type a) { \n "
" two_member_union_test_type b; \n "
" b.x = a.x * 2; \n "
" return b; \n "
" } \n " ;
return run_callback ( src , two_member_union_test_callback ) ;
}
2013-04-24 05:19:15 +04:00
/*
* Win64 calling convetntion test .
*/
typedef struct many_struct_test_type_s { long long a , b , c ; } many_struct_test_type ;
typedef many_struct_test_type ( * many_struct_test_function_type ) ( many_struct_test_type , many_struct_test_type , many_struct_test_type , many_struct_test_type , many_struct_test_type , many_struct_test_type ) ;
2015-07-29 23:53:57 +03:00
2013-04-24 05:19:15 +04:00
static int many_struct_test_callback ( void * ptr ) {
many_struct_test_function_type f = ( many_struct_test_function_type ) ptr ;
many_struct_test_type v = { 1 , 2 , 3 } ;
many_struct_test_type r = f ( v , v , v , v , v , v ) ;
return ( ( r . a = = 6 ) & & ( r . b = = 12 ) & & ( r . c = = 18 ) ) ? 0 : - 1 ;
}
static int many_struct_test ( void ) {
const char * src =
" typedef struct many_struct_test_type_s {long long a, b, c;} many_struct_test_type; \n "
" many_struct_test_type f(many_struct_test_type x1, many_struct_test_type x2, many_struct_test_type x3, many_struct_test_type x4, many_struct_test_type x5, many_struct_test_type x6) { \n "
" many_struct_test_type y; \n "
" y.a = x1.a + x2.a + x3.a + x4.a + x5.a + x6.a; \n "
" y.b = x1.b + x2.b + x3.b + x4.b + x5.b + x6.b; \n "
" y.c = x1.c + x2.c + x3.c + x4.c + x5.c + x6.c; \n "
" return y; \n "
" } \n " ;
return run_callback ( src , many_struct_test_callback ) ;
}
/*
* Win64 calling convention test .
*/
typedef struct many_struct_test_2_type_s { int a , b ; } many_struct_test_2_type ;
typedef many_struct_test_2_type ( * many_struct_test_2_function_type ) ( many_struct_test_2_type , many_struct_test_2_type , many_struct_test_2_type , many_struct_test_2_type , many_struct_test_2_type , many_struct_test_2_type ) ;
2015-07-29 23:53:57 +03:00
2013-04-24 05:19:15 +04:00
static int many_struct_test_2_callback ( void * ptr ) {
many_struct_test_2_function_type f = ( many_struct_test_2_function_type ) ptr ;
many_struct_test_2_type v = { 1 , 2 } ;
many_struct_test_2_type r = f ( v , v , v , v , v , v ) ;
return ( ( r . a = = 6 ) & & ( r . b = = 12 ) ) ? 0 : - 1 ;
}
static int many_struct_test_2 ( void ) {
const char * src =
" typedef struct many_struct_test_2_type_s {int a, b;} many_struct_test_2_type; \n "
" many_struct_test_2_type f(many_struct_test_2_type x1, many_struct_test_2_type x2, many_struct_test_2_type x3, many_struct_test_2_type x4, many_struct_test_2_type x5, many_struct_test_2_type x6) { \n "
" many_struct_test_2_type y; \n "
" y.a = x1.a + x2.a + x3.a + x4.a + x5.a + x6.a; \n "
" y.b = x1.b + x2.b + x3.b + x4.b + x5.b + x6.b; \n "
" return y; \n "
" } \n " ;
return run_callback ( src , many_struct_test_2_callback ) ;
}
fix a subtle x86-64 calling bug
I ran into an issue playing with tinycc, and tracked it down to a rather
weird assumption in the function calling code. This breaks only when
varargs and float/double arguments are combined, I think, and only when
calling GCC-generated (or non-TinyCC, at least) code. The problem is we
sometimes generate code like this:
804a468: 4c 89 d9 mov %r11,%rcx
804a46b: b8 01 00 00 00 mov $0x1,%eax
804a470: 48 8b 45 c0 mov -0x40(%rbp),%rax
804a474: 4c 8b 18 mov (%rax),%r11
804a477: 41 ff d3 callq *%r11
for a function call. Note how $eax is first set to the correct value,
then clobbered when we try to load the function pointer into R11. With
the patch, the code generated is:
804a468: 4c 89 d9 mov %r11,%rcx
804a46b: b8 01 00 00 00 mov $0x1,%eax
804a470: 4c 8b 5d c0 mov -0x40(%rbp),%r11
804a474: 4d 8b 1b mov (%r11),%r11
804a477: 41 ff d3 callq *%r11
which is correct.
This becomes an issue when get_reg(RC_INT) is modified not always to
return %rax after a save_regs(0), because then another register (%ecx,
say) is clobbered, and the function passed an invalid argument.
A rather convoluted test case that generates the above code is
included. Please note that the test will not cause a failure because
TinyCC code ignores the %rax argument, but it will cause incorrect
behavior when combined with GCC code, which might wrongly fail to save
XMM registers and cause data corruption.
2015-04-23 21:08:28 +03:00
/*
* Win64 calling convention test .
*/
typedef struct many_struct_test_3_type_s { int a , b ; } many_struct_test_3_type ;
typedef many_struct_test_3_type ( * many_struct_test_3_function_type ) ( many_struct_test_3_type , many_struct_test_3_type , many_struct_test_3_type , many_struct_test_3_type , many_struct_test_3_type , many_struct_test_3_type , . . . ) ;
typedef struct many_struct_test_3_struct_type { many_struct_test_3_function_type f ; many_struct_test_3_function_type * f2 ; } many_struct_test_3_struct_type ;
static void many_struct_test_3_dummy ( double d , . . . )
{
volatile double x = d ;
}
static int many_struct_test_3_callback ( void * ptr ) {
many_struct_test_3_struct_type s = { ptr , } ;
many_struct_test_3_struct_type * s2 = & s ;
s2 - > f2 = & s2 - > f ;
many_struct_test_3_dummy ( 1.0 , 1.0 , 1.0 , 1.0 , 1.0 , 1.0 , 1.0 , 1.0 , 1.0 , & s2 ) ;
many_struct_test_3_function_type f = * ( s2 - > f2 ) ;
many_struct_test_3_type v = { 1 , 2 } ;
many_struct_test_3_type r = ( * ( ( s2 - > f2 = & f ) + 0 ) ) ( v , v , v , v , v , v , 1.0 ) ;
return ( ( r . a = = 6 ) & & ( r . b = = 12 ) ) ? 0 : - 1 ;
}
static int many_struct_test_3 ( void ) {
const char * src =
" typedef struct many_struct_test_3_type_s {int a, b;} many_struct_test_3_type; \n "
" many_struct_test_3_type f(many_struct_test_3_type x1, many_struct_test_3_type x2, many_struct_test_3_type x3, many_struct_test_3_type x4, many_struct_test_3_type x5, many_struct_test_3_type x6, ...) { \n "
" many_struct_test_3_type y; \n "
" y.a = x1.a + x2.a + x3.a + x4.a + x5.a + x6.a; \n "
" y.b = x1.b + x2.b + x3.b + x4.b + x5.b + x6.b; \n "
" return y; \n "
" } \n " ;
return run_callback ( src , many_struct_test_3_callback ) ;
}
2013-04-19 14:08:12 +04:00
/*
* stdarg_test : Test variable argument list ABI
*/
2013-04-24 05:19:15 +04:00
typedef struct { long long a , b , c ; } stdarg_test_struct_type ;
typedef void ( * stdarg_test_function_type ) ( int , int , int , . . . ) ;
2013-04-19 14:08:12 +04:00
static int stdarg_test_callback ( void * ptr ) {
stdarg_test_function_type f = ( stdarg_test_function_type ) ptr ;
int x ;
double y ;
2013-04-24 05:19:15 +04:00
stdarg_test_struct_type z = { 1 , 2 , 3 } , w ;
f ( 10 , 10 , 5 ,
2013-04-19 14:08:12 +04:00
1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , & x ,
2013-04-24 05:19:15 +04:00
1.0 , 2.0 , 3.0 , 4.0 , 5.0 , 6.0 , 7.0 , 8.0 , 9.0 , 10.0 , & y ,
z , z , z , z , z , & w ) ;
return ( ( x = = 55 ) & & ( y = = 55 ) & & ( w . a = = 5 ) & & ( w . b = = 10 ) & & ( w . c = = 15 ) ) ? 0 : - 1 ;
2013-04-19 14:08:12 +04:00
}
static int stdarg_test ( void ) {
const char * src =
" #include <stdarg.h> \n "
2013-04-24 05:19:15 +04:00
" typedef struct {long long a, b, c;} stdarg_test_struct_type; \n "
" void f(int n_int, int n_float, int n_struct, ...) { \n "
2013-11-25 07:25:04 +04:00
" int i, ti = 0; \n "
" double td = 0.0; \n "
2013-04-24 05:19:15 +04:00
" stdarg_test_struct_type ts = {0,0,0}, tmp; \n "
2013-04-19 14:08:12 +04:00
" va_list ap; \n "
2013-04-24 05:19:15 +04:00
" va_start(ap, n_struct); \n "
2013-04-19 14:08:12 +04:00
" for (i = 0, ti = 0; i < n_int; ++i) \n "
" ti += va_arg(ap, int); \n "
" *va_arg(ap, int*) = ti; \n "
" for (i = 0, td = 0; i < n_float; ++i) \n "
" td += va_arg(ap, double); \n "
" *va_arg(ap, double*) = td; \n "
2013-04-24 05:19:15 +04:00
" for (i = 0; i < n_struct; ++i) { \n "
" tmp = va_arg(ap, stdarg_test_struct_type); \n "
" ts.a += tmp.a; ts.b += tmp.b; ts.c += tmp.c; "
" } \n "
" *va_arg(ap, stdarg_test_struct_type*) = ts; \n "
2013-04-19 14:08:12 +04:00
" va_end(ap); "
" } \n " ;
return run_callback ( src , stdarg_test_callback ) ;
}
2013-04-24 05:19:15 +04:00
/*
* Test Win32 stdarg handling , since the calling convention will pass a pointer
* to the struct and the stdarg pointer must point to that pointer initially .
*/
typedef struct { long long a , b , c ; } stdarg_struct_test_struct_type ;
typedef int ( * stdarg_struct_test_function_type ) ( stdarg_struct_test_struct_type a , . . . ) ;
static int stdarg_struct_test_callback ( void * ptr ) {
stdarg_struct_test_function_type f = ( stdarg_struct_test_function_type ) ptr ;
stdarg_struct_test_struct_type v = { 10 , 35 , 99 } ;
int x = f ( v , 234 ) ;
return ( x = = 378 ) ? 0 : - 1 ;
}
static int stdarg_struct_test ( void ) {
const char * src =
" #include <stdarg.h> \n "
" typedef struct {long long a, b, c;} stdarg_struct_test_struct_type; \n "
" int f(stdarg_struct_test_struct_type a, ...) { \n "
" va_list ap; \n "
" va_start(ap, a); \n "
" int z = va_arg(ap, int); \n "
" va_end(ap); \n "
" return z + a.a + a.b + a.c; \n "
" } \n " ;
return run_callback ( src , stdarg_struct_test_callback ) ;
}
2013-04-26 19:42:12 +04:00
/* Test that x86-64 arranges the stack correctly for arguments with alignment >8 bytes */
typedef LONG_DOUBLE ( * arg_align_test_callback_type ) ( LONG_DOUBLE , int , LONG_DOUBLE , int , LONG_DOUBLE ) ;
static int arg_align_test_callback ( void * ptr ) {
arg_align_test_callback_type f = ( arg_align_test_callback_type ) ptr ;
long double x = f ( 12 , 0 , 25 , 0 , 37 ) ;
return ( x = = 74 ) ? 0 : - 1 ;
}
static int arg_align_test ( void ) {
2015-07-29 23:53:57 +03:00
const char * src =
2013-04-26 19:42:12 +04:00
" long double f(long double a, int b, long double c, int d, long double e) { \n "
" return a + c + e; \n "
" } \n " ;
return run_callback ( src , arg_align_test_callback ) ;
}
2013-04-18 00:51:51 +04:00
# define RUN_TEST(t) \
2013-04-19 03:40:48 +04:00
if ( ! testname | | ( strcmp ( # t , testname ) = = 0 ) ) { \
2013-04-18 00:51:51 +04:00
fputs ( # t " ... " , stdout ) ; \
fflush ( stdout ) ; \
if ( t ( ) = = 0 ) { \
fputs ( " success \n " , stdout ) ; \
} else { \
fputs ( " failure \n " , stdout ) ; \
retval = EXIT_FAILURE ; \
} \
2013-04-19 03:40:48 +04:00
}
2013-04-18 00:51:51 +04:00
int main ( int argc , char * * argv ) {
2013-04-19 03:40:48 +04:00
int i ;
const char * testname = NULL ;
2013-04-18 00:51:51 +04:00
int retval = EXIT_SUCCESS ;
2015-07-29 23:53:57 +03:00
2013-04-18 00:51:51 +04:00
/* if tcclib.h and libtcc1.a are not installed, where can we find them */
2013-04-19 03:40:48 +04:00
for ( i = 1 ; i < argc ; + + i ) {
tccpp: fix issues, add tests
* fix some macro expansion issues
* add some pp tests in tests/pp
* improved tcc -E output for better diff'ability
* remove -dD feature (quirky code, exotic feature,
didn't work well)
Based partially on ideas / researches from PipCet
Some issues remain with VA_ARGS macros (if used in a
rather tricky way).
Also, to keep it simple, the pp doesn't automtically
add any extra spaces to separate tokens which otherwise
would form wrong tokens if re-read from tcc -E output
(such as '+' '=') GCC does that, other compilers don't.
* cleanups
- #line 01 "file" / # 01 "file" processing
- #pragma comment(lib,"foo")
- tcc -E: forward some pragmas to output (pack, comment(lib))
- fix macro parameter list parsing mess from
a3fc54345949535524d01319e1ca6378b7c2c201
a715d7143d9d17da17e67fec6af1c01409a71a31
(some coffee might help, next time ;)
- introduce TOK_PPSTR - to have character constants as
written in the file (similar to TOK_PPNUM)
- allow '\' appear in macros
- new functions begin/end_macro to:
- fix switching macro levels during expansion
- allow unget_tok to unget more than one tok
- slight speedup by using bitflags in isidnum_table
Also:
- x86_64.c : fix decl after statements
- i386-gen,c : fix a vstack leak with VLA on windows
- configure/Makefile : build on windows (MSYS) was broken
- tcc_warning: fflush stderr to keep output order (win32)
2015-05-09 15:29:39 +03:00
if ( ! memcmp ( argv [ i ] , " run_test= " , 9 ) )
2013-04-19 03:40:48 +04:00
testname = argv [ i ] + 9 ;
tccpp: fix issues, add tests
* fix some macro expansion issues
* add some pp tests in tests/pp
* improved tcc -E output for better diff'ability
* remove -dD feature (quirky code, exotic feature,
didn't work well)
Based partially on ideas / researches from PipCet
Some issues remain with VA_ARGS macros (if used in a
rather tricky way).
Also, to keep it simple, the pp doesn't automtically
add any extra spaces to separate tokens which otherwise
would form wrong tokens if re-read from tcc -E output
(such as '+' '=') GCC does that, other compilers don't.
* cleanups
- #line 01 "file" / # 01 "file" processing
- #pragma comment(lib,"foo")
- tcc -E: forward some pragmas to output (pack, comment(lib))
- fix macro parameter list parsing mess from
a3fc54345949535524d01319e1ca6378b7c2c201
a715d7143d9d17da17e67fec6af1c01409a71a31
(some coffee might help, next time ;)
- introduce TOK_PPSTR - to have character constants as
written in the file (similar to TOK_PPNUM)
- allow '\' appear in macros
- new functions begin/end_macro to:
- fix switching macro levels during expansion
- allow unget_tok to unget more than one tok
- slight speedup by using bitflags in isidnum_table
Also:
- x86_64.c : fix decl after statements
- i386-gen,c : fix a vstack leak with VLA on windows
- configure/Makefile : build on windows (MSYS) was broken
- tcc_warning: fflush stderr to keep output order (win32)
2015-05-09 15:29:39 +03:00
}
g_argv = argv , g_argc = argc ;
2013-04-18 20:55:00 +04:00
RUN_TEST ( ret_int_test ) ;
RUN_TEST ( ret_longlong_test ) ;
RUN_TEST ( ret_float_test ) ;
RUN_TEST ( ret_double_test ) ;
2013-04-19 03:40:48 +04:00
RUN_TEST ( ret_longdouble_test ) ;
2013-04-18 20:55:00 +04:00
RUN_TEST ( ret_2float_test ) ;
2013-04-19 14:08:12 +04:00
RUN_TEST ( ret_2double_test ) ;
fix-mixed-struct (patch by Pip Cet)
Jsut for testing. It works for me (don't break anything)
Small fixes for x86_64-gen.c in "tccpp: fix issues, add tests"
are dropped in flavor of this patch.
Pip Cet:
Okay, here's a first patch that fixes the problem (but I've found
another bug, yet unfixed, in the process), though it's not
particularly pretty code (I tried hard to keep the changes to the
minimum necessary). If we decide to actually get rid of VT_QLONG and
VT_QFLOAT (please, can we?), there are some further simplifications in
tccgen.c that might offset some of the cost of this patch.
The idea is that an integer is no longer enough to describe how an
argument is stored in registers. There are a number of possibilities
(none, integer register, two integer registers, float register, two
float registers, integer register plus float register, float register
plus integer register), and instead of enumerating them I've
introduced a RegArgs type that stores the offsets for each of our
registers (for the other architectures, it's simply an int specifying
the number of registers). If someone strongly prefers an enum, we
could do that instead, but I believe this is a place where keeping
things general is worth it, because this way it should be doable to
add SSE or AVX support.
There is one line in the patch that looks suspicious:
} else {
addr = (addr + align - 1) & -align;
param_addr = addr;
addr += size;
- sse_param_index += reg_count;
}
break;
However, this actually fixes one half of a bug we have when calling a
function with eight double arguments "interrupted" by a two-double
structure after the seventh double argument:
f(double,double,double,double,double,double,double,struct { double
x,y; },double);
In this case, the last argument should be passed in %xmm7. This patch
fixes the problem in gfunc_prolog, but not the corresponding problem
in gfunc_call, which I'll try tackling next.
2015-05-14 07:32:24 +03:00
RUN_TEST ( ret_8plus2double_test ) ;
RUN_TEST ( ret_6plus2longlong_test ) ;
RUN_TEST ( ret_mixed_test ) ;
RUN_TEST ( ret_mixed2_test ) ;
2015-04-25 13:49:55 +03:00
RUN_TEST ( ret_mixed3_test ) ;
2013-04-18 20:27:34 +04:00
RUN_TEST ( reg_pack_test ) ;
2013-04-19 14:08:12 +04:00
RUN_TEST ( reg_pack_longlong_test ) ;
2013-04-18 20:27:34 +04:00
RUN_TEST ( sret_test ) ;
2013-04-19 03:40:48 +04:00
RUN_TEST ( one_member_union_test ) ;
RUN_TEST ( two_member_union_test ) ;
2013-04-24 05:19:15 +04:00
RUN_TEST ( many_struct_test ) ;
RUN_TEST ( many_struct_test_2 ) ;
fix a subtle x86-64 calling bug
I ran into an issue playing with tinycc, and tracked it down to a rather
weird assumption in the function calling code. This breaks only when
varargs and float/double arguments are combined, I think, and only when
calling GCC-generated (or non-TinyCC, at least) code. The problem is we
sometimes generate code like this:
804a468: 4c 89 d9 mov %r11,%rcx
804a46b: b8 01 00 00 00 mov $0x1,%eax
804a470: 48 8b 45 c0 mov -0x40(%rbp),%rax
804a474: 4c 8b 18 mov (%rax),%r11
804a477: 41 ff d3 callq *%r11
for a function call. Note how $eax is first set to the correct value,
then clobbered when we try to load the function pointer into R11. With
the patch, the code generated is:
804a468: 4c 89 d9 mov %r11,%rcx
804a46b: b8 01 00 00 00 mov $0x1,%eax
804a470: 4c 8b 5d c0 mov -0x40(%rbp),%r11
804a474: 4d 8b 1b mov (%r11),%r11
804a477: 41 ff d3 callq *%r11
which is correct.
This becomes an issue when get_reg(RC_INT) is modified not always to
return %rax after a save_regs(0), because then another register (%ecx,
say) is clobbered, and the function passed an invalid argument.
A rather convoluted test case that generates the above code is
included. Please note that the test will not cause a failure because
TinyCC code ignores the %rax argument, but it will cause incorrect
behavior when combined with GCC code, which might wrongly fail to save
XMM registers and cause data corruption.
2015-04-23 21:08:28 +03:00
RUN_TEST ( many_struct_test_3 ) ;
2013-04-19 14:08:12 +04:00
RUN_TEST ( stdarg_test ) ;
2013-04-24 05:19:15 +04:00
RUN_TEST ( stdarg_struct_test ) ;
2013-04-26 19:42:12 +04:00
RUN_TEST ( arg_align_test ) ;
2013-04-18 00:51:51 +04:00
return retval ;
}