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 ) ;
2015-07-29 23:57:41 +03:00
/* RUN_TEST(ret_8plus2double_test); currently broken on x86_64 */
/* RUN_TEST(ret_6plus2longlong_test); currently broken on x86_64 */
/* RUN_TEST(ret_mixed_test); currently broken on x86_64 */
/* RUN_TEST(ret_mixed2_test); currently broken on x86_64 */
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 ;
}