From e900dd8d6f2ff7cef66fbd31898d375b71ef53d6 Mon Sep 17 00:00:00 2001 From: pommicket Date: Thu, 17 Feb 2022 13:22:13 -0500 Subject: procuding a (non-working) executable for tcc --- 05/codegen.b | 5 + 05/main.b | 19 +- 05/main.c | 6 +- 05/parse.b | 25 +- 05/preprocess.b | 99 +- 05/setjmp.h | 23 +- 05/signal.h | 179 +- 05/stdc_common.h | 173 +- 05/stdio.h | 28 +- 05/tcc-0.9.25/.cvsignore | 37 + 05/tcc-0.9.25/.gitignore | 4 + 05/tcc-0.9.25/COPYING | 504 ++++ 05/tcc-0.9.25/Changelog | 368 +++ 05/tcc-0.9.25/Makefile | 272 +++ 05/tcc-0.9.25/README | 90 + 05/tcc-0.9.25/TODO | 95 + 05/tcc-0.9.25/VERSION | 1 + 05/tcc-0.9.25/arm-gen.c | 1734 +++++++++++++ 05/tcc-0.9.25/assert.h | 7 + 05/tcc-0.9.25/c67-gen.c | 2548 +++++++++++++++++++ 05/tcc-0.9.25/coff.h | 446 ++++ 05/tcc-0.9.25/config.h | 10 + 05/tcc-0.9.25/configure | 382 +++ 05/tcc-0.9.25/ctype.h | 92 + 05/tcc-0.9.25/elf.h | 1714 +++++++++++++ 05/tcc-0.9.25/errno.h | 6 + 05/tcc-0.9.25/float.h | 34 + 05/tcc-0.9.25/i386-asm.c | 1211 +++++++++ 05/tcc-0.9.25/i386-asm.h | 446 ++++ 05/tcc-0.9.25/i386-gen.c | 1034 ++++++++ 05/tcc-0.9.25/il-gen.c | 667 +++++ 05/tcc-0.9.25/il-opcodes.h | 251 ++ 05/tcc-0.9.25/include/float.h | 57 + 05/tcc-0.9.25/include/stdarg.h | 67 + 05/tcc-0.9.25/include/stdbool.h | 10 + 05/tcc-0.9.25/include/stddef.h | 20 + 05/tcc-0.9.25/include/tcclib.h | 78 + 05/tcc-0.9.25/include/varargs.h | 11 + 05/tcc-0.9.25/lib/alloca86-bt.S | 45 + 05/tcc-0.9.25/lib/alloca86.S | 33 + 05/tcc-0.9.25/lib/bcheck.c | 868 +++++++ 05/tcc-0.9.25/lib/libtcc1.c | 607 +++++ 05/tcc-0.9.25/libtcc.c | 2276 +++++++++++++++++ 05/tcc-0.9.25/libtcc.h | 108 + 05/tcc-0.9.25/limits.h | 6 + 05/tcc-0.9.25/locale.h | 60 + 05/tcc-0.9.25/math.h | 409 ++++ 05/tcc-0.9.25/setjmp.h | 21 + 05/tcc-0.9.25/signal.h | 253 ++ 05/tcc-0.9.25/stab.def | 238 ++ 05/tcc-0.9.25/stab.h | 17 + 05/tcc-0.9.25/stdarg.h | 10 + 05/tcc-0.9.25/stdc_common.h | 683 ++++++ 05/tcc-0.9.25/stddef.h | 8 + 05/tcc-0.9.25/stdio.h | 2270 +++++++++++++++++ 05/tcc-0.9.25/stdlib.h | 193 ++ 05/tcc-0.9.25/string.h | 185 ++ 05/tcc-0.9.25/tcc-doc.html | 2241 +++++++++++++++++ 05/tcc-0.9.25/tcc-doc.texi | 1227 ++++++++++ 05/tcc-0.9.25/tcc.c | 556 +++++ 05/tcc-0.9.25/tcc.h | 757 ++++++ 05/tcc-0.9.25/tccasm.c | 1021 ++++++++ 05/tcc-0.9.25/tcccoff.c | 957 ++++++++ 05/tcc-0.9.25/tccelf.c | 2732 +++++++++++++++++++++ 05/tcc-0.9.25/tccgen.c | 5123 +++++++++++++++++++++++++++++++++++++++ 05/tcc-0.9.25/tccpe.c | 1559 ++++++++++++ 05/tcc-0.9.25/tccpp.c | 2935 ++++++++++++++++++++++ 05/tcc-0.9.25/tcctok.h | 469 ++++ 05/tcc-0.9.25/texi2pod.pl | 427 ++++ 05/tcc-0.9.25/time.h | 293 +++ 05/tcc-0.9.25/x86_64-gen.c | 1419 +++++++++++ 71 files changed, 42663 insertions(+), 96 deletions(-) create mode 100644 05/tcc-0.9.25/.cvsignore create mode 100644 05/tcc-0.9.25/.gitignore create mode 100644 05/tcc-0.9.25/COPYING create mode 100644 05/tcc-0.9.25/Changelog create mode 100644 05/tcc-0.9.25/Makefile create mode 100644 05/tcc-0.9.25/README create mode 100644 05/tcc-0.9.25/TODO create mode 100644 05/tcc-0.9.25/VERSION create mode 100644 05/tcc-0.9.25/arm-gen.c create mode 100644 05/tcc-0.9.25/assert.h create mode 100644 05/tcc-0.9.25/c67-gen.c create mode 100644 05/tcc-0.9.25/coff.h create mode 100644 05/tcc-0.9.25/config.h create mode 100755 05/tcc-0.9.25/configure create mode 100644 05/tcc-0.9.25/ctype.h create mode 100644 05/tcc-0.9.25/elf.h create mode 100644 05/tcc-0.9.25/errno.h create mode 100644 05/tcc-0.9.25/float.h create mode 100644 05/tcc-0.9.25/i386-asm.c create mode 100644 05/tcc-0.9.25/i386-asm.h create mode 100644 05/tcc-0.9.25/i386-gen.c create mode 100644 05/tcc-0.9.25/il-gen.c create mode 100644 05/tcc-0.9.25/il-opcodes.h create mode 100644 05/tcc-0.9.25/include/float.h create mode 100644 05/tcc-0.9.25/include/stdarg.h create mode 100644 05/tcc-0.9.25/include/stdbool.h create mode 100644 05/tcc-0.9.25/include/stddef.h create mode 100644 05/tcc-0.9.25/include/tcclib.h create mode 100644 05/tcc-0.9.25/include/varargs.h create mode 100644 05/tcc-0.9.25/lib/alloca86-bt.S create mode 100644 05/tcc-0.9.25/lib/alloca86.S create mode 100644 05/tcc-0.9.25/lib/bcheck.c create mode 100644 05/tcc-0.9.25/lib/libtcc1.c create mode 100644 05/tcc-0.9.25/libtcc.c create mode 100644 05/tcc-0.9.25/libtcc.h create mode 100644 05/tcc-0.9.25/limits.h create mode 100644 05/tcc-0.9.25/locale.h create mode 100644 05/tcc-0.9.25/math.h create mode 100644 05/tcc-0.9.25/setjmp.h create mode 100644 05/tcc-0.9.25/signal.h create mode 100644 05/tcc-0.9.25/stab.def create mode 100644 05/tcc-0.9.25/stab.h create mode 100644 05/tcc-0.9.25/stdarg.h create mode 100644 05/tcc-0.9.25/stdc_common.h create mode 100644 05/tcc-0.9.25/stddef.h create mode 100644 05/tcc-0.9.25/stdio.h create mode 100644 05/tcc-0.9.25/stdlib.h create mode 100644 05/tcc-0.9.25/string.h create mode 100644 05/tcc-0.9.25/tcc-doc.html create mode 100644 05/tcc-0.9.25/tcc-doc.texi create mode 100644 05/tcc-0.9.25/tcc.c create mode 100644 05/tcc-0.9.25/tcc.h create mode 100644 05/tcc-0.9.25/tccasm.c create mode 100644 05/tcc-0.9.25/tcccoff.c create mode 100644 05/tcc-0.9.25/tccelf.c create mode 100644 05/tcc-0.9.25/tccgen.c create mode 100644 05/tcc-0.9.25/tccpe.c create mode 100644 05/tcc-0.9.25/tccpp.c create mode 100644 05/tcc-0.9.25/tcctok.h create mode 100755 05/tcc-0.9.25/texi2pod.pl create mode 100644 05/tcc-0.9.25/time.h create mode 100644 05/tcc-0.9.25/x86_64-gen.c diff --git a/05/codegen.b b/05/codegen.b index 46354e9..f06f69e 100644 --- a/05/codegen.b +++ b/05/codegen.b @@ -2902,6 +2902,7 @@ function generate_function local out0 local n_stack_bytes + debug_puts(.str_gen_code_for) debug_putsln(function_name) function_type = ident_list_lookup(function_types, function_name) @@ -2930,6 +2931,10 @@ function generate_function generate_return() return + :str_gen_code_for + string Generating code for + byte 32 + byte 0 function generate_functions local addr diff --git a/05/main.b b/05/main.b index a35f3eb..f8f032e 100644 --- a/05/main.b +++ b/05/main.b @@ -1,4 +1,4 @@ -#define G_DEBUG 0 +#define G_DEBUG 1 ; add 24 + 16 = 40 to the stack pointer to put argc, argv in the right place byte 0x48 @@ -201,6 +201,18 @@ function compile_warning byte 32 byte 0 +:str_preprocessing + string Preprocessing... + byte 10 + byte 0 +:str_tokenizing + string Turning preprocessing tokens into tokens... + byte 10 + byte 0 +:str_parsing + string Parsing... + byte 10 + byte 0 function main argument argv2 @@ -292,6 +304,8 @@ function main output_file_data = mmap(0, RWDATA_END, PROT_READ_WRITE, MAP_SHARED, output_fd, 0) if output_file_data ] 0xffffffffffff0000 goto mmap_output_fd_failed + debug_puts(.str_preprocessing) + pptokens = split_into_preprocessing_tokens(input_filename) ;print_pptokens(pptokens) ;print_separator() @@ -304,12 +318,15 @@ function main ;print_object_macros() ;print_function_macros() + debug_puts(.str_tokenizing) + tokens = malloc(16000000) p = tokenize(pptokens, tokens, input_filename, 1) ;print_tokens(tokens, p) ;print_separator() ; NOTE: do NOT free pptokens; identifiers still reference them. + debug_puts(.str_parsing) parse_tokens(tokens) generate_code() diff --git a/05/main.c b/05/main.c index 0c991b7..34b15fb 100644 --- a/05/main.c +++ b/05/main.c @@ -6,10 +6,12 @@ #include #include #include +#include int main(int argc, char **argv) { - srand(time(NULL)); - printf("%d\n",rand()); + jmp_buf test; + setjmp(test); + longjmp(test, 5); return 0; } diff --git a/05/parse.b b/05/parse.b index 18258ab..dd6978a 100644 --- a/05/parse.b +++ b/05/parse.b @@ -344,13 +344,20 @@ function parse_toplevel_declaration ident_list_add(functions_required_stack_space, f_name, curr_function_stack_space) - ; ENABLE/DISABLE PARSING DEBUG OUTPUT: - if G_DEBUG == 0 goto skip_print_statement - print_statement(out0) - :skip_print_statement + debug_puts(.str_parsed) + debug_putsln(f_name) + + ;PARSING DEBUG OUTPUT: + ;if G_DEBUG == 0 goto skip_print_statement + ;print_statement(out0) + ;:skip_print_statement goto parse_tld_ret + :str_parsed + string Parsed + byte 32 + byte 0 :function_no_param_name token_error(base_type, .str_function_no_param_name) :str_function_no_param_name @@ -2127,7 +2134,7 @@ function parse_base_type goto base_type_normal_loop :base_type_flag_long c = flags & PARSETYPE_FLAG_LONG - if c != 0 goto repeated_base_type + ;if c != 0 goto repeated_base_type ; allow "long long" ...and also "long long long" i guess flags |= PARSETYPE_FLAG_LONG goto base_type_normal_loop :base_type_flag_unsigned @@ -3299,9 +3306,13 @@ function parse_expression return out :undeclared_variable ; @NONSTANDARD: C89 allows calling functions without declaring them - token_error(in, .str_undeclared_variable) + print_token_location(in) + puts(.str_undeclared_variable) + putsln(a) + exit(1) :str_undeclared_variable - string Undeclared variable. + string : Undeclared variable: + byte 32 byte 0 :found_local_variable diff --git a/05/preprocess.b b/05/preprocess.b index f5aac22..cd6d535 100644 --- a/05/preprocess.b +++ b/05/preprocess.b @@ -850,6 +850,8 @@ function translation_phase_4 local included_pptokens included_pptokens = split_into_preprocessing_tokens(inc_filename) + debug_puts(.str_including) + debug_putsln(inc_filename) out = translation_phase_4(inc_filename, included_pptokens, out) free(included_pptokens) free(inc_filename) @@ -865,6 +867,10 @@ function translation_phase_4 out += 1 goto process_pptoken + :str_including + string Including + byte 32 + byte 0 :pp_directive_ifdef pptoken_skip(&in) pptoken_skip_spaces(&in) @@ -917,7 +923,8 @@ function translation_phase_4 if_expr = if_tokens + 2500 p = if_pptokens - macro_replacement_to_terminator(filename, line_number, &in, &p, 10) + macro_replacement_to_terminator(filename, &line_number, &in, &p, 10) + if_tokens_end = tokenize(if_pptokens, if_tokens, filename, line_number) ; replace all identifiers with 0 p = if_tokens @@ -927,58 +934,11 @@ function translation_phase_4 p += 16 goto pp_if_idents0_loop :pp_if_replace_ident - p += 8 - b = str_equals(*8p, .str_defined) - p -= 8 - if b != 0 goto pp_replace_defined *1p = TOKEN_CONSTANT_INT p += 8 *8p = 0 p += 8 goto pp_if_idents0_loop - :pp_replace_defined - ; handle, e.g. #if defined(SOMETHING) - p += 16 - if *1p != SYMBOL_LPAREN goto pp_defined_nolparen - p += 16 - if *1p != TOKEN_IDENTIFIER goto pp_bad_defined - p += 8 - def_name = *8p - p += 8 - if *1p != SYMBOL_RPAREN goto pp_bad_defined - q = p + 16 - p -= 32 - n = if_tokens_end - q - memcpy(p, q, n) ; shift everything over because 0 is less tokens than defined(X) - p -= 16 - goto pp_defined_lparen_cont - :pp_defined_nolparen - if *1p != TOKEN_IDENTIFIER goto pp_bad_defined - p += 8 - def_name = *8p - p -= 8 - q = p + 16 - n = if_tokens_end - q - memcpy(p, q, n) ; shift everything over because 0 is less tokens than defined X - p -= 16 - :pp_defined_lparen_cont - - *1p = TOKEN_CONSTANT_INT - p += 8 - b = look_up_object_macro(def_name) - if b != 0 goto pp_defined_1 - b = look_up_function_macro(def_name) - if b != 0 goto pp_defined_1 - ; not defined - *8p = 0 - goto pp_defined_cont - :pp_defined_1 - ; defined - *8p = 1 - :pp_defined_cont - p += 8 - goto pp_if_idents0_loop - :pp_if_idents0_done ;print_tokens(if_tokens, p) parse_expression(if_tokens, p, if_expr) @@ -1176,16 +1136,59 @@ function macro_replacement_to_terminator argument terminator local in local out + local b + local lparen in = *8p_in out = *8p_out :macro_replacement_to_terminator_loop if *1in == terminator goto macro_replacement_to_terminator_loop_end + b = str_equals(in, .str_defined) + if b != 0 goto replace_defined macro_replacement(filename, p_line_number, &in, &out) goto macro_replacement_to_terminator_loop + :replace_defined + ; @NONSTANDARD: technically this should only happen in #ifs but whatever + pptoken_skip(&in) + pptoken_skip_spaces(&in) + lparen = 0 + if *1in != 40 goto defined_no_lparen + lparen = 1 + pptoken_skip(&in) + pptoken_skip_spaces(&in) + :defined_no_lparen + b = isalnum_or_underscore(*1in) + if b == 0 goto bad_defined + b = look_up_object_macro(in) + if b != 0 goto defined_1 + b = look_up_function_macro(in) + if b != 0 goto defined_1 + ; not defined + *1out = '0 + out += 1 + *1out = 0 + out += 1 + goto defined_cont + :defined_1 + *1out = '1 + out += 1 + *1out = 0 + out += 1 + :defined_cont + pptoken_skip(&in) + if lparen == 0 goto macro_replacement_to_terminator_loop + pptoken_skip_spaces(&in) + if *1in != 41 goto bad_defined + pptoken_skip(&in) + goto macro_replacement_to_terminator_loop :macro_replacement_to_terminator_loop_end *8p_in = in *8p_out = out return + :bad_defined + compile_error(filename, *8p_line_number, .str_bad_defined) + :str_bad_defined + string Bad use of defined(). + byte 0 ; @NONSTANDARD: ; Macro replacement isn't handled properly in the following ways: diff --git a/05/setjmp.h b/05/setjmp.h index ad74d75..b3ffce1 100644 --- a/05/setjmp.h +++ b/05/setjmp.h @@ -1,2 +1,21 @@ -// @NONSTANDARD -#error "longjmp is not supported." +#ifndef _SETJMP_H +#define _SETJMP_H + +#include + +typedef long jmp_buf[3]; + +// @NONSTANDARD: we don't actually support setjmp + +int setjmp(jmp_buf env) { + return 0; +} + +void __longjmp(jmp_buf env, int val, const char *filename, int line) { + fprintf(stderr, "Error: Tried to longjmp from %s:%d with value %d\n", filename, line, val); + _Exit(-1); +} + +#define longjmp(env, val) __longjmp(env, val, __FILE__, __LINE__) + +#endif diff --git a/05/signal.h b/05/signal.h index f885782..ded960c 100644 --- a/05/signal.h +++ b/05/signal.h @@ -13,10 +13,11 @@ typedef long sig_atomic_t; // there are no "asynchronous interrupts" typedef void (*_Sighandler)(int); struct sigaction { - void (*handler)(int); - unsigned long flags; - void (*restorer)(void); - unsigned long mask; + void (*sa_handler)(int); + #define sa_sigaction sa_handler + unsigned long sa_flags; + void (*sa_restorer)(void); + unsigned long sa_mask; }; unsigned char _signal_restorer[] = { @@ -49,11 +50,16 @@ unsigned char _signal_handler[] = { }; #define _SA_RESTORER 0x04000000 +#define SA_SIGINFO 4 +#define SA_RESETHAND 0x80000000 int __sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) { return __syscall(13, signum, act, oldact, 8, 0, 0); } +void sigemptyset(unsigned long *set) { + *set = 0; +} void _sig_ign(int signal) { return; @@ -75,10 +81,10 @@ _Sighandler signal(int sig, _Sighandler func) { _sig_mask |= 1ul << (sig-1); } struct sigaction act = {0}; - act.handler = func == SIG_DFL ? SIG_DFL : (void*)_signal_handler; - act.mask = _sig_mask; - act.flags = _SA_RESTORER; - act.restorer = _signal_restorer; + act.sa_handler = func == SIG_DFL ? SIG_DFL : (void*)_signal_handler; + act.sa_mask = _sig_mask; + act.sa_flags = _SA_RESTORER; + act.sa_restorer = _signal_restorer; __sigaction(sig, &act, NULL); return ret; } @@ -87,4 +93,161 @@ int raise(int signal) { return kill(getpid(), signal); } +#define FPE_INTDIV 1 +#define FPE_FLTDIV 3 + +#define __SI_MAX_SIZE 128 +#if __WORDSIZE == 64 +# define __SI_PAD_SIZE ((__SI_MAX_SIZE / sizeof (int)) - 4) +#else +# define __SI_PAD_SIZE ((__SI_MAX_SIZE / sizeof (int)) - 3) +#endif + +#ifndef __SI_ALIGNMENT +# define __SI_ALIGNMENT /* nothing */ +#endif +#ifndef __SI_BAND_TYPE +# define __SI_BAND_TYPE long int +#endif +#ifndef __SI_CLOCK_T +# define __SI_CLOCK_T __clock_t +#endif +#ifndef __SI_ERRNO_THEN_CODE +# define __SI_ERRNO_THEN_CODE 1 +#endif +#ifndef __SI_HAVE_SIGSYS +# define __SI_HAVE_SIGSYS 1 +#endif +#ifndef __SI_SIGFAULT_ADDL +# define __SI_SIGFAULT_ADDL /* nothing */ +#endif + +typedef int __pid_t; +typedef unsigned __uid_t; + +union __sigval +{ + int __sival_int; + void *__sival_ptr; +}; + +typedef union __sigval __sigval_t; +typedef long __clock_t; + +typedef struct + { + int si_signo; /* Signal number. */ +#if __SI_ERRNO_THEN_CODE + int si_errno; /* If non-zero, an errno value associated with + this signal, as defined in . */ + int si_code; /* Signal code. */ +#else + int si_code; + int si_errno; +#endif +#if __WORDSIZE == 64 + int __pad0; /* Explicit padding. */ +#endif + + union + { + int _pad[__SI_PAD_SIZE]; + + /* kill(). */ + struct + { + __pid_t si_pid; /* Sending process ID. */ + __uid_t si_uid; /* Real user ID of sending process. */ + } _kill; + + /* POSIX.1b timers. */ + struct + { + int si_tid; /* Timer ID. */ + int si_overrun; /* Overrun count. */ + __sigval_t si_sigval; /* Signal value. */ + } _timer; + + /* POSIX.1b signals. */ + struct + { + __pid_t si_pid; /* Sending process ID. */ + __uid_t si_uid; /* Real user ID of sending process. */ + __sigval_t si_sigval; /* Signal value. */ + } _rt; + + /* SIGCHLD. */ + struct + { + __pid_t si_pid; /* Which child. */ + __uid_t si_uid; /* Real user ID of sending process. */ + int si_status; /* Exit value or signal. */ + __SI_CLOCK_T si_utime; + __SI_CLOCK_T si_stime; + } _sigchld; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS. */ + struct + { + void *si_addr; /* Faulting insn/memory ref. */ + __SI_SIGFAULT_ADDL + short int si_addr_lsb; /* Valid LSB of the reported address. */ + union + { + /* used when si_code=SEGV_BNDERR */ + struct + { + void *_lower; + void *_upper; + } _addr_bnd; + /* used when si_code=SEGV_PKUERR */ + uint32_t _pkey; + } _bounds; + } _sigfault; + + /* SIGPOLL. */ + struct + { + __SI_BAND_TYPE si_band; /* Band event for SIGPOLL. */ + int si_fd; + } _sigpoll; + + /* SIGSYS. */ +#if __SI_HAVE_SIGSYS + struct + { + void *_call_addr; /* Calling user insn. */ + int _syscall; /* Triggering system call number. */ + unsigned int _arch; /* AUDIT_ARCH_* of syscall. */ + } _sigsys; +#endif + } _sifields; + } siginfo_t __SI_ALIGNMENT; + + +/* X/Open requires some more fields with fixed names. */ +#define si_pid _sifields._kill.si_pid +#define si_uid _sifields._kill.si_uid +#define si_timerid _sifields._timer.si_tid +#define si_overrun _sifields._timer.si_overrun +#define si_status _sifields._sigchld.si_status +#define si_utime _sifields._sigchld.si_utime +#define si_stime _sifields._sigchld.si_stime +#define si_value _sifields._rt.si_sigval +#define si_int _sifields._rt.si_sigval.sival_int +#define si_ptr _sifields._rt.si_sigval.sival_ptr +#define si_addr _sifields._sigfault.si_addr +#define si_addr_lsb _sifields._sigfault.si_addr_lsb +#define si_lower _sifields._sigfault._bounds._addr_bnd._lower +#define si_upper _sifields._sigfault._bounds._addr_bnd._upper +#define si_pkey _sifields._sigfault._bounds._pkey +#define si_band _sifields._sigpoll.si_band +#define si_fd _sifields._sigpoll.si_fd +#if __SI_HAVE_SIGSYS +# define si_call_addr _sifields._sigsys._call_addr +# define si_syscall _sifields._sigsys._syscall +# define si_arch _sifields._sigsys._arch +#endif + + #endif // _SIGNAL_H diff --git a/05/stdc_common.h b/05/stdc_common.h index a3a8e7f..1540f19 100644 --- a/05/stdc_common.h +++ b/05/stdc_common.h @@ -88,6 +88,111 @@ static unsigned char __syscall_data[] = { (((unsigned long (*)(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long))__syscall_data)\ (no, arg1, arg2, arg3, arg4, arg5, arg6)) +// we need to define ucontext_t +# define __ctx(fld) fld +typedef long long int greg_t; +#define __NGREG 23 +typedef greg_t gregset_t[__NGREG]; +#define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int))) +typedef struct +{ + unsigned long int __val[_SIGSET_NWORDS]; +} __sigset_t, sigset_t; +typedef struct +{ + void *ss_sp; + int ss_flags; + size_t ss_size; +} stack_t; +enum +{ + REG_R8 = 0, +# define REG_R8 REG_R8 + REG_R9, +# define REG_R9 REG_R9 + REG_R10, +# define REG_R10 REG_R10 + REG_R11, +# define REG_R11 REG_R11 + REG_R12, +# define REG_R12 REG_R12 + REG_R13, +# define REG_R13 REG_R13 + REG_R14, +# define REG_R14 REG_R14 + REG_R15, +# define REG_R15 REG_R15 + REG_RDI, +# define REG_RDI REG_RDI + REG_RSI, +# define REG_RSI REG_RSI + REG_RBP, +# define REG_RBP REG_RBP + REG_RBX, +# define REG_RBX REG_RBX + REG_RDX, +# define REG_RDX REG_RDX + REG_RAX, +# define REG_RAX REG_RAX + REG_RCX, +# define REG_RCX REG_RCX + REG_RSP, +# define REG_RSP REG_RSP + REG_RIP, +# define REG_RIP REG_RIP + REG_EFL, +# define REG_EFL REG_EFL + REG_CSGSFS, /* Actually short cs, gs, fs, __pad0. */ +# define REG_CSGSFS REG_CSGSFS + REG_ERR, +# define REG_ERR REG_ERR + REG_TRAPNO, +# define REG_TRAPNO REG_TRAPNO + REG_OLDMASK, +# define REG_OLDMASK REG_OLDMASK + REG_CR2 +# define REG_CR2 REG_CR2 +}; +struct _libc_fpxreg +{ + unsigned short int __ctx(significand)[4]; + unsigned short int __ctx(exponent); + unsigned short int __glibc_reserved1[3]; +}; +struct _libc_xmmreg +{ + uint32_t __ctx(element)[4]; +}; +struct _libc_fpstate +{ + uint16_t __ctx(cwd); + uint16_t __ctx(swd); + uint16_t __ctx(ftw); + uint16_t __ctx(fop); + uint64_t __ctx(rip); + uint64_t __ctx(rdp); + uint32_t __ctx(mxcsr); + uint32_t __ctx(mxcr_mask); + struct _libc_fpxreg _st[8]; + struct _libc_xmmreg _xmm[16]; + uint32_t __glibc_reserved1[24]; +}; +typedef struct _libc_fpstate *fpregset_t; +typedef struct { + gregset_t __ctx(gregs); + fpregset_t __ctx(fpregs); + unsigned long long __reserved1 [8]; +} mcontext_t; +typedef struct ucontext_t { + unsigned long int __ctx(uc_flags); + struct ucontext_t *uc_link; + stack_t uc_stack; + mcontext_t uc_mcontext; + sigset_t uc_sigmask; + struct _libc_fpstate __fpregs_mem; + unsigned long long int __ssp[4]; +} ucontext_t; + long read(int fd, void *buf, size_t count) { return __syscall(0, fd, buf, count, 0, 0, 0); } @@ -116,6 +221,32 @@ int execve(const char *pathname, char *const argv[], char *const envp[]) { return __syscall(59, pathname, argv, envp, 0, 0, 0); } +int gettimeofday(struct timeval *tv, struct timezone *tz) { + return __syscall(96, tv, tz, 0, 0, 0, 0); +} + +typedef long time_t; + +struct timespec { + time_t tv_sec; + long tv_nsec; +}; + +struct timeval { + time_t tv_sec; + long tv_usec; +}; + +struct timezone { + int tz_minuteswest; + int tz_dsttime; +}; + +char *getcwd(char *buf, size_t size) { + long n = __syscall(79, buf, size, 0, 0, 0, 0); + if (n < 0) return NULL; + return buf; +} #define _WEXITSTATUS(status) (((status) & 0xff00) >> 8) #define _WIFEXITED(status) (__WTERMSIG(status) == 0) @@ -132,16 +263,11 @@ int wait4(int pid, int *status, int options, struct rusage *rusage) { #define SIGINT 2 #define SIGSEGV 11 #define SIGTERM 15 +#define SIGBUS 7 void abort(void) { kill(getpid(), SIGABRT); } -typedef long time_t; - -struct timespec { - time_t tv_sec; - long tv_nsec; -}; #define CLOCK_REALTIME 0 #define CLOCK_MONOTONIC 1 @@ -183,6 +309,29 @@ int __assert_failed(const char *file, int line, const char *expr) { #define assert(x) (void)((x) || __assert_failed(__FILE__, __LINE__, #x)) #endif + +int _clamp_long_to_int(long x) { + if (x < INT_MIN) return INT_MIN; + if (x > INT_MAX) return INT_MAX; + return x; +} + +short _clamp_long_to_short(long x) { + if (x < SHRT_MIN) return SHRT_MIN; + if (x > SHRT_MAX) return SHRT_MAX; + return x; +} + +unsigned _clamp_ulong_to_uint(unsigned long x) { + if (x > UINT_MAX) return UINT_MAX; + return x; +} + +unsigned short _clamp_ulong_to_ushort(unsigned long x) { + if (x > USHRT_MAX) return USHRT_MAX; + return x; +} + #define EIO 5 #define EDOM 33 #define ERANGE 34 @@ -201,6 +350,10 @@ int munmap(void *addr, size_t length) { return __syscall(11, addr, length, 0, 0, 0, 0); } +int mprotect(void *addr, size_t len, int prot) { + return __syscall(10, addr, len, prot, 0, 0, 0); +} + #define MREMAP_MAYMOVE 1 void *_mremap(void *addr, size_t old_size, size_t new_size, int flags) { return __syscall(25, addr, old_size, new_size, flags, 0, 0); @@ -455,6 +608,14 @@ double strtod(const char *nptr, char **endptr) { return sum * sign; } +float strtof(const char *nptr, char **endptr) { + return strtod(nptr, endptr); +} + +long double strtold(const char *nptr, char **endptr) { + return strtod(nptr, endptr); +} + char *strerror(int errnum) { switch (errnum) { case ERANGE: return "Range error"; diff --git a/05/stdio.h b/05/stdio.h index e474fca..436dd62 100644 --- a/05/stdio.h +++ b/05/stdio.h @@ -1699,6 +1699,12 @@ FILE *fopen(const char *filename, const char *mode) { return _FILE_from_fd(fd); } + +FILE *fdopen(int fd, const char *mode) { + // mode doesn't matter, hopefully + return _FILE_from_fd(fd); +} + int fclose(FILE *stream) { int ret = close(stream->fd); free(stream); @@ -1932,28 +1938,6 @@ int _file_peek_char(void *dat) { return c; } -int _clamp_long_to_int(long x) { - if (x < INT_MIN) return INT_MIN; - if (x > INT_MAX) return INT_MAX; - return x; -} - -short _clamp_long_to_short(long x) { - if (x < SHRT_MIN) return SHRT_MIN; - if (x > SHRT_MAX) return SHRT_MAX; - return x; -} - -unsigned _clamp_ulong_to_uint(unsigned long x) { - if (x > UINT_MAX) return UINT_MAX; - return x; -} - -unsigned short _clamp_ulong_to_ushort(unsigned long x) { - if (x > USHRT_MAX) return USHRT_MAX; - return x; -} - void _bad_scanf(void) { fprintf(stderr, "bad scanf format.\n"); abort(); diff --git a/05/tcc-0.9.25/.cvsignore b/05/tcc-0.9.25/.cvsignore new file mode 100644 index 0000000..95daa1f --- /dev/null +++ b/05/tcc-0.9.25/.cvsignore @@ -0,0 +1,37 @@ +tcc_g +tcc +tc2.c +doc +tc3s.c +p3.c +tc1.c +error.c +i386-gen1.c +test.out2 +test.out3 +web.sh +memdebug.c +bench +Makefile.uClibc +boundtest +prog.ref +test.ref +test.out +tcc-doc.html +ideas +tcctest.ref +linux.tcc +ldtest +libtcc_test +instr.S +p.c +p2.c +tcctest[1234] +test[1234].out +.gdb_history +tcc.1 +tcc.pod +config.h +config.mak +config.texi +tests \ No newline at end of file diff --git a/05/tcc-0.9.25/.gitignore b/05/tcc-0.9.25/.gitignore new file mode 100644 index 0000000..aaa8247 --- /dev/null +++ b/05/tcc-0.9.25/.gitignore @@ -0,0 +1,4 @@ +tcc +examples +tests +win32 diff --git a/05/tcc-0.9.25/COPYING b/05/tcc-0.9.25/COPYING new file mode 100644 index 0000000..223ede7 --- /dev/null +++ b/05/tcc-0.9.25/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/05/tcc-0.9.25/Changelog b/05/tcc-0.9.25/Changelog new file mode 100644 index 0000000..b271054 --- /dev/null +++ b/05/tcc-0.9.25/Changelog @@ -0,0 +1,368 @@ +version 0.9.25: + +- first support for x86-64 target (Shinichiro Hamaji) +- support Clibc +- split tcc.c into tcc.h libtcc.c tccpp.c tccgen.c tcc.c +- improved preprocess output with linenumbers and spaces preserved +- tcc_relocate now copies code into user buffer +- fix bitfields with non-int types and in unions +- improve ARM cross-compiling (Daniel Glöckner) +- link stabstr sections from multiple objects +- better (still limited) support for multiple TCCStates + +version 0.9.24: + +- added verbosity levels -v, -vv, -vvv +- Accept standard input as an inputstream (Hanzac Chen) +- Support c89 compilers other than gcc (Hanzac Chen) +- -soname linker option (Marc Andre Tanner) +- Just warn about unknown directives, ignore quotes in #error/#warning +- Define __STDC_VERSION__=199901L (477) +- Switch to newer tccpe.c (includes support for resources) +- Handle backslashes within #include/#error/#warning +- Import changesets (part 4) 428,457,460,467: defines for openbsd etc. +- Use _WIN32 for a windows hosted tcc and define it for the PE target, + otherwise define __unix / __linux (Detlef Riekenberg) +- Import changesets (part 3) 409,410: ARM EABI by Daniel Glöckner +- Some in-between fixes: + TCC -E no longer hangs with macro calls involving newlines. + (next_nomacro1 now advances the read-pointer with TOK_LINEFEED) + Global cast (int g_i = 1LL;) no longer crashes tcc. + (nocode_wanted is initially 1, and only 0 for gen_function) + On win32 now tcc.exe finds 'include' & 'lib' even if itself is in 'bin'. + (new function w32_tcc_lib_path removes 'bin' if detected) + Added quick build batch file for mingw (win32/build-tcc.bat) + Last added case label optimization (455) produced wrong code. Reverted. + +- Import more changesets from Rob Landley's fork (part 2): + 487: Handle long long constants in gen_opic() (Rob Landley) + 484: Handle parentheses within __attribute__((...)) (Rob Landley) + 480: Remove a goto in decl_initializer_alloc (Rob Landley) + 475: Fix dereferences in inline assembly output (Joshua Phillips) + 474: Cast ptrs to ints of different sizes correctly (Joshua Phillips) + 473: Fix size of structs with empty array member (Joshua Phillips) + 470: No warning for && and || with mixed pointers/integers (Rob Landley) + 469: Fix symbol visibility problems in the linker (Vincent Pit) + 468: Allow && and || involving pointer arguments (Rob Landley) + 455: Optimize case labels with no code in between (Zdenek Pavlas) + 450: Implement alloca for x86 (grischka) + 415: Parse unicode escape sequences (Axel Liljencrantz) + 407: Add a simple va_copy() in stdarg.h (Hasso Tepper) + 400: Allow typedef names as symbols (Dave Dodge) + +- Import some changesets from Rob Landley's fork (part 1): + 462: Use LGPL with bcheck.c and il-gen.c + 458: Fix global compound literals (in unary: case '&':) (Andrew Johnson) + 456: Use return code from tcc_output_file in main() (Michael Somos) + 442: Fix indirections with function pointers (***fn)() (grischka) + 441: Fix LL left shift in libtcc1.c:__shldi3 (grischka) + 440: Pass structures and function ptrs through ?: (grischka) + 439: Keep rvalue in bit assignment (bit2 = bit1 = x) (grischka) + 438: Degrade nonportable pointer assignment to warning (grischka) + 437: Call 'saveregs()' before jumping with logical and/or/not (grischka) + 435: Put local static variables into global memory (grischka) + 432/434: Cast double and ptr to bool (grischka) + 420: Zero pad x87 tenbyte long doubles (Felix Nawothnig) + 417: Make 'sizeof' unsigned (Rob Landley) + 397: Fix save_reg for longlongs (Daniel Glöckner) + 396: Fix "invalid relocation entry" problem on ubuntu - (Bernhard Fischer) + +- ignore AS_NEEDED ld command +- mark executable sections as executable when running in memory +- added support for win32 wchar_t (Filip Navara) +- segment override prefix support (Filip Navara) +- normalized slashes in paths (Filip Navara) +- windows style fastcall (Filip Navara) +- support for empty input register section in asm (Filip Navara) +- anonymous union/struct support (Filip Navara) +- fixed parsing of function parameters +- workaround for function pointers in conditional expressions (Dave Dodge) +- initial '-E' option support to use the C preprocessor alone +- discard type qualifiers when comparing function parameters (Dave Dodge) +- Bug fix: A long long value used as a test expression ignores the + upper 32 bits at runtime (Dave Dodge) +- fixed multiple concatenation of PPNUM tokens (initial patch by Dave Dodge) +- fixed multiple typedef specifiers handling +- fixed sign extension in some type conversions (Dave Dodge) + +version 0.9.23: + +- initial PE executable format for windows version (grischka) +- '#pragma pack' support (grischka) +- '#include_next' support (Bernhard Fischer) +- ignore '-pipe' option +- added -f[no-]leading-underscore +- preprocessor function macro parsing fix (grischka) + +version 0.9.22: + +- simple memory optimisations: kernel compilation is 30% faster +- linker symbol definitions fixes +- gcc 3.4 fixes +- fixed value stack full error +- 'packed' attribute support for variables and structure fields +- ignore 'const' and 'volatile' in function prototypes +- allow '_Bool' in bit fields + +version 0.9.21: + +- ARM target support (Daniel Glöckner) +- added '-funsigned-char, '-fsigned-char' and + '-Wimplicit-function-declaration' +- fixed assignment of const struct in struct +- line comment fix (reported by Bertram Felgenhauer) +- initial TMS320C67xx target support (TK) +- win32 configure +- regparm() attribute +- many built-in assembler fixes +- added '.org', '.fill' and '.previous' assembler directives +- '-fno-common' option +- '-Ttext' linker option +- section alignment fixes +- bit fields fixes +- do not generate code for unused inline functions +- '-oformat' linker option. +- added 'binary' output format. + +version 0.9.20: + +- added '-w' option +- added '.gnu.linkonce' ELF sections support +- fixed libc linking when running in memory (avoid 'stat' function + errors). +- extended '-run' option to be able to give several arguments to a C + script. + +version 0.9.19: + +- "alacarte" linking (Dave Long) +- simpler function call +- more strict type checks +- added 'const' and 'volatile' support and associated warnings +- added -Werror, -Wunsupported, -Wwrite-strings, -Wall. +- added __builtin_types_compatible_p() and __builtin_constant_p() +- chars support in assembler (Dave Long) +- .string, .globl, .section, .text, .data and .bss asm directive + support (Dave Long) +- man page generated from tcc-doc.texi +- fixed macro argument substitution +- fixed zero argument macro parsing +- changed license to LGPL +- added -rdynamic option support + +version 0.9.18: + +- header fix (time.h) +- fixed inline asm without operand case +- fixed 'default:' or 'case x:' with '}' after (incorrect C construct accepted + by gcc) +- added 'A' inline asm constraint. + +version 0.9.17: + +- PLT generation fix +- tcc doc fixes (Peter Lund) +- struct parse fix (signaled by Pedro A. Aranda Gutierrez) +- better _Bool lvalue support (signaled by Alex Measday) +- function parameters must be converted to pointers (signaled by Neil Brown) +- sanitized string and character constant parsing +- fixed comment parse (signaled by Damian M Gryski) +- fixed macro function bug (signaled by Philippe Ribet) +- added configure (initial patch by Mitchell N Charity) +- added '-run' and '-v' options (initial patch by vlindos) +- added real date report in __DATE__ and __TIME__ macros + +version 0.9.16: + +- added assembler language support +- added GCC inline asm() support +- fixed multiple variable definitions : uninitialized variables are + created as COMMON symbols. +- optimized macro processing +- added GCC statement expressions support +- added GCC local labels support +- fixed array declaration in old style function parameters +- support casts in static structure initializations +- added various __xxx[__] keywords for GCC compatibility +- ignore __extension__ GCC in an expression or in a type (still not perfect) +- added '? :' GCC extension support + +version 0.9.15: + +- compilation fixes for glibc 2.2, gcc 2.95.3 and gcc 3.2. +- FreeBSD compile fixes. Makefile patches still missing (Carl Drougge). +- fixed file type guessing if '.' is in the path. +- fixed tcc_compile_string() +- add a dummy page in ELF files to fix RX/RW accesses (pageexec at + freemail dot hu). + +version 0.9.14: + +- added #warning. error message if invalid preprocessing directive. +- added CType structure to ease typing (faster parse). +- suppressed secondary hash tables (faster parse). +- rewrote parser by optimizing common cases (faster parse). +- fixed signed long long comparisons. +- fixed 'int a(), b();' declaration case. +- fixed structure init without '{}'. +- correct alignment support in structures. +- empty structures support. +- gcc testsuite now supported. +- output only warning if implicit integer/pointer conversions. +- added static bitfield init. + +version 0.9.13: + +- correct preprocessing token pasting (## operator) in all cases (added + preprocessing number token). +- fixed long long register spill. +- fixed signed long long '>>'. +- removed memory leaks. +- better error handling : processing can continue on link errors. A + custom callback can be added to display error messages. Most + errors do not call exit() now. +- ignore -O, -W, -m and -f options +- added old style function declarations +- added GCC __alignof__ support. +- added GCC typeof support. +- added GCC computed gotos support. +- added stack backtrace in runtime error message. Improved runtime + error position display. + +version 0.9.12: + +- more fixes for || and && handling. +- improved '? :' type handling. +- fixed bound checking generation with structures +- force '#endif' to be in same file as matching '#if' +- #include file optimization with '#ifndef #endif' construct detection +- macro handling optimization +- added tcc_relocate() and tcc_get_symbol() in libtcc. + +version 0.9.11: + +- stdarg.h fix for double type (thanks to Philippe Ribet). +- correct white space characters and added MSDOS newline support. +- fixed invalid implicit function call type declaration. +- special macros such as __LINE__ are defined if tested with defined(). +- fixed '!' operator with relocated address. +- added symbol + offset relocation (fixes some static variable initializers) +- '-l' option can be specified anywhere. '-c' option yields default + output name. added '-r' option for relocatable output. +- fixed '\nnn' octal parsing. +- fixed local extern variables declarations. + +version 0.9.10: + +- fixed lvalue type when saved in local stack. +- fixed '#include' syntax when using macros. +- fixed '#line' bug. +- removed size limit on strings. Unified string constants handling + with variable declarations. +- added correct support for '\xX' in wchar_t strings. +- added support for bound checking in generated executables +- fixed -I include order. +- fixed incorrect function displayed in runtime error. + +version 0.9.9: + +- fixed preprocessor expression parsing for #if/#elif. +- relocated debug info (.stab section). +- relocated bounds info (.bounds section). +- fixed cast to char of char constants ('\377' is -1 instead of 255) +- fixed implicit cast for unary plus. +- strings and '__func__' have now 'char[]' type instead of 'char *' + (fixes sizeof() return value). +- added __start_xxx and __stop_xxx symbols in linker. +- better DLL creation support (option -shared begins to work). +- ELF sections and hash tables are resized dynamically. +- executables and DLLs are stripped by default. + +version 0.9.8: + +- First version of full ELF linking support (generate objects, static + executable, dynamic executable, dynamic libraries). Dynamic library + support is not finished (need PIC support in compiler and some + patches in symbol exporting). +- First version of ELF loader for object (.o) and archive (.a) files. +- Support of simple GNU ld scripts (GROUP and FILE commands) +- Separated runtime library and bound check code from TCC (smaller + compiler core). +- fixed register reload in float compare. +- fixed implicit char/short to int casting. +- allow array type for address of ('&') operator. +- fixed unused || or && result. +- added GCC style variadic macro support. +- optimized bound checking code for array access. +- tcc includes are now in $(prefix)/lib/tcc/include. +- more command line options - more consistent handling of multiple + input files. +- added tcc man page (thanks to Cyril Bouthors). +- uClibc Makefile update +- converted documentation to texinfo format. +- added developper's guide in documentation. + +version 0.9.7: + +- added library API for easy dynamic compilation (see libtcc.h - first + draft). +- fixed long long register spill bug. +- fixed '? :' register spill bug. + +version 0.9.6: + +- added floating point constant propagation (fixes negative floating + point constants bug). + +version 0.9.5: + + - uClibc patches (submitted by Alfonso Martone). + - error reporting fix + - added CONFIG_TCC_BCHECK to get smaller code if needed. + +version 0.9.4: + + - windows port (currently cannot use -g, -b and dll functions). + - faster and simpler I/O handling. + - '-D' option works in all cases. + - preprocessor fixes (#elif and empty macro args) + - floating point fixes + - first code for CIL generation (does not work yet) + +version 0.9.3: + + - better and smaller code generator. + - full ISOC99 64 bit 'long long' support. + - full 32 bit 'float', 64 bit 'double' and 96 bit 'long double' support. + - added '-U' option. + - added assembly sections support. + - even faster startup time by mmaping sections instead of mallocing them. + - added GNUC __attribute__ keyword support (currently supports + 'section' and 'aligned' attributes). + - added ELF file output (only usable for debugging now) + - added debug symbol generation (STAB format). + - added integrated runtime error analysis ('-g' option: print clear + run time error messages instead of "Segmentation fault"). + - added first version of tiny memory and bound checker ('-b' option). + +version 0.9.2: + + - even faster parsing. + - various syntax parsing fixes. + - fixed external relocation handling for variables or functions pointers. + - better function pointers type handling. + - can compile multiple files (-i option). + - ANSI C bit fields are supported. + - beginning of float/double/long double support. + - beginning of long long support. + +version 0.9.1: + + - full ISOC99 initializers handling. + - compound literals. + - structures handle in assignments and as function param or return value. + - wide chars and strings. + - macro bug fix + +version 0.9: + - initial version. diff --git a/05/tcc-0.9.25/Makefile b/05/tcc-0.9.25/Makefile new file mode 100644 index 0000000..1a2b5f7 --- /dev/null +++ b/05/tcc-0.9.25/Makefile @@ -0,0 +1,272 @@ +# +# Tiny C Compiler Makefile +# + +TOP ?= . +include $(TOP)/config.mak + +CFLAGS+=-g -Wall +CFLAGS_P=$(CFLAGS) -pg -static -DCONFIG_TCC_STATIC +LIBS_P= + +ifneq ($(GCC_MAJOR),2) +CFLAGS+=-fno-strict-aliasing +endif + +ifeq ($(ARCH),i386) +CFLAGS+=-mpreferred-stack-boundary=2 +ifeq ($(GCC_MAJOR),2) +CFLAGS+=-m386 -malign-functions=0 +else +CFLAGS+=-march=i386 -falign-functions=0 +ifneq ($(GCC_MAJOR),3) +CFLAGS+=-Wno-pointer-sign -Wno-sign-compare -D_FORTIFY_SOURCE=0 +endif +endif +endif + +ifeq ($(ARCH),x86-64) +CFLAGS+=-Wno-pointer-sign +endif + +ifndef CONFIG_WIN32 +LIBS=-lm +ifndef CONFIG_NOLDL +LIBS+=-ldl +endif +endif + +ifdef CONFIG_WIN32 +NATIVE_TARGET=-DTCC_TARGET_PE +LIBTCC1=libtcc1.a +else +ifeq ($(ARCH),i386) +NATIVE_TARGET=-DTCC_TARGET_I386 +LIBTCC1=libtcc1.a +BCHECK_O=bcheck.o +else +ifeq ($(ARCH),arm) +NATIVE_TARGET=-DTCC_TARGET_ARM +NATIVE_TARGET+=$(if $(wildcard /lib/ld-linux.so.3),-DTCC_ARM_EABI) +NATIVE_TARGET+=$(if $(shell grep -l "^Features.* \(vfp\|iwmmxt\) " /proc/cpuinfo),-DTCC_ARM_VFP) +else +ifeq ($(ARCH),x86-64) +NATIVE_TARGET=-DTCC_TARGET_X86_64 +LIBTCC1=libtcc1.a +endif +endif +endif +endif + +ifneq ($(wildcard /lib/ld-uClibc.so.0),) +NATIVE_TARGET+=-DTCC_UCLIBC +BCHECK_O= +endif + +ifdef CONFIG_USE_LIBGCC +LIBTCC1= +endif + +ifeq ($(TOP),.) + +PROGS=tcc$(EXESUF) + +I386_CROSS = i386-tcc$(EXESUF) +WIN32_CROSS = i386-win32-tcc$(EXESUF) +X64_CROSS = x86_64-tcc$(EXESUF) +ARM_CROSS = arm-tcc-fpa$(EXESUF) arm-tcc-fpa-ld$(EXESUF) \ + arm-tcc-vfp$(EXESUF) arm-tcc-vfp-eabi$(EXESUF) +C67_CROSS = c67-tcc$(EXESUF) + +CORE_FILES = tcc.c libtcc.c tccpp.c tccgen.c tccelf.c tccasm.c \ + tcc.h config.h libtcc.h tcctok.h +I386_FILES = $(CORE_FILES) i386-gen.c i386-asm.c i386-asm.h +WIN32_FILES = $(CORE_FILES) i386-gen.c i386-asm.c i386-asm.h tccpe.c +X86_64_FILES = $(CORE_FILES) x86_64-gen.c +ARM_FILES = $(CORE_FILES) arm-gen.c +C67_FILES = $(CORE_FILES) c67-gen.c tcccoff.c + +ifdef CONFIG_WIN32 +PROGS+=tiny_impdef$(EXESUF) tiny_libmaker$(EXESUF) +NATIVE_FILES=$(WIN32_FILES) +PROGS_CROSS=$(I386_CROSS) $(X64_CROSS) $(ARM_CROSS) $(C67_CROSS) +else +ifeq ($(ARCH),i386) +NATIVE_FILES=$(I386_FILES) +PROGS_CROSS=$(X64_CROSS) $(WIN32_CROSS) $(ARM_CROSS) $(C67_CROSS) +else +ifeq ($(ARCH),x86-64) +NATIVE_FILES=$(X86_64_FILES) +PROGS_CROSS=$(I386_CROSS) $(WIN32_CROSS) $(ARM_CROSS) $(C67_CROSS) +else +ifeq ($(ARCH),arm) +NATIVE_FILES=$(ARM_FILES) +PROGS_CROSS=$(I386_CROSS) $(X64_CROSS) $(WIN32_CROSS) $(C67_CROSS) +endif +endif +endif +endif + +ifdef CONFIG_CROSS +PROGS+=$(PROGS_CROSS) +endif + +all: $(PROGS) $(LIBTCC1) $(BCHECK_O) libtcc.a tcc-doc.html tcc.1 libtcc_test$(EXESUF) + +# Host Tiny C Compiler +tcc$(EXESUF): $(NATIVE_FILES) + $(CC) -o $@ $< $(NATIVE_TARGET) $(CFLAGS) $(LIBS) + +# Cross Tiny C Compilers +i386-tcc$(EXESUF): $(I386_FILES) + $(CC) -o $@ $< -DTCC_TARGET_I386 $(CFLAGS) $(LIBS) + +i386-win32-tcc$(EXESUF): $(WIN32_FILES) + $(CC) -o $@ $< -DTCC_TARGET_PE $(CFLAGS) $(LIBS) + +x86_64-tcc$(EXESUF): $(X86_64_FILES) + $(CC) -o $@ $< -DTCC_TARGET_X86_64 $(CFLAGS) $(LIBS) + +c67-tcc$(EXESUF): $(C67_FILES) + $(CC) -o $@ $< -DTCC_TARGET_C67 $(CFLAGS) $(LIBS) + +arm-tcc-fpa$(EXESUF): $(ARM_FILES) + $(CC) -o $@ $< -DTCC_TARGET_ARM $(CFLAGS) $(LIBS) + +arm-tcc-fpa-ld$(EXESUF): $(ARM_FILES) + $(CC) -o $@ $< -DTCC_TARGET_ARM -DLDOUBLE_SIZE=12 $(CFLAGS) $(LIBS) + +arm-tcc-vfp$(EXESUF): $(ARM_FILES) + $(CC) -o $@ $< -DTCC_TARGET_ARM -DTCC_ARM_VFP $(CFLAGS) $(LIBS) + +arm-tcc-vfp-eabi$(EXESUF): $(ARM_FILES) + $(CC) -o $@ $< -DTCC_TARGET_ARM -DTCC_ARM_EABI $(CFLAGS) $(LIBS) + +# libtcc generation and test +libtcc.o: $(NATIVE_FILES) + $(CC) -o $@ -c libtcc.c $(NATIVE_TARGET) $(CFLAGS) + +libtcc.a: libtcc.o + $(AR) rcs $@ $^ + +libtcc_test$(EXESUF): tests/libtcc_test.c libtcc.a + $(CC) -o $@ $^ -I. $(CFLAGS) $(LIBS) + +libtest: libtcc_test$(EXESUF) $(LIBTCC1) + ./libtcc_test$(EXESUF) lib_path=. + +# profiling version +tcc_p$(EXESUF): $(NATIVE_FILES) + $(CC) -o $@ $< $(NATIVE_TARGET) $(CFLAGS_P) $(LIBS_P) + +# windows utilities +tiny_impdef$(EXESUF): win32/tools/tiny_impdef.c + $(CC) -o $@ $< $(CFLAGS) +tiny_libmaker$(EXESUF): win32/tools/tiny_libmaker.c + $(CC) -o $@ $< $(CFLAGS) + +# TinyCC runtime libraries +LIBTCC1_OBJS=libtcc1.o +LIBTCC1_CC=$(CC) +VPATH+=lib +ifdef CONFIG_WIN32 +# for windows, we must use TCC because we generate ELF objects +LIBTCC1_OBJS+=crt1.o wincrt1.o dllcrt1.o dllmain.o chkstk.o +LIBTCC1_CC=./tcc.exe -Bwin32 -DTCC_TARGET_PE +VPATH+=win32/lib +endif +ifeq ($(ARCH),i386) +LIBTCC1_OBJS+=alloca86.o alloca86-bt.o +endif + +%.o: %.c + $(LIBTCC1_CC) -o $@ -c $< -O2 -Wall + +%.o: %.S + $(LIBTCC1_CC) -o $@ -c $< + +libtcc1.a: $(LIBTCC1_OBJS) + $(AR) rcs $@ $^ + +bcheck.o: bcheck.c + $(CC) -o $@ -c $< -O2 -Wall + +# install +TCC_INCLUDES = stdarg.h stddef.h stdbool.h float.h varargs.h tcclib.h +INSTALL=install + +ifndef CONFIG_WIN32 +install: $(PROGS) $(LIBTCC1) $(BCHECK_O) libtcc.a tcc.1 tcc-doc.html + mkdir -p "$(bindir)" + $(INSTALL) -s -m755 $(PROGS) "$(bindir)" + mkdir -p "$(mandir)/man1" + $(INSTALL) tcc.1 "$(mandir)/man1" + mkdir -p "$(tccdir)" + mkdir -p "$(tccdir)/include" +ifneq ($(LIBTCC1),) + $(INSTALL) -m644 $(LIBTCC1) "$(tccdir)" +endif +ifneq ($(BCHECK_O),) + $(INSTALL) -m644 $(BCHECK_O) "$(tccdir)" +endif + $(INSTALL) -m644 $(addprefix include/,$(TCC_INCLUDES)) "$(tccdir)/include" + mkdir -p "$(docdir)" + $(INSTALL) -m644 tcc-doc.html "$(docdir)" + mkdir -p "$(libdir)" + $(INSTALL) -m644 libtcc.a "$(libdir)" + mkdir -p "$(includedir)" + $(INSTALL) -m644 libtcc.h "$(includedir)" + +uninstall: + rm -fv $(foreach P,$(PROGS),"$(bindir)/$P") + rm -fv $(foreach P,$(LIBTCC1) $(BCHECK_O),"$(tccdir)/$P") + rm -fv $(foreach P,$(TCC_INCLUDES),"$(tccdir)/include/$P") + rm -fv "$(docdir)/tcc-doc.html" "$(mandir)/man1/tcc.1" + rm -fv "$(libdir)/libtcc.a" "$(includedir)/libtcc.h" + +else +install: $(PROGS) $(LIBTCC1) libtcc.a tcc-doc.html + mkdir -p "$(tccdir)" + mkdir -p "$(tccdir)/lib" + mkdir -p "$(tccdir)/include" + mkdir -p "$(tccdir)/examples" + mkdir -p "$(tccdir)/doc" + mkdir -p "$(tccdir)/libtcc" + $(INSTALL) -s -m755 $(PROGS) "$(tccdir)" + $(INSTALL) -m644 $(LIBTCC1) win32/lib/*.def "$(tccdir)/lib" + cp -r win32/include/. "$(tccdir)/include" + cp -r win32/examples/. "$(tccdir)/examples" +# $(INSTALL) -m644 $(addprefix include/,$(TCC_INCLUDES)) "$(tccdir)/include" + $(INSTALL) -m644 tcc-doc.html win32/tcc-win32.txt "$(tccdir)/doc" + $(INSTALL) -m644 libtcc.a libtcc.h "$(tccdir)/libtcc" +endif + +# documentation and man page +tcc-doc.html: tcc-doc.texi + -texi2html -monolithic -number $< + +tcc.1: tcc-doc.texi + -./texi2pod.pl $< tcc.pod + -pod2man --section=1 --center=" " --release=" " tcc.pod > $@ + +# tar release (use 'make -k tar' on a checkouted tree) +TCC-VERSION=tcc-$(shell cat VERSION) +tar: + rm -rf /tmp/$(TCC-VERSION) + cp -r . /tmp/$(TCC-VERSION) + ( cd /tmp ; tar zcvf ~/$(TCC-VERSION).tar.gz $(TCC-VERSION) --exclude CVS ) + rm -rf /tmp/$(TCC-VERSION) + +# in tests subdir +test clean : + $(MAKE) -C tests $@ + +# clean +clean: local_clean +local_clean: + rm -vf $(PROGS) tcc_p$(EXESUF) tcc.pod *~ *.o *.a *.out libtcc_test$(EXESUF) + +distclean: clean + rm -vf config.h config.mak config.texi tcc.1 tcc-doc.html + +endif # ifeq ($(TOP),.) diff --git a/05/tcc-0.9.25/README b/05/tcc-0.9.25/README new file mode 100644 index 0000000..bfaab39 --- /dev/null +++ b/05/tcc-0.9.25/README @@ -0,0 +1,90 @@ +Tiny C Compiler - C Scripting Everywhere - The Smallest ANSI C compiler +----------------------------------------------------------------------- + +Features: +-------- + +- SMALL! You can compile and execute C code everywhere, for example on + rescue disks. + +- FAST! tcc generates optimized x86 code. No byte code + overhead. Compile, assemble and link about 7 times faster than 'gcc + -O0'. + +- UNLIMITED! Any C dynamic library can be used directly. TCC is + heading torward full ISOC99 compliance. TCC can of course compile + itself. + +- SAFE! tcc includes an optional memory and bound checker. Bound + checked code can be mixed freely with standard code. + +- Compile and execute C source directly. No linking or assembly + necessary. Full C preprocessor included. + +- C script supported : just add '#!/usr/local/bin/tcc -run' at the first + line of your C source, and execute it directly from the command + line. + +Documentation: +------------- + +1) Installation on a i386 Linux host (for Windows read tcc-win32.txt) + + ./configure + make + make test + make install + +By default, tcc is installed in /usr/local/bin. +./configure --help shows configuration options. + + +2) Introduction + +We assume here that you know ANSI C. Look at the example ex1.c to know +what the programs look like. + +The include file can be used if you want a small basic libc +include support (especially useful for floppy disks). Of course, you +can also use standard headers, although they are slower to compile. + +You can begin your C script with '#!/usr/local/bin/tcc -run' on the first +line and set its execute bits (chmod a+x your_script). Then, you can +launch the C code as a shell or perl script :-) The command line +arguments are put in 'argc' and 'argv' of the main functions, as in +ANSI C. + +3) Examples + +ex1.c: simplest example (hello world). Can also be launched directly +as a script: './ex1.c'. + +ex2.c: more complicated example: find a number with the four +operations given a list of numbers (benchmark). + +ex3.c: compute fibonacci numbers (benchmark). + +ex4.c: more complicated: X11 program. Very complicated test in fact +because standard headers are being used ! + +ex5.c: 'hello world' with standard glibc headers. + +tcc.c: TCC can of course compile itself. Used to check the code +generator. + +tcctest.c: auto test for TCC which tests many subtle possible bugs. Used +when doing 'make test'. + +4) Full Documentation + +Please read tcc-doc.html to have all the features of TCC. + +Additional information is available for the Windows port in tcc-win32.txt. + +License: +------- + +TCC is distributed under the GNU Lesser General Public License (see +COPYING file). + +Fabrice Bellard. diff --git a/05/tcc-0.9.25/TODO b/05/tcc-0.9.25/TODO new file mode 100644 index 0000000..6f49c5d --- /dev/null +++ b/05/tcc-0.9.25/TODO @@ -0,0 +1,95 @@ +TODO list: + +Bugs: + +- fix macro substitution with nested definitions (ShangHongzhang) +- FPU st(0) is left unclean (kwisatz haderach). Incompatible with + optimized gcc/msc code + +- constructors +- cast bug (Peter Wang) +- define incomplete type if defined several times (Peter Wang). +- configure --cc=tcc (still one bug in libtcc1.c) +- test binutils/gcc compile +- tci patch + argument. +- see -lxxx bug (Michael Charity). +- see transparent union pb in /urs/include/sys/socket.h +- precise behaviour of typeof with arrays ? (__put_user macro) + but should suffice for most cases) +- handle '? x, y : z' in unsized variable initialization (',' is + considered incorrectly as separator in preparser) +- transform functions to function pointers in function parameters + (net/ipv4/ip_output.c) +- fix function pointer type display +- check lcc test suite -> fix bitfield binary operations +- check section alignment in C +- fix invalid cast in comparison 'if (v == (int8_t)v)' +- finish varargs.h support (gcc 3.2 testsuite issue) +- fix static functions declared inside block +- fix multiple unions init +- sizeof, alignof, typeof can still generate code in some cases. +- Fix the remaining libtcc memory leaks. +- make libtcc fully reentrant (except for the compilation stage itself). + +Bound checking: + +- '-b' bug. +- fix bound exit on RedHat 7.3 +- setjmp is not supported properly in bound checking. +- fix bound check code with '&' on local variables (currently done + only for local arrays). +- bound checking and float/long long/struct copy code. bound + checking and symbol + offset optimization + +Missing features: + +- disable-asm and disable-bcheck options +- __builtin_expect() +- improve '-E' option. +- add '-MD' option +- atexit (Nigel Horne) +- packed attribute +- C99: add variable size arrays (gcc 3.2 testsuite issue) +- C99: add complex types (gcc 3.2 testsuite issue) +- postfix compound literals (see 20010124-1.c) + +Optimizations: + +- suppress specific anonymous symbol handling +- more parse optimizations (=even faster compilation) +- memory alloc optimizations (=even faster compilation) +- optimize VT_LOCAL + const +- better local variables handling (needed for other targets) + +Not critical: + +- C99: fix multiple compound literals inits in blocks (ISOC99 + normative example - only relevant when using gotos! -> must add + boolean variable to tell if compound literal was already + initialized). +- add PowerPC or ARM code generator and improve codegen for RISC (need + to suppress VT_LOCAL and use a base register instead). +- interactive mode / integrated debugger +- fix preprocessor symbol redefinition +- better constant opt (&&, ||, ?:) +- add portable byte code generator and interpreter for other + unsupported architectures. +- C++: variable declaration in for, minimal 'class' support. +- win32: __intxx. use resolve for bchecked malloc et al. + check exception code (exception filter func). +- handle void (__attribute__() *ptr)() + +Fixed (probably): + +- bug with defines: + #define spin_lock(lock) do { } while (0) + #define wq_spin_lock spin_lock + #define TEST() wq_spin_lock(a) +- typedefs can be structure fields +- see bugfixes.diff + improvement.diff from Daniel Glockner +- long long constant evaluation +- add alloca() +- gcc '-E' option. +- #include_next support for /usr/include/limits ? +- function pointers/lvalues in ? : (linux kernel net/core/dev.c) +- win32: add __stdcall, check GetModuleHandle for dlls. diff --git a/05/tcc-0.9.25/VERSION b/05/tcc-0.9.25/VERSION new file mode 100644 index 0000000..f5b38be --- /dev/null +++ b/05/tcc-0.9.25/VERSION @@ -0,0 +1 @@ +0.9.25 \ No newline at end of file diff --git a/05/tcc-0.9.25/arm-gen.c b/05/tcc-0.9.25/arm-gen.c new file mode 100644 index 0000000..42feecf --- /dev/null +++ b/05/tcc-0.9.25/arm-gen.c @@ -0,0 +1,1734 @@ +/* + * ARMv4 code generator for TCC + * + * Copyright (c) 2003 Daniel Glckner + * + * Based on i386-gen.c by Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef TCC_ARM_EABI +#define TCC_ARM_VFP +#endif + + +/* number of available registers */ +#ifdef TCC_ARM_VFP +#define NB_REGS 13 +#else +#define NB_REGS 9 +#endif + +/* a register can belong to several classes. The classes must be + sorted from more general to more precise (see gv2() code which does + assumptions on it). */ +#define RC_INT 0x0001 /* generic integer register */ +#define RC_FLOAT 0x0002 /* generic float register */ +#define RC_R0 0x0004 +#define RC_R1 0x0008 +#define RC_R2 0x0010 +#define RC_R3 0x0020 +#define RC_R12 0x0040 +#define RC_F0 0x0080 +#define RC_F1 0x0100 +#define RC_F2 0x0200 +#define RC_F3 0x0400 +#ifdef TCC_ARM_VFP +#define RC_F4 0x0800 +#define RC_F5 0x1000 +#define RC_F6 0x2000 +#define RC_F7 0x4000 +#endif +#define RC_IRET RC_R0 /* function return: integer register */ +#define RC_LRET RC_R1 /* function return: second integer register */ +#define RC_FRET RC_F0 /* function return: float register */ + +/* pretty names for the registers */ +enum { + TREG_R0 = 0, + TREG_R1, + TREG_R2, + TREG_R3, + TREG_R12, + TREG_F0, + TREG_F1, + TREG_F2, + TREG_F3, +#ifdef TCC_ARM_VFP + TREG_F4, + TREG_F5, + TREG_F6, + TREG_F7, +#endif +}; + +int reg_classes[NB_REGS] = { + /* r0 */ RC_INT | RC_R0, + /* r1 */ RC_INT | RC_R1, + /* r2 */ RC_INT | RC_R2, + /* r3 */ RC_INT | RC_R3, + /* r12 */ RC_INT | RC_R12, + /* f0 */ RC_FLOAT | RC_F0, + /* f1 */ RC_FLOAT | RC_F1, + /* f2 */ RC_FLOAT | RC_F2, + /* f3 */ RC_FLOAT | RC_F3, +#ifdef TCC_ARM_VFP + /* d4/s8 */ RC_FLOAT | RC_F4, +/* d5/s10 */ RC_FLOAT | RC_F5, +/* d6/s12 */ RC_FLOAT | RC_F6, +/* d7/s14 */ RC_FLOAT | RC_F7, +#endif +}; + +static int two2mask(int a,int b) { + return (reg_classes[a]|reg_classes[b])&~(RC_INT|RC_FLOAT); +} + +static int regmask(int r) { + return reg_classes[r]&~(RC_INT|RC_FLOAT); +} + +#ifdef TCC_ARM_VFP +#define T2CPR(t) (((t) & VT_BTYPE) != VT_FLOAT ? 0x100 : 0) +#endif + +/* return registers for function */ +#define REG_IRET TREG_R0 /* single word int return register */ +#define REG_LRET TREG_R1 /* second word return register (for long long) */ +#define REG_FRET TREG_F0 /* float return register */ + +#ifdef TCC_ARM_EABI +#define TOK___divdi3 TOK___aeabi_ldivmod +#define TOK___moddi3 TOK___aeabi_ldivmod +#define TOK___udivdi3 TOK___aeabi_uldivmod +#define TOK___umoddi3 TOK___aeabi_uldivmod +#endif + +/* defined if function parameters must be evaluated in reverse order */ +#define INVERT_FUNC_PARAMS + +/* defined if structures are passed as pointers. Otherwise structures + are directly pushed on stack. */ +//#define FUNC_STRUCT_PARAM_AS_PTR + +#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) +static CType float_type, double_type, func_float_type, func_double_type; +#define func_ldouble_type func_double_type +#else +#define func_float_type func_old_type +#define func_double_type func_old_type +#define func_ldouble_type func_old_type +#endif + +/* pointer size, in bytes */ +#define PTR_SIZE 4 + +/* long double size and alignment, in bytes */ +#ifdef TCC_ARM_VFP +#define LDOUBLE_SIZE 8 +#endif + +#ifndef LDOUBLE_SIZE +#define LDOUBLE_SIZE 8 +#endif + +#ifdef TCC_ARM_EABI +#define LDOUBLE_ALIGN 8 +#else +#define LDOUBLE_ALIGN 4 +#endif + +/* maximum alignment (for aligned attribute support) */ +#define MAX_ALIGN 8 + +#define CHAR_IS_UNSIGNED + +/******************************************************/ +/* ELF defines */ + +#define EM_TCC_TARGET EM_ARM + +/* relocation type for 32 bit data relocation */ +#define R_DATA_32 R_ARM_ABS32 +#define R_JMP_SLOT R_ARM_JUMP_SLOT +#define R_COPY R_ARM_COPY + +#define ELF_START_ADDR 0x00008000 +#define ELF_PAGE_SIZE 0x1000 + +/******************************************************/ +static unsigned long func_sub_sp_offset,last_itod_magic; +static int leaffunc; + +void o(unsigned long i) +{ + /* this is a good place to start adding big-endian support*/ + int ind1; + + ind1 = ind + 4; + if (!cur_text_section) + error("compiler error! This happens f.ex. if the compiler\n" + "can't evaluate constant expressions outside of a function."); + if (ind1 > cur_text_section->data_allocated) + section_realloc(cur_text_section, ind1); + cur_text_section->data[ind++] = i&255; + i>>=8; + cur_text_section->data[ind++] = i&255; + i>>=8; + cur_text_section->data[ind++] = i&255; + i>>=8; + cur_text_section->data[ind++] = i; +} + +static unsigned long stuff_const(unsigned long op,unsigned long c) +{ + int try_neg=0; + unsigned long nc = 0,negop = 0; + + switch(op&0x1F00000) + { + case 0x800000: //add + case 0x400000: //sub + try_neg=1; + negop=op^0xC00000; + nc=-c; + break; + case 0x1A00000: //mov + case 0x1E00000: //mvn + try_neg=1; + negop=op^0x400000; + nc=~c; + break; + case 0x200000: //xor + if(c==~0) + return (op&0xF010F000)|((op>>16)&0xF)|0x1E00000; + break; + case 0x0: //and + if(c==~0) + return (op&0xF010F000)|((op>>16)&0xF)|0x1A00000; + case 0x1C00000: //bic + try_neg=1; + negop=op^0x1C00000; + nc=~c; + break; + case 0x1800000: //orr + if(c==~0) + return (op&0xFFF0FFFF)|0x1E00000; + break; + } + do { + unsigned long m; + int i; + if(c<256) /* catch undefined <<32 */ + return op|c; + for(i=2;i<32;i+=2) { + m=(0xff>>i)|(0xff<<(32-i)); + if(!(c&~m)) + return op|(i<<7)|(c<>(32-i)); + } + op=negop; + c=nc; + } while(try_neg--); + return 0; +} + + +//only add,sub +void stuff_const_harder(unsigned long op,unsigned long v) { + unsigned long x; + x=stuff_const(op,v); + if(x) + o(x); + else { + unsigned long a[16],nv,no,o2,n2; + int i,j,k; + a[0]=0xff; + o2=(op&0xfff0ffff)|((op&0xf000)<<4);; + for(i=1;i<16;i++) + a[i]=(a[i-1]>>2)|(a[i-1]<<30); + for(i=0;i<12;i++) + for(j=i<4?i+12:15;j>=i+4;j--) + if((v&(a[i]|a[j]))==v) { + o(stuff_const(op,v&a[i])); + o(stuff_const(o2,v&a[j])); + return; + } + no=op^0xC00000; + n2=o2^0xC00000; + nv=-v; + for(i=0;i<12;i++) + for(j=i<4?i+12:15;j>=i+4;j--) + if((nv&(a[i]|a[j]))==nv) { + o(stuff_const(no,nv&a[i])); + o(stuff_const(n2,nv&a[j])); + return; + } + for(i=0;i<8;i++) + for(j=i+4;j<12;j++) + for(k=i<4?i+12:15;k>=j+4;k--) + if((v&(a[i]|a[j]|a[k]))==v) { + o(stuff_const(op,v&a[i])); + o(stuff_const(o2,v&a[j])); + o(stuff_const(o2,v&a[k])); + return; + } + no=op^0xC00000; + nv=-v; + for(i=0;i<8;i++) + for(j=i+4;j<12;j++) + for(k=i<4?i+12:15;k>=j+4;k--) + if((nv&(a[i]|a[j]|a[k]))==nv) { + o(stuff_const(no,nv&a[i])); + o(stuff_const(n2,nv&a[j])); + o(stuff_const(n2,nv&a[k])); + return; + } + o(stuff_const(op,v&a[0])); + o(stuff_const(o2,v&a[4])); + o(stuff_const(o2,v&a[8])); + o(stuff_const(o2,v&a[12])); + } +} + +unsigned long encbranch(int pos,int addr,int fail) +{ + addr-=pos+8; + addr/=4; + if(addr>=0x1000000 || addr<-0x1000000) { + if(fail) + error("FIXME: function bigger than 32MB"); + return 0; + } + return 0x0A000000|(addr&0xffffff); +} + +int decbranch(int pos) +{ + int x; + x=*(int *)(cur_text_section->data + pos); + x&=0x00ffffff; + if(x&0x800000) + x-=0x1000000; + return x*4+pos+8; +} + +/* output a symbol and patch all calls to it */ +void gsym_addr(int t, int a) +{ + unsigned long *x; + int lt; + while(t) { + x=(unsigned long *)(cur_text_section->data + t); + t=decbranch(lt=t); + if(a==lt+4) + *x=0xE1A00000; // nop + else { + *x &= 0xff000000; + *x |= encbranch(lt,a,1); + } + } +} + +void gsym(int t) +{ + gsym_addr(t, ind); +} + +#ifdef TCC_ARM_VFP +static unsigned long vfpr(int r) +{ + if(rTREG_F7) + error("compiler error! register %i is no vfp register",r); + return r-5; +} +#else +static unsigned long fpr(int r) +{ + if(rTREG_F3) + error("compiler error! register %i is no fpa register",r); + return r-5; +} +#endif + +static unsigned long intr(int r) +{ + if(r==4) + return 12; + if((r<0 || r>4) && r!=14) + error("compiler error! register %i is no int register",r); + return r; +} + +static void calcaddr(unsigned long *base,int *off,int *sgn,int maxoff,unsigned shift) +{ + if(*off>maxoff || *off&((1<r; + ft = sv->type.t; + fc = sv->c.ul; + + if(fc>=0) + sign=0; + else { + sign=1; + fc=-fc; + } + + v = fr & VT_VALMASK; + if (fr & VT_LVAL) { + unsigned long base=0xB; // fp + if(v == VT_LLOCAL) { + v1.type.t = VT_PTR; + v1.r = VT_LOCAL | VT_LVAL; + v1.c.ul = sv->c.ul; + load(base=14 /* lr */, &v1); + fc=sign=0; + v=VT_LOCAL; + } else if(v == VT_CONST) { + v1.type.t = VT_PTR; + v1.r = fr&~VT_LVAL; + v1.c.ul = sv->c.ul; + v1.sym=sv->sym; + load(base=14, &v1); + fc=sign=0; + v=VT_LOCAL; + } else if(v < VT_CONST) { + base=intr(v); + fc=sign=0; + v=VT_LOCAL; + } + if(v == VT_LOCAL) { + if(is_float(ft)) { + calcaddr(&base,&fc,&sign,1020,2); +#ifdef TCC_ARM_VFP + op=0xED100A00; /* flds */ + if(!sign) + op|=0x800000; + if ((ft & VT_BTYPE) != VT_FLOAT) + op|=0x100; /* flds -> fldd */ + o(op|(vfpr(r)<<12)|(fc>>2)|(base<<16)); +#else + op=0xED100100; + if(!sign) + op|=0x800000; +#if LDOUBLE_SIZE == 8 + if ((ft & VT_BTYPE) != VT_FLOAT) + op|=0x8000; +#else + if ((ft & VT_BTYPE) == VT_DOUBLE) + op|=0x8000; + else if ((ft & VT_BTYPE) == VT_LDOUBLE) + op|=0x400000; +#endif + o(op|(fpr(r)<<12)|(fc>>2)|(base<<16)); +#endif + } else if((ft & (VT_BTYPE|VT_UNSIGNED)) == VT_BYTE + || (ft & VT_BTYPE) == VT_SHORT) { + calcaddr(&base,&fc,&sign,255,0); + op=0xE1500090; + if ((ft & VT_BTYPE) == VT_SHORT) + op|=0x20; + if ((ft & VT_UNSIGNED) == 0) + op|=0x40; + if(!sign) + op|=0x800000; + o(op|(intr(r)<<12)|(base<<16)|((fc&0xf0)<<4)|(fc&0xf)); + } else { + calcaddr(&base,&fc,&sign,4095,0); + op=0xE5100000; + if(!sign) + op|=0x800000; + if ((ft & VT_BTYPE) == VT_BYTE) + op|=0x400000; + o(op|(intr(r)<<12)|fc|(base<<16)); + } + return; + } + } else { + if (v == VT_CONST) { + op=stuff_const(0xE3A00000|(intr(r)<<12),sv->c.ul); + if (fr & VT_SYM || !op) { + o(0xE59F0000|(intr(r)<<12)); + o(0xEA000000); + if(fr & VT_SYM) + greloc(cur_text_section, sv->sym, ind, R_ARM_ABS32); + o(sv->c.ul); + } else + o(op); + return; + } else if (v == VT_LOCAL) { + op=stuff_const(0xE28B0000|(intr(r)<<12),sv->c.ul); + if (fr & VT_SYM || !op) { + o(0xE59F0000|(intr(r)<<12)); + o(0xEA000000); + if(fr & VT_SYM) // needed ? + greloc(cur_text_section, sv->sym, ind, R_ARM_ABS32); + o(sv->c.ul); + o(0xE08B0000|(intr(r)<<12)|intr(r)); + } else + o(op); + return; + } else if(v == VT_CMP) { + o(mapcc(sv->c.ul)|0x3A00001|(intr(r)<<12)); + o(mapcc(negcc(sv->c.ul))|0x3A00000|(intr(r)<<12)); + return; + } else if (v == VT_JMP || v == VT_JMPI) { + int t; + t = v & 1; + o(0xE3A00000|(intr(r)<<12)|t); + o(0xEA000000); + gsym(sv->c.ul); + o(0xE3A00000|(intr(r)<<12)|(t^1)); + return; + } else if (v < VT_CONST) { + if(is_float(ft)) +#ifdef TCC_ARM_VFP + o(0xEEB00A40|(vfpr(r)<<12)|vfpr(v)|T2CPR(ft)); /* fcpyX */ +#else + o(0xEE008180|(fpr(r)<<12)|fpr(v)); +#endif + else + o(0xE1A00000|(intr(r)<<12)|intr(v)); + return; + } + } + error("load unimplemented!"); +} + +/* store register 'r' in lvalue 'v' */ +void store(int r, SValue *sv) +{ + SValue v1; + int v, ft, fc, fr, sign; + unsigned long op; + + fr = sv->r; + ft = sv->type.t; + fc = sv->c.ul; + + if(fc>=0) + sign=0; + else { + sign=1; + fc=-fc; + } + + v = fr & VT_VALMASK; + if (fr & VT_LVAL || fr == VT_LOCAL) { + unsigned long base=0xb; + if(v < VT_CONST) { + base=intr(v); + v=VT_LOCAL; + fc=sign=0; + } else if(v == VT_CONST) { + v1.type.t = ft; + v1.r = fr&~VT_LVAL; + v1.c.ul = sv->c.ul; + v1.sym=sv->sym; + load(base=14, &v1); + fc=sign=0; + v=VT_LOCAL; + } + if(v == VT_LOCAL) { + if(is_float(ft)) { + calcaddr(&base,&fc,&sign,1020,2); +#ifdef TCC_ARM_VFP + op=0xED000A00; /* fsts */ + if(!sign) + op|=0x800000; + if ((ft & VT_BTYPE) != VT_FLOAT) + op|=0x100; /* fsts -> fstd */ + o(op|(vfpr(r)<<12)|(fc>>2)|(base<<16)); +#else + op=0xED000100; + if(!sign) + op|=0x800000; +#if LDOUBLE_SIZE == 8 + if ((ft & VT_BTYPE) != VT_FLOAT) + op|=0x8000; +#else + if ((ft & VT_BTYPE) == VT_DOUBLE) + op|=0x8000; + if ((ft & VT_BTYPE) == VT_LDOUBLE) + op|=0x400000; +#endif + o(op|(fpr(r)<<12)|(fc>>2)|(base<<16)); +#endif + return; + } else if((ft & VT_BTYPE) == VT_SHORT) { + calcaddr(&base,&fc,&sign,255,0); + op=0xE14000B0; + if(!sign) + op|=0x800000; + o(op|(intr(r)<<12)|(base<<16)|((fc&0xf0)<<4)|(fc&0xf)); + } else { + calcaddr(&base,&fc,&sign,4095,0); + op=0xE5000000; + if(!sign) + op|=0x800000; + if ((ft & VT_BTYPE) == VT_BYTE) + op|=0x400000; + o(op|(intr(r)<<12)|fc|(base<<16)); + } + return; + } + } + error("store unimplemented"); +} + +static void gadd_sp(int val) +{ + stuff_const_harder(0xE28DD000,val); +} + +/* 'is_jmp' is '1' if it is a jump */ +static void gcall_or_jmp(int is_jmp) +{ + int r; + if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { + unsigned long x; + /* constant case */ + x=encbranch(ind,ind+vtop->c.ul,0); + if(x) { + if (vtop->r & VT_SYM) { + /* relocation case */ + greloc(cur_text_section, vtop->sym, ind, R_ARM_PC24); + } else + put_elf_reloc(symtab_section, cur_text_section, ind, R_ARM_PC24, 0); + o(x|(is_jmp?0xE0000000:0xE1000000)); + } else { + if(!is_jmp) + o(0xE28FE004); // add lr,pc,#4 + o(0xE51FF004); // ldr pc,[pc,#-4] + if (vtop->r & VT_SYM) + greloc(cur_text_section, vtop->sym, ind, R_ARM_ABS32); + o(vtop->c.ul); + } + } else { + /* otherwise, indirect call */ + r = gv(RC_INT); + if(!is_jmp) + o(0xE1A0E00F); // mov lr,pc + o(0xE1A0F000|intr(r)); // mov pc,r + } +} + +/* Generate function call. The function address is pushed first, then + all the parameters in call order. This functions pops all the + parameters and the function address. */ +void gfunc_call(int nb_args) +{ + int size, align, r, args_size, i; + Sym *func_sym; + signed char plan[4][2]={{-1,-1},{-1,-1},{-1,-1},{-1,-1}}; + int todo=0xf, keep, plan2[4]={0,0,0,0}; + + r = vtop->r & VT_VALMASK; + if (r == VT_CMP || (r & ~1) == VT_JMP) + gv(RC_INT); +#ifdef TCC_ARM_EABI + if((vtop[-nb_args].type.ref->type.t & VT_BTYPE) == VT_STRUCT + && type_size(&vtop[-nb_args].type, &align) <= 4) { + SValue tmp; + tmp=vtop[-nb_args]; + vtop[-nb_args]=vtop[-nb_args+1]; + vtop[-nb_args+1]=tmp; + --nb_args; + } + + vpushi(0); + vtop->type.t = VT_LLONG; + args_size = 0; + for(i = nb_args + 1 ; i-- ;) { + size = type_size(&vtop[-i].type, &align); + if(args_size & (align-1)) { + vpushi(0); + vtop->type.t = VT_VOID; /* padding */ + vrott(i+2); + args_size += 4; + ++nb_args; + } + args_size += (size + 3) & -4; + } + vtop--; +#endif + args_size = 0; + for(i = nb_args ; i-- && args_size < 16 ;) { + switch(vtop[-i].type.t & VT_BTYPE) { + case VT_STRUCT: + case VT_FLOAT: + case VT_DOUBLE: + case VT_LDOUBLE: + size = type_size(&vtop[-i].type, &align); + size = (size + 3) & -4; + args_size += size; + break; + default: + plan[nb_args-1-i][0]=args_size/4; + args_size += 4; + if ((vtop[-i].type.t & VT_BTYPE) == VT_LLONG && args_size < 16) { + plan[nb_args-1-i][1]=args_size/4; + args_size += 4; + } + } + } + args_size = keep = 0; + for(i = 0;i < nb_args; i++) { + vnrott(keep+1); + if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { + size = type_size(&vtop->type, &align); + /* align to stack align size */ + size = (size + 3) & -4; + /* allocate the necessary size on stack */ + gadd_sp(-size); + /* generate structure store */ + r = get_reg(RC_INT); + o(0xE1A0000D|(intr(r)<<12)); + vset(&vtop->type, r | VT_LVAL, 0); + vswap(); + vstore(); + vtop--; + args_size += size; + } else if (is_float(vtop->type.t)) { +#ifdef TCC_ARM_VFP + r=vfpr(gv(RC_FLOAT))<<12; + size=4; + if ((vtop->type.t & VT_BTYPE) != VT_FLOAT) + { + size=8; + r|=0x101; /* fstms -> fstmd */ + } + o(0xED2D0A01+r); +#else + r=fpr(gv(RC_FLOAT))<<12; + if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) + size = 4; + else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) + size = 8; + else + size = LDOUBLE_SIZE; + + if (size == 12) + r|=0x400000; + else if(size == 8) + r|=0x8000; + + o(0xED2D0100|r|(size>>2)); +#endif + vtop--; + args_size += size; + } else { + int s; + /* simple type (currently always same size) */ + /* XXX: implicit cast ? */ + size=4; + if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { + lexpand_nr(); + s=RC_INT; + if(nb_args-i<5 && plan[nb_args-i-1][1]!=-1) { + s=regmask(plan[nb_args-i-1][1]); + todo&=~(1<type.t == VT_VOID) { + if(s == RC_INT) + o(0xE24DD004); /* sub sp,sp,#4 */ + vtop--; + } else +#endif + if(s == RC_INT) { + r = gv(s); + o(0xE52D0004|(intr(r)<<12)); /* str r,[sp,#-4]! */ + vtop--; + } else { + plan2[keep]=s; + keep++; + } + args_size += size; + } + } + for(i=keep;i--;) { + gv(plan2[i]); + vrott(keep); + } +save_regs(keep); /* save used temporary registers */ + keep++; + if(args_size) { + int n; + n=args_size/4; + if(n>4) + n=4; + todo&=((1<r=i; + keep++; + } + } + args_size-=n*4; + } + vnrott(keep); + func_sym = vtop->type.ref; + gcall_or_jmp(0); + if (args_size) + gadd_sp(args_size); +#ifdef TCC_ARM_EABI + if((vtop->type.ref->type.t & VT_BTYPE) == VT_STRUCT + && type_size(&vtop->type.ref->type, &align) <= 4) + { + store(REG_IRET,vtop-keep); + ++keep; + } +#ifdef TCC_ARM_VFP + else if(is_float(vtop->type.ref->type.t)) { + if((vtop->type.ref->type.t & VT_BTYPE) == VT_FLOAT) { + o(0xEE000A10); /* fmsr s0,r0 */ + } else { + o(0xEE000B10); /* fmdlr d0,r0 */ + o(0xEE201B10); /* fmdhr d0,r1 */ + } + } +#endif +#endif + vtop-=keep; + leaffunc = 0; +} + +/* generate function prolog of type 't' */ +void gfunc_prolog(CType *func_type) +{ + Sym *sym,*sym2; + int n,addr,size,align; + + sym = func_type->ref; + func_vt = sym->type; + + n = 0; + addr = 0; + if((func_vt.t & VT_BTYPE) == VT_STRUCT + && type_size(&func_vt,&align) > 4) + { + func_vc = addr; + addr += 4; + n++; + } + for(sym2=sym->next;sym2 && n<4;sym2=sym2->next) { + size = type_size(&sym2->type, &align); + n += (size + 3) / 4; + } + o(0xE1A0C00D); /* mov ip,sp */ + if(func_type->ref->c == FUNC_ELLIPSIS) + n=4; + if(n) { + if(n>4) + n=4; +#ifdef TCC_ARM_EABI + n=(n+1)&-2; +#endif + o(0xE92D0000|((1<next)) { + CType *type; + type = &sym->type; + size = type_size(type, &align); + size = (size + 3) & -4; +#ifdef TCC_ARM_EABI + addr = (addr + align - 1) & -align; +#endif + sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | lvalue_type(type->t), addr); + addr += size; + } + last_itod_magic=0; + leaffunc = 1; + loc = -12; +} + +/* generate function epilog */ +void gfunc_epilog(void) +{ + unsigned long x; + int diff; +#ifdef TCC_ARM_EABI + if(is_float(func_vt.t)) { + if((func_vt.t & VT_BTYPE) == VT_FLOAT) + o(0xEE100A10); /* fmrs r0, s0 */ + else { + o(0xEE100B10); /* fmrdl r0, d0 */ + o(0xEE301B10); /* fmrdh r1, d0 */ + } + } +#endif + o(0xE91BA800); /* restore fp, sp, pc */ + diff = (-loc + 3) & -4; +#ifdef TCC_ARM_EABI + if(!leaffunc) + diff = (diff + 7) & -8; +#endif + if(diff > 12) { + x=stuff_const(0xE24BD000, diff); /* sub sp,fp,# */ + if(x) + *(unsigned long *)(cur_text_section->data + func_sub_sp_offset) = x; + else { + unsigned long addr; + addr=ind; + o(0xE59FC004); /* ldr ip,[pc+4] */ + o(0xE04BD00C); /* sub sp,fp,ip */ + o(0xE1A0F00E); /* mov pc,lr */ + o(diff); + *(unsigned long *)(cur_text_section->data + func_sub_sp_offset) = 0xE1000000|encbranch(func_sub_sp_offset,addr,1); + } + } +} + +/* generate a jump to a label */ +int gjmp(int t) +{ + int r; + r=ind; + o(0xE0000000|encbranch(r,t,1)); + return r; +} + +/* generate a jump to a fixed address */ +void gjmp_addr(int a) +{ + gjmp(a); +} + +/* generate a test. set 'inv' to invert test. Stack entry is popped */ +int gtst(int inv, int t) +{ + int v, r; + unsigned long op; + v = vtop->r & VT_VALMASK; + r=ind; + if (v == VT_CMP) { + op=mapcc(inv?negcc(vtop->c.i):vtop->c.i); + op|=encbranch(r,t,1); + o(op); + t=r; + } else if (v == VT_JMP || v == VT_JMPI) { + if ((v & 1) == inv) { + if(!vtop->c.i) + vtop->c.i=t; + else { + unsigned long *x; + int p,lp; + if(t) { + p = vtop->c.i; + do { + p = decbranch(lp=p); + } while(p); + x = (unsigned long *)(cur_text_section->data + lp); + *x &= 0xff000000; + *x |= encbranch(lp,t,1); + } + t = vtop->c.i; + } + } else { + t = gjmp(t); + gsym(vtop->c.i); + } + } else { + if (is_float(vtop->type.t)) { + r=gv(RC_FLOAT); +#ifdef TCC_ARM_VFP + o(0xEEB50A40|(vfpr(r)<<12)|T2CPR(vtop->type.t)); /* fcmpzX */ + o(0xEEF1FA10); /* fmstat */ +#else + o(0xEE90F118|(fpr(r)<<16)); +#endif + vtop->r = VT_CMP; + vtop->c.i = TOK_NE; + return gtst(inv, t); + } else if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + /* constant jmp optimization */ + if ((vtop->c.i != 0) != inv) + t = gjmp(t); + } else { + v = gv(RC_INT); + o(0xE3300000|(intr(v)<<16)); + vtop->r = VT_CMP; + vtop->c.i = TOK_NE; + return gtst(inv, t); + } + } + vtop--; + return t; +} + +/* generate an integer binary operation */ +void gen_opi(int op) +{ + int c, func = 0; + unsigned long opc = 0,r,fr; + unsigned short retreg = REG_IRET; + + c=0; + switch(op) { + case '+': + opc = 0x8; + c=1; + break; + case TOK_ADDC1: /* add with carry generation */ + opc = 0x9; + c=1; + break; + case '-': + opc = 0x4; + c=1; + break; + case TOK_SUBC1: /* sub with carry generation */ + opc = 0x5; + c=1; + break; + case TOK_ADDC2: /* add with carry use */ + opc = 0xA; + c=1; + break; + case TOK_SUBC2: /* sub with carry use */ + opc = 0xC; + c=1; + break; + case '&': + opc = 0x0; + c=1; + break; + case '^': + opc = 0x2; + c=1; + break; + case '|': + opc = 0x18; + c=1; + break; + case '*': + gv2(RC_INT, RC_INT); + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + o(0xE0000090|(intr(r)<<16)|(intr(r)<<8)|intr(fr)); + return; + case TOK_SHL: + opc = 0; + c=2; + break; + case TOK_SHR: + opc = 1; + c=2; + break; + case TOK_SAR: + opc = 2; + c=2; + break; + case '/': + case TOK_PDIV: + func=TOK___divsi3; + c=3; + break; + case TOK_UDIV: + func=TOK___udivsi3; + c=3; + break; + case '%': +#ifdef TCC_ARM_EABI + func=TOK___aeabi_idivmod; + retreg=REG_LRET; +#else + func=TOK___modsi3; +#endif + c=3; + break; + case TOK_UMOD: +#ifdef TCC_ARM_EABI + func=TOK___aeabi_uidivmod; + retreg=REG_LRET; +#else + func=TOK___umodsi3; +#endif + c=3; + break; + case TOK_UMULL: + gv2(RC_INT, RC_INT); + r=intr(vtop[-1].r2=get_reg(RC_INT)); + c=vtop[-1].r; + vtop[-1].r=get_reg_ex(RC_INT,regmask(c)); + vtop--; + o(0xE0800090|(r<<16)|(intr(vtop->r)<<12)|(intr(c)<<8)|intr(vtop[1].r)); + return; + default: + opc = 0x15; + c=1; + break; + } + switch(c) { + case 1: + if((vtop[-1].r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + if(opc == 4 || opc == 5 || opc == 0xc) { + vswap(); + opc|=2; // sub -> rsb + } + } + if ((vtop->r & VT_VALMASK) == VT_CMP || + (vtop->r & (VT_VALMASK & ~1)) == VT_JMP) + gv(RC_INT); + vswap(); + c=intr(gv(RC_INT)); + vswap(); + opc=0xE0000000|(opc<<20)|(c<<16); + if((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + unsigned long x; + x=stuff_const(opc|0x2000000,vtop->c.i); + if(x) { + r=intr(vtop[-1].r=get_reg_ex(RC_INT,regmask(vtop[-1].r))); + o(x|(r<<12)); + goto done; + } + } + fr=intr(gv(RC_INT)); + r=intr(vtop[-1].r=get_reg_ex(RC_INT,two2mask(vtop->r,vtop[-1].r))); + o(opc|(r<<12)|fr); +done: + vtop--; + if (op >= TOK_ULT && op <= TOK_GT) { + vtop->r = VT_CMP; + vtop->c.i = op; + } + break; + case 2: + opc=0xE1A00000|(opc<<5); + if ((vtop->r & VT_VALMASK) == VT_CMP || + (vtop->r & (VT_VALMASK & ~1)) == VT_JMP) + gv(RC_INT); + vswap(); + r=intr(gv(RC_INT)); + vswap(); + opc|=r; + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + fr=intr(vtop[-1].r=get_reg_ex(RC_INT,regmask(vtop[-1].r))); + c = vtop->c.i & 0x1f; + o(opc|(c<<7)|(fr<<12)); + } else { + fr=intr(gv(RC_INT)); + c=intr(vtop[-1].r=get_reg_ex(RC_INT,two2mask(vtop->r,vtop[-1].r))); + o(opc|(c<<12)|(fr<<8)|0x10); + } + vtop--; + break; + case 3: + vpush_global_sym(&func_old_type, func); + vrott(3); + gfunc_call(2); + vpushi(0); + vtop->r = retreg; + break; + default: + error("gen_opi %i unimplemented!",op); + } +} + +#ifdef TCC_ARM_VFP +static int is_zero(int i) +{ + if((vtop[i].r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) + return 0; + if (vtop[i].type.t == VT_FLOAT) + return (vtop[i].c.f == 0.f); + else if (vtop[i].type.t == VT_DOUBLE) + return (vtop[i].c.d == 0.0); + return (vtop[i].c.ld == 0.l); +} + +/* generate a floating point operation 'v = t1 op t2' instruction. The + * two operands are guaranted to have the same floating point type */ +void gen_opf(int op) +{ + unsigned long x; + int fneg=0,r; + x=0xEE000A00|T2CPR(vtop->type.t); + switch(op) { + case '+': + if(is_zero(-1)) + vswap(); + if(is_zero(0)) { + vtop--; + return; + } + x|=0x300000; + break; + case '-': + x|=0x300040; + if(is_zero(0)) { + vtop--; + return; + } + if(is_zero(-1)) { + x|=0x810000; /* fsubX -> fnegX */ + vswap(); + vtop--; + fneg=1; + } + break; + case '*': + x|=0x200000; + break; + case '/': + x|=0x800000; + break; + default: + if(op < TOK_ULT && op > TOK_GT) { + error("unknown fp op %x!",op); + return; + } + if(is_zero(-1)) { + vswap(); + switch(op) { + case TOK_LT: op=TOK_GT; break; + case TOK_GE: op=TOK_ULE; break; + case TOK_LE: op=TOK_GE; break; + case TOK_GT: op=TOK_ULT; break; + } + } + x|=0xB40040; /* fcmpX */ + if(op!=TOK_EQ && op!=TOK_NE) + x|=0x80; /* fcmpX -> fcmpeX */ + if(is_zero(0)) { + vtop--; + o(x|0x10000|(vfpr(gv(RC_FLOAT))<<12)); /* fcmp(e)X -> fcmp(e)zX */ + } else { + x|=vfpr(gv(RC_FLOAT)); + vswap(); + o(x|(vfpr(gv(RC_FLOAT))<<12)); + vtop--; + } + o(0xEEF1FA10); /* fmstat */ + + switch(op) { + case TOK_LE: op=TOK_ULE; break; + case TOK_LT: op=TOK_ULT; break; + case TOK_UGE: op=TOK_GE; break; + case TOK_UGT: op=TOK_GT; break; + } + + vtop->r = VT_CMP; + vtop->c.i = op; + return; + } + r=gv(RC_FLOAT); + x|=vfpr(r); + r=regmask(r); + if(!fneg) { + int r2; + vswap(); + r2=gv(RC_FLOAT); + x|=vfpr(r2)<<16; + r|=regmask(r2); + } + vtop->r=get_reg_ex(RC_FLOAT,r); + if(!fneg) + vtop--; + o(x|(vfpr(vtop->r)<<12)); +} + +#else +static int is_fconst() +{ + long double f; + int r; + if((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) + return 0; + if (vtop->type.t == VT_FLOAT) + f = vtop->c.f; + else if (vtop->type.t == VT_DOUBLE) + f = vtop->c.d; + else + f = vtop->c.ld; + if(!ieee_finite(f)) + return 0; + r=0x8; + if(f<0.0) { + r=0x18; + f=-f; + } + if(f==0.0) + return r; + if(f==1.0) + return r|1; + if(f==2.0) + return r|2; + if(f==3.0) + return r|3; + if(f==4.0) + return r|4; + if(f==5.0) + return r|5; + if(f==0.5) + return r|6; + if(f==10.0) + return r|7; + return 0; +} + +/* generate a floating point operation 'v = t1 op t2' instruction. The + two operands are guaranted to have the same floating point type */ +void gen_opf(int op) +{ + unsigned long x; + int r,r2,c1,c2; + //fputs("gen_opf\n",stderr); + vswap(); + c1 = is_fconst(); + vswap(); + c2 = is_fconst(); + x=0xEE000100; +#if LDOUBLE_SIZE == 8 + if ((vtop->type.t & VT_BTYPE) != VT_FLOAT) + x|=0x80; +#else + if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) + x|=0x80; + else if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) + x|=0x80000; +#endif + switch(op) + { + case '+': + if(!c2) { + vswap(); + c2=c1; + } + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + if(c2) { + if(c2>0xf) + x|=0x200000; // suf + r2=c2&0xf; + } else { + r2=fpr(gv(RC_FLOAT)); + } + break; + case '-': + if(c2) { + if(c2<=0xf) + x|=0x200000; // suf + r2=c2&0xf; + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + } else if(c1 && c1<=0xf) { + x|=0x300000; // rsf + r2=c1; + r=fpr(gv(RC_FLOAT)); + vswap(); + } else { + x|=0x200000; // suf + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + r2=fpr(gv(RC_FLOAT)); + } + break; + case '*': + if(!c2 || c2>0xf) { + vswap(); + c2=c1; + } + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + if(c2 && c2<=0xf) + r2=c2; + else + r2=fpr(gv(RC_FLOAT)); + x|=0x100000; // muf + break; + case '/': + if(c2 && c2<=0xf) { + x|=0x400000; // dvf + r2=c2; + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + } else if(c1 && c1<=0xf) { + x|=0x500000; // rdf + r2=c1; + r=fpr(gv(RC_FLOAT)); + vswap(); + } else { + x|=0x400000; // dvf + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + r2=fpr(gv(RC_FLOAT)); + } + break; + default: + if(op >= TOK_ULT && op <= TOK_GT) { + x|=0xd0f110; // cmfe +/* bug (intention?) in Linux FPU emulator + doesn't set carry if equal */ + switch(op) { + case TOK_ULT: + case TOK_UGE: + case TOK_ULE: + case TOK_UGT: + error("unsigned comparision on floats?"); + break; + case TOK_LT: + op=TOK_Nset; + break; + case TOK_LE: + op=TOK_ULE; /* correct in unordered case only if AC bit in FPSR set */ + break; + case TOK_EQ: + case TOK_NE: + x&=~0x400000; // cmfe -> cmf + break; + } + if(c1 && !c2) { + c2=c1; + vswap(); + switch(op) { + case TOK_Nset: + op=TOK_GT; + break; + case TOK_GE: + op=TOK_ULE; + break; + case TOK_ULE: + op=TOK_GE; + break; + case TOK_GT: + op=TOK_Nset; + break; + } + } + vswap(); + r=fpr(gv(RC_FLOAT)); + vswap(); + if(c2) { + if(c2>0xf) + x|=0x200000; + r2=c2&0xf; + } else { + r2=fpr(gv(RC_FLOAT)); + } + vtop[-1].r = VT_CMP; + vtop[-1].c.i = op; + } else { + error("unknown fp op %x!",op); + return; + } + } + if(vtop[-1].r == VT_CMP) + c1=15; + else { + c1=vtop->r; + if(r2&0x8) + c1=vtop[-1].r; + vtop[-1].r=get_reg_ex(RC_FLOAT,two2mask(vtop[-1].r,c1)); + c1=fpr(vtop[-1].r); + } + vtop--; + o(x|(r<<16)|(c1<<12)|r2); +} +#endif + +/* convert integers to fp 't' type. Must handle 'int', 'unsigned int' + and 'long long' cases. */ +void gen_cvt_itof1(int t) +{ + int r,r2,bt; + bt=vtop->type.t & VT_BTYPE; + if(bt == VT_INT || bt == VT_SHORT || bt == VT_BYTE) { +#ifndef TCC_ARM_VFP + unsigned int dsize=0; +#endif + r=intr(gv(RC_INT)); +#ifdef TCC_ARM_VFP + r2=vfpr(vtop->r=get_reg(RC_FLOAT)); + o(0xEE000A10|(r<<12)|(r2<<16)); /* fmsr */ + r2<<=12; + if(!(vtop->type.t & VT_UNSIGNED)) + r2|=0x80; /* fuitoX -> fsituX */ + o(0xEEB80A40|r2|T2CPR(t)); /* fYitoX*/ +#else + r2=fpr(vtop->r=get_reg(RC_FLOAT)); + if((t & VT_BTYPE) != VT_FLOAT) + dsize=0x80; /* flts -> fltd */ + o(0xEE000110|dsize|(r2<<16)|(r<<12)); /* flts */ + if((vtop->type.t & (VT_UNSIGNED|VT_BTYPE)) == (VT_UNSIGNED|VT_INT)) { + unsigned int off=0; + o(0xE3500000|(r<<12)); /* cmp */ + r=fpr(get_reg(RC_FLOAT)); + if(last_itod_magic) { + off=ind+8-last_itod_magic; + off/=4; + if(off>255) + off=0; + } + o(0xBD1F0100|(r<<12)|off); /* ldflts */ + if(!off) { + o(0xEA000000); /* b */ + last_itod_magic=ind; + o(0x4F800000); /* 4294967296.0f */ + } + o(0xBE000100|dsize|(r2<<16)|(r2<<12)|r); /* adflt */ + } +#endif + return; + } else if(bt == VT_LLONG) { + int func; + CType *func_type = 0; + if((t & VT_BTYPE) == VT_FLOAT) { + func_type = &func_float_type; + if(vtop->type.t & VT_UNSIGNED) + func=TOK___floatundisf; + else + func=TOK___floatdisf; +#if LDOUBLE_SIZE != 8 + } else if((t & VT_BTYPE) == VT_LDOUBLE) { + func_type = &func_ldouble_type; + if(vtop->type.t & VT_UNSIGNED) + func=TOK___floatundixf; + else + func=TOK___floatdixf; + } else if((t & VT_BTYPE) == VT_DOUBLE) { +#else + } else if((t & VT_BTYPE) == VT_DOUBLE || (t & VT_BTYPE) == VT_LDOUBLE) { +#endif + func_type = &func_double_type; + if(vtop->type.t & VT_UNSIGNED) + func=TOK___floatundidf; + else + func=TOK___floatdidf; + } + if(func_type) { + vpush_global_sym(func_type, func); + vswap(); + gfunc_call(1); + vpushi(0); + vtop->r=TREG_F0; + return; + } + } + error("unimplemented gen_cvt_itof %x!",vtop->type.t); +} + +/* convert fp to int 't' type */ +void gen_cvt_ftoi(int t) +{ + int r,r2,u,func=0; + u=t&VT_UNSIGNED; + t&=VT_BTYPE; + r2=vtop->type.t & VT_BTYPE; + if(t==VT_INT) { +#ifdef TCC_ARM_VFP + r=vfpr(gv(RC_FLOAT)); + u=u?0:0x10000; + o(0xEEBC0A40|(r<<12)|r|T2CPR(r2)); /* ftoXiY */ + r2=intr(vtop->r=get_reg(RC_INT)); + o(0xEE100A10|(r<<16)|(r2<<12)); + return; +#else + if(u) { + if(r2 == VT_FLOAT) + func=TOK___fixunssfsi; +#if LDOUBLE_SIZE != 8 + else if(r2 == VT_LDOUBLE) + func=TOK___fixunsxfsi; + else if(r2 == VT_DOUBLE) +#else + else if(r2 == VT_LDOUBLE || r2 == VT_DOUBLE) +#endif + func=TOK___fixunsdfsi; + } else { + r=fpr(gv(RC_FLOAT)); + r2=intr(vtop->r=get_reg(RC_INT)); + o(0xEE100170|(r2<<12)|r); + return; + } +#endif + } else if(t == VT_LLONG) { // unsigned handled in gen_cvt_ftoi1 + if(r2 == VT_FLOAT) + func=TOK___fixsfdi; +#if LDOUBLE_SIZE != 8 + else if(r2 == VT_LDOUBLE) + func=TOK___fixxfdi; + else if(r2 == VT_DOUBLE) +#else + else if(r2 == VT_LDOUBLE || r2 == VT_DOUBLE) +#endif + func=TOK___fixdfdi; + } + if(func) { + vpush_global_sym(&func_old_type, func); + vswap(); + gfunc_call(1); + vpushi(0); + if(t == VT_LLONG) + vtop->r2 = REG_LRET; + vtop->r = REG_IRET; + return; + } + error("unimplemented gen_cvt_ftoi!"); +} + +/* convert from one floating point type to another */ +void gen_cvt_ftof(int t) +{ +#ifdef TCC_ARM_VFP + if(((vtop->type.t & VT_BTYPE) == VT_FLOAT) != ((t & VT_BTYPE) == VT_FLOAT)) { + int r=vfpr(gv(RC_FLOAT)); + o(0xEEB70AC0|(r<<12)|r|T2CPR(vtop->type.t)); + } +#else + /* all we have to do on i386 and FPA ARM is to put the float in a register */ + gv(RC_FLOAT); +#endif +} + +/* computed goto support */ +void ggoto(void) +{ + gcall_or_jmp(1); + vtop--; +} + +/* end of ARM code generator */ +/*************************************************************/ + diff --git a/05/tcc-0.9.25/assert.h b/05/tcc-0.9.25/assert.h new file mode 100644 index 0000000..8cac979 --- /dev/null +++ b/05/tcc-0.9.25/assert.h @@ -0,0 +1,7 @@ +#ifndef _ASSERT_H +#define _ASSERT_H + +// assert is defined in stdc_common.h +#include + +#endif // _ASSERT_H diff --git a/05/tcc-0.9.25/c67-gen.c b/05/tcc-0.9.25/c67-gen.c new file mode 100644 index 0000000..04f8a12 --- /dev/null +++ b/05/tcc-0.9.25/c67-gen.c @@ -0,0 +1,2548 @@ +/* + * TMS320C67xx code generator for TCC + * + * Copyright (c) 2001, 2002 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +//#define ASSEMBLY_LISTING_C67 + +/* number of available registers */ +#define NB_REGS 24 + +/* a register can belong to several classes. The classes must be + sorted from more general to more precise (see gv2() code which does + assumptions on it). */ +#define RC_INT 0x0001 /* generic integer register */ +#define RC_FLOAT 0x0002 /* generic float register */ +#define RC_EAX 0x0004 +#define RC_ST0 0x0008 +#define RC_ECX 0x0010 +#define RC_EDX 0x0020 +#define RC_INT_BSIDE 0x00000040 /* generic integer register on b side */ +#define RC_C67_A4 0x00000100 +#define RC_C67_A5 0x00000200 +#define RC_C67_B4 0x00000400 +#define RC_C67_B5 0x00000800 +#define RC_C67_A6 0x00001000 +#define RC_C67_A7 0x00002000 +#define RC_C67_B6 0x00004000 +#define RC_C67_B7 0x00008000 +#define RC_C67_A8 0x00010000 +#define RC_C67_A9 0x00020000 +#define RC_C67_B8 0x00040000 +#define RC_C67_B9 0x00080000 +#define RC_C67_A10 0x00100000 +#define RC_C67_A11 0x00200000 +#define RC_C67_B10 0x00400000 +#define RC_C67_B11 0x00800000 +#define RC_C67_A12 0x01000000 +#define RC_C67_A13 0x02000000 +#define RC_C67_B12 0x04000000 +#define RC_C67_B13 0x08000000 +#define RC_IRET RC_C67_A4 /* function return: integer register */ +#define RC_LRET RC_C67_A5 /* function return: second integer register */ +#define RC_FRET RC_C67_A4 /* function return: float register */ + +/* pretty names for the registers */ +enum { + TREG_EAX = 0, // really A2 + TREG_ECX, // really A3 + TREG_EDX, // really B0 + TREG_ST0, // really B1 + TREG_C67_A4, + TREG_C67_A5, + TREG_C67_B4, + TREG_C67_B5, + TREG_C67_A6, + TREG_C67_A7, + TREG_C67_B6, + TREG_C67_B7, + TREG_C67_A8, + TREG_C67_A9, + TREG_C67_B8, + TREG_C67_B9, + TREG_C67_A10, + TREG_C67_A11, + TREG_C67_B10, + TREG_C67_B11, + TREG_C67_A12, + TREG_C67_A13, + TREG_C67_B12, + TREG_C67_B13, +}; + +int reg_classes[NB_REGS] = { + /* eax */ RC_INT | RC_FLOAT | RC_EAX, + // only allow even regs for floats (allow for doubles) + /* ecx */ RC_INT | RC_ECX, + /* edx */ RC_INT | RC_INT_BSIDE | RC_FLOAT | RC_EDX, + // only allow even regs for floats (allow for doubles) + /* st0 */ RC_INT | RC_INT_BSIDE | RC_ST0, + /* A4 */ RC_C67_A4, + /* A5 */ RC_C67_A5, + /* B4 */ RC_C67_B4, + /* B5 */ RC_C67_B5, + /* A6 */ RC_C67_A6, + /* A7 */ RC_C67_A7, + /* B6 */ RC_C67_B6, + /* B7 */ RC_C67_B7, + /* A8 */ RC_C67_A8, + /* A9 */ RC_C67_A9, + /* B8 */ RC_C67_B8, + /* B9 */ RC_C67_B9, + /* A10 */ RC_C67_A10, + /* A11 */ RC_C67_A11, + /* B10 */ RC_C67_B10, + /* B11 */ RC_C67_B11, + /* A12 */ RC_C67_A10, + /* A13 */ RC_C67_A11, + /* B12 */ RC_C67_B10, + /* B13 */ RC_C67_B11 +}; + +/* return registers for function */ +#define REG_IRET TREG_C67_A4 /* single word int return register */ +#define REG_LRET TREG_C67_A5 /* second word return register (for long long) */ +#define REG_FRET TREG_C67_A4 /* float return register */ + + +#define ALWAYS_ASSERT(x) \ +do {\ + if (!(x))\ + error("internal compiler error file at %s:%d", __FILE__, __LINE__);\ +} while (0) + +// although tcc thinks it is passing parameters on the stack, +// the C67 really passes up to the first 10 params in special +// regs or regs pairs (for 64 bit params). So keep track of +// the stack offsets so we can translate to the appropriate +// reg (pair) + + +#define NoCallArgsPassedOnStack 10 +int NoOfCurFuncArgs; +int TranslateStackToReg[NoCallArgsPassedOnStack]; +int ParamLocOnStack[NoCallArgsPassedOnStack]; +int TotalBytesPushedOnStack; + +/* defined if function parameters must be evaluated in reverse order */ + +//#define INVERT_FUNC_PARAMS + +/* defined if structures are passed as pointers. Otherwise structures + are directly pushed on stack. */ +//#define FUNC_STRUCT_PARAM_AS_PTR + +/* pointer size, in bytes */ +#define PTR_SIZE 4 + +/* long double size and alignment, in bytes */ +#define LDOUBLE_SIZE 12 +#define LDOUBLE_ALIGN 4 +/* maximum alignment (for aligned attribute support) */ +#define MAX_ALIGN 8 + +/******************************************************/ +/* ELF defines */ + +#define EM_TCC_TARGET EM_C60 + +/* relocation type for 32 bit data relocation */ +#define R_DATA_32 R_C60_32 +#define R_JMP_SLOT R_C60_JMP_SLOT +#define R_COPY R_C60_COPY + +#define ELF_START_ADDR 0x00000400 +#define ELF_PAGE_SIZE 0x1000 + +/******************************************************/ + +static unsigned long func_sub_sp_offset; +static int func_ret_sub; + + +static BOOL C67_invert_test; +static int C67_compare_reg; + +#ifdef ASSEMBLY_LISTING_C67 +FILE *f = NULL; +#endif + + +void C67_g(int c) +{ + int ind1; + +#ifdef ASSEMBLY_LISTING_C67 + fprintf(f, " %08X", c); +#endif + ind1 = ind + 4; + if (ind1 > (int) cur_text_section->data_allocated) + section_realloc(cur_text_section, ind1); + cur_text_section->data[ind] = c & 0xff; + cur_text_section->data[ind + 1] = (c >> 8) & 0xff; + cur_text_section->data[ind + 2] = (c >> 16) & 0xff; + cur_text_section->data[ind + 3] = (c >> 24) & 0xff; + ind = ind1; +} + + +/* output a symbol and patch all calls to it */ +void gsym_addr(int t, int a) +{ + int n, *ptr; + while (t) { + ptr = (int *) (cur_text_section->data + t); + { + Sym *sym; + + // extract 32 bit address from MVKH/MVKL + n = ((*ptr >> 7) & 0xffff); + n |= ((*(ptr + 1) >> 7) & 0xffff) << 16; + + // define a label that will be relocated + + sym = get_sym_ref(&char_pointer_type, cur_text_section, a, 0); + greloc(cur_text_section, sym, t, R_C60LO16); + greloc(cur_text_section, sym, t + 4, R_C60HI16); + + // clear out where the pointer was + + *ptr &= ~(0xffff << 7); + *(ptr + 1) &= ~(0xffff << 7); + } + t = n; + } +} + +void gsym(int t) +{ + gsym_addr(t, ind); +} + +// these are regs that tcc doesn't really know about, +// but asign them unique values so the mapping routines +// can distinquish them + +#define C67_A0 105 +#define C67_SP 106 +#define C67_B3 107 +#define C67_FP 108 +#define C67_B2 109 +#define C67_CREG_ZERO -1 // Special code for no condition reg test + + +int ConvertRegToRegClass(int r) +{ + // only works for A4-B13 + + return RC_C67_A4 << (r - TREG_C67_A4); +} + + +// map TCC reg to C67 reg number + +int C67_map_regn(int r) +{ + if (r == 0) // normal tcc regs + return 0x2; // A2 + else if (r == 1) // normal tcc regs + return 3; // A3 + else if (r == 2) // normal tcc regs + return 0; // B0 + else if (r == 3) // normal tcc regs + return 1; // B1 + else if (r >= TREG_C67_A4 && r <= TREG_C67_B13) // these form a pattern of alt pairs + return (((r & 0xfffffffc) >> 1) | (r & 1)) + 2; + else if (r == C67_A0) + return 0; // set to A0 (offset reg) + else if (r == C67_B2) + return 2; // set to B2 (offset reg) + else if (r == C67_B3) + return 3; // set to B3 (return address reg) + else if (r == C67_SP) + return 15; // set to SP (B15) (offset reg) + else if (r == C67_FP) + return 15; // set to FP (A15) (offset reg) + else if (r == C67_CREG_ZERO) + return 0; // Special code for no condition reg test + else + ALWAYS_ASSERT(FALSE); + + return 0; +} + +// mapping from tcc reg number to +// C67 register to condition code field +// +// valid condition code regs are: +// +// tcc reg 2 ->B0 -> 1 +// tcc reg 3 ->B1 -> 2 +// tcc reg 0 -> A2 -> 5 +// tcc reg 1 -> A3 -> X +// tcc reg B2 -> 3 + +int C67_map_regc(int r) +{ + if (r == 0) // normal tcc regs + return 0x5; + else if (r == 2) // normal tcc regs + return 0x1; + else if (r == 3) // normal tcc regs + return 0x2; + else if (r == C67_B2) // normal tcc regs + return 0x3; + else if (r == C67_CREG_ZERO) + return 0; // Special code for no condition reg test + else + ALWAYS_ASSERT(FALSE); + + return 0; +} + + +// map TCC reg to C67 reg side A or B + +int C67_map_regs(int r) +{ + if (r == 0) // normal tcc regs + return 0x0; + else if (r == 1) // normal tcc regs + return 0x0; + else if (r == 2) // normal tcc regs + return 0x1; + else if (r == 3) // normal tcc regs + return 0x1; + else if (r >= TREG_C67_A4 && r <= TREG_C67_B13) // these form a pattern of alt pairs + return (r & 2) >> 1; + else if (r == C67_A0) + return 0; // set to A side + else if (r == C67_B2) + return 1; // set to B side + else if (r == C67_B3) + return 1; // set to B side + else if (r == C67_SP) + return 0x1; // set to SP (B15) B side + else if (r == C67_FP) + return 0x0; // set to FP (A15) A side + else + ALWAYS_ASSERT(FALSE); + + return 0; +} + +int C67_map_S12(char *s) +{ + if (strstr(s, ".S1") != NULL) + return 0; + else if (strcmp(s, ".S2")) + return 1; + else + ALWAYS_ASSERT(FALSE); + + return 0; +} + +int C67_map_D12(char *s) +{ + if (strstr(s, ".D1") != NULL) + return 0; + else if (strcmp(s, ".D2")) + return 1; + else + ALWAYS_ASSERT(FALSE); + + return 0; +} + + + +void C67_asm(char *s, int a, int b, int c) +{ + BOOL xpath; + +#ifdef ASSEMBLY_LISTING_C67 + if (!f) { + f = fopen("TCC67_out.txt", "wt"); + } + fprintf(f, "%04X ", ind); +#endif + + if (strstr(s, "MVKL") == s) { + C67_g((C67_map_regn(b) << 23) | + ((a & 0xffff) << 7) | (0x0a << 2) | (C67_map_regs(b) << 1)); + } else if (strstr(s, "MVKH") == s) { + C67_g((C67_map_regn(b) << 23) | + (((a >> 16) & 0xffff) << 7) | + (0x1a << 2) | (C67_map_regs(b) << 1)); + } else if (strstr(s, "STW.D SP POST DEC") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (15 << 18) | //SP B15 + (2 << 13) | //ucst5 (must keep 8 byte boundary !!) + (0xa << 9) | //mode a = post dec ucst + (0 << 8) | //r (LDDW bit 0) + (1 << 7) | //y D1/D2 use B side + (7 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STB.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (3 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STH.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (5 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STB.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (3 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STH.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (5 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STW.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (7 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STW.D *") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (C67_map_regn(b) << 18) | //base reg A0 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(b) << 7) | //y D1/D2 base reg side + (7 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STH.D *") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (C67_map_regn(b) << 18) | //base reg A0 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(b) << 7) | //y D1/D2 base reg side + (5 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STB.D *") == s) { + C67_g((C67_map_regn(a) << 23) | //src + (C67_map_regn(b) << 18) | //base reg A0 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(b) << 7) | //y D1/D2 base reg side + (3 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "STW.D +*") == s) { + ALWAYS_ASSERT(c < 32); + C67_g((C67_map_regn(a) << 23) | //src + (C67_map_regn(b) << 18) | //base reg A0 + (c << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(b) << 7) | //y D1/D2 base reg side + (7 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of src + (0 << 0)); //parallel + } else if (strstr(s, "LDW.D SP PRE INC") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg B15 + (2 << 13) | //ucst5 (must keep 8 byte boundary) + (9 << 9) | //mode 9 = pre inc ucst5 + (0 << 8) | //r (LDDW bit 0) + (1 << 7) | //y D1/D2 B side + (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDDW.D SP PRE INC") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg B15 + (1 << 13) | //ucst5 (must keep 8 byte boundary) + (9 << 9) | //mode 9 = pre inc ucst5 + (1 << 8) | //r (LDDW bit 1) + (1 << 7) | //y D1/D2 B side + (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDW.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDDW.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (1 << 8) | //r (LDDW bit 1) + (0 << 7) | //y D1/D2 A side + (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDH.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (4 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDB.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (2 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDHU.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (0 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDBU.D *+SP[A0]") == s) { + C67_g((C67_map_regn(a) << 23) | //dst + (15 << 18) | //base reg A15 + (0 << 13) | //offset reg A0 + (5 << 9) | //mode 5 = pos offset, base reg + off reg + (0 << 8) | //r (LDDW bit 0) + (0 << 7) | //y D1/D2 A side + (1 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(a) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDW.D *") == s) { + C67_g((C67_map_regn(b) << 23) | //dst + (C67_map_regn(a) << 18) | //base reg A15 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(a) << 7) | //y D1/D2 src side + (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDDW.D *") == s) { + C67_g((C67_map_regn(b) << 23) | //dst + (C67_map_regn(a) << 18) | //base reg A15 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (1 << 8) | //r (LDDW bit 1) + (C67_map_regs(a) << 7) | //y D1/D2 src side + (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDH.D *") == s) { + C67_g((C67_map_regn(b) << 23) | //dst + (C67_map_regn(a) << 18) | //base reg A15 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(a) << 7) | //y D1/D2 src side + (4 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDB.D *") == s) { + C67_g((C67_map_regn(b) << 23) | //dst + (C67_map_regn(a) << 18) | //base reg A15 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(a) << 7) | //y D1/D2 src side + (2 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDHU.D *") == s) { + C67_g((C67_map_regn(b) << 23) | //dst + (C67_map_regn(a) << 18) | //base reg A15 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(a) << 7) | //y D1/D2 src side + (0 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDBU.D *") == s) { + C67_g((C67_map_regn(b) << 23) | //dst + (C67_map_regn(a) << 18) | //base reg A15 + (0 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(a) << 7) | //y D1/D2 src side + (1 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "LDW.D +*") == s) { + C67_g((C67_map_regn(b) << 23) | //dst + (C67_map_regn(a) << 18) | //base reg A15 + (1 << 13) | //cst5 + (1 << 9) | //mode 1 = pos cst offset + (0 << 8) | //r (LDDW bit 0) + (C67_map_regs(a) << 7) | //y D1/D2 src side + (6 << 4) | //ldst 3=STB, 5=STH 5, 7=STW, 6=LDW 4=LDH 2=LDB 0=LDHU 1=LDBU + (1 << 2) | //opcode + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "CMPLTSP") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x3a << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPGTSP") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x39 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPEQSP") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x38 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } + + else if (strstr(s, "CMPLTDP") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x2a << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPGTDP") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x29 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPEQDP") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x28 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPLT") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x57 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPGT") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x47 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPEQ") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x53 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPLTU") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x5f << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "CMPGTU") == s) { + xpath = C67_map_regs(a) ^ C67_map_regs(b); + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x use cross path for src2 + (0x4f << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side for reg c + (0 << 0)); //parallel + } else if (strstr(s, "B DISP") == s) { + C67_g((0 << 29) | //creg + (0 << 28) | //z + (a << 7) | //cnst + (0x4 << 2) | //opcode fixed + (0 << 1) | //S0/S1 + (0 << 0)); //parallel + } else if (strstr(s, "B.") == s) { + xpath = C67_map_regs(c) ^ 1; + + C67_g((C67_map_regc(b) << 29) | //creg + (a << 28) | //inv + (0 << 23) | //dst + (C67_map_regn(c) << 18) | //src2 + (0 << 13) | // + (xpath << 12) | //x cross path if !B side + (0xd << 6) | //opcode + (0x8 << 2) | //opcode fixed + (1 << 1) | //must be S2 + (0 << 0)); //parallel + } else if (strstr(s, "MV.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (0 << 13) | //src1 (cst5) + (xpath << 12) | //x cross path if opposite sides + (0x2 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SPTRUNC.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (0 << 13) | //src1 NA + (xpath << 12) | //x cross path if opposite sides + (0xb << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "DPTRUNC.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + ((C67_map_regn(b) + 1) << 18) | //src2 WEIRD CPU must specify odd reg for some reason + (0 << 13) | //src1 NA + (xpath << 12) | //x cross path if opposite sides + (0x1 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "INTSP.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (0 << 13) | //src1 NA + (xpath << 12) | //x cross path if opposite sides + (0x4a << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "INTSPU.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (0 << 13) | //src1 NA + (xpath << 12) | //x cross path if opposite sides + (0x49 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "INTDP.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (0 << 13) | //src1 NA + (xpath << 12) | //x cross path if opposite sides + (0x39 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "INTDPU.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + ((C67_map_regn(b) + 1) << 18) | //src2 WEIRD CPU must specify odd reg for some reason + (0 << 13) | //src1 NA + (xpath << 12) | //x cross path if opposite sides + (0x3b << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SPDP.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (0 << 13) | //src1 NA + (xpath << 12) | //x cross path if opposite sides + (0x2 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "DPSP.L") == s) { + ALWAYS_ASSERT(C67_map_regs(b) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + ((C67_map_regn(b) + 1) << 18) | //src2 WEIRD CPU must specify odd reg for some reason + (0 << 13) | //src1 NA + (0 << 12) | //x cross path if opposite sides + (0x9 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "ADD.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x3 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SUB.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x7 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "OR.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x7f << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "AND.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x7b << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "XOR.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x6f << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "ADDSP.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x10 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "ADDDP.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x18 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SUBSP.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x11 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SUBDP.L") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x19 << 5) | //opcode + (0x6 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "MPYSP.M") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x1c << 7) | //opcode + (0x0 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "MPYDP.M") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 (possible x path) + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x0e << 7) | //opcode + (0x0 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "MPYI.M") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(a) == C67_map_regs(c)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 (cst5) + (xpath << 12) | //x cross path if opposite sides + (0x4 << 7) | //opcode + (0x0 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SHR.S") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x37 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SHRU.S") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x27 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "SHL.S") == s) { + xpath = C67_map_regs(b) ^ C67_map_regs(c); + + ALWAYS_ASSERT(C67_map_regs(c) == C67_map_regs(a)); + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(c) << 23) | //dst + (C67_map_regn(b) << 18) | //src2 + (C67_map_regn(a) << 13) | //src1 + (xpath << 12) | //x cross path if opposite sides + (0x33 << 6) | //opcode + (0x8 << 2) | //opcode fixed + (C67_map_regs(c) << 1) | //side of dest + (0 << 0)); //parallel + } else if (strstr(s, "||ADDK") == s) { + xpath = 0; // no xpath required just use the side of the src/dst + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(b) << 23) | //dst + (a << 07) | //scst16 + (0x14 << 2) | //opcode fixed + (C67_map_regs(b) << 1) | //side of dst + (1 << 0)); //parallel + } else if (strstr(s, "ADDK") == s) { + xpath = 0; // no xpath required just use the side of the src/dst + + C67_g((0 << 29) | //creg + (0 << 28) | //inv + (C67_map_regn(b) << 23) | //dst + (a << 07) | //scst16 + (0x14 << 2) | //opcode fixed + (C67_map_regs(b) << 1) | //side of dst + (0 << 0)); //parallel + } else if (strstr(s, "NOP") == s) { + C67_g(((a - 1) << 13) | //no of cycles + (0 << 0)); //parallel + } else + ALWAYS_ASSERT(FALSE); + +#ifdef ASSEMBLY_LISTING_C67 + fprintf(f, " %s %d %d %d\n", s, a, b, c); +#endif + +} + +//r=reg to load, fr=from reg, symbol for relocation, constant + +void C67_MVKL(int r, int fc) +{ + C67_asm("MVKL.", fc, r, 0); +} + +void C67_MVKH(int r, int fc) +{ + C67_asm("MVKH.", fc, r, 0); +} + +void C67_STB_SP_A0(int r) +{ + C67_asm("STB.D *+SP[A0]", r, 0, 0); // STB r,*+SP[A0] +} + +void C67_STH_SP_A0(int r) +{ + C67_asm("STH.D *+SP[A0]", r, 0, 0); // STH r,*+SP[A0] +} + +void C67_STW_SP_A0(int r) +{ + C67_asm("STW.D *+SP[A0]", r, 0, 0); // STW r,*+SP[A0] +} + +void C67_STB_PTR(int r, int r2) +{ + C67_asm("STB.D *", r, r2, 0); // STB r, *r2 +} + +void C67_STH_PTR(int r, int r2) +{ + C67_asm("STH.D *", r, r2, 0); // STH r, *r2 +} + +void C67_STW_PTR(int r, int r2) +{ + C67_asm("STW.D *", r, r2, 0); // STW r, *r2 +} + +void C67_STW_PTR_PRE_INC(int r, int r2, int n) +{ + C67_asm("STW.D +*", r, r2, n); // STW r, *+r2 +} + +void C67_PUSH(int r) +{ + C67_asm("STW.D SP POST DEC", r, 0, 0); // STW r,*SP-- +} + +void C67_LDW_SP_A0(int r) +{ + C67_asm("LDW.D *+SP[A0]", r, 0, 0); // LDW *+SP[A0],r +} + +void C67_LDDW_SP_A0(int r) +{ + C67_asm("LDDW.D *+SP[A0]", r, 0, 0); // LDDW *+SP[A0],r +} + +void C67_LDH_SP_A0(int r) +{ + C67_asm("LDH.D *+SP[A0]", r, 0, 0); // LDH *+SP[A0],r +} + +void C67_LDB_SP_A0(int r) +{ + C67_asm("LDB.D *+SP[A0]", r, 0, 0); // LDB *+SP[A0],r +} + +void C67_LDHU_SP_A0(int r) +{ + C67_asm("LDHU.D *+SP[A0]", r, 0, 0); // LDHU *+SP[A0],r +} + +void C67_LDBU_SP_A0(int r) +{ + C67_asm("LDBU.D *+SP[A0]", r, 0, 0); // LDBU *+SP[A0],r +} + +void C67_LDW_PTR(int r, int r2) +{ + C67_asm("LDW.D *", r, r2, 0); // LDW *r,r2 +} + +void C67_LDDW_PTR(int r, int r2) +{ + C67_asm("LDDW.D *", r, r2, 0); // LDDW *r,r2 +} + +void C67_LDH_PTR(int r, int r2) +{ + C67_asm("LDH.D *", r, r2, 0); // LDH *r,r2 +} + +void C67_LDB_PTR(int r, int r2) +{ + C67_asm("LDB.D *", r, r2, 0); // LDB *r,r2 +} + +void C67_LDHU_PTR(int r, int r2) +{ + C67_asm("LDHU.D *", r, r2, 0); // LDHU *r,r2 +} + +void C67_LDBU_PTR(int r, int r2) +{ + C67_asm("LDBU.D *", r, r2, 0); // LDBU *r,r2 +} + +void C67_LDW_PTR_PRE_INC(int r, int r2) +{ + C67_asm("LDW.D +*", r, r2, 0); // LDW *+r,r2 +} + +void C67_POP(int r) +{ + C67_asm("LDW.D SP PRE INC", r, 0, 0); // LDW *++SP,r +} + +void C67_POP_DW(int r) +{ + C67_asm("LDDW.D SP PRE INC", r, 0, 0); // LDDW *++SP,r +} + +void C67_CMPLT(int s1, int s2, int dst) +{ + C67_asm("CMPLT.L1", s1, s2, dst); +} + +void C67_CMPGT(int s1, int s2, int dst) +{ + C67_asm("CMPGT.L1", s1, s2, dst); +} + +void C67_CMPEQ(int s1, int s2, int dst) +{ + C67_asm("CMPEQ.L1", s1, s2, dst); +} + +void C67_CMPLTU(int s1, int s2, int dst) +{ + C67_asm("CMPLTU.L1", s1, s2, dst); +} + +void C67_CMPGTU(int s1, int s2, int dst) +{ + C67_asm("CMPGTU.L1", s1, s2, dst); +} + + +void C67_CMPLTSP(int s1, int s2, int dst) +{ + C67_asm("CMPLTSP.S1", s1, s2, dst); +} + +void C67_CMPGTSP(int s1, int s2, int dst) +{ + C67_asm("CMPGTSP.S1", s1, s2, dst); +} + +void C67_CMPEQSP(int s1, int s2, int dst) +{ + C67_asm("CMPEQSP.S1", s1, s2, dst); +} + +void C67_CMPLTDP(int s1, int s2, int dst) +{ + C67_asm("CMPLTDP.S1", s1, s2, dst); +} + +void C67_CMPGTDP(int s1, int s2, int dst) +{ + C67_asm("CMPGTDP.S1", s1, s2, dst); +} + +void C67_CMPEQDP(int s1, int s2, int dst) +{ + C67_asm("CMPEQDP.S1", s1, s2, dst); +} + + +void C67_IREG_B_REG(int inv, int r1, int r2) // [!R] B r2 +{ + C67_asm("B.S2", inv, r1, r2); +} + + +// call with how many 32 bit words to skip +// (0 would branch to the branch instruction) + +void C67_B_DISP(int disp) // B +2 Branch with constant displacement +{ + // Branch point is relative to the 8 word fetch packet + // + // we will assume the text section always starts on an 8 word (32 byte boundary) + // + // so add in how many words into the fetch packet the branch is + + + C67_asm("B DISP", disp + ((ind & 31) >> 2), 0, 0); +} + +void C67_NOP(int n) +{ + C67_asm("NOP", n, 0, 0); +} + +void C67_ADDK(int n, int r) +{ + ALWAYS_ASSERT(abs(n) < 32767); + + C67_asm("ADDK", n, r, 0); +} + +void C67_ADDK_PARALLEL(int n, int r) +{ + ALWAYS_ASSERT(abs(n) < 32767); + + C67_asm("||ADDK", n, r, 0); +} + +void C67_Adjust_ADDK(int *inst, int n) +{ + ALWAYS_ASSERT(abs(n) < 32767); + + *inst = (*inst & (~(0xffff << 7))) | ((n & 0xffff) << 7); +} + +void C67_MV(int r, int v) +{ + C67_asm("MV.L", 0, r, v); +} + + +void C67_DPTRUNC(int r, int v) +{ + C67_asm("DPTRUNC.L", 0, r, v); +} + +void C67_SPTRUNC(int r, int v) +{ + C67_asm("SPTRUNC.L", 0, r, v); +} + +void C67_INTSP(int r, int v) +{ + C67_asm("INTSP.L", 0, r, v); +} + +void C67_INTDP(int r, int v) +{ + C67_asm("INTDP.L", 0, r, v); +} + +void C67_INTSPU(int r, int v) +{ + C67_asm("INTSPU.L", 0, r, v); +} + +void C67_INTDPU(int r, int v) +{ + C67_asm("INTDPU.L", 0, r, v); +} + +void C67_SPDP(int r, int v) +{ + C67_asm("SPDP.L", 0, r, v); +} + +void C67_DPSP(int r, int v) // note regs must be on the same side +{ + C67_asm("DPSP.L", 0, r, v); +} + +void C67_ADD(int r, int v) +{ + C67_asm("ADD.L", v, r, v); +} + +void C67_SUB(int r, int v) +{ + C67_asm("SUB.L", v, r, v); +} + +void C67_AND(int r, int v) +{ + C67_asm("AND.L", v, r, v); +} + +void C67_OR(int r, int v) +{ + C67_asm("OR.L", v, r, v); +} + +void C67_XOR(int r, int v) +{ + C67_asm("XOR.L", v, r, v); +} + +void C67_ADDSP(int r, int v) +{ + C67_asm("ADDSP.L", v, r, v); +} + +void C67_SUBSP(int r, int v) +{ + C67_asm("SUBSP.L", v, r, v); +} + +void C67_MPYSP(int r, int v) +{ + C67_asm("MPYSP.M", v, r, v); +} + +void C67_ADDDP(int r, int v) +{ + C67_asm("ADDDP.L", v, r, v); +} + +void C67_SUBDP(int r, int v) +{ + C67_asm("SUBDP.L", v, r, v); +} + +void C67_MPYDP(int r, int v) +{ + C67_asm("MPYDP.M", v, r, v); +} + +void C67_MPYI(int r, int v) +{ + C67_asm("MPYI.M", v, r, v); +} + +void C67_SHL(int r, int v) +{ + C67_asm("SHL.S", r, v, v); +} + +void C67_SHRU(int r, int v) +{ + C67_asm("SHRU.S", r, v, v); +} + +void C67_SHR(int r, int v) +{ + C67_asm("SHR.S", r, v, v); +} + + + +/* load 'r' from value 'sv' */ +void load(int r, SValue * sv) +{ + int v, t, ft, fc, fr, size = 0, element; + BOOL Unsigned = false; + SValue v1; + + fr = sv->r; + ft = sv->type.t; + fc = sv->c.ul; + + v = fr & VT_VALMASK; + if (fr & VT_LVAL) { + if (v == VT_LLOCAL) { + v1.type.t = VT_INT; + v1.r = VT_LOCAL | VT_LVAL; + v1.c.ul = fc; + load(r, &v1); + fr = r; + } else if ((ft & VT_BTYPE) == VT_LDOUBLE) { + error("long double not supported"); + } else if ((ft & VT_TYPE) == VT_BYTE) { + size = 1; + } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) { + size = 1; + Unsigned = TRUE; + } else if ((ft & VT_TYPE) == VT_SHORT) { + size = 2; + } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) { + size = 2; + Unsigned = TRUE; + } else if ((ft & VT_BTYPE) == VT_DOUBLE) { + size = 8; + } else { + size = 4; + } + + // check if fc is a positive reference on the stack, + // if it is tcc is referencing what it thinks is a parameter + // on the stack, so check if it is really in a register. + + + if (v == VT_LOCAL && fc > 0) { + int stack_pos = 8; + + for (t = 0; t < NoCallArgsPassedOnStack; t++) { + if (fc == stack_pos) + break; + + stack_pos += TranslateStackToReg[t]; + } + + // param has been pushed on stack, get it like a local var + + fc = ParamLocOnStack[t] - 8; + } + + if ((fr & VT_VALMASK) < VT_CONST) // check for pure indirect + { + if (size == 1) { + if (Unsigned) + C67_LDBU_PTR(v, r); // LDBU *v,r + else + C67_LDB_PTR(v, r); // LDB *v,r + } else if (size == 2) { + if (Unsigned) + C67_LDHU_PTR(v, r); // LDHU *v,r + else + C67_LDH_PTR(v, r); // LDH *v,r + } else if (size == 4) { + C67_LDW_PTR(v, r); // LDW *v,r + } else if (size == 8) { + C67_LDDW_PTR(v, r); // LDDW *v,r + } + + C67_NOP(4); // NOP 4 + return; + } else if (fr & VT_SYM) { + greloc(cur_text_section, sv->sym, ind, R_C60LO16); // rem the inst need to be patched + greloc(cur_text_section, sv->sym, ind + 4, R_C60HI16); + + + C67_MVKL(C67_A0, fc); //r=reg to load, constant + C67_MVKH(C67_A0, fc); //r=reg to load, constant + + + if (size == 1) { + if (Unsigned) + C67_LDBU_PTR(C67_A0, r); // LDBU *A0,r + else + C67_LDB_PTR(C67_A0, r); // LDB *A0,r + } else if (size == 2) { + if (Unsigned) + C67_LDHU_PTR(C67_A0, r); // LDHU *A0,r + else + C67_LDH_PTR(C67_A0, r); // LDH *A0,r + } else if (size == 4) { + C67_LDW_PTR(C67_A0, r); // LDW *A0,r + } else if (size == 8) { + C67_LDDW_PTR(C67_A0, r); // LDDW *A0,r + } + + C67_NOP(4); // NOP 4 + return; + } else { + element = size; + + // divide offset in bytes to create element index + C67_MVKL(C67_A0, (fc / element) + 8 / element); //r=reg to load, constant + C67_MVKH(C67_A0, (fc / element) + 8 / element); //r=reg to load, constant + + if (size == 1) { + if (Unsigned) + C67_LDBU_SP_A0(r); // LDBU r, SP[A0] + else + C67_LDB_SP_A0(r); // LDB r, SP[A0] + } else if (size == 2) { + if (Unsigned) + C67_LDHU_SP_A0(r); // LDHU r, SP[A0] + else + C67_LDH_SP_A0(r); // LDH r, SP[A0] + } else if (size == 4) { + C67_LDW_SP_A0(r); // LDW r, SP[A0] + } else if (size == 8) { + C67_LDDW_SP_A0(r); // LDDW r, SP[A0] + } + + + C67_NOP(4); // NOP 4 + return; + } + } else { + if (v == VT_CONST) { + if (fr & VT_SYM) { + greloc(cur_text_section, sv->sym, ind, R_C60LO16); // rem the inst need to be patched + greloc(cur_text_section, sv->sym, ind + 4, R_C60HI16); + } + C67_MVKL(r, fc); //r=reg to load, constant + C67_MVKH(r, fc); //r=reg to load, constant + } else if (v == VT_LOCAL) { + C67_MVKL(r, fc + 8); //r=reg to load, constant C67 stack points to next free + C67_MVKH(r, fc + 8); //r=reg to load, constant + C67_ADD(C67_FP, r); // MV v,r v -> r + } else if (v == VT_CMP) { + C67_MV(C67_compare_reg, r); // MV v,r v -> r + } else if (v == VT_JMP || v == VT_JMPI) { + t = v & 1; + C67_B_DISP(4); // Branch with constant displacement, skip over this branch, load, nop, load + C67_MVKL(r, t); // r=reg to load, 0 or 1 (do this while branching) + C67_NOP(4); // NOP 4 + gsym(fc); // modifies other branches to branch here + C67_MVKL(r, t ^ 1); // r=reg to load, 0 or 1 + } else if (v != r) { + C67_MV(v, r); // MV v,r v -> r + + if ((ft & VT_BTYPE) == VT_DOUBLE) + C67_MV(v + 1, r + 1); // MV v,r v -> r + } + } +} + + +/* store register 'r' in lvalue 'v' */ +void store(int r, SValue * v) +{ + int fr, bt, ft, fc, size, t, element; + + ft = v->type.t; + fc = v->c.ul; + fr = v->r & VT_VALMASK; + bt = ft & VT_BTYPE; + /* XXX: incorrect if float reg to reg */ + + if (bt == VT_LDOUBLE) { + error("long double not supported"); + } else { + if (bt == VT_SHORT) + size = 2; + else if (bt == VT_BYTE) + size = 1; + else if (bt == VT_DOUBLE) + size = 8; + else + size = 4; + + if ((v->r & VT_VALMASK) == VT_CONST) { + /* constant memory reference */ + + if (v->r & VT_SYM) { + greloc(cur_text_section, v->sym, ind, R_C60LO16); // rem the inst need to be patched + greloc(cur_text_section, v->sym, ind + 4, R_C60HI16); + } + C67_MVKL(C67_A0, fc); //r=reg to load, constant + C67_MVKH(C67_A0, fc); //r=reg to load, constant + + if (size == 1) + C67_STB_PTR(r, C67_A0); // STB r, *A0 + else if (size == 2) + C67_STH_PTR(r, C67_A0); // STH r, *A0 + else if (size == 4 || size == 8) + C67_STW_PTR(r, C67_A0); // STW r, *A0 + + if (size == 8) + C67_STW_PTR_PRE_INC(r + 1, C67_A0, 1); // STW r, *+A0[1] + } else if ((v->r & VT_VALMASK) == VT_LOCAL) { + // check case of storing to passed argument that + // tcc thinks is on the stack but for C67 is + // passed as a reg. However it may have been + // saved to the stack, if that reg was required + // for a call to a child function + + if (fc > 0) // argument ?? + { + // walk through sizes and figure which param + + int stack_pos = 8; + + for (t = 0; t < NoCallArgsPassedOnStack; t++) { + if (fc == stack_pos) + break; + + stack_pos += TranslateStackToReg[t]; + } + + // param has been pushed on stack, get it like a local var + fc = ParamLocOnStack[t] - 8; + } + + if (size == 8) + element = 4; + else + element = size; + + // divide offset in bytes to create word index + C67_MVKL(C67_A0, (fc / element) + 8 / element); //r=reg to load, constant + C67_MVKH(C67_A0, (fc / element) + 8 / element); //r=reg to load, constant + + + + if (size == 1) + C67_STB_SP_A0(r); // STB r, SP[A0] + else if (size == 2) + C67_STH_SP_A0(r); // STH r, SP[A0] + else if (size == 4 || size == 8) + C67_STW_SP_A0(r); // STW r, SP[A0] + + if (size == 8) { + C67_ADDK(1, C67_A0); // ADDK 1,A0 + C67_STW_SP_A0(r + 1); // STW r, SP[A0] + } + } else { + if (size == 1) + C67_STB_PTR(r, fr); // STB r, *fr + else if (size == 2) + C67_STH_PTR(r, fr); // STH r, *fr + else if (size == 4 || size == 8) + C67_STW_PTR(r, fr); // STW r, *fr + + if (size == 8) { + C67_STW_PTR_PRE_INC(r + 1, fr, 1); // STW r, *+fr[1] + } + } + } +} + +/* 'is_jmp' is '1' if it is a jump */ +static void gcall_or_jmp(int is_jmp) +{ + int r; + Sym *sym; + + if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { + /* constant case */ + if (vtop->r & VT_SYM) { + /* relocation case */ + + // get add into A0, then start the jump B3 + + greloc(cur_text_section, vtop->sym, ind, R_C60LO16); // rem the inst need to be patched + greloc(cur_text_section, vtop->sym, ind + 4, R_C60HI16); + + C67_MVKL(C67_A0, 0); //r=reg to load, constant + C67_MVKH(C67_A0, 0); //r=reg to load, constant + C67_IREG_B_REG(0, C67_CREG_ZERO, C67_A0); // B.S2x A0 + + if (is_jmp) { + C67_NOP(5); // simple jump, just put NOP + } else { + // Call, must load return address into B3 during delay slots + + sym = get_sym_ref(&char_pointer_type, cur_text_section, ind + 12, 0); // symbol for return address + greloc(cur_text_section, sym, ind, R_C60LO16); // rem the inst need to be patched + greloc(cur_text_section, sym, ind + 4, R_C60HI16); + C67_MVKL(C67_B3, 0); //r=reg to load, constant + C67_MVKH(C67_B3, 0); //r=reg to load, constant + C67_NOP(3); // put remaining NOPs + } + } else { + /* put an empty PC32 relocation */ + ALWAYS_ASSERT(FALSE); + } + } else { + /* otherwise, indirect call */ + r = gv(RC_INT); + C67_IREG_B_REG(0, C67_CREG_ZERO, r); // B.S2x r + + if (is_jmp) { + C67_NOP(5); // simple jump, just put NOP + } else { + // Call, must load return address into B3 during delay slots + + sym = get_sym_ref(&char_pointer_type, cur_text_section, ind + 12, 0); // symbol for return address + greloc(cur_text_section, sym, ind, R_C60LO16); // rem the inst need to be patched + greloc(cur_text_section, sym, ind + 4, R_C60HI16); + C67_MVKL(C67_B3, 0); //r=reg to load, constant + C67_MVKH(C67_B3, 0); //r=reg to load, constant + C67_NOP(3); // put remaining NOPs + } + } +} + +/* generate function call with address in (vtop->t, vtop->c) and free function + context. Stack entry is popped */ +void gfunc_call(int nb_args) +{ + int i, r, size = 0; + int args_sizes[NoCallArgsPassedOnStack]; + + if (nb_args > NoCallArgsPassedOnStack) { + error("more than 10 function params not currently supported"); + // handle more than 10, put some on the stack + } + + for (i = 0; i < nb_args; i++) { + if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { + ALWAYS_ASSERT(FALSE); + } else if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { + ALWAYS_ASSERT(FALSE); + } else { + /* simple type (currently always same size) */ + /* XXX: implicit cast ? */ + + + if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { + error("long long not supported"); + } else if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { + error("long double not supported"); + } else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) { + size = 8; + } else { + size = 4; + } + + // put the parameter into the corresponding reg (pair) + + r = gv(RC_C67_A4 << (2 * i)); + + // must put on stack because with 1 pass compiler , no way to tell + // if an up coming nested call might overwrite these regs + + C67_PUSH(r); + + if (size == 8) { + C67_STW_PTR_PRE_INC(r + 1, C67_SP, 3); // STW r, *+SP[3] (go back and put the other) + } + args_sizes[i] = size; + } + vtop--; + } + // POP all the params on the stack into registers for the + // immediate call (in reverse order) + + for (i = nb_args - 1; i >= 0; i--) { + + if (args_sizes[i] == 8) + C67_POP_DW(TREG_C67_A4 + i * 2); + else + C67_POP(TREG_C67_A4 + i * 2); + } + gcall_or_jmp(0); + vtop--; +} + + +// to be compatible with Code Composer for the C67 +// the first 10 parameters must be passed in registers +// (pairs for 64 bits) starting wit; A4:A5, then B4:B5 and +// ending with B12:B13. +// +// When a call is made, if the caller has its parameters +// in regs A4-B13 these must be saved before/as the call +// parameters are loaded and restored upon return (or if/when needed). + +/* generate function prolog of type 't' */ +void gfunc_prolog(CType * func_type) +{ + int addr, align, size, func_call, i; + Sym *sym; + CType *type; + + sym = func_type->ref; + func_call = sym->r; + addr = 8; + /* if the function returns a structure, then add an + implicit pointer parameter */ + func_vt = sym->type; + if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { + func_vc = addr; + addr += 4; + } + + NoOfCurFuncArgs = 0; + + /* define parameters */ + while ((sym = sym->next) != NULL) { + type = &sym->type; + sym_push(sym->v & ~SYM_FIELD, type, VT_LOCAL | lvalue_type(type->t), addr); + size = type_size(type, &align); + size = (size + 3) & ~3; + + // keep track of size of arguments so + // we can translate where tcc thinks they + // are on the stack into the appropriate reg + + TranslateStackToReg[NoOfCurFuncArgs] = size; + NoOfCurFuncArgs++; + +#ifdef FUNC_STRUCT_PARAM_AS_PTR + /* structs are passed as pointer */ + if ((type->t & VT_BTYPE) == VT_STRUCT) { + size = 4; + } +#endif + addr += size; + } + func_ret_sub = 0; + /* pascal type call ? */ + if (func_call == FUNC_STDCALL) + func_ret_sub = addr - 8; + + C67_MV(C67_FP, C67_A0); // move FP -> A0 + C67_MV(C67_SP, C67_FP); // move SP -> FP + + // place all the args passed in regs onto the stack + + loc = 0; + for (i = 0; i < NoOfCurFuncArgs; i++) { + + ParamLocOnStack[i] = loc; // remember where the param is + loc += -8; + + C67_PUSH(TREG_C67_A4 + i * 2); + + if (TranslateStackToReg[i] == 8) { + C67_STW_PTR_PRE_INC(TREG_C67_A4 + i * 2 + 1, C67_SP, 3); // STW r, *+SP[1] (go back and put the other) + } + } + + TotalBytesPushedOnStack = -loc; + + func_sub_sp_offset = ind; // remember where we put the stack instruction + C67_ADDK(0, C67_SP); // ADDK.L2 loc,SP (just put zero temporarily) + + C67_PUSH(C67_A0); + C67_PUSH(C67_B3); +} + +/* generate function epilog */ +void gfunc_epilog(void) +{ + { + int local = (-loc + 7) & -8; // stack must stay aligned to 8 bytes for LDDW instr + C67_POP(C67_B3); + C67_NOP(4); // NOP wait for load + C67_IREG_B_REG(0, C67_CREG_ZERO, C67_B3); // B.S2 B3 + C67_POP(C67_FP); + C67_ADDK(local, C67_SP); // ADDK.L2 loc,SP + C67_Adjust_ADDK((int *) (cur_text_section->data + + func_sub_sp_offset), + -local + TotalBytesPushedOnStack); + C67_NOP(3); // NOP + } +} + +/* generate a jump to a label */ +int gjmp(int t) +{ + int ind1 = ind; + + C67_MVKL(C67_A0, t); //r=reg to load, constant + C67_MVKH(C67_A0, t); //r=reg to load, constant + C67_IREG_B_REG(0, C67_CREG_ZERO, C67_A0); // [!R] B.S2x A0 + C67_NOP(5); + return ind1; +} + +/* generate a jump to a fixed address */ +void gjmp_addr(int a) +{ + Sym *sym; + // I guess this routine is used for relative short + // local jumps, for now just handle it as the general + // case + + // define a label that will be relocated + + sym = get_sym_ref(&char_pointer_type, cur_text_section, a, 0); + greloc(cur_text_section, sym, ind, R_C60LO16); + greloc(cur_text_section, sym, ind + 4, R_C60HI16); + + gjmp(0); // place a zero there later the symbol will be added to it +} + +/* generate a test. set 'inv' to invert test. Stack entry is popped */ +int gtst(int inv, int t) +{ + int ind1, n; + int v, *p; + + v = vtop->r & VT_VALMASK; + if (v == VT_CMP) { + /* fast case : can jump directly since flags are set */ + // C67 uses B2 sort of as flags register + ind1 = ind; + C67_MVKL(C67_A0, t); //r=reg to load, constant + C67_MVKH(C67_A0, t); //r=reg to load, constant + + if (C67_compare_reg != TREG_EAX && // check if not already in a conditional test reg + C67_compare_reg != TREG_EDX && + C67_compare_reg != TREG_ST0 && C67_compare_reg != C67_B2) { + C67_MV(C67_compare_reg, C67_B2); + C67_compare_reg = C67_B2; + } + + C67_IREG_B_REG(C67_invert_test ^ inv, C67_compare_reg, C67_A0); // [!R] B.S2x A0 + C67_NOP(5); + t = ind1; //return where we need to patch + + } else if (v == VT_JMP || v == VT_JMPI) { + /* && or || optimization */ + if ((v & 1) == inv) { + /* insert vtop->c jump list in t */ + p = &vtop->c.i; + + // I guess the idea is to traverse to the + // null at the end of the list and store t + // there + + n = *p; + while (n != 0) { + p = (int *) (cur_text_section->data + n); + + // extract 32 bit address from MVKH/MVKL + n = ((*p >> 7) & 0xffff); + n |= ((*(p + 1) >> 7) & 0xffff) << 16; + } + *p |= (t & 0xffff) << 7; + *(p + 1) |= ((t >> 16) & 0xffff) << 7; + t = vtop->c.i; + + } else { + t = gjmp(t); + gsym(vtop->c.i); + } + } else { + if (is_float(vtop->type.t)) { + vpushi(0); + gen_op(TOK_NE); + } + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + /* constant jmp optimization */ + if ((vtop->c.i != 0) != inv) + t = gjmp(t); + } else { + // I think we need to get the value on the stack + // into a register, test it, and generate a branch + // return the address of the branch, so it can be + // later patched + + v = gv(RC_INT); // get value into a reg + ind1 = ind; + C67_MVKL(C67_A0, t); //r=reg to load, constant + C67_MVKH(C67_A0, t); //r=reg to load, constant + + if (v != TREG_EAX && // check if not already in a conditional test reg + v != TREG_EDX && v != TREG_ST0 && v != C67_B2) { + C67_MV(v, C67_B2); + v = C67_B2; + } + + C67_IREG_B_REG(inv, v, C67_A0); // [!R] B.S2x A0 + C67_NOP(5); + t = ind1; //return where we need to patch + ind1 = ind; + } + } + vtop--; + return t; +} + +/* generate an integer binary operation */ +void gen_opi(int op) +{ + int r, fr, opc, t; + + switch (op) { + case '+': + case TOK_ADDC1: /* add with carry generation */ + opc = 0; + gen_op8: + + +// C67 can't do const compares, must load into a reg +// so just go to gv2 directly - tktk + + + + if (op >= TOK_ULT && op <= TOK_GT) + gv2(RC_INT_BSIDE, RC_INT); // make sure r (src1) is on the B Side of CPU + else + gv2(RC_INT, RC_INT); + + r = vtop[-1].r; + fr = vtop[0].r; + + C67_compare_reg = C67_B2; + + + if (op == TOK_LT) { + C67_CMPLT(r, fr, C67_B2); + C67_invert_test = false; + } else if (op == TOK_GE) { + C67_CMPLT(r, fr, C67_B2); + C67_invert_test = true; + } else if (op == TOK_GT) { + C67_CMPGT(r, fr, C67_B2); + C67_invert_test = false; + } else if (op == TOK_LE) { + C67_CMPGT(r, fr, C67_B2); + C67_invert_test = true; + } else if (op == TOK_EQ) { + C67_CMPEQ(r, fr, C67_B2); + C67_invert_test = false; + } else if (op == TOK_NE) { + C67_CMPEQ(r, fr, C67_B2); + C67_invert_test = true; + } else if (op == TOK_ULT) { + C67_CMPLTU(r, fr, C67_B2); + C67_invert_test = false; + } else if (op == TOK_UGE) { + C67_CMPLTU(r, fr, C67_B2); + C67_invert_test = true; + } else if (op == TOK_UGT) { + C67_CMPGTU(r, fr, C67_B2); + C67_invert_test = false; + } else if (op == TOK_ULE) { + C67_CMPGTU(r, fr, C67_B2); + C67_invert_test = true; + } else if (op == '+') + C67_ADD(fr, r); // ADD r,fr,r + else if (op == '-') + C67_SUB(fr, r); // SUB r,fr,r + else if (op == '&') + C67_AND(fr, r); // AND r,fr,r + else if (op == '|') + C67_OR(fr, r); // OR r,fr,r + else if (op == '^') + C67_XOR(fr, r); // XOR r,fr,r + else + ALWAYS_ASSERT(FALSE); + + vtop--; + if (op >= TOK_ULT && op <= TOK_GT) { + vtop->r = VT_CMP; + vtop->c.i = op; + } + break; + case '-': + case TOK_SUBC1: /* sub with carry generation */ + opc = 5; + goto gen_op8; + case TOK_ADDC2: /* add with carry use */ + opc = 2; + goto gen_op8; + case TOK_SUBC2: /* sub with carry use */ + opc = 3; + goto gen_op8; + case '&': + opc = 4; + goto gen_op8; + case '^': + opc = 6; + goto gen_op8; + case '|': + opc = 1; + goto gen_op8; + case '*': + case TOK_UMULL: + gv2(RC_INT, RC_INT); + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + C67_MPYI(fr, r); // 32 bit bultiply fr,r,fr + C67_NOP(8); // NOP 8 for worst case + break; + case TOK_SHL: + gv2(RC_INT_BSIDE, RC_INT_BSIDE); // shift amount must be on same side as dst + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + C67_SHL(fr, r); // arithmetic/logical shift + break; + + case TOK_SHR: + gv2(RC_INT_BSIDE, RC_INT_BSIDE); // shift amount must be on same side as dst + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + C67_SHRU(fr, r); // logical shift + break; + + case TOK_SAR: + gv2(RC_INT_BSIDE, RC_INT_BSIDE); // shift amount must be on same side as dst + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + C67_SHR(fr, r); // arithmetic shift + break; + + case '/': + t = TOK__divi; + call_func: + vswap(); + /* call generic idiv function */ + vpush_global_sym(&func_old_type, t); + vrott(3); + gfunc_call(2); + vpushi(0); + vtop->r = REG_IRET; + vtop->r2 = VT_CONST; + break; + case TOK_UDIV: + case TOK_PDIV: + t = TOK__divu; + goto call_func; + case '%': + t = TOK__remi; + goto call_func; + case TOK_UMOD: + t = TOK__remu; + goto call_func; + + default: + opc = 7; + goto gen_op8; + } +} + +/* generate a floating point operation 'v = t1 op t2' instruction. The + two operands are guaranted to have the same floating point type */ +/* XXX: need to use ST1 too */ +void gen_opf(int op) +{ + int ft, fc, fr, r; + + if (op >= TOK_ULT && op <= TOK_GT) + gv2(RC_EDX, RC_EAX); // make sure src2 is on b side + else + gv2(RC_FLOAT, RC_FLOAT); // make sure src2 is on b side + + ft = vtop->type.t; + fc = vtop->c.ul; + r = vtop->r; + fr = vtop[-1].r; + + + if ((ft & VT_BTYPE) == VT_LDOUBLE) + error("long doubles not supported"); + + if (op >= TOK_ULT && op <= TOK_GT) { + + r = vtop[-1].r; + fr = vtop[0].r; + + C67_compare_reg = C67_B2; + + if (op == TOK_LT) { + if ((ft & VT_BTYPE) == VT_DOUBLE) + C67_CMPLTDP(r, fr, C67_B2); + else + C67_CMPLTSP(r, fr, C67_B2); + + C67_invert_test = false; + } else if (op == TOK_GE) { + if ((ft & VT_BTYPE) == VT_DOUBLE) + C67_CMPLTDP(r, fr, C67_B2); + else + C67_CMPLTSP(r, fr, C67_B2); + + C67_invert_test = true; + } else if (op == TOK_GT) { + if ((ft & VT_BTYPE) == VT_DOUBLE) + C67_CMPGTDP(r, fr, C67_B2); + else + C67_CMPGTSP(r, fr, C67_B2); + + C67_invert_test = false; + } else if (op == TOK_LE) { + if ((ft & VT_BTYPE) == VT_DOUBLE) + C67_CMPGTDP(r, fr, C67_B2); + else + C67_CMPGTSP(r, fr, C67_B2); + + C67_invert_test = true; + } else if (op == TOK_EQ) { + if ((ft & VT_BTYPE) == VT_DOUBLE) + C67_CMPEQDP(r, fr, C67_B2); + else + C67_CMPEQSP(r, fr, C67_B2); + + C67_invert_test = false; + } else if (op == TOK_NE) { + if ((ft & VT_BTYPE) == VT_DOUBLE) + C67_CMPEQDP(r, fr, C67_B2); + else + C67_CMPEQSP(r, fr, C67_B2); + + C67_invert_test = true; + } else { + ALWAYS_ASSERT(FALSE); + } + vtop->r = VT_CMP; // tell TCC that result is in "flags" actually B2 + } else { + if (op == '+') { + if ((ft & VT_BTYPE) == VT_DOUBLE) { + C67_ADDDP(r, fr); // ADD fr,r,fr + C67_NOP(6); + } else { + C67_ADDSP(r, fr); // ADD fr,r,fr + C67_NOP(3); + } + vtop--; + } else if (op == '-') { + if ((ft & VT_BTYPE) == VT_DOUBLE) { + C67_SUBDP(r, fr); // SUB fr,r,fr + C67_NOP(6); + } else { + C67_SUBSP(r, fr); // SUB fr,r,fr + C67_NOP(3); + } + vtop--; + } else if (op == '*') { + if ((ft & VT_BTYPE) == VT_DOUBLE) { + C67_MPYDP(r, fr); // MPY fr,r,fr + C67_NOP(9); + } else { + C67_MPYSP(r, fr); // MPY fr,r,fr + C67_NOP(3); + } + vtop--; + } else if (op == '/') { + if ((ft & VT_BTYPE) == VT_DOUBLE) { + // must call intrinsic DP floating point divide + vswap(); + /* call generic idiv function */ + vpush_global_sym(&func_old_type, TOK__divd); + vrott(3); + gfunc_call(2); + vpushi(0); + vtop->r = REG_FRET; + vtop->r2 = REG_LRET; + + } else { + // must call intrinsic SP floating point divide + vswap(); + /* call generic idiv function */ + vpush_global_sym(&func_old_type, TOK__divf); + vrott(3); + gfunc_call(2); + vpushi(0); + vtop->r = REG_FRET; + vtop->r2 = VT_CONST; + } + } else + ALWAYS_ASSERT(FALSE); + + + } +} + + +/* convert integers to fp 't' type. Must handle 'int', 'unsigned int' + and 'long long' cases. */ +void gen_cvt_itof(int t) +{ + int r; + + gv(RC_INT); + r = vtop->r; + + if ((t & VT_BTYPE) == VT_DOUBLE) { + if (t & VT_UNSIGNED) + C67_INTDPU(r, r); + else + C67_INTDP(r, r); + + C67_NOP(4); + vtop->type.t = VT_DOUBLE; + } else { + if (t & VT_UNSIGNED) + C67_INTSPU(r, r); + else + C67_INTSP(r, r); + C67_NOP(3); + vtop->type.t = VT_FLOAT; + } + +} + +/* convert fp to int 't' type */ +/* XXX: handle long long case */ +void gen_cvt_ftoi(int t) +{ + int r; + + gv(RC_FLOAT); + r = vtop->r; + + if (t != VT_INT) + error("long long not supported"); + else { + if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) { + C67_DPTRUNC(r, r); + C67_NOP(3); + } else { + C67_SPTRUNC(r, r); + C67_NOP(3); + } + + vtop->type.t = VT_INT; + + } +} + +/* convert from one floating point type to another */ +void gen_cvt_ftof(int t) +{ + int r, r2; + + if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE && + (t & VT_BTYPE) == VT_FLOAT) { + // convert double to float + + gv(RC_FLOAT); // get it in a register pair + + r = vtop->r; + + C67_DPSP(r, r); // convert it to SP same register + C67_NOP(3); + + vtop->type.t = VT_FLOAT; + vtop->r2 = VT_CONST; // set this as unused + } else if ((vtop->type.t & VT_BTYPE) == VT_FLOAT && + (t & VT_BTYPE) == VT_DOUBLE) { + // convert float to double + + gv(RC_FLOAT); // get it in a register + + r = vtop->r; + + if (r == TREG_EAX) { // make sure the paired reg is avail + r2 = get_reg(RC_ECX); + } else if (r == TREG_EDX) { + r2 = get_reg(RC_ST0); + } else { + ALWAYS_ASSERT(FALSE); + r2 = 0; /* avoid warning */ + } + + C67_SPDP(r, r); // convert it to DP same register + C67_NOP(1); + + vtop->type.t = VT_DOUBLE; + vtop->r2 = r2; // set this as unused + } else { + ALWAYS_ASSERT(FALSE); + } +} + +/* computed goto support */ +void ggoto(void) +{ + gcall_or_jmp(1); + vtop--; +} + +/* end of X86 code generator */ +/*************************************************************/ diff --git a/05/tcc-0.9.25/coff.h b/05/tcc-0.9.25/coff.h new file mode 100644 index 0000000..38960b4 --- /dev/null +++ b/05/tcc-0.9.25/coff.h @@ -0,0 +1,446 @@ +/**************************************************************************/ +/* COFF.H */ +/* COFF data structures and related definitions used by the linker */ +/**************************************************************************/ + +/*------------------------------------------------------------------------*/ +/* COFF FILE HEADER */ +/*------------------------------------------------------------------------*/ +struct filehdr { + unsigned short f_magic; /* magic number */ + unsigned short f_nscns; /* number of sections */ + long f_timdat; /* time & date stamp */ + long f_symptr; /* file pointer to symtab */ + long f_nsyms; /* number of symtab entries */ + unsigned short f_opthdr; /* sizeof(optional hdr) */ + unsigned short f_flags; /* flags */ + unsigned short f_TargetID; /* for C6x = 0x0099 */ + }; + +/*------------------------------------------------------------------------*/ +/* File header flags */ +/*------------------------------------------------------------------------*/ +#define F_RELFLG 0x01 /* relocation info stripped from file */ +#define F_EXEC 0x02 /* file is executable (no unresolved refs) */ +#define F_LNNO 0x04 /* line nunbers stripped from file */ +#define F_LSYMS 0x08 /* local symbols stripped from file */ +#define F_GSP10 0x10 /* 34010 version */ +#define F_GSP20 0x20 /* 34020 version */ +#define F_SWABD 0x40 /* bytes swabbed (in names) */ +#define F_AR16WR 0x80 /* byte ordering of an AR16WR (PDP-11) */ +#define F_LITTLE 0x100 /* byte ordering of an AR32WR (vax) */ +#define F_BIG 0x200 /* byte ordering of an AR32W (3B, maxi) */ +#define F_PATCH 0x400 /* contains "patch" list in optional header */ +#define F_NODF 0x400 + +#define F_VERSION (F_GSP10 | F_GSP20) +#define F_BYTE_ORDER (F_LITTLE | F_BIG) +#define FILHDR struct filehdr + +//#define FILHSZ sizeof(FILHDR) +#define FILHSZ 22 // above rounds to align on 4 bytes which causes problems + +#define COFF_C67_MAGIC 0x00c2 + +/*------------------------------------------------------------------------*/ +/* Macros to recognize magic numbers */ +/*------------------------------------------------------------------------*/ +#define ISMAGIC(x) (((unsigned short)(x))==(unsigned short)magic) +#define ISARCHIVE(x) ((((unsigned short)(x))==(unsigned short)ARTYPE)) +#define BADMAGIC(x) (((unsigned short)(x) & 0x8080) && !ISMAGIC(x)) + + +/*------------------------------------------------------------------------*/ +/* OPTIONAL FILE HEADER */ +/*------------------------------------------------------------------------*/ +typedef struct aouthdr { + short magic; /* see magic.h */ + short vstamp; /* version stamp */ + long tsize; /* text size in bytes, padded to FW bdry*/ + long dsize; /* initialized data " " */ + long bsize; /* uninitialized data " " */ + long entrypt; /* entry pt. */ + long text_start; /* base of text used for this file */ + long data_start; /* base of data used for this file */ +} AOUTHDR; + +#define AOUTSZ sizeof(AOUTHDR) + +/*----------------------------------------------------------------------*/ +/* When a UNIX aout header is to be built in the optional header, */ +/* the following magic numbers can appear in that header: */ +/* */ +/* AOUT1MAGIC : default : readonly sharable text segment */ +/* AOUT2MAGIC: : writable text segment */ +/* PAGEMAGIC : : configured for paging */ +/*----------------------------------------------------------------------*/ +#define AOUT1MAGIC 0410 +#define AOUT2MAGIC 0407 +#define PAGEMAGIC 0413 + + +/*------------------------------------------------------------------------*/ +/* COMMON ARCHIVE FILE STRUCTURES */ +/* */ +/* ARCHIVE File Organization: */ +/* _______________________________________________ */ +/* |__________ARCHIVE_MAGIC_STRING_______________| */ +/* |__________ARCHIVE_FILE_MEMBER_1______________| */ +/* | | */ +/* | Archive File Header "ar_hdr" | */ +/* |.............................................| */ +/* | Member Contents | */ +/* | 1. External symbol directory | */ +/* | 2. Text file | */ +/* |_____________________________________________| */ +/* |________ARCHIVE_FILE_MEMBER_2________________| */ +/* | "ar_hdr" | */ +/* |.............................................| */ +/* | Member Contents (.o or text file) | */ +/* |_____________________________________________| */ +/* | . . . | */ +/* | . . . | */ +/* | . . . | */ +/* |_____________________________________________| */ +/* |________ARCHIVE_FILE_MEMBER_n________________| */ +/* | "ar_hdr" | */ +/* |.............................................| */ +/* | Member Contents | */ +/* |_____________________________________________| */ +/* */ +/*------------------------------------------------------------------------*/ + +#define COFF_ARMAG "!\n" +#define SARMAG 8 +#define ARFMAG "`\n" + +struct ar_hdr /* archive file member header - printable ascii */ +{ + char ar_name[16]; /* file member name - `/' terminated */ + char ar_date[12]; /* file member date - decimal */ + char ar_uid[6]; /* file member user id - decimal */ + char ar_gid[6]; /* file member group id - decimal */ + char ar_mode[8]; /* file member mode - octal */ + char ar_size[10]; /* file member size - decimal */ + char ar_fmag[2]; /* ARFMAG - string to end header */ +}; + + +/*------------------------------------------------------------------------*/ +/* SECTION HEADER */ +/*------------------------------------------------------------------------*/ +struct scnhdr { + char s_name[8]; /* section name */ + long s_paddr; /* physical address */ + long s_vaddr; /* virtual address */ + long s_size; /* section size */ + long s_scnptr; /* file ptr to raw data for section */ + long s_relptr; /* file ptr to relocation */ + long s_lnnoptr; /* file ptr to line numbers */ + unsigned int s_nreloc; /* number of relocation entries */ + unsigned int s_nlnno; /* number of line number entries */ + unsigned int s_flags; /* flags */ + unsigned short s_reserved; /* reserved byte */ + unsigned short s_page; /* memory page id */ + }; + +#define SCNHDR struct scnhdr +#define SCNHSZ sizeof(SCNHDR) + +/*------------------------------------------------------------------------*/ +/* Define constants for names of "special" sections */ +/*------------------------------------------------------------------------*/ +//#define _TEXT ".text" +#define _DATA ".data" +#define _BSS ".bss" +#define _CINIT ".cinit" +#define _TV ".tv" + +/*------------------------------------------------------------------------*/ +/* The low 4 bits of s_flags is used as a section "type" */ +/*------------------------------------------------------------------------*/ +#define STYP_REG 0x00 /* "regular" : allocated, relocated, loaded */ +#define STYP_DSECT 0x01 /* "dummy" : not allocated, relocated, not loaded */ +#define STYP_NOLOAD 0x02 /* "noload" : allocated, relocated, not loaded */ +#define STYP_GROUP 0x04 /* "grouped" : formed of input sections */ +#define STYP_PAD 0x08 /* "padding" : not allocated, not relocated, loaded */ +#define STYP_COPY 0x10 /* "copy" : used for C init tables - + not allocated, relocated, + loaded; reloc & lineno + entries processed normally */ +#define STYP_TEXT 0x20 /* section contains text only */ +#define STYP_DATA 0x40 /* section contains data only */ +#define STYP_BSS 0x80 /* section contains bss only */ + +#define STYP_ALIGN 0x100 /* align flag passed by old version assemblers */ +#define ALIGN_MASK 0x0F00 /* part of s_flags that is used for align vals */ +#define ALIGNSIZE(x) (1 << ((x & ALIGN_MASK) >> 8)) + + +/*------------------------------------------------------------------------*/ +/* RELOCATION ENTRIES */ +/*------------------------------------------------------------------------*/ +struct reloc +{ + long r_vaddr; /* (virtual) address of reference */ + short r_symndx; /* index into symbol table */ + unsigned short r_disp; /* additional bits for address calculation */ + unsigned short r_type; /* relocation type */ +}; + +#define RELOC struct reloc +#define RELSZ 10 /* sizeof(RELOC) */ + +/*--------------------------------------------------------------------------*/ +/* define all relocation types */ +/*--------------------------------------------------------------------------*/ + +#define R_ABS 0 /* absolute address - no relocation */ +#define R_DIR16 01 /* UNUSED */ +#define R_REL16 02 /* UNUSED */ +#define R_DIR24 04 /* UNUSED */ +#define R_REL24 05 /* 24 bits, direct */ +#define R_DIR32 06 /* UNUSED */ +#define R_RELBYTE 017 /* 8 bits, direct */ +#define R_RELWORD 020 /* 16 bits, direct */ +#define R_RELLONG 021 /* 32 bits, direct */ +#define R_PCRBYTE 022 /* 8 bits, PC-relative */ +#define R_PCRWORD 023 /* 16 bits, PC-relative */ +#define R_PCRLONG 024 /* 32 bits, PC-relative */ +#define R_OCRLONG 030 /* GSP: 32 bits, one's complement direct */ +#define R_GSPPCR16 031 /* GSP: 16 bits, PC relative (in words) */ +#define R_GSPOPR32 032 /* GSP: 32 bits, direct big-endian */ +#define R_PARTLS16 040 /* Brahma: 16 bit offset of 24 bit address*/ +#define R_PARTMS8 041 /* Brahma: 8 bit page of 24 bit address */ +#define R_PARTLS7 050 /* DSP: 7 bit offset of 16 bit address */ +#define R_PARTMS9 051 /* DSP: 9 bit page of 16 bit address */ +#define R_REL13 052 /* DSP: 13 bits, direct */ + + +/*------------------------------------------------------------------------*/ +/* LINE NUMBER ENTRIES */ +/*------------------------------------------------------------------------*/ +struct lineno +{ + union + { + long l_symndx ; /* sym. table index of function name + iff l_lnno == 0 */ + long l_paddr ; /* (physical) address of line number */ + } l_addr ; + unsigned short l_lnno ; /* line number */ +}; + +#define LINENO struct lineno +#define LINESZ 6 /* sizeof(LINENO) */ + + +/*------------------------------------------------------------------------*/ +/* STORAGE CLASSES */ +/*------------------------------------------------------------------------*/ +#define C_EFCN -1 /* physical end of function */ +#define C_NULL 0 +#define C_AUTO 1 /* automatic variable */ +#define C_EXT 2 /* external symbol */ +#define C_STAT 3 /* static */ +#define C_REG 4 /* register variable */ +#define C_EXTDEF 5 /* external definition */ +#define C_LABEL 6 /* label */ +#define C_ULABEL 7 /* undefined label */ +#define C_MOS 8 /* member of structure */ +#define C_ARG 9 /* function argument */ +#define C_STRTAG 10 /* structure tag */ +#define C_MOU 11 /* member of union */ +#define C_UNTAG 12 /* union tag */ +#define C_TPDEF 13 /* type definition */ +#define C_USTATIC 14 /* undefined static */ +#define C_ENTAG 15 /* enumeration tag */ +#define C_MOE 16 /* member of enumeration */ +#define C_REGPARM 17 /* register parameter */ +#define C_FIELD 18 /* bit field */ + +#define C_BLOCK 100 /* ".bb" or ".eb" */ +#define C_FCN 101 /* ".bf" or ".ef" */ +#define C_EOS 102 /* end of structure */ +#define C_FILE 103 /* file name */ +#define C_LINE 104 /* dummy sclass for line number entry */ +#define C_ALIAS 105 /* duplicate tag */ +#define C_HIDDEN 106 /* special storage class for external */ + /* symbols in dmert public libraries */ + +/*------------------------------------------------------------------------*/ +/* SYMBOL TABLE ENTRIES */ +/*------------------------------------------------------------------------*/ + +#define SYMNMLEN 8 /* Number of characters in a symbol name */ +#define FILNMLEN 14 /* Number of characters in a file name */ +#define DIMNUM 4 /* Number of array dimensions in auxiliary entry */ + + +struct syment +{ + union + { + char _n_name[SYMNMLEN]; /* old COFF version */ + struct + { + long _n_zeroes; /* new == 0 */ + long _n_offset; /* offset into string table */ + } _n_n; + char *_n_nptr[2]; /* allows for overlaying */ + } _n; + long n_value; /* value of symbol */ + short n_scnum; /* section number */ + unsigned short n_type; /* type and derived type */ + char n_sclass; /* storage class */ + char n_numaux; /* number of aux. entries */ +}; + +#define n_name _n._n_name +#define n_nptr _n._n_nptr[1] +#define n_zeroes _n._n_n._n_zeroes +#define n_offset _n._n_n._n_offset + +/*------------------------------------------------------------------------*/ +/* Relocatable symbols have a section number of the */ +/* section in which they are defined. Otherwise, section */ +/* numbers have the following meanings: */ +/*------------------------------------------------------------------------*/ +#define N_UNDEF 0 /* undefined symbol */ +#define N_ABS -1 /* value of symbol is absolute */ +#define N_DEBUG -2 /* special debugging symbol */ +#define N_TV (unsigned short)-3 /* needs transfer vector (preload) */ +#define P_TV (unsigned short)-4 /* needs transfer vector (postload) */ + + +/*------------------------------------------------------------------------*/ +/* The fundamental type of a symbol packed into the low */ +/* 4 bits of the word. */ +/*------------------------------------------------------------------------*/ +#define _EF ".ef" + +#define T_NULL 0 /* no type info */ +#define T_ARG 1 /* function argument (only used by compiler) */ +#define T_CHAR 2 /* character */ +#define T_SHORT 3 /* short integer */ +#define T_INT 4 /* integer */ +#define T_LONG 5 /* long integer */ +#define T_FLOAT 6 /* floating point */ +#define T_DOUBLE 7 /* double word */ +#define T_STRUCT 8 /* structure */ +#define T_UNION 9 /* union */ +#define T_ENUM 10 /* enumeration */ +#define T_MOE 11 /* member of enumeration */ +#define T_UCHAR 12 /* unsigned character */ +#define T_USHORT 13 /* unsigned short */ +#define T_UINT 14 /* unsigned integer */ +#define T_ULONG 15 /* unsigned long */ + +/*------------------------------------------------------------------------*/ +/* derived types are: */ +/*------------------------------------------------------------------------*/ +#define DT_NON 0 /* no derived type */ +#define DT_PTR 1 /* pointer */ +#define DT_FCN 2 /* function */ +#define DT_ARY 3 /* array */ + +#define MKTYPE(basic, d1,d2,d3,d4,d5,d6) \ + ((basic) | ((d1) << 4) | ((d2) << 6) | ((d3) << 8) |\ + ((d4) << 10) | ((d5) << 12) | ((d6) << 14)) + +/*------------------------------------------------------------------------*/ +/* type packing constants and macros */ +/*------------------------------------------------------------------------*/ +#define N_BTMASK_COFF 017 +#define N_TMASK_COFF 060 +#define N_TMASK1_COFF 0300 +#define N_TMASK2_COFF 0360 +#define N_BTSHFT_COFF 4 +#define N_TSHIFT_COFF 2 + +#define BTYPE_COFF(x) ((x) & N_BTMASK_COFF) +#define ISINT(x) (((x) >= T_CHAR && (x) <= T_LONG) || \ + ((x) >= T_UCHAR && (x) <= T_ULONG) || (x) == T_ENUM) +#define ISFLT_COFF(x) ((x) == T_DOUBLE || (x) == T_FLOAT) +#define ISPTR_COFF(x) (((x) & N_TMASK_COFF) == (DT_PTR << N_BTSHFT_COFF)) +#define ISFCN_COFF(x) (((x) & N_TMASK_COFF) == (DT_FCN << N_BTSHFT_COFF)) +#define ISARY_COFF(x) (((x) & N_TMASK_COFF) == (DT_ARY << N_BTSHFT_COFF)) +#define ISTAG_COFF(x) ((x)==C_STRTAG || (x)==C_UNTAG || (x)==C_ENTAG) + +#define INCREF_COFF(x) ((((x)&~N_BTMASK_COFF)<>N_TSHIFT_COFF)&~N_BTMASK_COFF)|((x)&N_BTMASK_COFF)) + + +/*------------------------------------------------------------------------*/ +/* AUXILIARY SYMBOL ENTRY */ +/*------------------------------------------------------------------------*/ +union auxent +{ + struct + { + long x_tagndx; /* str, un, or enum tag indx */ + union + { + struct + { + unsigned short x_lnno; /* declaration line number */ + unsigned short x_size; /* str, union, array size */ + } x_lnsz; + long x_fsize; /* size of function */ + } x_misc; + union + { + struct /* if ISFCN, tag, or .bb */ + { + long x_lnnoptr; /* ptr to fcn line # */ + long x_endndx; /* entry ndx past block end */ + } x_fcn; + struct /* if ISARY, up to 4 dimen. */ + { + unsigned short x_dimen[DIMNUM]; + } x_ary; + } x_fcnary; + unsigned short x_regcount; /* number of registers used by func */ + } x_sym; + struct + { + char x_fname[FILNMLEN]; + } x_file; + struct + { + long x_scnlen; /* section length */ + unsigned short x_nreloc; /* number of relocation entries */ + unsigned short x_nlinno; /* number of line numbers */ + } x_scn; +}; + +#define SYMENT struct syment +#define SYMESZ 18 /* sizeof(SYMENT) */ + +#define AUXENT union auxent +#define AUXESZ 18 /* sizeof(AUXENT) */ + +/*------------------------------------------------------------------------*/ +/* NAMES OF "SPECIAL" SYMBOLS */ +/*------------------------------------------------------------------------*/ +#define _STEXT ".text" +#define _ETEXT "etext" +#define _SDATA ".data" +#define _EDATA "edata" +#define _SBSS ".bss" +#define _END "end" +#define _CINITPTR "cinit" + +/*--------------------------------------------------------------------------*/ +/* ENTRY POINT SYMBOLS */ +/*--------------------------------------------------------------------------*/ +#define _START "_start" +#define _MAIN "_main" + /* _CSTART "_c_int00" (defined in params.h) */ + + +#define _TVORIG "_tvorig" +#define _TORIGIN "_torigin" +#define _DORIGIN "_dorigin" + +#define _SORIGIN "_sorigin" diff --git a/05/tcc-0.9.25/config.h b/05/tcc-0.9.25/config.h new file mode 100644 index 0000000..abc985d --- /dev/null +++ b/05/tcc-0.9.25/config.h @@ -0,0 +1,10 @@ +#ifndef CONFIG_TCCDIR +# define CONFIG_TCCDIR "/usr/local/lib/tcc-bootstrap" +#endif +#define TCC_VERSION "0.9.27" +#define CONFIG_TCC_STATIC 1 +#define TCC_TARGET_X86_64 1 +#define CONFIG_TCC_ELFINTERP "/XXX" +#define CONFIG_TCC_CRT_PREFIX "/XXX" +#define CONFIG_SYSROOT "/XXX" +#define inline diff --git a/05/tcc-0.9.25/configure b/05/tcc-0.9.25/configure new file mode 100755 index 0000000..5b38f28 --- /dev/null +++ b/05/tcc-0.9.25/configure @@ -0,0 +1,382 @@ +#!/bin/sh +# +# tcc configure script (c) 2003 Fabrice Bellard +# +# set temporary file name +if test ! -z "$TMPDIR" ; then + TMPDIR1="${TMPDIR}" +elif test ! -z "$TEMPDIR" ; then + TMPDIR1="${TEMPDIR}" +else + TMPDIR1="/tmp" +fi + +TMPC="${TMPDIR1}/tcc-conf-${RANDOM}-$$-${RANDOM}.c" +TMPO="${TMPDIR1}/tcc-conf-${RANDOM}-$$-${RANDOM}.o" +TMPE="${TMPDIR1}/tcc-conf-${RANDOM}-$$-${RANDOM}" +TMPS="${TMPDIR1}/tcc-conf-${RANDOM}-$$-${RANDOM}.S" +TMPH="${TMPDIR1}/tcc-conf-${RANDOM}-$$-${RANDOM}.h" + +# default parameters +build_cross="no" +use_libgcc="no" +prefix="" +execprefix="" +bindir="" +libdir="" +tccdir="" +includedir="" +mandir="" +sysroot="" +cross_prefix="" +cc="gcc" +host_cc="gcc" +ar="ar" +strip="strip" +cpu=`uname -m` +case "$cpu" in + i386|i486|i586|i686|i86pc|BePC) + cpu="x86" + ;; + x86_64) + cpu="x86-64" + ;; + armv4l) + cpu="armv4l" + ;; + alpha) + cpu="alpha" + ;; + "Power Macintosh"|ppc|ppc64) + cpu="powerpc" + ;; + mips) + cpu="mips" + ;; + s390) + cpu="s390" + ;; + *) + cpu="unknown" + ;; +esac +gprof="no" +bigendian="no" +mingw32="no" +LIBSUF=".a" +EXESUF="" + +# OS specific +targetos=`uname -s` +case $targetos in +MINGW32*) +mingw32="yes" +;; +DragonFly) +noldl="yes" +;; +OpenBSD) +noldl="yes" +;; +*) ;; +esac + +# find source path +# XXX: we assume an absolute path is given when launching configure, +# except in './configure' case. +source_path=${0%configure} +source_path=${source_path%/} +source_path_used="yes" +if test -z "$source_path" -o "$source_path" = "." ; then + source_path=`pwd` + source_path_used="no" +fi + +for opt do + case "$opt" in + --prefix=*) prefix=`echo $opt | cut -d '=' -f 2` + ;; + --exec-prefix=*) execprefix=`echo $opt | cut -d '=' -f 2` + ;; + --bindir=*) bindir=`echo $opt | cut -d '=' -f 2` + ;; + --libdir=*) libdir=`echo $opt | cut -d '=' -f 2` + ;; + --includedir=*) includedir=`echo $opt | cut -d '=' -f 2` + ;; + --mandir=*) mandir=`echo $opt | cut -d '=' -f 2` + ;; + --sysroot=*) sysroot=`echo $opt | cut -d '=' -f 2` + ;; + --source-path=*) source_path=`echo $opt | cut -d '=' -f 2` + ;; + --cross-prefix=*) cross_prefix=`echo $opt | cut -d '=' -f 2` + ;; + --cc=*) cc=`echo $opt | cut -d '=' -f 2` + ;; + --extra-cflags=*) CFLAGS="${opt#--extra-cflags=}" + ;; + --extra-ldflags=*) LDFLAGS="${opt#--extra-ldflags=}" + ;; + --extra-libs=*) extralibs=${opt#--extra-libs=} + ;; + --cpu=*) cpu=`echo $opt | cut -d '=' -f 2` + ;; + --enable-gprof) gprof="yes" + ;; + --enable-mingw32) mingw32="yes" ; cross_prefix="i386-mingw32-" + ;; + --enable-cross) build_cross="yes" + ;; + --with-libgcc) use_libgcc="yes" + ;; + esac +done + +# Checking for CFLAGS +if test -z "$CFLAGS"; then + CFLAGS="-O2" +fi + +cc="${cross_prefix}${cc}" +ar="${cross_prefix}${ar}" +strip="${cross_prefix}${strip}" + +if test "$mingw32" = "yes" ; then + LIBSUF=".lib" + EXESUF=".exe" +fi + +if test -z "$cross_prefix" ; then + +# --- +# big/little endian test +cat > $TMPC << EOF +#include +int main(int argc, char ** argv){ + volatile uint32_t i=0x01234567; + return (*((uint8_t*)(&i))) == 0x67; +} +EOF + +if $cc -o $TMPE $TMPC 2>/dev/null ; then + $TMPE && bigendian="yes" +else + echo big/little test failed +fi + +else + +# if cross compiling, cannot launch a program, so make a static guess +if test "$cpu" = "powerpc" -o "$cpu" = "mips" -o "$cpu" = "s390" ; then + bigendian="yes" +fi + +fi + +# check gcc version +cat > $TMPC < 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2) +return 0; +#else +#error gcc < 3.2 +#endif +} +EOF + +gcc_major="2" +if $cc -o $TMPO $TMPC 2> /dev/null ; then + gcc_major="3" +fi +cat > $TMPC <= 4 +return 0; +#else +#error gcc < 4 +#endif +} +EOF + +if $cc -o $TMPO $TMPC 2> /dev/null ; then + gcc_major="4" +fi + +if test x"$1" = x"-h" -o x"$1" = x"--help" ; then +cat << EOF + +Usage: configure [options] +Options: [defaults in brackets after descriptions] + +EOF +echo "Standard options:" +echo " --help print this message" +echo " --prefix=PREFIX install in PREFIX [$prefix]" +echo " --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX" +echo " [same as prefix]" +echo " --bindir=DIR user executables in DIR [EPREFIX/bin]" +echo " --libdir=DIR object code libraries in DIR [EPREFIX/lib]" +echo " --includedir=DIR C header files in DIR [PREFIX/include]" +echo " --mandir=DIR man documentation in DIR [PREFIX/man]" +echo " --enable-cross build cross compilers" +echo "" +echo "Advanced options (experts only):" +echo " --source-path=PATH path of source code [$source_path]" +echo " --cross-prefix=PREFIX use PREFIX for compile tools [$cross_prefix]" +echo " --sysroot=PREFIX prepend PREFIX to library/include paths []" +echo " --cc=CC use C compiler CC [$cc]" +echo " --with-libgcc use /lib/libgcc_s.so.1 instead of libtcc1.a" +echo "" +#echo "NOTE: The object files are build at the place where configure is launched" +exit 1 +fi + +if test "$mingw32" = "yes" ; then + if test -z "$prefix" ; then + prefix="C:/Program Files/tcc" + fi + execprefix="$prefix" + bindir="$prefix" + tccdir="$prefix" + docdir="$prefix/doc" +else + if test -z "$prefix" ; then + prefix="/usr/local" + fi + if test x"$execprefix" = x""; then + execprefix="${prefix}" + fi + if test x"$bindir" = x""; then + bindir="${execprefix}/bin" + fi + if test x"$docdir" = x""; then + docdir="$prefix/share/doc/tcc" + fi +fi # mingw32 + +if test x"$libdir" = x""; then +libdir="${execprefix}/lib" +fi +if test x"$tccdir" = x""; then +tccdir="${execprefix}/lib/tcc" +fi +if test x"$mandir" = x""; then +mandir="${prefix}/man" +fi +if test x"$includedir" = x""; then +includedir="${prefix}/include" +fi + +echo "Binary directory $bindir" +echo "TinyCC directory $tccdir" +echo "Library directory $libdir" +echo "Include directory $includedir" +echo "Manual directory $mandir" +echo "Doc directory $docdir" +echo "Target root prefix $sysroot" +echo "Source path $source_path" +echo "C compiler $cc" +echo "CPU $cpu" +echo "Big Endian $bigendian" +echo "gprof enabled $gprof" +echo "cross compilers $build_cross" +echo "use libgcc $use_libgcc" + +echo "Creating config.mak and config.h" + +echo "# Automatically generated by configure - do not modify" > config.mak +echo "/* Automatically generated by configure - do not modify */" > $TMPH + +echo "prefix=$prefix" >> config.mak +echo "bindir=$bindir" >> config.mak +echo "tccdir=$tccdir" >> config.mak +echo "libdir=$libdir" >> config.mak +echo "includedir=$includedir" >> config.mak +echo "mandir=$mandir" >> config.mak +echo "docdir=$docdir" >> config.mak +echo "#define CONFIG_SYSROOT \"$sysroot\"" >> $TMPH +echo "#define CONFIG_TCCDIR \"$tccdir\"" >> $TMPH +echo "CC=$cc" >> config.mak +echo "GCC_MAJOR=$gcc_major" >> config.mak +echo "#define GCC_MAJOR $gcc_major" >> $TMPH +echo "HOST_CC=$host_cc" >> config.mak +echo "AR=$ar" >> config.mak +echo "STRIP=$strip -s -R .comment -R .note" >> config.mak +echo "CFLAGS=$CFLAGS" >> config.mak +echo "LDFLAGS=$LDFLAGS" >> config.mak +echo "LIBSUF=$LIBSUF" >> config.mak +echo "EXESUF=$EXESUF" >> config.mak +if test "$cpu" = "x86" ; then + echo "ARCH=i386" >> config.mak + echo "#define HOST_I386 1" >> $TMPH +elif test "$cpu" = "x86-64" ; then + echo "ARCH=x86-64" >> config.mak + echo "#define HOST_X86_64 1" >> $TMPH +elif test "$cpu" = "armv4l" ; then + echo "ARCH=arm" >> config.mak + echo "#define HOST_ARM 1" >> $TMPH +elif test "$cpu" = "powerpc" ; then + echo "ARCH=ppc" >> config.mak + echo "#define HOST_PPC 1" >> $TMPH +elif test "$cpu" = "mips" ; then + echo "ARCH=mips" >> config.mak + echo "#define HOST_MIPS 1" >> $TMPH +elif test "$cpu" = "s390" ; then + echo "ARCH=s390" >> config.mak + echo "#define HOST_S390 1" >> $TMPH +elif test "$cpu" = "alpha" ; then + echo "ARCH=alpha" >> config.mak + echo "#define HOST_ALPHA 1" >> $TMPH +else + echo "Unsupported CPU" + exit 1 +fi +if test "$noldl" = "yes" ; then + echo "CONFIG_NOLDL=yes" >> config.mak +fi +if test "$mingw32" = "yes" ; then + echo "CONFIG_WIN32=yes" >> config.mak + echo "#define CONFIG_WIN32 1" >> $TMPH +fi +if test "$bigendian" = "yes" ; then + echo "WORDS_BIGENDIAN=yes" >> config.mak + echo "#define WORDS_BIGENDIAN 1" >> $TMPH +fi +if test "$gprof" = "yes" ; then + echo "TARGET_GPROF=yes" >> config.mak + echo "#define HAVE_GPROF 1" >> $TMPH +fi +if test "$build_cross" = "yes" ; then + echo "CONFIG_CROSS=yes" >> config.mak +fi +if test "$use_libgcc" = "yes" ; then + echo "#define CONFIG_USE_LIBGCC" >> $TMPH + echo "CONFIG_USE_LIBGCC=yes" >> config.mak +fi +version=`head $source_path/VERSION` +echo "VERSION=$version" >>config.mak +echo "#define TCC_VERSION \"$version\"" >> $TMPH +echo "@set VERSION $version" > config.texi + +# build tree in object directory if source path is different from current one +if test "$source_path_used" = "yes" ; then + DIRS="tests" + FILES="Makefile tests/Makefile" + for dir in $DIRS ; do + mkdir -p $dir + done + for f in $FILES ; do + ln -sf $source_path/$f $f + done +fi +echo "SRC_PATH=$source_path" >> config.mak + +diff $TMPH config.h >/dev/null 2>&1 +if test $? -ne 0 ; then + mv -f $TMPH config.h +else + echo "config.h is unchanged" +fi + +rm -f $TMPO $TMPC $TMPE $TMPS $TMPH diff --git a/05/tcc-0.9.25/ctype.h b/05/tcc-0.9.25/ctype.h new file mode 100644 index 0000000..ed6833d --- /dev/null +++ b/05/tcc-0.9.25/ctype.h @@ -0,0 +1,92 @@ +#ifndef _CTYPE_H +#define _CTYPE_H + +#include + +int islower(int c) { + return c >= 'a' && c <= 'z'; +} + +int isupper(int c) { + return c >= 'A' && c <= 'Z'; +} + +int isalpha(int c) { + return isupper(c) || islower(c); +} + +int isalnum(int c) { + return isalpha(c) || isdigit(c); +} + +int isprint(int c) { + if (isalnum(c)) return 1; + switch (c) { + case '!': return 1; + case '@': return 1; + case '#': return 1; + case '$': return 1; + case '%': return 1; + case '^': return 1; + case '&': return 1; + case '*': return 1; + case '(': return 1; + case ')': return 1; + case '-': return 1; + case '=': return 1; + case '_': return 1; + case '+': return 1; + case '`': return 1; + case '~': return 1; + case '[': return 1; + case '{': return 1; + case ']': return 1; + case '}': return 1; + case '\\': return 1; + case '|': return 1; + case ';': return 1; + case ':': return 1; + case '\'': return 1; + case '"': return 1; + case ',': return 1; + case '<': return 1; + case '.': return 1; + case '>': return 1; + case '/': return 1; + case '?': return 1; + } + return 0; +} + +int iscntrl(int c) { + return !isprint(c); +} + +int isgraph(int c) { + return isprint(c) && c != ' '; +} + +int ispunct(int c) { + return isprint(c) && c != ' ' && !isalnum(c); +} + +int isxdigit(int c) { + if (isdigit(c)) return 1; + if (c >= 'a' && c <= 'f') return 1; + if (c >= 'A' && c <= 'F') return 1; + return 0; +} + +int tolower(int c) { + if (c >= 'A' && c <= 'Z') + return c - 'A' + 'a'; + return c; +} + +int toupper(int c) { + if (c >= 'a' && c <= 'z') + return c - 'a' + 'A'; + return c; +} + +#endif // _CTYPE_H diff --git a/05/tcc-0.9.25/elf.h b/05/tcc-0.9.25/elf.h new file mode 100644 index 0000000..d728766 --- /dev/null +++ b/05/tcc-0.9.25/elf.h @@ -0,0 +1,1714 @@ +/* This file defines standard ELF types, structures, and macros. + Copyright (C) 1995, 1996, 1997, 1998, 1999 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Ian Lance Taylor . + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef _ELF_H +#define _ELF_H 1 + +#ifndef _WIN32 +#else +#ifndef __int8_t_defined +#define __int8_t_defined +typedef signed char int8_t; +typedef short int int16_t; +typedef int int32_t; +typedef long long int int64_t; +#endif + +typedef unsigned char uint8_t; +typedef unsigned short int uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long int uint64_t; +#endif + +/* Standard ELF types. */ + +/* Type for a 16-bit quantity. */ +typedef uint16_t Elf32_Half; +typedef uint16_t Elf64_Half; + +/* Types for signed and unsigned 32-bit quantities. */ +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf64_Word; +typedef int32_t Elf64_Sword; + +/* Types for signed and unsigned 64-bit quantities. */ +typedef uint64_t Elf32_Xword; +typedef int64_t Elf32_Sxword; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; + +/* Type of addresses. */ +typedef uint32_t Elf32_Addr; +typedef uint64_t Elf64_Addr; + +/* Type of file offsets. */ +typedef uint32_t Elf32_Off; +typedef uint64_t Elf64_Off; + +/* Type for section indices, which are 16-bit quantities. */ +typedef uint16_t Elf32_Section; +typedef uint16_t Elf64_Section; + +/* Type of symbol indices. */ +typedef uint32_t Elf32_Symndx; +typedef uint64_t Elf64_Symndx; + + +/* The ELF file header. This appears at the start of every ELF file. */ + +#define EI_NIDENT (16) + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ +} Elf32_Ehdr; + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf64_Half e_type; /* Object file type */ + Elf64_Half e_machine; /* Architecture */ + Elf64_Word e_version; /* Object file version */ + Elf64_Addr e_entry; /* Entry point virtual address */ + Elf64_Off e_phoff; /* Program header table file offset */ + Elf64_Off e_shoff; /* Section header table file offset */ + Elf64_Word e_flags; /* Processor-specific flags */ + Elf64_Half e_ehsize; /* ELF header size in bytes */ + Elf64_Half e_phentsize; /* Program header table entry size */ + Elf64_Half e_phnum; /* Program header table entry count */ + Elf64_Half e_shentsize; /* Section header table entry size */ + Elf64_Half e_shnum; /* Section header table entry count */ + Elf64_Half e_shstrndx; /* Section header string table index */ +} Elf64_Ehdr; + +/* Fields in the e_ident array. The EI_* macros are indices into the + array. The macros under each EI_* macro are the values the byte + may have. */ + +#define EI_MAG0 0 /* File identification byte 0 index */ +#define ELFMAG0 0x7f /* Magic number byte 0 */ + +#define EI_MAG1 1 /* File identification byte 1 index */ +#define ELFMAG1 'E' /* Magic number byte 1 */ + +#define EI_MAG2 2 /* File identification byte 2 index */ +#define ELFMAG2 'L' /* Magic number byte 2 */ + +#define EI_MAG3 3 /* File identification byte 3 index */ +#define ELFMAG3 'F' /* Magic number byte 3 */ + +/* Conglomeration of the identification bytes, for easy testing as a word. */ +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASSNONE 0 /* Invalid class */ +#define ELFCLASS32 1 /* 32-bit objects */ +#define ELFCLASS64 2 /* 64-bit objects */ +#define ELFCLASSNUM 3 + +#define EI_DATA 5 /* Data encoding byte index */ +#define ELFDATANONE 0 /* Invalid data encoding */ +#define ELFDATA2LSB 1 /* 2's complement, little endian */ +#define ELFDATA2MSB 2 /* 2's complement, big endian */ +#define ELFDATANUM 3 + +#define EI_VERSION 6 /* File version byte index */ + /* Value must be EV_CURRENT */ + +#define EI_OSABI 7 /* OS ABI identification */ +#define ELFOSABI_SYSV 0 /* UNIX System V ABI */ +#define ELFOSABI_HPUX 1 /* HP-UX */ +#define ELFOSABI_FREEBSD 9 /* Free BSD */ +#define ELFOSABI_ARM 97 /* ARM */ +#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ + +#define EI_ABIVERSION 8 /* ABI version */ + +#define EI_PAD 9 /* Byte index of padding bytes */ + +/* Legal values for e_type (object file type). */ + +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* Relocatable file */ +#define ET_EXEC 2 /* Executable file */ +#define ET_DYN 3 /* Shared object file */ +#define ET_CORE 4 /* Core file */ +#define ET_NUM 5 /* Number of defined types */ +#define ET_LOPROC 0xff00 /* Processor-specific */ +#define ET_HIPROC 0xffff /* Processor-specific */ + +/* Legal values for e_machine (architecture). */ + +#define EM_NONE 0 /* No machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SUN SPARC */ +#define EM_386 3 /* Intel 80386 */ +#define EM_68K 4 /* Motorola m68k family */ +#define EM_88K 5 /* Motorola m88k family */ +#define EM_486 6 /* Intel 80486 */ +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS R3000 big-endian */ +#define EM_S370 9 /* Amdahl */ +#define EM_MIPS_RS4_BE 10 /* MIPS R4000 big-endian */ +#define EM_RS6000 11 /* RS6000 */ + +#define EM_PARISC 15 /* HPPA */ +#define EM_nCUBE 16 /* nCUBE */ +#define EM_VPP500 17 /* Fujitsu VPP500 */ +#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ +#define EM_960 19 /* Intel 80960 */ +#define EM_PPC 20 /* PowerPC */ + +#define EM_V800 36 /* NEC V800 series */ +#define EM_FR20 37 /* Fujitsu FR20 */ +#define EM_RH32 38 /* TRW RH32 */ +#define EM_RCE 39 /* Motorola RCE */ +#define EM_ARM 40 /* ARM */ +#define EM_FAKE_ALPHA 41 /* Digital Alpha */ +#define EM_SH 42 /* Hitachi SH */ +#define EM_SPARCV9 43 /* SPARC v9 64-bit */ +#define EM_TRICORE 44 /* Siemens Tricore */ +#define EM_ARC 45 /* Argonaut RISC Core */ +#define EM_H8_300 46 /* Hitachi H8/300 */ +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ +#define EM_H8_500 49 /* Hitachi H8/500 */ +#define EM_IA_64 50 /* Intel Merced */ +#define EM_MIPS_X 51 /* Stanford MIPS-X */ +#define EM_COLDFIRE 52 /* Motorola Coldfire */ +#define EM_68HC12 53 /* Motorola M68HC12 */ +#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ +#define EM_PCP 55 /* Siemens PCP */ +#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ +#define EM_NDR1 57 /* Denso NDR1 microprocessor */ +#define EM_STARCORE 58 /* Motorola Start*Core processor */ +#define EM_ME16 59 /* Toyota ME16 processor */ +#define EM_ST100 60 /* STMicroelectronic ST100 processor */ +#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ +#define EM_X86_64 62 /* AMD x86-64 architecture */ +#define EM_PDSP 63 /* Sony DSP Processor */ +#define EM_FX66 66 /* Siemens FX66 microcontroller */ +#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ +#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ +#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ +#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ +#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ +#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ +#define EM_SVX 73 /* Silicon Graphics SVx */ +#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ +#define EM_VAX 75 /* Digital VAX */ +#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ +#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ +#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ +#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ +#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ +#define EM_HUANY 81 /* Harvard University machine-independent object files */ +#define EM_PRISM 82 /* SiTera Prism */ +#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ +#define EM_FR30 84 /* Fujitsu FR30 */ +#define EM_D10V 85 /* Mitsubishi D10V */ +#define EM_D30V 86 /* Mitsubishi D30V */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Mitsubishi M32R */ +#define EM_MN10300 89 /* Matsushita MN10300 */ +#define EM_MN10200 90 /* Matsushita MN10200 */ +#define EM_PJ 91 /* picoJava */ +#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ +#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ +#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define EM_NUM 95 + +/* If it is necessary to assign new unofficial EM_* values, please + pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the + chances of collision with official or non-GNU unofficial values. */ + +#define EM_ALPHA 0x9026 +#define EM_C60 0x9c60 + +/* Legal values for e_version (version). */ + +#define EV_NONE 0 /* Invalid ELF version */ +#define EV_CURRENT 1 /* Current version */ +#define EV_NUM 2 + +/* Section header. */ + +typedef struct +{ + Elf32_Word sh_name; /* Section name (string tbl index) */ + Elf32_Word sh_type; /* Section type */ + Elf32_Word sh_flags; /* Section flags */ + Elf32_Addr sh_addr; /* Section virtual addr at execution */ + Elf32_Off sh_offset; /* Section file offset */ + Elf32_Word sh_size; /* Section size in bytes */ + Elf32_Word sh_link; /* Link to another section */ + Elf32_Word sh_info; /* Additional section information */ + Elf32_Word sh_addralign; /* Section alignment */ + Elf32_Word sh_entsize; /* Entry size if section holds table */ +} Elf32_Shdr; + +typedef struct +{ + Elf64_Word sh_name; /* Section name (string tbl index) */ + Elf64_Word sh_type; /* Section type */ + Elf64_Xword sh_flags; /* Section flags */ + Elf64_Addr sh_addr; /* Section virtual addr at execution */ + Elf64_Off sh_offset; /* Section file offset */ + Elf64_Xword sh_size; /* Section size in bytes */ + Elf64_Word sh_link; /* Link to another section */ + Elf64_Word sh_info; /* Additional section information */ + Elf64_Xword sh_addralign; /* Section alignment */ + Elf64_Xword sh_entsize; /* Entry size if section holds table */ +} Elf64_Shdr; + +/* Special section indices. */ + +#define SHN_UNDEF 0 /* Undefined section */ +#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ +#define SHN_LOPROC 0xff00 /* Start of processor-specific */ +#define SHN_HIPROC 0xff1f /* End of processor-specific */ +#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ +#define SHN_COMMON 0xfff2 /* Associated symbol is common */ +#define SHN_HIRESERVE 0xffff /* End of reserved indices */ + +/* Legal values for sh_type (section type). */ + +#define SHT_NULL 0 /* Section header table entry unused */ +#define SHT_PROGBITS 1 /* Program data */ +#define SHT_SYMTAB 2 /* Symbol table */ +#define SHT_STRTAB 3 /* String table */ +#define SHT_RELA 4 /* Relocation entries with addends */ +#define SHT_HASH 5 /* Symbol hash table */ +#define SHT_DYNAMIC 6 /* Dynamic linking information */ +#define SHT_NOTE 7 /* Notes */ +#define SHT_NOBITS 8 /* Program space with no data (bss) */ +#define SHT_REL 9 /* Relocation entries, no addends */ +#define SHT_SHLIB 10 /* Reserved */ +#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ +#define SHT_NUM 12 /* Number of defined types. */ +#define SHT_LOOS 0x60000000 /* Start OS-specific */ +#define SHT_LOSUNW 0x6ffffffb /* Sun-specific low bound. */ +#define SHT_SUNW_COMDAT 0x6ffffffb +#define SHT_SUNW_syminfo 0x6ffffffc +#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ +#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ +#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ +#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ +#define SHT_HIOS 0x6fffffff /* End OS-specific type */ +#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ +#define SHT_ARM_EXIDX 0x70000001 /* Exception Index table */ +#define SHT_ARM_PREEMPTMAP 0x70000002 /* dynamic linking pre-emption map */ +#define SHT_ARM_ATTRIBUTES 0x70000003 /* Object file compatibility attrs */ +#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ +#define SHT_LOUSER 0x80000000 /* Start of application-specific */ +#define SHT_HIUSER 0x8fffffff /* End of application-specific */ + +/* Legal values for sh_flags (section flags). */ + +#define SHF_WRITE (1 << 0) /* Writable */ +#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ +#define SHF_EXECINSTR (1 << 2) /* Executable */ +#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ + +/* Symbol table entry. */ + +typedef struct +{ + Elf32_Word st_name; /* Symbol name (string tbl index) */ + Elf32_Addr st_value; /* Symbol value */ + Elf32_Word st_size; /* Symbol size */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* No defined meaning, 0 */ + Elf32_Section st_shndx; /* Section index */ +} Elf32_Sym; + +typedef struct +{ + Elf64_Word st_name; /* Symbol name (string tbl index) */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* No defined meaning, 0 */ + Elf64_Section st_shndx; /* Section index */ + Elf64_Addr st_value; /* Symbol value */ + Elf64_Xword st_size; /* Symbol size */ +} Elf64_Sym; + +/* The syminfo section if available contains additional information about + every dynamic symbol. */ + +typedef struct +{ + Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf32_Half si_flags; /* Per symbol flags */ +} Elf32_Syminfo; + +typedef struct +{ + Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf64_Half si_flags; /* Per symbol flags */ +} Elf64_Syminfo; + +/* Possible values for si_boundto. */ +#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ +#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ +#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ + +/* Possible bitmasks for si_flags. */ +#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ +#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ +#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ +#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy + loaded */ +/* Syminfo version values. */ +#define SYMINFO_NONE 0 +#define SYMINFO_CURRENT 1 +#define SYMINFO_NUM 2 + + +/* Special section index. */ +#undef SHN_UNDEF +#define SHN_UNDEF 0 /* No section, undefined symbol. */ + +/* How to extract and insert information held in the st_info field. */ + +#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) +#define ELF32_ST_TYPE(val) ((val) & 0xf) +#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) + +/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ +#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) +#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) +#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) + +/* Legal values for ST_BIND subfield of st_info (symbol binding). */ + +#define STB_LOCAL 0 /* Local symbol */ +#define STB_GLOBAL 1 /* Global symbol */ +#define STB_WEAK 2 /* Weak symbol */ +#define STB_NUM 3 /* Number of defined types. */ +#define STB_LOOS 10 /* Start of OS-specific */ +#define STB_HIOS 12 /* End of OS-specific */ +#define STB_LOPROC 13 /* Start of processor-specific */ +#define STB_HIPROC 15 /* End of processor-specific */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_NOTYPE 0 /* Symbol type is unspecified */ +#define STT_OBJECT 1 /* Symbol is a data object */ +#define STT_FUNC 2 /* Symbol is a code object */ +#define STT_SECTION 3 /* Symbol associated with a section */ +#define STT_FILE 4 /* Symbol's name is file name */ +#define STT_NUM 5 /* Number of defined types. */ +#define STT_LOOS 11 /* Start of OS-specific */ +#define STT_HIOS 12 /* End of OS-specific */ +#define STT_LOPROC 13 /* Start of processor-specific */ +#define STT_HIPROC 15 /* End of processor-specific */ + + +/* Symbol table indices are found in the hash buckets and chain table + of a symbol hash table section. This special index value indicates + the end of a chain, meaning no further symbols are found in that bucket. */ + +#define STN_UNDEF 0 /* End of a chain. */ + + +/* How to extract and insert information held in the st_other field. */ + +#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) + +/* For ELF64 the definitions are the same. */ +#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) + +/* Symbol visibility specification encoded in the st_other field. */ +#define STV_DEFAULT 0 /* Default symbol visibility rules */ +#define STV_INTERNAL 1 /* Processor specific hidden class */ +#define STV_HIDDEN 2 /* Sym unavailable in other modules */ +#define STV_PROTECTED 3 /* Not preemptible, not exported */ + + +/* Relocation table entry without addend (in section of type SHT_REL). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ +} Elf32_Rel; + +/* I have seen two different definitions of the Elf64_Rel and + Elf64_Rela structures, so we'll leave them out until Novell (or + whoever) gets their act together. */ +/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ +} Elf64_Rel; + +/* Relocation table entry with addend (in section of type SHT_RELA). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ + Elf32_Sword r_addend; /* Addend */ +} Elf32_Rela; + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ + Elf64_Sxword r_addend; /* Addend */ +} Elf64_Rela; + +/* How to extract and insert information held in the r_info field. */ + +#define ELF32_R_SYM(val) ((val) >> 8) +#define ELF32_R_TYPE(val) ((val) & 0xff) +#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i) & 0xffffffff) +#define ELF64_R_INFO(sym,type) ((((Elf64_Xword)(sym)) << 32) + (type)) + +/* Program segment header. */ + +typedef struct +{ + Elf32_Word p_type; /* Segment type */ + Elf32_Off p_offset; /* Segment file offset */ + Elf32_Addr p_vaddr; /* Segment virtual address */ + Elf32_Addr p_paddr; /* Segment physical address */ + Elf32_Word p_filesz; /* Segment size in file */ + Elf32_Word p_memsz; /* Segment size in memory */ + Elf32_Word p_flags; /* Segment flags */ + Elf32_Word p_align; /* Segment alignment */ +} Elf32_Phdr; + +typedef struct +{ + Elf64_Word p_type; /* Segment type */ + Elf64_Word p_flags; /* Segment flags */ + Elf64_Off p_offset; /* Segment file offset */ + Elf64_Addr p_vaddr; /* Segment virtual address */ + Elf64_Addr p_paddr; /* Segment physical address */ + Elf64_Xword p_filesz; /* Segment size in file */ + Elf64_Xword p_memsz; /* Segment size in memory */ + Elf64_Xword p_align; /* Segment alignment */ +} Elf64_Phdr; + +/* Legal values for p_type (segment type). */ + +#define PT_NULL 0 /* Program header table entry unused */ +#define PT_LOAD 1 /* Loadable program segment */ +#define PT_DYNAMIC 2 /* Dynamic linking information */ +#define PT_INTERP 3 /* Program interpreter */ +#define PT_NOTE 4 /* Auxiliary information */ +#define PT_SHLIB 5 /* Reserved */ +#define PT_PHDR 6 /* Entry for header table itself */ +#define PT_NUM 7 /* Number of defined types. */ +#define PT_LOOS 0x60000000 /* Start of OS-specific */ +#define PT_HIOS 0x6fffffff /* End of OS-specific */ +#define PT_LOPROC 0x70000000 /* Start of processor-specific */ +#define PT_HIPROC 0x7fffffff /* End of processor-specific */ + +/* Legal values for p_flags (segment flags). */ + +#define PF_X (1 << 0) /* Segment is executable */ +#define PF_W (1 << 1) /* Segment is writable */ +#define PF_R (1 << 2) /* Segment is readable */ +#define PF_MASKPROC 0xf0000000 /* Processor-specific */ + +/* Legal values for note segment descriptor types for core files. */ + +#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ +#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ +#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ +#define NT_PRXREG 4 /* Contains copy of prxregset struct */ +#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ +#define NT_AUXV 6 /* Contains copy of auxv array */ +#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ +#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ +#define NT_PSINFO 13 /* Contains copy of psinfo struct */ +#define NT_PRCRED 14 /* Contains copy of prcred struct */ +#define NT_UTSNAME 15 /* Contains copy of utsname struct */ +#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ +#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ + +/* Legal values for the note segment descriptor types for object files. */ + +#define NT_VERSION 1 /* Contains a version string. */ + + +/* Dynamic section entry. */ + +typedef struct +{ + Elf32_Sword d_tag; /* Dynamic entry type */ + union + { + Elf32_Word d_val; /* Integer value */ + Elf32_Addr d_ptr; /* Address value */ + } d_un; +} Elf32_Dyn; + +typedef struct +{ + Elf64_Sxword d_tag; /* Dynamic entry type */ + union + { + Elf64_Xword d_val; /* Integer value */ + Elf64_Addr d_ptr; /* Address value */ + } d_un; +} Elf64_Dyn; + +/* Legal values for d_tag (dynamic entry type). */ + +#define DT_NULL 0 /* Marks end of dynamic section */ +#define DT_NEEDED 1 /* Name of needed library */ +#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ +#define DT_PLTGOT 3 /* Processor defined value */ +#define DT_HASH 4 /* Address of symbol hash table */ +#define DT_STRTAB 5 /* Address of string table */ +#define DT_SYMTAB 6 /* Address of symbol table */ +#define DT_RELA 7 /* Address of Rela relocs */ +#define DT_RELASZ 8 /* Total size of Rela relocs */ +#define DT_RELAENT 9 /* Size of one Rela reloc */ +#define DT_STRSZ 10 /* Size of string table */ +#define DT_SYMENT 11 /* Size of one symbol table entry */ +#define DT_INIT 12 /* Address of init function */ +#define DT_FINI 13 /* Address of termination function */ +#define DT_SONAME 14 /* Name of shared object */ +#define DT_RPATH 15 /* Library search path */ +#define DT_SYMBOLIC 16 /* Start symbol search here */ +#define DT_REL 17 /* Address of Rel relocs */ +#define DT_RELSZ 18 /* Total size of Rel relocs */ +#define DT_RELENT 19 /* Size of one Rel reloc */ +#define DT_PLTREL 20 /* Type of reloc in PLT */ +#define DT_DEBUG 21 /* For debugging; unspecified */ +#define DT_TEXTREL 22 /* Reloc might modify .text */ +#define DT_JMPREL 23 /* Address of PLT relocs */ +#define DT_BIND_NOW 24 /* Process relocations of object */ +#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ +#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ +#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ +#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ +#define DT_NUM 29 /* Number used */ +#define DT_LOOS 0x60000000 /* Start of OS-specific */ +#define DT_HIOS 0x6fffffff /* End of OS-specific */ +#define DT_LOPROC 0x70000000 /* Start of processor-specific */ +#define DT_HIPROC 0x7fffffff /* End of processor-specific */ +#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ + +/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the + Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's + approach. */ +#define DT_VALRNGLO 0x6ffffd00 +#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting + the following DT_* entry. */ +#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ +#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ +#define DT_VALRNGHI 0x6ffffdff + +/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the + Dyn.d_un.d_ptr field of the Elf*_Dyn structure. + + If any adjustment is made to the ELF object after it has been + built these entries will need to be adjusted. */ +#define DT_ADDRRNGLO 0x6ffffe00 +#define DT_SYMINFO 0x6ffffeff /* syminfo table */ +#define DT_ADDRRNGHI 0x6ffffeff + +/* The versioning entry types. The next are defined as part of the + GNU extension. */ +#define DT_VERSYM 0x6ffffff0 + +/* These were chosen by Sun. */ +#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ +#define DT_VERDEF 0x6ffffffc /* Address of version definition + table */ +#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ +#define DT_VERNEED 0x6ffffffe /* Address of table with needed + versions */ +#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ +#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ +#define DT_VERSIONTAGNUM 16 + +/* Sun added these machine-independent extensions in the "processor-specific" + range. Be compatible. */ +#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ +#define DT_FILTER 0x7fffffff /* Shared object to get values from */ +#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) +#define DT_EXTRANUM 3 + +/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 + entry in the dynamic section. */ +#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ +#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ +#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ +#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ +#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ +#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ +#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ + +/* Version definition sections. */ + +typedef struct +{ + Elf32_Half vd_version; /* Version revision */ + Elf32_Half vd_flags; /* Version information */ + Elf32_Half vd_ndx; /* Version Index */ + Elf32_Half vd_cnt; /* Number of associated aux entries */ + Elf32_Word vd_hash; /* Version name hash value */ + Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf32_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf32_Verdef; + +typedef struct +{ + Elf64_Half vd_version; /* Version revision */ + Elf64_Half vd_flags; /* Version information */ + Elf64_Half vd_ndx; /* Version Index */ + Elf64_Half vd_cnt; /* Number of associated aux entries */ + Elf64_Word vd_hash; /* Version name hash value */ + Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf64_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf64_Verdef; + + +/* Legal values for vd_version (version revision). */ +#define VER_DEF_NONE 0 /* No version */ +#define VER_DEF_CURRENT 1 /* Current version */ +#define VER_DEF_NUM 2 /* Given version number */ + +/* Legal values for vd_flags (version information flags). */ +#define VER_FLG_BASE 0x1 /* Version definition of file itself */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + +/* Auxialiary version information. */ + +typedef struct +{ + Elf32_Word vda_name; /* Version or dependency names */ + Elf32_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf32_Verdaux; + +typedef struct +{ + Elf64_Word vda_name; /* Version or dependency names */ + Elf64_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf64_Verdaux; + + +/* Version dependency section. */ + +typedef struct +{ + Elf32_Half vn_version; /* Version of structure */ + Elf32_Half vn_cnt; /* Number of associated aux entries */ + Elf32_Word vn_file; /* Offset of filename for this + dependency */ + Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf32_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf32_Verneed; + +typedef struct +{ + Elf64_Half vn_version; /* Version of structure */ + Elf64_Half vn_cnt; /* Number of associated aux entries */ + Elf64_Word vn_file; /* Offset of filename for this + dependency */ + Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf64_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf64_Verneed; + + +/* Legal values for vn_version (version revision). */ +#define VER_NEED_NONE 0 /* No version */ +#define VER_NEED_CURRENT 1 /* Current version */ +#define VER_NEED_NUM 2 /* Given version number */ + +/* Auxiliary needed version information. */ + +typedef struct +{ + Elf32_Word vna_hash; /* Hash value of dependency name */ + Elf32_Half vna_flags; /* Dependency specific information */ + Elf32_Half vna_other; /* Unused */ + Elf32_Word vna_name; /* Dependency name string offset */ + Elf32_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf32_Vernaux; + +typedef struct +{ + Elf64_Word vna_hash; /* Hash value of dependency name */ + Elf64_Half vna_flags; /* Dependency specific information */ + Elf64_Half vna_other; /* Unused */ + Elf64_Word vna_name; /* Dependency name string offset */ + Elf64_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf64_Vernaux; + + +/* Legal values for vna_flags. */ +#undef VER_FLG_WEAK +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + + +/* Auxiliary vector. */ + +/* This vector is normally only used by the program interpreter. The + usual definition in an ABI supplement uses the name auxv_t. The + vector is not usually defined in a standard file, but it + can't hurt. We rename it to avoid conflicts. The sizes of these + types are an arrangement between the exec server and the program + interpreter, so we don't fully specify them here. */ + +typedef struct +{ + int a_type; /* Entry type */ + union + { + long int a_val; /* Integer value */ + void *a_ptr; /* Pointer value */ + void (*a_fcn) (void); /* Function pointer value */ + } a_un; +} Elf32_auxv_t; + +typedef struct +{ + long int a_type; /* Entry type */ + union + { + long int a_val; /* Integer value */ + void *a_ptr; /* Pointer value */ + void (*a_fcn) (void); /* Function pointer value */ + } a_un; +} Elf64_auxv_t; + +/* Legal values for a_type (entry type). */ + +#define AT_NULL 0 /* End of vector */ +#define AT_IGNORE 1 /* Entry should be ignored */ +#define AT_EXECFD 2 /* File descriptor of program */ +#define AT_PHDR 3 /* Program headers for program */ +#define AT_PHENT 4 /* Size of program header entry */ +#define AT_PHNUM 5 /* Number of program headers */ +#define AT_PAGESZ 6 /* System page size */ +#define AT_BASE 7 /* Base address of interpreter */ +#define AT_FLAGS 8 /* Flags */ +#define AT_ENTRY 9 /* Entry point of program */ +#define AT_NOTELF 10 /* Program is not ELF */ +#define AT_UID 11 /* Real uid */ +#define AT_EUID 12 /* Effective uid */ +#define AT_GID 13 /* Real gid */ +#define AT_EGID 14 /* Effective gid */ + +/* Some more special a_type values describing the hardware. */ +#define AT_PLATFORM 15 /* String identifying platform. */ +#define AT_HWCAP 16 /* Machine dependent hints about + processor capabilities. */ + +/* This entry gives some information about the FPU initialization + performed by the kernel. */ +#define AT_FPUCW 17 /* Used FPU control word. */ + + +/* Note section contents. Each entry in the note section begins with + a header of a fixed form. */ + +typedef struct +{ + Elf32_Word n_namesz; /* Length of the note's name. */ + Elf32_Word n_descsz; /* Length of the note's descriptor. */ + Elf32_Word n_type; /* Type of the note. */ +} Elf32_Nhdr; + +typedef struct +{ + Elf64_Word n_namesz; /* Length of the note's name. */ + Elf64_Word n_descsz; /* Length of the note's descriptor. */ + Elf64_Word n_type; /* Type of the note. */ +} Elf64_Nhdr; + +/* Known names of notes. */ + +/* Solaris entries in the note section have this name. */ +#define ELF_NOTE_SOLARIS "SUNW Solaris" + +/* Note entries for GNU systems have this name. */ +#define ELF_NOTE_GNU "GNU" + + +/* Defined types of notes for Solaris. */ + +/* Value of descriptor (one word) is desired pagesize for the binary. */ +#define ELF_NOTE_PAGESIZE_HINT 1 + + +/* Defined note types for GNU systems. */ + +/* ABI information. The descriptor consists of words: + word 0: OS descriptor + word 1: major version of the ABI + word 2: minor version of the ABI + word 3: subminor version of the ABI +*/ +#define ELF_NOTE_ABI 1 + +/* Known OSes. These value can appear in word 0 of an ELF_NOTE_ABI + note section entry. */ +#define ELF_NOTE_OS_LINUX 0 +#define ELF_NOTE_OS_GNU 1 +#define ELF_NOTE_OS_SOLARIS2 2 + + +/* Motorola 68k specific definitions. */ + +/* m68k relocs. */ + +#define R_68K_NONE 0 /* No reloc */ +#define R_68K_32 1 /* Direct 32 bit */ +#define R_68K_16 2 /* Direct 16 bit */ +#define R_68K_8 3 /* Direct 8 bit */ +#define R_68K_PC32 4 /* PC relative 32 bit */ +#define R_68K_PC16 5 /* PC relative 16 bit */ +#define R_68K_PC8 6 /* PC relative 8 bit */ +#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ +#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ +#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ +#define R_68K_GOT32O 10 /* 32 bit GOT offset */ +#define R_68K_GOT16O 11 /* 16 bit GOT offset */ +#define R_68K_GOT8O 12 /* 8 bit GOT offset */ +#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ +#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ +#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ +#define R_68K_PLT32O 16 /* 32 bit PLT offset */ +#define R_68K_PLT16O 17 /* 16 bit PLT offset */ +#define R_68K_PLT8O 18 /* 8 bit PLT offset */ +#define R_68K_COPY 19 /* Copy symbol at runtime */ +#define R_68K_GLOB_DAT 20 /* Create GOT entry */ +#define R_68K_JMP_SLOT 21 /* Create PLT entry */ +#define R_68K_RELATIVE 22 /* Adjust by program base */ +/* Keep this the last entry. */ +#define R_68K_NUM 23 + +/* Intel 80386 specific definitions. */ + +/* i386 relocs. */ + +#define R_386_NONE 0 /* No reloc */ +#define R_386_32 1 /* Direct 32 bit */ +#define R_386_PC32 2 /* PC relative 32 bit */ +#define R_386_GOT32 3 /* 32 bit GOT entry */ +#define R_386_PLT32 4 /* 32 bit PLT address */ +#define R_386_COPY 5 /* Copy symbol at runtime */ +#define R_386_GLOB_DAT 6 /* Create GOT entry */ +#define R_386_JMP_SLOT 7 /* Create PLT entry */ +#define R_386_RELATIVE 8 /* Adjust by program base */ +#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ +#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ +/* Keep this the last entry. */ +#define R_386_NUM 11 + +/* SUN SPARC specific definitions. */ + +/* Values for Elf64_Ehdr.e_flags. */ + +#define EF_SPARCV9_MM 3 +#define EF_SPARCV9_TSO 0 +#define EF_SPARCV9_PSO 1 +#define EF_SPARCV9_RMO 2 +#define EF_SPARC_EXT_MASK 0xFFFF00 +#define EF_SPARC_SUN_US1 0x000200 +#define EF_SPARC_HAL_R1 0x000400 + +/* SPARC relocs. */ + +#define R_SPARC_NONE 0 /* No reloc */ +#define R_SPARC_8 1 /* Direct 8 bit */ +#define R_SPARC_16 2 /* Direct 16 bit */ +#define R_SPARC_32 3 /* Direct 32 bit */ +#define R_SPARC_DISP8 4 /* PC relative 8 bit */ +#define R_SPARC_DISP16 5 /* PC relative 16 bit */ +#define R_SPARC_DISP32 6 /* PC relative 32 bit */ +#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ +#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ +#define R_SPARC_HI22 9 /* High 22 bit */ +#define R_SPARC_22 10 /* Direct 22 bit */ +#define R_SPARC_13 11 /* Direct 13 bit */ +#define R_SPARC_LO10 12 /* Truncated 10 bit */ +#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ +#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ +#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ +#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ +#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ +#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ +#define R_SPARC_COPY 19 /* Copy symbol at runtime */ +#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ +#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ +#define R_SPARC_RELATIVE 22 /* Adjust by program base */ +#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ + +/* Additional Sparc64 relocs. */ + +#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ +#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ +#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ +#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ +#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ +#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ +#define R_SPARC_10 30 /* Direct 10 bit */ +#define R_SPARC_11 31 /* Direct 11 bit */ +#define R_SPARC_64 32 /* Direct 64 bit */ +#define R_SPARC_OLO10 33 /* ?? */ +#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ +#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ +#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ +#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ +#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ +#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ +#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ +#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ +#define R_SPARC_7 43 /* Direct 7 bit */ +#define R_SPARC_5 44 /* Direct 5 bit */ +#define R_SPARC_6 45 /* Direct 6 bit */ +#define R_SPARC_DISP64 46 /* PC relative 64 bit */ +#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ +#define R_SPARC_HIX22 48 /* High 22 bit complemented */ +#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ +#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ +#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ +#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ +#define R_SPARC_REGISTER 53 /* Global register usage */ +#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ +#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ +/* Keep this the last entry. */ +#define R_SPARC_NUM 56 + +/* AMD x86-64 relocations. */ +#define R_X86_64_NONE 0 /* No reloc */ +#define R_X86_64_64 1 /* Direct 64 bit */ +#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ +#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ +#define R_X86_64_PLT32 4 /* 32 bit PLT address */ +#define R_X86_64_COPY 5 /* Copy symbol at runtime */ +#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ +#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ +#define R_X86_64_RELATIVE 8 /* Adjust by program base */ +#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative + offset to GOT */ +#define R_X86_64_32 10 /* Direct 32 bit zero extended */ +#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ +#define R_X86_64_16 12 /* Direct 16 bit zero extended */ +#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ +#define R_X86_64_8 14 /* Direct 8 bit sign extended */ +#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ +#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ +#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ +#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ +#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset + to two GOT entries for GD symbol */ +#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset + to two GOT entries for LD symbol */ +#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ +#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset + to GOT entry for IE symbol */ +#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ + +#define R_X86_64_NUM 24 + +/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ + +#define DT_SPARC_REGISTER 0x70000001 +#define DT_SPARC_NUM 2 + +/* Bits present in AT_HWCAP, primarily for Sparc32. */ + +#define HWCAP_SPARC_FLUSH 1 /* The cpu supports flush insn. */ +#define HWCAP_SPARC_STBAR 2 +#define HWCAP_SPARC_SWAP 4 +#define HWCAP_SPARC_MULDIV 8 +#define HWCAP_SPARC_V9 16 /* The cpu is v9, so v8plus is ok. */ + +/* MIPS R3000 specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ +#define EF_MIPS_PIC 2 /* Contains PIC code */ +#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ +#define EF_MIPS_XGOT 8 +#define EF_MIPS_64BIT_WHIRL 16 +#define EF_MIPS_ABI2 32 +#define EF_MIPS_ABI_ON32 64 +#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ + +/* Legal values for MIPS architecture level. */ + +#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ + +/* The following are non-official names and should not be used. */ + +#define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ + +/* Special section indices. */ + +#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ +#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ +#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ +#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ +#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ +#define SHT_MIPS_MSYM 0x70000001 +#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ +#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ +#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ +#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ +#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ +#define SHT_MIPS_PACKAGE 0x70000007 +#define SHT_MIPS_PACKSYM 0x70000008 +#define SHT_MIPS_RELD 0x70000009 +#define SHT_MIPS_IFACE 0x7000000b +#define SHT_MIPS_CONTENT 0x7000000c +#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ +#define SHT_MIPS_SHDR 0x70000010 +#define SHT_MIPS_FDESC 0x70000011 +#define SHT_MIPS_EXTSYM 0x70000012 +#define SHT_MIPS_DENSE 0x70000013 +#define SHT_MIPS_PDESC 0x70000014 +#define SHT_MIPS_LOCSYM 0x70000015 +#define SHT_MIPS_AUXSYM 0x70000016 +#define SHT_MIPS_OPTSYM 0x70000017 +#define SHT_MIPS_LOCSTR 0x70000018 +#define SHT_MIPS_LINE 0x70000019 +#define SHT_MIPS_RFDESC 0x7000001a +#define SHT_MIPS_DELTASYM 0x7000001b +#define SHT_MIPS_DELTAINST 0x7000001c +#define SHT_MIPS_DELTACLASS 0x7000001d +#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ +#define SHT_MIPS_DELTADECL 0x7000001f +#define SHT_MIPS_SYMBOL_LIB 0x70000020 +#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ +#define SHT_MIPS_TRANSLATE 0x70000022 +#define SHT_MIPS_PIXIE 0x70000023 +#define SHT_MIPS_XLATE 0x70000024 +#define SHT_MIPS_XLATE_DEBUG 0x70000025 +#define SHT_MIPS_WHIRL 0x70000026 +#define SHT_MIPS_EH_REGION 0x70000027 +#define SHT_MIPS_XLATE_OLD 0x70000028 +#define SHT_MIPS_PDR_EXCEPTION 0x70000029 + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ +#define SHF_MIPS_MERGE 0x20000000 +#define SHF_MIPS_ADDR 0x40000000 +#define SHF_MIPS_STRINGS 0x80000000 +#define SHF_MIPS_NOSTRIP 0x08000000 +#define SHF_MIPS_LOCAL 0x04000000 +#define SHF_MIPS_NAMES 0x02000000 +#define SHF_MIPS_NODUPE 0x01000000 + + +/* Symbol tables. */ + +/* MIPS specific values for `st_other'. */ +#define STO_MIPS_DEFAULT 0x0 +#define STO_MIPS_INTERNAL 0x1 +#define STO_MIPS_HIDDEN 0x2 +#define STO_MIPS_PROTECTED 0x3 +#define STO_MIPS_SC_ALIGN_UNUSED 0xff + +/* MIPS specific values for `st_info'. */ +#define STB_MIPS_SPLIT_COMMON 13 + +/* Entries found in sections of type SHT_MIPS_GPTAB. */ + +typedef union +{ + struct + { + Elf32_Word gt_current_g_value; /* -G value used for compilation */ + Elf32_Word gt_unused; /* Not used */ + } gt_header; /* First entry in section */ + struct + { + Elf32_Word gt_g_value; /* If this value were used for -G */ + Elf32_Word gt_bytes; /* This many bytes would be used */ + } gt_entry; /* Subsequent entries in section */ +} Elf32_gptab; + +/* Entry found in sections of type SHT_MIPS_REGINFO. */ + +typedef struct +{ + Elf32_Word ri_gprmask; /* General registers used */ + Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ + Elf32_Sword ri_gp_value; /* $gp register value */ +} Elf32_RegInfo; + +/* Entries found in sections of type SHT_MIPS_OPTIONS. */ + +typedef struct +{ + unsigned char kind; /* Determines interpretation of the + variable part of descriptor. */ + unsigned char size; /* Size of descriptor, including header. */ + Elf32_Section section; /* Section header index of section affected, + 0 for global options. */ + Elf32_Word info; /* Kind-specific information. */ +} Elf_Options; + +/* Values for `kind' field in Elf_Options. */ + +#define ODK_NULL 0 /* Undefined. */ +#define ODK_REGINFO 1 /* Register usage information. */ +#define ODK_EXCEPTIONS 2 /* Exception processing options. */ +#define ODK_PAD 3 /* Section padding options. */ +#define ODK_HWPATCH 4 /* Hardware workarounds performed */ +#define ODK_FILL 5 /* record the fill value used by the linker. */ +#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ +#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ +#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ + +/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ + +#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ +#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ +#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ +#define OEX_SMM 0x20000 /* Force sequential memory mode? */ +#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ +#define OEX_PRECISEFP OEX_FPDBUG +#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ + +#define OEX_FPU_INVAL 0x10 +#define OEX_FPU_DIV0 0x08 +#define OEX_FPU_OFLO 0x04 +#define OEX_FPU_UFLO 0x02 +#define OEX_FPU_INEX 0x01 + +/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ + +#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ +#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ +#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ +#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ + +#define OPAD_PREFIX 0x1 +#define OPAD_POSTFIX 0x2 +#define OPAD_SYMBOL 0x4 + +/* Entry found in `.options' section. */ + +typedef struct +{ + Elf32_Word hwp_flags1; /* Extra flags. */ + Elf32_Word hwp_flags2; /* Extra flags. */ +} Elf_Options_Hw; + +/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ + +#define OHWA0_R4KEOP_CHECKED 0x00000001 +#define OHWA1_R4KEOP_CLEAN 0x00000002 + +/* MIPS relocs. */ + +#define R_MIPS_NONE 0 /* No reloc */ +#define R_MIPS_16 1 /* Direct 16 bit */ +#define R_MIPS_32 2 /* Direct 32 bit */ +#define R_MIPS_REL32 3 /* PC relative 32 bit */ +#define R_MIPS_26 4 /* Direct 26 bit shifted */ +#define R_MIPS_HI16 5 /* High 16 bit */ +#define R_MIPS_LO16 6 /* Low 16 bit */ +#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ +#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ +#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ +#define R_MIPS_PC16 10 /* PC relative 16 bit */ +#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ +#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ + +#define R_MIPS_SHIFT5 16 +#define R_MIPS_SHIFT6 17 +#define R_MIPS_64 18 +#define R_MIPS_GOT_DISP 19 +#define R_MIPS_GOT_PAGE 20 +#define R_MIPS_GOT_OFST 21 +#define R_MIPS_GOT_HI16 22 +#define R_MIPS_GOT_LO16 23 +#define R_MIPS_SUB 24 +#define R_MIPS_INSERT_A 25 +#define R_MIPS_INSERT_B 26 +#define R_MIPS_DELETE 27 +#define R_MIPS_HIGHER 28 +#define R_MIPS_HIGHEST 29 +#define R_MIPS_CALL_HI16 30 +#define R_MIPS_CALL_LO16 31 +#define R_MIPS_SCN_DISP 32 +#define R_MIPS_REL16 33 +#define R_MIPS_ADD_IMMEDIATE 34 +#define R_MIPS_PJUMP 35 +#define R_MIPS_RELGOT 36 +#define R_MIPS_JALR 37 +/* Keep this the last entry. */ +#define R_MIPS_NUM 38 + +/* Legal values for p_type field of Elf32_Phdr. */ + +#define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ +#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ +#define PT_MIPS_OPTIONS 0x70000002 + +/* Special program header types. */ + +#define PF_MIPS_LOCAL 0x10000000 + +/* Legal values for d_tag field of Elf32_Dyn. */ + +#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ +#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ +#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ +#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ +#define DT_MIPS_FLAGS 0x70000005 /* Flags */ +#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ +#define DT_MIPS_MSYM 0x70000007 +#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ +#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ +#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ +#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ +#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ +#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ +#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ +#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ +#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ +#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ +#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ +#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in + DT_MIPS_DELTA_CLASS. */ +#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ +#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in + DT_MIPS_DELTA_INSTANCE. */ +#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ +#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in + DT_MIPS_DELTA_RELOC. */ +#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta + relocations refer to. */ +#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in + DT_MIPS_DELTA_SYM. */ +#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the + class declaration. */ +#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in + DT_MIPS_DELTA_CLASSSYM. */ +#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ +#define DT_MIPS_PIXIE_INIT 0x70000023 +#define DT_MIPS_SYMBOL_LIB 0x70000024 +#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 +#define DT_MIPS_LOCAL_GOTIDX 0x70000026 +#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 +#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 +#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ +#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ +#define DT_MIPS_DYNSTR_ALIGN 0x7000002b +#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ +#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve + function stored in GOT. */ +#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added + by rld on dlopen() calls. */ +#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ +#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ +#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ +#define DT_MIPS_NUM 0x32 + +/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ + +#define RHF_NONE 0 /* No flags */ +#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ +#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ +#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ +#define RHF_NO_MOVE (1 << 3) +#define RHF_SGI_ONLY (1 << 4) +#define RHF_GUARANTEE_INIT (1 << 5) +#define RHF_DELTA_C_PLUS_PLUS (1 << 6) +#define RHF_GUARANTEE_START_INIT (1 << 7) +#define RHF_PIXIE (1 << 8) +#define RHF_DEFAULT_DELAY_LOAD (1 << 9) +#define RHF_REQUICKSTART (1 << 10) +#define RHF_REQUICKSTARTED (1 << 11) +#define RHF_CORD (1 << 12) +#define RHF_NO_UNRES_UNDEF (1 << 13) +#define RHF_RLD_ORDER_SAFE (1 << 14) + +/* Entries found in sections of type SHT_MIPS_LIBLIST. */ + +typedef struct +{ + Elf32_Word l_name; /* Name (string table index) */ + Elf32_Word l_time_stamp; /* Timestamp */ + Elf32_Word l_checksum; /* Checksum */ + Elf32_Word l_version; /* Interface version */ + Elf32_Word l_flags; /* Flags */ +} Elf32_Lib; + +typedef struct +{ + Elf64_Word l_name; /* Name (string table index) */ + Elf64_Word l_time_stamp; /* Timestamp */ + Elf64_Word l_checksum; /* Checksum */ + Elf64_Word l_version; /* Interface version */ + Elf64_Word l_flags; /* Flags */ +} Elf64_Lib; + + +/* Legal values for l_flags. */ + +#define LL_NONE 0 +#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ +#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ +#define LL_REQUIRE_MINOR (1 << 2) +#define LL_EXPORTS (1 << 3) +#define LL_DELAY_LOAD (1 << 4) +#define LL_DELTA (1 << 5) + +/* Entries found in sections of type SHT_MIPS_CONFLICT. */ + +typedef Elf32_Addr Elf32_Conflict; + + +/* HPPA specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_PARISC_TRAPNL 1 /* Trap nil pointer dereference. */ +#define EF_PARISC_EXT 2 /* Program uses arch. extensions. */ +#define EF_PARISC_ARCH 0xffff0000 /* Architecture version. */ +/* Defined values are: + 0x020b PA-RISC 1.0 big-endian + 0x0210 PA-RISC 1.1 big-endian + 0x028b PA-RISC 1.0 little-endian + 0x0290 PA-RISC 1.1 little-endian +*/ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_PARISC_GOT 0x70000000 /* GOT for external data. */ +#define SHT_PARISC_ARCH 0x70000001 /* Architecture extensions. */ +#define SHT_PARISC_GLOBAL 0x70000002 /* Definition of $global$. */ +#define SHT_PARISC_MILLI 0x70000003 /* Millicode routines. */ +#define SHT_PARISC_UNWIND 0x70000004 /* Unwind information. */ +#define SHT_PARISC_PLT 0x70000005 /* Procedure linkage table. */ +#define SHT_PARISC_SDATA 0x70000006 /* Short initialized data. */ +#define SHT_PARISC_SBSS 0x70000007 /* Short uninitialized data. */ +#define SHT_PARISC_SYMEXTN 0x70000008 /* Argument/relocation info. */ +#define SHT_PARISC_STUBS 0x70000009 /* Linker stubs. */ + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_PARISC_GLOBAL 0x10000000 /* Section defines dp. */ +#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ + +/* HPPA relocs. */ + +#define R_PARISC_NONE 0 /* No reloc. */ +#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ +#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ +#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ +#define R_PARISC_DIR14R 4 /* Right 14 bits of eff. address. */ +#define R_PARISC_PCREL21L 5 /* PC-relative, left 21 bits. */ +#define R_PARISC_PCREL14R 6 /* PC-relative, right 14 bits. */ +#define R_PARISC_PCREL17C 7 /* Conditional PC-relative, ignore + if displacement > 17bits. */ +#define R_PARISC_PCREL17F 8 /* Conditional PC-relative, must + fit in 17bits. */ +#define R_PARISC_DPREL21L 9 /* DP-relative, left 21 bits. */ +#define R_PARISC_DPREL14R 10 /* DP-relative, right 14 bits. */ +#define R_PARISC_DPREL14F 11 /* DP-relative, must bit in 14 bits. */ +#define R_PARISC_DLTREL21L 12 /* DLT-relative, left 21 bits. */ +#define R_PARISC_DLTREL14R 13 /* DLT-relative, right 14 bits. */ +#define R_PARISC_DLTREL14F 14 /* DLT-relative, must fit in 14 bits.*/ +#define R_PARISC_DLTIND21L 15 /* DLT-relative indirect, left + 21 bits. */ +#define R_PARISC_DLTIND14R 16 /* DLT-relative indirect, right + 14 bits. */ +#define R_PARISC_DLTIND14F 17 /* DLT-relative indirect, must fit + int 14 bits. */ +#define R_PARISC_PLABEL32 18 /* Direct 32-bit reference to proc. */ + +/* Alpha specific definitions. */ + +/* Legal values for e_flags field of Elf64_Ehdr. */ + +#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ +#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ + +/* Legal values for sh_type field of Elf64_Shdr. */ + +/* These two are primerily concerned with ECOFF debugging info. */ +#define SHT_ALPHA_DEBUG 0x70000001 +#define SHT_ALPHA_REGINFO 0x70000002 + +/* Legal values for sh_flags field of Elf64_Shdr. */ + +#define SHF_ALPHA_GPREL 0x10000000 + +/* Legal values for st_other field of Elf64_Sym. */ +#define STO_ALPHA_NOPV 0x80 /* No PV required. */ +#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ + +/* Alpha relocs. */ + +#define R_ALPHA_NONE 0 /* No reloc */ +#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ +#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ +#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ +#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ +#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ +#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ +#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ +#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ +#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ +#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ +#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ +#define R_ALPHA_OP_PUSH 12 /* OP stack push */ +#define R_ALPHA_OP_STORE 13 /* OP stack pop and store */ +#define R_ALPHA_OP_PSUB 14 /* OP stack subtract */ +#define R_ALPHA_OP_PRSHIFT 15 /* OP stack right shift */ +#define R_ALPHA_GPVALUE 16 +#define R_ALPHA_GPRELHIGH 17 +#define R_ALPHA_GPRELLOW 18 +#define R_ALPHA_IMMED_GP_16 19 +#define R_ALPHA_IMMED_GP_HI32 20 +#define R_ALPHA_IMMED_SCN_HI32 21 +#define R_ALPHA_IMMED_BR_HI32 22 +#define R_ALPHA_IMMED_LO32 23 +#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ +#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ +#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ +#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ +/* Keep this the last entry. */ +#define R_ALPHA_NUM 28 + + +/* PowerPC specific declarations */ + +/* PowerPC relocations defined by the ABIs */ +#define R_PPC_NONE 0 +#define R_PPC_ADDR32 1 /* 32bit absolute address */ +#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ +#define R_PPC_ADDR16 3 /* 16bit absolute address */ +#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ +#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ +#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ +#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ +#define R_PPC_ADDR14_BRTAKEN 8 +#define R_PPC_ADDR14_BRNTAKEN 9 +#define R_PPC_REL24 10 /* PC relative 26 bit */ +#define R_PPC_REL14 11 /* PC relative 16 bit */ +#define R_PPC_REL14_BRTAKEN 12 +#define R_PPC_REL14_BRNTAKEN 13 +#define R_PPC_GOT16 14 +#define R_PPC_GOT16_LO 15 +#define R_PPC_GOT16_HI 16 +#define R_PPC_GOT16_HA 17 +#define R_PPC_PLTREL24 18 +#define R_PPC_COPY 19 +#define R_PPC_GLOB_DAT 20 +#define R_PPC_JMP_SLOT 21 +#define R_PPC_RELATIVE 22 +#define R_PPC_LOCAL24PC 23 +#define R_PPC_UADDR32 24 +#define R_PPC_UADDR16 25 +#define R_PPC_REL32 26 +#define R_PPC_PLT32 27 +#define R_PPC_PLTREL32 28 +#define R_PPC_PLT16_LO 29 +#define R_PPC_PLT16_HI 30 +#define R_PPC_PLT16_HA 31 +#define R_PPC_SDAREL16 32 +#define R_PPC_SECTOFF 33 +#define R_PPC_SECTOFF_LO 34 +#define R_PPC_SECTOFF_HI 35 +#define R_PPC_SECTOFF_HA 36 +/* Keep this the last entry. */ +#define R_PPC_NUM 37 + +/* The remaining relocs are from the Embedded ELF ABI, and are not + in the SVR4 ELF ABI. */ +#define R_PPC_EMB_NADDR32 101 +#define R_PPC_EMB_NADDR16 102 +#define R_PPC_EMB_NADDR16_LO 103 +#define R_PPC_EMB_NADDR16_HI 104 +#define R_PPC_EMB_NADDR16_HA 105 +#define R_PPC_EMB_SDAI16 106 +#define R_PPC_EMB_SDA2I16 107 +#define R_PPC_EMB_SDA2REL 108 +#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ +#define R_PPC_EMB_MRKREF 110 +#define R_PPC_EMB_RELSEC16 111 +#define R_PPC_EMB_RELST_LO 112 +#define R_PPC_EMB_RELST_HI 113 +#define R_PPC_EMB_RELST_HA 114 +#define R_PPC_EMB_BIT_FLD 115 +#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ + +/* Diab tool relocations. */ +#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ +#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ +#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ +#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ +#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ +#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ + +/* This is a phony reloc to handle any old fashioned TOC16 references + that may still be in object files. */ +#define R_PPC_TOC16 255 + + +/* ARM specific declarations */ + +/* Processor specific flags for the ELF header e_flags field. */ +#define EF_ARM_RELEXEC 0x01 +#define EF_ARM_HASENTRY 0x02 +#define EF_ARM_INTERWORK 0x04 +#define EF_ARM_APCS_26 0x08 +#define EF_ARM_APCS_FLOAT 0x10 +#define EF_ARM_PIC 0x20 +#define EF_ALIGN8 0x40 /* 8-bit structure alignment is in use */ +#define EF_NEW_ABI 0x80 +#define EF_OLD_ABI 0x100 + +/* Additional symbol types for Thumb */ +#define STT_ARM_TFUNC 0xd + +/* ARM-specific values for sh_flags */ +#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ +#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined + in the input to a link step */ + +/* ARM-specific program header flags */ +#define PF_ARM_SB 0x10000000 /* Segment contains the location + addressed by the static base */ + +/* ARM relocs. */ +#define R_ARM_NONE 0 /* No reloc */ +#define R_ARM_PC24 1 /* PC relative 26 bit branch */ +#define R_ARM_ABS32 2 /* Direct 32 bit */ +#define R_ARM_REL32 3 /* PC relative 32 bit */ +#define R_ARM_PC13 4 +#define R_ARM_ABS16 5 /* Direct 16 bit */ +#define R_ARM_ABS12 6 /* Direct 12 bit */ +#define R_ARM_THM_ABS5 7 +#define R_ARM_ABS8 8 /* Direct 8 bit */ +#define R_ARM_SBREL32 9 +#define R_ARM_THM_PC22 10 +#define R_ARM_THM_PC8 11 +#define R_ARM_AMP_VCALL9 12 +#define R_ARM_SWI24 13 +#define R_ARM_THM_SWI8 14 +#define R_ARM_XPC25 15 +#define R_ARM_THM_XPC22 16 +#define R_ARM_COPY 20 /* Copy symbol at runtime */ +#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ +#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ +#define R_ARM_RELATIVE 23 /* Adjust by program base */ +#define R_ARM_GOTOFF32 24 /* 32 bit offset to GOT */ +#define R_ARM_BASE_PREL 25 /* 32 bit PC relative offset to GOT */ +#define R_ARM_GOT_BREL 26 /* 32 bit GOT entry */ +#define R_ARM_PLT32 27 /* 32 bit PLT address */ +#define R_ARM_CALL 28 +#define R_ARM_JUMP24 29 +#define R_ARM_PREL31 42 +#define R_ARM_GNU_VTENTRY 100 +#define R_ARM_GNU_VTINHERIT 101 +#define R_ARM_THM_PC11 102 /* thumb unconditional branch */ +#define R_ARM_THM_PC9 103 /* thumb conditional branch */ +#define R_ARM_RXPC25 249 +#define R_ARM_RSBREL32 250 +#define R_ARM_THM_RPC22 251 +#define R_ARM_RREL32 252 +#define R_ARM_RABS22 253 +#define R_ARM_RPC24 254 +#define R_ARM_RBASE 255 +/* Keep this the last entry. */ +#define R_ARM_NUM 256 + +/* TMS320C67xx specific declarations */ +/* XXX: no ELF standard yet */ + +/* TMS320C67xx relocs. */ +#define R_C60_32 1 +#define R_C60_GOT32 3 /* 32 bit GOT entry */ +#define R_C60_PLT32 4 /* 32 bit PLT address */ +#define R_C60_COPY 5 /* Copy symbol at runtime */ +#define R_C60_GLOB_DAT 6 /* Create GOT entry */ +#define R_C60_JMP_SLOT 7 /* Create PLT entry */ +#define R_C60_RELATIVE 8 /* Adjust by program base */ +#define R_C60_GOTOFF 9 /* 32 bit offset to GOT */ +#define R_C60_GOTPC 10 /* 32 bit PC relative offset to GOT */ + +#define R_C60HI16 0x55 // high 16 bit MVKH embedded +#define R_C60LO16 0x54 // low 16 bit MVKL embedded + +#ifdef TCC_TARGET_X86_64 +#define TCC_ELFCLASS ELFCLASS64 +#define ElfW(type) Elf##64##_##type +#define ELFW(type) ELF##64##_##type +#else +#define TCC_ELFCLASS ELFCLASS32 +#define ElfW(type) Elf##32##_##type +#define ELFW(type) ELF##32##_##type +#endif + +#endif /* elf.h */ diff --git a/05/tcc-0.9.25/errno.h b/05/tcc-0.9.25/errno.h new file mode 100644 index 0000000..426f351 --- /dev/null +++ b/05/tcc-0.9.25/errno.h @@ -0,0 +1,6 @@ +#ifndef _ERRNO_H +#define _ERRNO_H + +#include // we define all the relevant things here + +#endif // _ERRNO_H diff --git a/05/tcc-0.9.25/float.h b/05/tcc-0.9.25/float.h new file mode 100644 index 0000000..6ae607d --- /dev/null +++ b/05/tcc-0.9.25/float.h @@ -0,0 +1,34 @@ +#ifndef _FLOAT_H +#define _FLOAT_H + +#define DBL_DIG 15 +#define DBL_EPSILON 2.2204460492503131e-16 +#define DBL_MANT_DIG 53 +#define DBL_MAX 1.7976931348623157e+308 +#define DBL_MAX_10_EXP 308 +#define DBL_MAX_EXP 1024 +#define DBL_MIN 2.2250738585072027e-308 +#define DBL_MIN_10_EXP (-307) +#define DBL_MIN_EXP (-1021) +#define FLT_DIG 6 +#define FLT_EPSILON 1.19209290e-07 +#define FLT_MANT_DIG 24 +#define FLT_MAX 3.40282347e+38 +#define FLT_MAX_10_EXP +38 +#define FLT_MAX_EXP 128 +#define FLT_MIN 1.17549435e-38 +#define FLT_MIN_10_EXP (-37) +#define FLT_MIN_EXP (-125) +#define FLT_RADIX 2 +#define FLT_ROUNDS 1 +#define LDBL_DIG DBL_DIG +#define LDBL_EPSILON DBL_EPSILON +#define LDBL_MANT_DIG DBL_MANT_DIG +#define LDBL_MAX DBL_MAX +#define LDBL_MAX_10_EXP DBL_MAX_10_EXP +#define LDBL_MAX_EXP DBL_MAX_EXP +#define LDBL_MIN DBL_MIN +#define LDBL_MIN_10_EXP DBL_MIN_10_EXP +#define LDBL_MIN_EXP DBL_MIN_EXP + +#endif // _FLOAT_H diff --git a/05/tcc-0.9.25/i386-asm.c b/05/tcc-0.9.25/i386-asm.c new file mode 100644 index 0000000..21b28d7 --- /dev/null +++ b/05/tcc-0.9.25/i386-asm.c @@ -0,0 +1,1211 @@ +/* + * i386 specific functions for TCC assembler + * + * Copyright (c) 2001, 2002 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define MAX_OPERANDS 3 + +typedef struct ASMInstr { + uint16_t sym; + uint16_t opcode; + uint16_t instr_type; +#define OPC_JMP 0x01 /* jmp operand */ +#define OPC_B 0x02 /* only used zith OPC_WL */ +#define OPC_WL 0x04 /* accepts w, l or no suffix */ +#define OPC_BWL (OPC_B | OPC_WL) /* accepts b, w, l or no suffix */ +#define OPC_REG 0x08 /* register is added to opcode */ +#define OPC_MODRM 0x10 /* modrm encoding */ +#define OPC_FWAIT 0x20 /* add fwait opcode */ +#define OPC_TEST 0x40 /* test opcodes */ +#define OPC_SHIFT 0x80 /* shift opcodes */ +#define OPC_D16 0x0100 /* generate data16 prefix */ +#define OPC_ARITH 0x0200 /* arithmetic opcodes */ +#define OPC_SHORTJMP 0x0400 /* short jmp operand */ +#define OPC_FARITH 0x0800 /* FPU arithmetic opcodes */ +#define OPC_GROUP_SHIFT 13 + +/* in order to compress the operand type, we use specific operands and + we or only with EA */ +#define OPT_REG8 0 /* warning: value is hardcoded from TOK_ASM_xxx */ +#define OPT_REG16 1 /* warning: value is hardcoded from TOK_ASM_xxx */ +#define OPT_REG32 2 /* warning: value is hardcoded from TOK_ASM_xxx */ +#define OPT_MMX 3 /* warning: value is hardcoded from TOK_ASM_xxx */ +#define OPT_SSE 4 /* warning: value is hardcoded from TOK_ASM_xxx */ +#define OPT_CR 5 /* warning: value is hardcoded from TOK_ASM_xxx */ +#define OPT_TR 6 /* warning: value is hardcoded from TOK_ASM_xxx */ +#define OPT_DB 7 /* warning: value is hardcoded from TOK_ASM_xxx */ +#define OPT_SEG 8 +#define OPT_ST 9 +#define OPT_IM8 10 +#define OPT_IM8S 11 +#define OPT_IM16 12 +#define OPT_IM32 13 +#define OPT_EAX 14 /* %al, %ax or %eax register */ +#define OPT_ST0 15 /* %st(0) register */ +#define OPT_CL 16 /* %cl register */ +#define OPT_DX 17 /* %dx register */ +#define OPT_ADDR 18 /* OP_EA with only offset */ +#define OPT_INDIR 19 /* *(expr) */ + +/* composite types */ +#define OPT_COMPOSITE_FIRST 20 +#define OPT_IM 20 /* IM8 | IM16 | IM32 */ +#define OPT_REG 21 /* REG8 | REG16 | REG32 */ +#define OPT_REGW 22 /* REG16 | REG32 */ +#define OPT_IMW 23 /* IM16 | IM32 */ + +/* can be ored with any OPT_xxx */ +#define OPT_EA 0x80 + + uint8_t nb_ops; + uint8_t op_type[MAX_OPERANDS]; /* see OP_xxx */ +} ASMInstr; + +typedef struct Operand { + uint32_t type; +#define OP_REG8 (1 << OPT_REG8) +#define OP_REG16 (1 << OPT_REG16) +#define OP_REG32 (1 << OPT_REG32) +#define OP_MMX (1 << OPT_MMX) +#define OP_SSE (1 << OPT_SSE) +#define OP_CR (1 << OPT_CR) +#define OP_TR (1 << OPT_TR) +#define OP_DB (1 << OPT_DB) +#define OP_SEG (1 << OPT_SEG) +#define OP_ST (1 << OPT_ST) +#define OP_IM8 (1 << OPT_IM8) +#define OP_IM8S (1 << OPT_IM8S) +#define OP_IM16 (1 << OPT_IM16) +#define OP_IM32 (1 << OPT_IM32) +#define OP_EAX (1 << OPT_EAX) +#define OP_ST0 (1 << OPT_ST0) +#define OP_CL (1 << OPT_CL) +#define OP_DX (1 << OPT_DX) +#define OP_ADDR (1 << OPT_ADDR) +#define OP_INDIR (1 << OPT_INDIR) + +#define OP_EA 0x40000000 +#define OP_REG (OP_REG8 | OP_REG16 | OP_REG32) +#define OP_IM OP_IM32 + int8_t reg; /* register, -1 if none */ + int8_t reg2; /* second register, -1 if none */ + uint8_t shift; + ExprValue e; +} Operand; + +static const uint8_t reg_to_size[5] = { +/* + [OP_REG8] = 0, + [OP_REG16] = 1, + [OP_REG32] = 2, +*/ + 0, 0, 1, 0, 2 +}; + +#define WORD_PREFIX_OPCODE 0x66 + +#define NB_TEST_OPCODES 30 + +static const uint8_t test_bits[NB_TEST_OPCODES] = { + 0x00, /* o */ + 0x01, /* no */ + 0x02, /* b */ + 0x02, /* c */ + 0x02, /* nae */ + 0x03, /* nb */ + 0x03, /* nc */ + 0x03, /* ae */ + 0x04, /* e */ + 0x04, /* z */ + 0x05, /* ne */ + 0x05, /* nz */ + 0x06, /* be */ + 0x06, /* na */ + 0x07, /* nbe */ + 0x07, /* a */ + 0x08, /* s */ + 0x09, /* ns */ + 0x0a, /* p */ + 0x0a, /* pe */ + 0x0b, /* np */ + 0x0b, /* po */ + 0x0c, /* l */ + 0x0c, /* nge */ + 0x0d, /* nl */ + 0x0d, /* ge */ + 0x0e, /* le */ + 0x0e, /* ng */ + 0x0f, /* nle */ + 0x0f, /* g */ +}; + +static const uint8_t segment_prefixes[] = { + 0x26, /* es */ + 0x2e, /* cs */ + 0x36, /* ss */ + 0x3e, /* ds */ + 0x64, /* fs */ + 0x65 /* gs */ +}; + +static const ASMInstr asm_instrs[] = { +#define ALT(x) x +#define DEF_ASM_OP0(name, opcode) +#define DEF_ASM_OP0L(name, opcode, group, instr_type) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 0 }, +#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 1, { op0 }}, +#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 2, { op0, op1 }}, +#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) { TOK_ASM_ ## name, opcode, (instr_type | group << OPC_GROUP_SHIFT), 3, { op0, op1, op2 }}, +#include "i386-asm.h" + + /* last operation */ + { 0, }, +}; + +static const uint16_t op0_codes[] = { +#define ALT(x) +#define DEF_ASM_OP0(x, opcode) opcode, +#define DEF_ASM_OP0L(name, opcode, group, instr_type) +#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) +#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) +#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) +#include "i386-asm.h" +}; + +static inline int get_reg_shift(TCCState *s1) +{ + int shift, v; + + v = asm_int_expr(s1); + switch(v) { + case 1: + shift = 0; + break; + case 2: + shift = 1; + break; + case 4: + shift = 2; + break; + case 8: + shift = 3; + break; + default: + expect("1, 2, 4 or 8 constant"); + shift = 0; + break; + } + return shift; +} + +static int asm_parse_reg(void) +{ + int reg; + if (tok != '%') + goto error_32; + next(); + if (tok >= TOK_ASM_eax && tok <= TOK_ASM_edi) { + reg = tok - TOK_ASM_eax; + next(); + return reg; + } else { + error_32: + expect("32 bit register"); + return 0; + } +} + +static void parse_operand(TCCState *s1, Operand *op) +{ + ExprValue e; + int reg, indir; + const char *p; + + indir = 0; + if (tok == '*') { + next(); + indir = OP_INDIR; + } + + if (tok == '%') { + next(); + if (tok >= TOK_ASM_al && tok <= TOK_ASM_db7) { + reg = tok - TOK_ASM_al; + op->type = 1 << (reg >> 3); /* WARNING: do not change constant order */ + op->reg = reg & 7; + if ((op->type & OP_REG) && op->reg == TREG_EAX) + op->type |= OP_EAX; + else if (op->type == OP_REG8 && op->reg == TREG_ECX) + op->type |= OP_CL; + else if (op->type == OP_REG16 && op->reg == TREG_EDX) + op->type |= OP_DX; + } else if (tok >= TOK_ASM_dr0 && tok <= TOK_ASM_dr7) { + op->type = OP_DB; + op->reg = tok - TOK_ASM_dr0; + } else if (tok >= TOK_ASM_es && tok <= TOK_ASM_gs) { + op->type = OP_SEG; + op->reg = tok - TOK_ASM_es; + } else if (tok == TOK_ASM_st) { + op->type = OP_ST; + op->reg = 0; + next(); + if (tok == '(') { + next(); + if (tok != TOK_PPNUM) + goto reg_error; + p = tokc.cstr->data; + reg = p[0] - '0'; + if ((unsigned)reg >= 8 || p[1] != '\0') + goto reg_error; + op->reg = reg; + next(); + skip(')'); + } + if (op->reg == 0) + op->type |= OP_ST0; + goto no_skip; + } else { + reg_error: + error("unknown register"); + } + next(); + no_skip: ; + } else if (tok == '$') { + /* constant value */ + next(); + asm_expr(s1, &e); + op->type = OP_IM32; + op->e.v = e.v; + op->e.sym = e.sym; + if (!op->e.sym) { + if (op->e.v == (uint8_t)op->e.v) + op->type |= OP_IM8; + if (op->e.v == (int8_t)op->e.v) + op->type |= OP_IM8S; + if (op->e.v == (uint16_t)op->e.v) + op->type |= OP_IM16; + } + } else { + /* address(reg,reg2,shift) with all variants */ + op->type = OP_EA; + op->reg = -1; + op->reg2 = -1; + op->shift = 0; + if (tok != '(') { + asm_expr(s1, &e); + op->e.v = e.v; + op->e.sym = e.sym; + } else { + op->e.v = 0; + op->e.sym = NULL; + } + if (tok == '(') { + next(); + if (tok != ',') { + op->reg = asm_parse_reg(); + } + if (tok == ',') { + next(); + if (tok != ',') { + op->reg2 = asm_parse_reg(); + } + if (tok == ',') { + next(); + op->shift = get_reg_shift(s1); + } + } + skip(')'); + } + if (op->reg == -1 && op->reg2 == -1) + op->type |= OP_ADDR; + } + op->type |= indir; +} + +/* XXX: unify with C code output ? */ +static void gen_expr32(ExprValue *pe) +{ + if (pe->sym) + greloc(cur_text_section, pe->sym, ind, R_386_32); + gen_le32(pe->v); +} + +/* XXX: unify with C code output ? */ +static void gen_disp32(ExprValue *pe) +{ + Sym *sym; + sym = pe->sym; + if (sym) { + if (sym->r == cur_text_section->sh_num) { + /* same section: we can output an absolute value. Note + that the TCC compiler behaves differently here because + it always outputs a relocation to ease (future) code + elimination in the linker */ + gen_le32(pe->v + (long)sym->next - ind - 4); + } else { + greloc(cur_text_section, sym, ind, R_386_PC32); + gen_le32(pe->v - 4); + } + } else { + /* put an empty PC32 relocation */ + put_elf_reloc(symtab_section, cur_text_section, + ind, R_386_PC32, 0); + gen_le32(pe->v - 4); + } +} + + +static void gen_le16(int v) +{ + g(v); + g(v >> 8); +} + +/* generate the modrm operand */ +static inline void asm_modrm(int reg, Operand *op) +{ + int mod, reg1, reg2, sib_reg1; + + if (op->type & (OP_REG | OP_MMX | OP_SSE)) { + g(0xc0 + (reg << 3) + op->reg); + } else if (op->reg == -1 && op->reg2 == -1) { + /* displacement only */ + g(0x05 + (reg << 3)); + gen_expr32(&op->e); + } else { + sib_reg1 = op->reg; + /* fist compute displacement encoding */ + if (sib_reg1 == -1) { + sib_reg1 = 5; + mod = 0x00; + } else if (op->e.v == 0 && !op->e.sym && op->reg != 5) { + mod = 0x00; + } else if (op->e.v == (int8_t)op->e.v && !op->e.sym) { + mod = 0x40; + } else { + mod = 0x80; + } + /* compute if sib byte needed */ + reg1 = op->reg; + if (op->reg2 != -1) + reg1 = 4; + g(mod + (reg << 3) + reg1); + if (reg1 == 4) { + /* add sib byte */ + reg2 = op->reg2; + if (reg2 == -1) + reg2 = 4; /* indicate no index */ + g((op->shift << 6) + (reg2 << 3) + sib_reg1); + } + + /* add offset */ + if (mod == 0x40) { + g(op->e.v); + } else if (mod == 0x80 || op->reg == -1) { + gen_expr32(&op->e); + } + } +} + +static void asm_opcode(TCCState *s1, int opcode) +{ + const ASMInstr *pa; + int i, modrm_index, reg, v, op1, is_short_jmp, seg_prefix; + int nb_ops, s, ss; + Operand ops[MAX_OPERANDS], *pop; + int op_type[3]; /* decoded op type */ + + /* get operands */ + pop = ops; + nb_ops = 0; + seg_prefix = 0; + for(;;) { + if (tok == ';' || tok == TOK_LINEFEED) + break; + if (nb_ops >= MAX_OPERANDS) { + error("incorrect number of operands"); + } + parse_operand(s1, pop); + if (tok == ':') { + if (pop->type != OP_SEG || seg_prefix) { + error("incorrect prefix"); + } + seg_prefix = segment_prefixes[pop->reg]; + next(); + parse_operand(s1, pop); + if (!(pop->type & OP_EA)) { + error("segment prefix must be followed by memory reference"); + } + } + pop++; + nb_ops++; + if (tok != ',') + break; + next(); + } + + is_short_jmp = 0; + s = 0; /* avoid warning */ + + /* optimize matching by using a lookup table (no hashing is needed + !) */ + for(pa = asm_instrs; pa->sym != 0; pa++) { + s = 0; + if (pa->instr_type & OPC_FARITH) { + v = opcode - pa->sym; + if (!((unsigned)v < 8 * 6 && (v % 6) == 0)) + continue; + } else if (pa->instr_type & OPC_ARITH) { + if (!(opcode >= pa->sym && opcode < pa->sym + 8 * 4)) + continue; + goto compute_size; + } else if (pa->instr_type & OPC_SHIFT) { + if (!(opcode >= pa->sym && opcode < pa->sym + 7 * 4)) + continue; + goto compute_size; + } else if (pa->instr_type & OPC_TEST) { + if (!(opcode >= pa->sym && opcode < pa->sym + NB_TEST_OPCODES)) + continue; + } else if (pa->instr_type & OPC_B) { + if (!(opcode >= pa->sym && opcode <= pa->sym + 3)) + continue; + compute_size: + s = (opcode - pa->sym) & 3; + } else if (pa->instr_type & OPC_WL) { + if (!(opcode >= pa->sym && opcode <= pa->sym + 2)) + continue; + s = opcode - pa->sym + 1; + } else { + if (pa->sym != opcode) + continue; + } + if (pa->nb_ops != nb_ops) + continue; + /* now decode and check each operand */ + for(i = 0; i < nb_ops; i++) { + int op1, op2; + op1 = pa->op_type[i]; + op2 = op1 & 0x1f; + switch(op2) { + case OPT_IM: + v = OP_IM8 | OP_IM16 | OP_IM32; + break; + case OPT_REG: + v = OP_REG8 | OP_REG16 | OP_REG32; + break; + case OPT_REGW: + v = OP_REG16 | OP_REG32; + break; + case OPT_IMW: + v = OP_IM16 | OP_IM32; + break; + default: + v = 1 << op2; + break; + } + if (op1 & OPT_EA) + v |= OP_EA; + op_type[i] = v; + if ((ops[i].type & v) == 0) + goto next; + } + /* all is matching ! */ + break; + next: ; + } + if (pa->sym == 0) { + if (opcode >= TOK_ASM_pusha && opcode <= TOK_ASM_emms) { + int b; + b = op0_codes[opcode - TOK_ASM_pusha]; + if (b & 0xff00) + g(b >> 8); + g(b); + return; + } else { + error("unknown opcode '%s'", + get_tok_str(opcode, NULL)); + } + } + /* if the size is unknown, then evaluate it (OPC_B or OPC_WL case) */ + if (s == 3) { + for(i = 0; s == 3 && i < nb_ops; i++) { + if ((ops[i].type & OP_REG) && !(op_type[i] & (OP_CL | OP_DX))) + s = reg_to_size[ops[i].type & OP_REG]; + } + if (s == 3) { + if ((opcode == TOK_ASM_push || opcode == TOK_ASM_pop) && + (ops[0].type & (OP_SEG | OP_IM8S | OP_IM32))) + s = 2; + else + error("cannot infer opcode suffix"); + } + } + + /* generate data16 prefix if needed */ + ss = s; + if (s == 1 || (pa->instr_type & OPC_D16)) + g(WORD_PREFIX_OPCODE); + else if (s == 2) + s = 1; + /* now generates the operation */ + if (pa->instr_type & OPC_FWAIT) + g(0x9b); + if (seg_prefix) + g(seg_prefix); + + v = pa->opcode; + if (v == 0x69 || v == 0x69) { + /* kludge for imul $im, %reg */ + nb_ops = 3; + ops[2] = ops[1]; + } else if (v == 0xcd && ops[0].e.v == 3 && !ops[0].e.sym) { + v--; /* int $3 case */ + nb_ops = 0; + } else if ((v == 0x06 || v == 0x07)) { + if (ops[0].reg >= 4) { + /* push/pop %fs or %gs */ + v = 0x0fa0 + (v - 0x06) + ((ops[0].reg - 4) << 3); + } else { + v += ops[0].reg << 3; + } + nb_ops = 0; + } else if (v <= 0x05) { + /* arith case */ + v += ((opcode - TOK_ASM_addb) >> 2) << 3; + } else if ((pa->instr_type & (OPC_FARITH | OPC_MODRM)) == OPC_FARITH) { + /* fpu arith case */ + v += ((opcode - pa->sym) / 6) << 3; + } + if (pa->instr_type & OPC_REG) { + for(i = 0; i < nb_ops; i++) { + if (op_type[i] & (OP_REG | OP_ST)) { + v += ops[i].reg; + break; + } + } + /* mov $im, %reg case */ + if (pa->opcode == 0xb0 && s >= 1) + v += 7; + } + if (pa->instr_type & OPC_B) + v += s; + if (pa->instr_type & OPC_TEST) + v += test_bits[opcode - pa->sym]; + if (pa->instr_type & OPC_SHORTJMP) { + Sym *sym; + int jmp_disp; + + /* see if we can really generate the jump with a byte offset */ + sym = ops[0].e.sym; + if (!sym) + goto no_short_jump; + if (sym->r != cur_text_section->sh_num) + goto no_short_jump; + jmp_disp = ops[0].e.v + (long)sym->next - ind - 2; + if (jmp_disp == (int8_t)jmp_disp) { + /* OK to generate jump */ + is_short_jmp = 1; + ops[0].e.v = jmp_disp; + } else { + no_short_jump: + if (pa->instr_type & OPC_JMP) { + /* long jump will be allowed. need to modify the + opcode slightly */ + if (v == 0xeb) + v = 0xe9; + else + v += 0x0f10; + } else { + error("invalid displacement"); + } + } + } + op1 = v >> 8; + if (op1) + g(op1); + g(v); + + /* search which operand will used for modrm */ + modrm_index = 0; + if (pa->instr_type & OPC_SHIFT) { + reg = (opcode - pa->sym) >> 2; + if (reg == 6) + reg = 7; + } else if (pa->instr_type & OPC_ARITH) { + reg = (opcode - pa->sym) >> 2; + } else if (pa->instr_type & OPC_FARITH) { + reg = (opcode - pa->sym) / 6; + } else { + reg = (pa->instr_type >> OPC_GROUP_SHIFT) & 7; + } + if (pa->instr_type & OPC_MODRM) { + /* first look for an ea operand */ + for(i = 0;i < nb_ops; i++) { + if (op_type[i] & OP_EA) + goto modrm_found; + } + /* then if not found, a register or indirection (shift instructions) */ + for(i = 0;i < nb_ops; i++) { + if (op_type[i] & (OP_REG | OP_MMX | OP_SSE | OP_INDIR)) + goto modrm_found; + } +#ifdef ASM_DEBUG + error("bad op table"); +#endif + modrm_found: + modrm_index = i; + /* if a register is used in another operand then it is + used instead of group */ + for(i = 0;i < nb_ops; i++) { + v = op_type[i]; + if (i != modrm_index && + (v & (OP_REG | OP_MMX | OP_SSE | OP_CR | OP_TR | OP_DB | OP_SEG))) { + reg = ops[i].reg; + break; + } + } + + asm_modrm(reg, &ops[modrm_index]); + } + + /* emit constants */ + if (pa->opcode == 0x9a || pa->opcode == 0xea) { + /* ljmp or lcall kludge */ + gen_expr32(&ops[1].e); + if (ops[0].e.sym) + error("cannot relocate"); + gen_le16(ops[0].e.v); + } else { + for(i = 0;i < nb_ops; i++) { + v = op_type[i]; + if (v & (OP_IM8 | OP_IM16 | OP_IM32 | OP_IM8S | OP_ADDR)) { + /* if multiple sizes are given it means we must look + at the op size */ + if (v == (OP_IM8 | OP_IM16 | OP_IM32) || + v == (OP_IM16 | OP_IM32)) { + if (ss == 0) + v = OP_IM8; + else if (ss == 1) + v = OP_IM16; + else + v = OP_IM32; + } + if (v & (OP_IM8 | OP_IM8S)) { + if (ops[i].e.sym) + goto error_relocate; + g(ops[i].e.v); + } else if (v & OP_IM16) { + if (ops[i].e.sym) { + error_relocate: + error("cannot relocate"); + } + gen_le16(ops[i].e.v); + } else { + if (pa->instr_type & (OPC_JMP | OPC_SHORTJMP)) { + if (is_short_jmp) + g(ops[i].e.v); + else + gen_disp32(&ops[i].e); + } else { + gen_expr32(&ops[i].e); + } + } + } + } + } +} + +#define NB_SAVED_REGS 3 +#define NB_ASM_REGS 8 + +/* return the constraint priority (we allocate first the lowest + numbered constraints) */ +static inline int constraint_priority(const char *str) +{ + int priority, c, pr; + + /* we take the lowest priority */ + priority = 0; + for(;;) { + c = *str; + if (c == '\0') + break; + str++; + switch(c) { + case 'A': + pr = 0; + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'S': + case 'D': + pr = 1; + break; + case 'q': + pr = 2; + break; + case 'r': + pr = 3; + break; + case 'N': + case 'M': + case 'I': + case 'i': + case 'm': + case 'g': + pr = 4; + break; + default: + error("unknown constraint '%c'", c); + pr = 0; + } + if (pr > priority) + priority = pr; + } + return priority; +} + +static const char *skip_constraint_modifiers(const char *p) +{ + while (*p == '=' || *p == '&' || *p == '+' || *p == '%') + p++; + return p; +} + +#define REG_OUT_MASK 0x01 +#define REG_IN_MASK 0x02 + +#define is_reg_allocated(reg) (regs_allocated[reg] & reg_mask) + +static void asm_compute_constraints(ASMOperand *operands, + int nb_operands, int nb_outputs, + const uint8_t *clobber_regs, + int *pout_reg) +{ + ASMOperand *op; + int sorted_op[MAX_ASM_OPERANDS]; + int i, j, k, p1, p2, tmp, reg, c, reg_mask; + const char *str; + uint8_t regs_allocated[NB_ASM_REGS]; + + /* init fields */ + for(i=0;iinput_index = -1; + op->ref_index = -1; + op->reg = -1; + op->is_memory = 0; + op->is_rw = 0; + } + /* compute constraint priority and evaluate references to output + constraints if input constraints */ + for(i=0;iconstraint; + str = skip_constraint_modifiers(str); + if (isnum(*str) || *str == '[') { + /* this is a reference to another constraint */ + k = find_constraint(operands, nb_operands, str, NULL); + if ((unsigned)k >= i || i < nb_outputs) + error("invalid reference in constraint %d ('%s')", + i, str); + op->ref_index = k; + if (operands[k].input_index >= 0) + error("cannot reference twice the same operand"); + operands[k].input_index = i; + op->priority = 5; + } else { + op->priority = constraint_priority(str); + } + } + + /* sort operands according to their priority */ + for(i=0;iconstraint; + /* no need to allocate references */ + if (op->ref_index >= 0) + continue; + /* select if register is used for output, input or both */ + if (op->input_index >= 0) { + reg_mask = REG_IN_MASK | REG_OUT_MASK; + } else if (j < nb_outputs) { + reg_mask = REG_OUT_MASK; + } else { + reg_mask = REG_IN_MASK; + } + try_next: + c = *str++; + switch(c) { + case '=': + goto try_next; + case '+': + op->is_rw = 1; + /* FALL THRU */ + case '&': + if (j >= nb_outputs) + error("'%c' modifier can only be applied to outputs", c); + reg_mask = REG_IN_MASK | REG_OUT_MASK; + goto try_next; + case 'A': + /* allocate both eax and edx */ + if (is_reg_allocated(TREG_EAX) || + is_reg_allocated(TREG_EDX)) + goto try_next; + op->is_llong = 1; + op->reg = TREG_EAX; + regs_allocated[TREG_EAX] |= reg_mask; + regs_allocated[TREG_EDX] |= reg_mask; + break; + case 'a': + reg = TREG_EAX; + goto alloc_reg; + case 'b': + reg = 3; + goto alloc_reg; + case 'c': + reg = TREG_ECX; + goto alloc_reg; + case 'd': + reg = TREG_EDX; + goto alloc_reg; + case 'S': + reg = 6; + goto alloc_reg; + case 'D': + reg = 7; + alloc_reg: + if (is_reg_allocated(reg)) + goto try_next; + goto reg_found; + case 'q': + /* eax, ebx, ecx or edx */ + for(reg = 0; reg < 4; reg++) { + if (!is_reg_allocated(reg)) + goto reg_found; + } + goto try_next; + case 'r': + /* any general register */ + for(reg = 0; reg < 8; reg++) { + if (!is_reg_allocated(reg)) + goto reg_found; + } + goto try_next; + reg_found: + /* now we can reload in the register */ + op->is_llong = 0; + op->reg = reg; + regs_allocated[reg] |= reg_mask; + break; + case 'i': + if (!((op->vt->r & (VT_VALMASK | VT_LVAL)) == VT_CONST)) + goto try_next; + break; + case 'I': + case 'N': + case 'M': + if (!((op->vt->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST)) + goto try_next; + break; + case 'm': + case 'g': + /* nothing special to do because the operand is already in + memory, except if the pointer itself is stored in a + memory variable (VT_LLOCAL case) */ + /* XXX: fix constant case */ + /* if it is a reference to a memory zone, it must lie + in a register, so we reserve the register in the + input registers and a load will be generated + later */ + if (j < nb_outputs || c == 'm') { + if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) { + /* any general register */ + for(reg = 0; reg < 8; reg++) { + if (!(regs_allocated[reg] & REG_IN_MASK)) + goto reg_found1; + } + goto try_next; + reg_found1: + /* now we can reload in the register */ + regs_allocated[reg] |= REG_IN_MASK; + op->reg = reg; + op->is_memory = 1; + } + } + break; + default: + error("asm constraint %d ('%s') could not be satisfied", + j, op->constraint); + break; + } + /* if a reference is present for that operand, we assign it too */ + if (op->input_index >= 0) { + operands[op->input_index].reg = op->reg; + operands[op->input_index].is_llong = op->is_llong; + } + } + + /* compute out_reg. It is used to store outputs registers to memory + locations references by pointers (VT_LLOCAL case) */ + *pout_reg = -1; + for(i=0;ireg >= 0 && + (op->vt->r & VT_VALMASK) == VT_LLOCAL && + !op->is_memory) { + for(reg = 0; reg < 8; reg++) { + if (!(regs_allocated[reg] & REG_OUT_MASK)) + goto reg_found2; + } + error("could not find free output register for reloading"); + reg_found2: + *pout_reg = reg; + break; + } + } + + /* print sorted constraints */ +#ifdef ASM_DEBUG + for(i=0;iid ? get_tok_str(op->id, NULL) : "", + op->constraint, + op->vt->r, + op->reg); + } + if (*pout_reg >= 0) + printf("out_reg=%d\n", *pout_reg); +#endif +} + +static void subst_asm_operand(CString *add_str, + SValue *sv, int modifier) +{ + int r, reg, size, val; + char buf[64]; + + r = sv->r; + if ((r & VT_VALMASK) == VT_CONST) { + if (!(r & VT_LVAL) && modifier != 'c' && modifier != 'n') + cstr_ccat(add_str, '$'); + if (r & VT_SYM) { + cstr_cat(add_str, get_tok_str(sv->sym->v, NULL)); + if (sv->c.i != 0) { + cstr_ccat(add_str, '+'); + } else { + return; + } + } + val = sv->c.i; + if (modifier == 'n') + val = -val; + snprintf(buf, sizeof(buf), "%d", sv->c.i); + cstr_cat(add_str, buf); + } else if ((r & VT_VALMASK) == VT_LOCAL) { + snprintf(buf, sizeof(buf), "%d(%%ebp)", sv->c.i); + cstr_cat(add_str, buf); + } else if (r & VT_LVAL) { + reg = r & VT_VALMASK; + if (reg >= VT_CONST) + error("internal compiler error"); + snprintf(buf, sizeof(buf), "(%%%s)", + get_tok_str(TOK_ASM_eax + reg, NULL)); + cstr_cat(add_str, buf); + } else { + /* register case */ + reg = r & VT_VALMASK; + if (reg >= VT_CONST) + error("internal compiler error"); + + /* choose register operand size */ + if ((sv->type.t & VT_BTYPE) == VT_BYTE) + size = 1; + else if ((sv->type.t & VT_BTYPE) == VT_SHORT) + size = 2; + else + size = 4; + if (size == 1 && reg >= 4) + size = 4; + + if (modifier == 'b') { + if (reg >= 4) + error("cannot use byte register"); + size = 1; + } else if (modifier == 'h') { + if (reg >= 4) + error("cannot use byte register"); + size = -1; + } else if (modifier == 'w') { + size = 2; + } + + switch(size) { + case -1: + reg = TOK_ASM_ah + reg; + break; + case 1: + reg = TOK_ASM_al + reg; + break; + case 2: + reg = TOK_ASM_ax + reg; + break; + default: + reg = TOK_ASM_eax + reg; + break; + } + snprintf(buf, sizeof(buf), "%%%s", get_tok_str(reg, NULL)); + cstr_cat(add_str, buf); + } +} + +/* generate prolog and epilog code for asm statment */ +static void asm_gen_code(ASMOperand *operands, int nb_operands, + int nb_outputs, int is_output, + uint8_t *clobber_regs, + int out_reg) +{ + uint8_t regs_allocated[NB_ASM_REGS]; + ASMOperand *op; + int i, reg; + static uint8_t reg_saved[NB_SAVED_REGS] = { 3, 6, 7 }; + + /* mark all used registers */ + memcpy(regs_allocated, clobber_regs, sizeof(regs_allocated)); + for(i = 0; i < nb_operands;i++) { + op = &operands[i]; + if (op->reg >= 0) + regs_allocated[op->reg] = 1; + } + if (!is_output) { + /* generate reg save code */ + for(i = 0; i < NB_SAVED_REGS; i++) { + reg = reg_saved[i]; + if (regs_allocated[reg]) + g(0x50 + reg); + } + + /* generate load code */ + for(i = 0; i < nb_operands; i++) { + op = &operands[i]; + if (op->reg >= 0) { + if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && + op->is_memory) { + /* memory reference case (for both input and + output cases) */ + SValue sv; + sv = *op->vt; + sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL; + load(op->reg, &sv); + } else if (i >= nb_outputs || op->is_rw) { + /* load value in register */ + load(op->reg, op->vt); + if (op->is_llong) { + SValue sv; + sv = *op->vt; + sv.c.ul += 4; + load(TREG_EDX, &sv); + } + } + } + } + } else { + /* generate save code */ + for(i = 0 ; i < nb_outputs; i++) { + op = &operands[i]; + if (op->reg >= 0) { + if ((op->vt->r & VT_VALMASK) == VT_LLOCAL) { + if (!op->is_memory) { + SValue sv; + sv = *op->vt; + sv.r = (sv.r & ~VT_VALMASK) | VT_LOCAL; + load(out_reg, &sv); + + sv.r = (sv.r & ~VT_VALMASK) | out_reg; + store(op->reg, &sv); + } + } else { + store(op->reg, op->vt); + if (op->is_llong) { + SValue sv; + sv = *op->vt; + sv.c.ul += 4; + store(TREG_EDX, &sv); + } + } + } + } + /* generate reg restore code */ + for(i = NB_SAVED_REGS - 1; i >= 0; i--) { + reg = reg_saved[i]; + if (regs_allocated[reg]) + g(0x58 + reg); + } + } +} + +static void asm_clobber(uint8_t *clobber_regs, const char *str) +{ + int reg; + TokenSym *ts; + + if (!strcmp(str, "memory") || + !strcmp(str, "cc")) + return; + ts = tok_alloc(str, strlen(str)); + reg = ts->tok; + if (reg >= TOK_ASM_eax && reg <= TOK_ASM_edi) { + reg -= TOK_ASM_eax; + } else if (reg >= TOK_ASM_ax && reg <= TOK_ASM_di) { + reg -= TOK_ASM_ax; + } else { + error("invalid clobber register '%s'", str); + } + clobber_regs[reg] = 1; +} diff --git a/05/tcc-0.9.25/i386-asm.h b/05/tcc-0.9.25/i386-asm.h new file mode 100644 index 0000000..a3b28d4 --- /dev/null +++ b/05/tcc-0.9.25/i386-asm.h @@ -0,0 +1,446 @@ + DEF_ASM_OP0(pusha, 0x60) /* must be first OP0 */ + DEF_ASM_OP0(popa, 0x61) + DEF_ASM_OP0(clc, 0xf8) + DEF_ASM_OP0(cld, 0xfc) + DEF_ASM_OP0(cli, 0xfa) + DEF_ASM_OP0(clts, 0x0f06) + DEF_ASM_OP0(cmc, 0xf5) + DEF_ASM_OP0(lahf, 0x9f) + DEF_ASM_OP0(sahf, 0x9e) + DEF_ASM_OP0(pushfl, 0x9c) + DEF_ASM_OP0(popfl, 0x9d) + DEF_ASM_OP0(pushf, 0x9c) + DEF_ASM_OP0(popf, 0x9d) + DEF_ASM_OP0(stc, 0xf9) + DEF_ASM_OP0(std, 0xfd) + DEF_ASM_OP0(sti, 0xfb) + DEF_ASM_OP0(aaa, 0x37) + DEF_ASM_OP0(aas, 0x3f) + DEF_ASM_OP0(daa, 0x27) + DEF_ASM_OP0(das, 0x2f) + DEF_ASM_OP0(aad, 0xd50a) + DEF_ASM_OP0(aam, 0xd40a) + DEF_ASM_OP0(cbw, 0x6698) + DEF_ASM_OP0(cwd, 0x6699) + DEF_ASM_OP0(cwde, 0x98) + DEF_ASM_OP0(cdq, 0x99) + DEF_ASM_OP0(cbtw, 0x6698) + DEF_ASM_OP0(cwtl, 0x98) + DEF_ASM_OP0(cwtd, 0x6699) + DEF_ASM_OP0(cltd, 0x99) + DEF_ASM_OP0(int3, 0xcc) + DEF_ASM_OP0(into, 0xce) + DEF_ASM_OP0(iret, 0xcf) + DEF_ASM_OP0(rsm, 0x0faa) + DEF_ASM_OP0(hlt, 0xf4) + DEF_ASM_OP0(wait, 0x9b) + DEF_ASM_OP0(nop, 0x90) + DEF_ASM_OP0(xlat, 0xd7) + + /* strings */ +ALT(DEF_ASM_OP0L(cmpsb, 0xa6, 0, OPC_BWL)) +ALT(DEF_ASM_OP0L(scmpb, 0xa6, 0, OPC_BWL)) + +ALT(DEF_ASM_OP0L(insb, 0x6c, 0, OPC_BWL)) +ALT(DEF_ASM_OP0L(outsb, 0x6e, 0, OPC_BWL)) + +ALT(DEF_ASM_OP0L(lodsb, 0xac, 0, OPC_BWL)) +ALT(DEF_ASM_OP0L(slodb, 0xac, 0, OPC_BWL)) + +ALT(DEF_ASM_OP0L(movsb, 0xa4, 0, OPC_BWL)) +ALT(DEF_ASM_OP0L(smovb, 0xa4, 0, OPC_BWL)) + +ALT(DEF_ASM_OP0L(scasb, 0xae, 0, OPC_BWL)) +ALT(DEF_ASM_OP0L(sscab, 0xae, 0, OPC_BWL)) + +ALT(DEF_ASM_OP0L(stosb, 0xaa, 0, OPC_BWL)) +ALT(DEF_ASM_OP0L(sstob, 0xaa, 0, OPC_BWL)) + + /* bits */ + +ALT(DEF_ASM_OP2(bsfw, 0x0fbc, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW)) +ALT(DEF_ASM_OP2(bsrw, 0x0fbd, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA, OPT_REGW)) + +ALT(DEF_ASM_OP2(btw, 0x0fa3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA)) +ALT(DEF_ASM_OP2(btw, 0x0fba, 4, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA)) + +ALT(DEF_ASM_OP2(btsw, 0x0fab, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA)) +ALT(DEF_ASM_OP2(btsw, 0x0fba, 5, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA)) + +ALT(DEF_ASM_OP2(btrw, 0x0fb3, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA)) +ALT(DEF_ASM_OP2(btrw, 0x0fba, 6, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA)) + +ALT(DEF_ASM_OP2(btcw, 0x0fbb, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_REGW | OPT_EA)) +ALT(DEF_ASM_OP2(btcw, 0x0fba, 7, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW | OPT_EA)) + + /* prefixes */ + DEF_ASM_OP0(aword, 0x67) + DEF_ASM_OP0(addr16, 0x67) + DEF_ASM_OP0(word, 0x66) + DEF_ASM_OP0(data16, 0x66) + DEF_ASM_OP0(lock, 0xf0) + DEF_ASM_OP0(rep, 0xf3) + DEF_ASM_OP0(repe, 0xf3) + DEF_ASM_OP0(repz, 0xf3) + DEF_ASM_OP0(repne, 0xf2) + DEF_ASM_OP0(repnz, 0xf2) + + DEF_ASM_OP0(invd, 0x0f08) + DEF_ASM_OP0(wbinvd, 0x0f09) + DEF_ASM_OP0(cpuid, 0x0fa2) + DEF_ASM_OP0(wrmsr, 0x0f30) + DEF_ASM_OP0(rdtsc, 0x0f31) + DEF_ASM_OP0(rdmsr, 0x0f32) + DEF_ASM_OP0(rdpmc, 0x0f33) + DEF_ASM_OP0(ud2, 0x0f0b) + + /* NOTE: we took the same order as gas opcode definition order */ +ALT(DEF_ASM_OP2(movb, 0xa0, 0, OPC_BWL, OPT_ADDR, OPT_EAX)) +ALT(DEF_ASM_OP2(movb, 0xa2, 0, OPC_BWL, OPT_EAX, OPT_ADDR)) +ALT(DEF_ASM_OP2(movb, 0x88, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) +ALT(DEF_ASM_OP2(movb, 0x8a, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG)) +ALT(DEF_ASM_OP2(movb, 0xb0, 0, OPC_REG | OPC_BWL, OPT_IM, OPT_REG)) +ALT(DEF_ASM_OP2(movb, 0xc6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_REG | OPT_EA)) + +ALT(DEF_ASM_OP2(movw, 0x8c, 0, OPC_MODRM | OPC_WL, OPT_SEG, OPT_EA | OPT_REG)) +ALT(DEF_ASM_OP2(movw, 0x8e, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_SEG)) + +ALT(DEF_ASM_OP2(movw, 0x0f20, 0, OPC_MODRM | OPC_WL, OPT_CR, OPT_REG32)) +ALT(DEF_ASM_OP2(movw, 0x0f21, 0, OPC_MODRM | OPC_WL, OPT_DB, OPT_REG32)) +ALT(DEF_ASM_OP2(movw, 0x0f24, 0, OPC_MODRM | OPC_WL, OPT_TR, OPT_REG32)) +ALT(DEF_ASM_OP2(movw, 0x0f22, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_CR)) +ALT(DEF_ASM_OP2(movw, 0x0f23, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_DB)) +ALT(DEF_ASM_OP2(movw, 0x0f26, 0, OPC_MODRM | OPC_WL, OPT_REG32, OPT_TR)) + +ALT(DEF_ASM_OP2(movsbl, 0x0fbe, 0, OPC_MODRM, OPT_REG8 | OPT_EA, OPT_REG32)) +ALT(DEF_ASM_OP2(movsbw, 0x0fbe, 0, OPC_MODRM | OPC_D16, OPT_REG8 | OPT_EA, OPT_REG16)) +ALT(DEF_ASM_OP2(movswl, 0x0fbf, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32)) +ALT(DEF_ASM_OP2(movzbw, 0x0fb6, 0, OPC_MODRM | OPC_WL, OPT_REG8 | OPT_EA, OPT_REGW)) +ALT(DEF_ASM_OP2(movzwl, 0x0fb7, 0, OPC_MODRM, OPT_REG16 | OPT_EA, OPT_REG32)) + +ALT(DEF_ASM_OP1(pushw, 0x50, 0, OPC_REG | OPC_WL, OPT_REGW)) +ALT(DEF_ASM_OP1(pushw, 0xff, 6, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA)) +ALT(DEF_ASM_OP1(pushw, 0x6a, 0, OPC_WL, OPT_IM8S)) +ALT(DEF_ASM_OP1(pushw, 0x68, 0, OPC_WL, OPT_IM32)) +ALT(DEF_ASM_OP1(pushw, 0x06, 0, OPC_WL, OPT_SEG)) + +ALT(DEF_ASM_OP1(popw, 0x58, 0, OPC_REG | OPC_WL, OPT_REGW)) +ALT(DEF_ASM_OP1(popw, 0x8f, 0, OPC_MODRM | OPC_WL, OPT_REGW | OPT_EA)) +ALT(DEF_ASM_OP1(popw, 0x07, 0, OPC_WL, OPT_SEG)) + +ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_REG, OPT_EAX)) +ALT(DEF_ASM_OP2(xchgw, 0x90, 0, OPC_REG | OPC_WL, OPT_EAX, OPT_REG)) +ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) +ALT(DEF_ASM_OP2(xchgb, 0x86, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG)) + +ALT(DEF_ASM_OP2(inb, 0xe4, 0, OPC_BWL, OPT_IM8, OPT_EAX)) +ALT(DEF_ASM_OP1(inb, 0xe4, 0, OPC_BWL, OPT_IM8)) +ALT(DEF_ASM_OP2(inb, 0xec, 0, OPC_BWL, OPT_DX, OPT_EAX)) +ALT(DEF_ASM_OP1(inb, 0xec, 0, OPC_BWL, OPT_DX)) + +ALT(DEF_ASM_OP2(outb, 0xe6, 0, OPC_BWL, OPT_EAX, OPT_IM8)) +ALT(DEF_ASM_OP1(outb, 0xe6, 0, OPC_BWL, OPT_IM8)) +ALT(DEF_ASM_OP2(outb, 0xee, 0, OPC_BWL, OPT_EAX, OPT_DX)) +ALT(DEF_ASM_OP1(outb, 0xee, 0, OPC_BWL, OPT_DX)) + +ALT(DEF_ASM_OP2(leaw, 0x8d, 0, OPC_MODRM | OPC_WL, OPT_EA, OPT_REG)) + +ALT(DEF_ASM_OP2(les, 0xc4, 0, OPC_MODRM, OPT_EA, OPT_REG32)) +ALT(DEF_ASM_OP2(lds, 0xc5, 0, OPC_MODRM, OPT_EA, OPT_REG32)) +ALT(DEF_ASM_OP2(lss, 0x0fb2, 0, OPC_MODRM, OPT_EA, OPT_REG32)) +ALT(DEF_ASM_OP2(lfs, 0x0fb4, 0, OPC_MODRM, OPT_EA, OPT_REG32)) +ALT(DEF_ASM_OP2(lgs, 0x0fb5, 0, OPC_MODRM, OPT_EA, OPT_REG32)) + + /* arith */ +ALT(DEF_ASM_OP2(addb, 0x00, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) /* XXX: use D bit ? */ +ALT(DEF_ASM_OP2(addb, 0x02, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG)) +ALT(DEF_ASM_OP2(addb, 0x04, 0, OPC_ARITH | OPC_BWL, OPT_IM, OPT_EAX)) +ALT(DEF_ASM_OP2(addb, 0x80, 0, OPC_ARITH | OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG)) +ALT(DEF_ASM_OP2(addw, 0x83, 0, OPC_ARITH | OPC_MODRM | OPC_WL, OPT_IM8S, OPT_EA | OPT_REG)) + +ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_EA | OPT_REG, OPT_REG)) +ALT(DEF_ASM_OP2(testb, 0x84, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_EA | OPT_REG)) +ALT(DEF_ASM_OP2(testb, 0xa8, 0, OPC_BWL, OPT_IM, OPT_EAX)) +ALT(DEF_ASM_OP2(testb, 0xf6, 0, OPC_MODRM | OPC_BWL, OPT_IM, OPT_EA | OPT_REG)) + +ALT(DEF_ASM_OP1(incw, 0x40, 0, OPC_REG | OPC_WL, OPT_REGW)) +ALT(DEF_ASM_OP1(incb, 0xfe, 0, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) +ALT(DEF_ASM_OP1(decw, 0x48, 0, OPC_REG | OPC_WL, OPT_REGW)) +ALT(DEF_ASM_OP1(decb, 0xfe, 1, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) + +ALT(DEF_ASM_OP1(notb, 0xf6, 2, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) +ALT(DEF_ASM_OP1(negb, 0xf6, 3, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) + +ALT(DEF_ASM_OP1(mulb, 0xf6, 4, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) +ALT(DEF_ASM_OP1(imulb, 0xf6, 5, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) + +ALT(DEF_ASM_OP2(imulw, 0x0faf, 0, OPC_MODRM | OPC_WL, OPT_REG | OPT_EA, OPT_REG)) +ALT(DEF_ASM_OP3(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW | OPT_EA, OPT_REGW)) +ALT(DEF_ASM_OP2(imulw, 0x6b, 0, OPC_MODRM | OPC_WL, OPT_IM8S, OPT_REGW)) +ALT(DEF_ASM_OP3(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW | OPT_EA, OPT_REGW)) +ALT(DEF_ASM_OP2(imulw, 0x69, 0, OPC_MODRM | OPC_WL, OPT_IMW, OPT_REGW)) + +ALT(DEF_ASM_OP1(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) +ALT(DEF_ASM_OP2(divb, 0xf6, 6, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX)) +ALT(DEF_ASM_OP1(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA)) +ALT(DEF_ASM_OP2(idivb, 0xf6, 7, OPC_MODRM | OPC_BWL, OPT_REG | OPT_EA, OPT_EAX)) + + /* shifts */ +ALT(DEF_ASM_OP2(rolb, 0xc0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_IM8, OPT_EA | OPT_REG)) +ALT(DEF_ASM_OP2(rolb, 0xd2, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_CL, OPT_EA | OPT_REG)) +ALT(DEF_ASM_OP1(rolb, 0xd0, 0, OPC_MODRM | OPC_BWL | OPC_SHIFT, OPT_EA | OPT_REG)) + +ALT(DEF_ASM_OP3(shldw, 0x0fa4, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW)) +ALT(DEF_ASM_OP3(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW)) +ALT(DEF_ASM_OP2(shldw, 0x0fa5, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW)) +ALT(DEF_ASM_OP3(shrdw, 0x0fac, 0, OPC_MODRM | OPC_WL, OPT_IM8, OPT_REGW, OPT_EA | OPT_REGW)) +ALT(DEF_ASM_OP3(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_CL, OPT_REGW, OPT_EA | OPT_REGW)) +ALT(DEF_ASM_OP2(shrdw, 0x0fad, 0, OPC_MODRM | OPC_WL, OPT_REGW, OPT_EA | OPT_REGW)) + +ALT(DEF_ASM_OP1(call, 0xff, 2, OPC_MODRM, OPT_INDIR)) +ALT(DEF_ASM_OP1(call, 0xe8, 0, OPC_JMP, OPT_ADDR)) +ALT(DEF_ASM_OP1(jmp, 0xff, 4, OPC_MODRM, OPT_INDIR)) +ALT(DEF_ASM_OP1(jmp, 0xeb, 0, OPC_SHORTJMP | OPC_JMP, OPT_ADDR)) + +ALT(DEF_ASM_OP2(lcall, 0x9a, 0, 0, OPT_IM16, OPT_IM32)) +ALT(DEF_ASM_OP1(lcall, 0xff, 3, 0, OPT_EA)) +ALT(DEF_ASM_OP2(ljmp, 0xea, 0, 0, OPT_IM16, OPT_IM32)) +ALT(DEF_ASM_OP1(ljmp, 0xff, 5, 0, OPT_EA)) + +ALT(DEF_ASM_OP1(int, 0xcd, 0, 0, OPT_IM8)) +ALT(DEF_ASM_OP1(seto, 0x0f90, 0, OPC_MODRM | OPC_TEST, OPT_REG8 | OPT_EA)) + DEF_ASM_OP2(enter, 0xc8, 0, 0, OPT_IM16, OPT_IM8) + DEF_ASM_OP0(leave, 0xc9) + DEF_ASM_OP0(ret, 0xc3) +ALT(DEF_ASM_OP1(ret, 0xc2, 0, 0, OPT_IM16)) + DEF_ASM_OP0(lret, 0xcb) +ALT(DEF_ASM_OP1(lret, 0xca, 0, 0, OPT_IM16)) + +ALT(DEF_ASM_OP1(jo, 0x70, 0, OPC_SHORTJMP | OPC_JMP | OPC_TEST, OPT_ADDR)) + DEF_ASM_OP1(loopne, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR) + DEF_ASM_OP1(loopnz, 0xe0, 0, OPC_SHORTJMP, OPT_ADDR) + DEF_ASM_OP1(loope, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR) + DEF_ASM_OP1(loopz, 0xe1, 0, OPC_SHORTJMP, OPT_ADDR) + DEF_ASM_OP1(loop, 0xe2, 0, OPC_SHORTJMP, OPT_ADDR) + DEF_ASM_OP1(jecxz, 0xe3, 0, OPC_SHORTJMP, OPT_ADDR) + + /* float */ + /* specific fcomp handling */ +ALT(DEF_ASM_OP0L(fcomp, 0xd8d9, 0, 0)) + +ALT(DEF_ASM_OP1(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST)) +ALT(DEF_ASM_OP2(fadd, 0xd8c0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0)) +ALT(DEF_ASM_OP0L(fadd, 0xdec1, 0, OPC_FARITH)) +ALT(DEF_ASM_OP1(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST)) +ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST, OPT_ST0)) +ALT(DEF_ASM_OP2(faddp, 0xdec0, 0, OPC_FARITH | OPC_REG, OPT_ST0, OPT_ST)) +ALT(DEF_ASM_OP0L(faddp, 0xdec1, 0, OPC_FARITH)) +ALT(DEF_ASM_OP1(fadds, 0xd8, 0, OPC_FARITH | OPC_MODRM, OPT_EA)) +ALT(DEF_ASM_OP1(fiaddl, 0xda, 0, OPC_FARITH | OPC_MODRM, OPT_EA)) +ALT(DEF_ASM_OP1(faddl, 0xdc, 0, OPC_FARITH | OPC_MODRM, OPT_EA)) +ALT(DEF_ASM_OP1(fiadds, 0xde, 0, OPC_FARITH | OPC_MODRM, OPT_EA)) + + DEF_ASM_OP0(fucompp, 0xdae9) + DEF_ASM_OP0(ftst, 0xd9e4) + DEF_ASM_OP0(fxam, 0xd9e5) + DEF_ASM_OP0(fld1, 0xd9e8) + DEF_ASM_OP0(fldl2t, 0xd9e9) + DEF_ASM_OP0(fldl2e, 0xd9ea) + DEF_ASM_OP0(fldpi, 0xd9eb) + DEF_ASM_OP0(fldlg2, 0xd9ec) + DEF_ASM_OP0(fldln2, 0xd9ed) + DEF_ASM_OP0(fldz, 0xd9ee) + + DEF_ASM_OP0(f2xm1, 0xd9f0) + DEF_ASM_OP0(fyl2x, 0xd9f1) + DEF_ASM_OP0(fptan, 0xd9f2) + DEF_ASM_OP0(fpatan, 0xd9f3) + DEF_ASM_OP0(fxtract, 0xd9f4) + DEF_ASM_OP0(fprem1, 0xd9f5) + DEF_ASM_OP0(fdecstp, 0xd9f6) + DEF_ASM_OP0(fincstp, 0xd9f7) + DEF_ASM_OP0(fprem, 0xd9f8) + DEF_ASM_OP0(fyl2xp1, 0xd9f9) + DEF_ASM_OP0(fsqrt, 0xd9fa) + DEF_ASM_OP0(fsincos, 0xd9fb) + DEF_ASM_OP0(frndint, 0xd9fc) + DEF_ASM_OP0(fscale, 0xd9fd) + DEF_ASM_OP0(fsin, 0xd9fe) + DEF_ASM_OP0(fcos, 0xd9ff) + DEF_ASM_OP0(fchs, 0xd9e0) + DEF_ASM_OP0(fabs, 0xd9e1) + DEF_ASM_OP0(fninit, 0xdbe3) + DEF_ASM_OP0(fnclex, 0xdbe2) + DEF_ASM_OP0(fnop, 0xd9d0) + DEF_ASM_OP0(fwait, 0x9b) + + /* fp load */ + DEF_ASM_OP1(fld, 0xd9c0, 0, OPC_REG, OPT_ST) + DEF_ASM_OP1(fldl, 0xd9c0, 0, OPC_REG, OPT_ST) + DEF_ASM_OP1(flds, 0xd9, 0, OPC_MODRM, OPT_EA) +ALT(DEF_ASM_OP1(fldl, 0xdd, 0, OPC_MODRM, OPT_EA)) + DEF_ASM_OP1(fildl, 0xdb, 0, OPC_MODRM, OPT_EA) + DEF_ASM_OP1(fildq, 0xdf, 5, OPC_MODRM, OPT_EA) + DEF_ASM_OP1(fildll, 0xdf, 5, OPC_MODRM,OPT_EA) + DEF_ASM_OP1(fldt, 0xdb, 5, OPC_MODRM, OPT_EA) + DEF_ASM_OP1(fbld, 0xdf, 4, OPC_MODRM, OPT_EA) + + /* fp store */ + DEF_ASM_OP1(fst, 0xddd0, 0, OPC_REG, OPT_ST) + DEF_ASM_OP1(fstl, 0xddd0, 0, OPC_REG, OPT_ST) + DEF_ASM_OP1(fsts, 0xd9, 2, OPC_MODRM, OPT_EA) + DEF_ASM_OP1(fstps, 0xd9, 3, OPC_MODRM, OPT_EA) +ALT(DEF_ASM_OP1(fstl, 0xdd, 2, OPC_MODRM, OPT_EA)) + DEF_ASM_OP1(fstpl, 0xdd, 3, OPC_MODRM, OPT_EA) + DEF_ASM_OP1(fist, 0xdf, 2, OPC_MODRM, OPT_EA) + DEF_ASM_OP1(fistp, 0xdf, 3, OPC_MODRM, OPT_EA) + DEF_ASM_OP1(fistl, 0xdb, 2, OPC_MODRM, OPT_EA) + DEF_ASM_OP1(fistpl, 0xdb, 3, OPC_MODRM, OPT_EA) + + DEF_ASM_OP1(fstp, 0xddd8, 0, OPC_REG, OPT_ST) + DEF_ASM_OP1(fistpq, 0xdf, 7, OPC_MODRM, OPT_EA) + DEF_ASM_OP1(fistpll, 0xdf, 7, OPC_MODRM, OPT_EA) + DEF_ASM_OP1(fstpt, 0xdb, 7, OPC_MODRM, OPT_EA) + DEF_ASM_OP1(fbstp, 0xdf, 6, OPC_MODRM, OPT_EA) + + /* exchange */ + DEF_ASM_OP0(fxch, 0xd9c9) +ALT(DEF_ASM_OP1(fxch, 0xd9c8, 0, OPC_REG, OPT_ST)) + + /* misc FPU */ + DEF_ASM_OP1(fucom, 0xdde0, 0, OPC_REG, OPT_ST ) + DEF_ASM_OP1(fucomp, 0xdde8, 0, OPC_REG, OPT_ST ) + + DEF_ASM_OP0L(finit, 0xdbe3, 0, OPC_FWAIT) + DEF_ASM_OP1(fldcw, 0xd9, 5, OPC_MODRM, OPT_EA ) + DEF_ASM_OP1(fnstcw, 0xd9, 7, OPC_MODRM, OPT_EA ) + DEF_ASM_OP1(fstcw, 0xd9, 7, OPC_MODRM | OPC_FWAIT, OPT_EA ) + DEF_ASM_OP0(fnstsw, 0xdfe0) +ALT(DEF_ASM_OP1(fnstsw, 0xdfe0, 0, 0, OPT_EAX )) +ALT(DEF_ASM_OP1(fnstsw, 0xdd, 7, OPC_MODRM, OPT_EA )) + DEF_ASM_OP1(fstsw, 0xdfe0, 0, OPC_FWAIT, OPT_EAX ) +ALT(DEF_ASM_OP0L(fstsw, 0xdfe0, 0, OPC_FWAIT)) +ALT(DEF_ASM_OP1(fstsw, 0xdd, 7, OPC_MODRM | OPC_FWAIT, OPT_EA )) + DEF_ASM_OP0L(fclex, 0xdbe2, 0, OPC_FWAIT) + DEF_ASM_OP1(fnstenv, 0xd9, 6, OPC_MODRM, OPT_EA ) + DEF_ASM_OP1(fstenv, 0xd9, 6, OPC_MODRM | OPC_FWAIT, OPT_EA ) + DEF_ASM_OP1(fldenv, 0xd9, 4, OPC_MODRM, OPT_EA ) + DEF_ASM_OP1(fnsave, 0xdd, 6, OPC_MODRM, OPT_EA ) + DEF_ASM_OP1(fsave, 0xdd, 6, OPC_MODRM | OPC_FWAIT, OPT_EA ) + DEF_ASM_OP1(frstor, 0xdd, 4, OPC_MODRM, OPT_EA ) + DEF_ASM_OP1(ffree, 0xddc0, 4, OPC_REG, OPT_ST ) + DEF_ASM_OP1(ffreep, 0xdfc0, 4, OPC_REG, OPT_ST ) + DEF_ASM_OP1(fxsave, 0x0fae, 0, OPC_MODRM, OPT_EA ) + DEF_ASM_OP1(fxrstor, 0x0fae, 1, OPC_MODRM, OPT_EA ) + + /* segments */ + DEF_ASM_OP2(arpl, 0x63, 0, OPC_MODRM, OPT_REG16, OPT_REG16 | OPT_EA) + DEF_ASM_OP2(lar, 0x0f02, 0, OPC_MODRM, OPT_REG32 | OPT_EA, OPT_REG32) + DEF_ASM_OP1(lgdt, 0x0f01, 2, OPC_MODRM, OPT_EA) + DEF_ASM_OP1(lidt, 0x0f01, 3, OPC_MODRM, OPT_EA) + DEF_ASM_OP1(lldt, 0x0f00, 2, OPC_MODRM, OPT_EA | OPT_REG) + DEF_ASM_OP1(lmsw, 0x0f01, 6, OPC_MODRM, OPT_EA | OPT_REG) +ALT(DEF_ASM_OP2(lslw, 0x0f03, 0, OPC_MODRM | OPC_WL, OPT_EA | OPT_REG, OPT_REG)) + DEF_ASM_OP1(ltr, 0x0f00, 3, OPC_MODRM, OPT_EA | OPT_REG) + DEF_ASM_OP1(sgdt, 0x0f01, 0, OPC_MODRM, OPT_EA) + DEF_ASM_OP1(sidt, 0x0f01, 1, OPC_MODRM, OPT_EA) + DEF_ASM_OP1(sldt, 0x0f00, 0, OPC_MODRM, OPT_REG | OPT_EA) + DEF_ASM_OP1(smsw, 0x0f01, 4, OPC_MODRM, OPT_REG | OPT_EA) + DEF_ASM_OP1(str, 0x0f00, 1, OPC_MODRM, OPT_REG16| OPT_EA) + DEF_ASM_OP1(verr, 0x0f00, 4, OPC_MODRM, OPT_REG | OPT_EA) + DEF_ASM_OP1(verw, 0x0f00, 5, OPC_MODRM, OPT_REG | OPT_EA) + + /* 486 */ + DEF_ASM_OP1(bswap, 0x0fc8, 0, OPC_REG, OPT_REG32 ) +ALT(DEF_ASM_OP2(xaddb, 0x0fc0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA )) +ALT(DEF_ASM_OP2(cmpxchgb, 0x0fb0, 0, OPC_MODRM | OPC_BWL, OPT_REG, OPT_REG | OPT_EA )) + DEF_ASM_OP1(invlpg, 0x0f01, 7, OPC_MODRM, OPT_EA ) + + DEF_ASM_OP2(boundl, 0x62, 0, OPC_MODRM, OPT_REG32, OPT_EA) + DEF_ASM_OP2(boundw, 0x62, 0, OPC_MODRM | OPC_D16, OPT_REG16, OPT_EA) + + /* pentium */ + DEF_ASM_OP1(cmpxchg8b, 0x0fc7, 1, OPC_MODRM, OPT_EA ) + + /* pentium pro */ + ALT(DEF_ASM_OP2(cmovo, 0x0f40, 0, OPC_MODRM | OPC_TEST, OPT_REG32 | OPT_EA, OPT_REG32)) + + DEF_ASM_OP2(fcmovb, 0xdac0, 0, OPC_REG, OPT_ST, OPT_ST0 ) + DEF_ASM_OP2(fcmove, 0xdac8, 0, OPC_REG, OPT_ST, OPT_ST0 ) + DEF_ASM_OP2(fcmovbe, 0xdad0, 0, OPC_REG, OPT_ST, OPT_ST0 ) + DEF_ASM_OP2(fcmovu, 0xdad8, 0, OPC_REG, OPT_ST, OPT_ST0 ) + DEF_ASM_OP2(fcmovnb, 0xdbc0, 0, OPC_REG, OPT_ST, OPT_ST0 ) + DEF_ASM_OP2(fcmovne, 0xdbc8, 0, OPC_REG, OPT_ST, OPT_ST0 ) + DEF_ASM_OP2(fcmovnbe, 0xdbd0, 0, OPC_REG, OPT_ST, OPT_ST0 ) + DEF_ASM_OP2(fcmovnu, 0xdbd8, 0, OPC_REG, OPT_ST, OPT_ST0 ) + + DEF_ASM_OP2(fucomi, 0xdbe8, 0, OPC_REG, OPT_ST, OPT_ST0 ) + DEF_ASM_OP2(fcomi, 0xdbf0, 0, OPC_REG, OPT_ST, OPT_ST0 ) + DEF_ASM_OP2(fucomip, 0xdfe8, 0, OPC_REG, OPT_ST, OPT_ST0 ) + DEF_ASM_OP2(fcomip, 0xdff0, 0, OPC_REG, OPT_ST, OPT_ST0 ) + + /* mmx */ + DEF_ASM_OP0(emms, 0x0f77) /* must be last OP0 */ + DEF_ASM_OP2(movd, 0x0f6e, 0, OPC_MODRM, OPT_EA | OPT_REG32, OPT_MMX ) +ALT(DEF_ASM_OP2(movd, 0x0f7e, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_REG32 )) + DEF_ASM_OP2(movq, 0x0f6f, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) +ALT(DEF_ASM_OP2(movq, 0x0f7f, 0, OPC_MODRM, OPT_MMX, OPT_EA | OPT_MMX )) + DEF_ASM_OP2(packssdw, 0x0f6b, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(packsswb, 0x0f63, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(packuswb, 0x0f67, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(paddb, 0x0ffc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(paddw, 0x0ffd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(paddd, 0x0ffe, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(paddsb, 0x0fec, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(paddsw, 0x0fed, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(paddusb, 0x0fdc, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(paddusw, 0x0fdd, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(pand, 0x0fdb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(pandn, 0x0fdf, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(pcmpeqb, 0x0f74, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(pcmpeqw, 0x0f75, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(pcmpeqd, 0x0f76, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(pcmpgtb, 0x0f64, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(pcmpgtw, 0x0f65, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(pcmpgtd, 0x0f66, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(pmaddwd, 0x0ff5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(pmulhw, 0x0fe5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(pmullw, 0x0fd5, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(por, 0x0feb, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(psllw, 0x0ff1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) +ALT(DEF_ASM_OP2(psllw, 0x0f71, 6, OPC_MODRM, OPT_IM8, OPT_MMX )) + DEF_ASM_OP2(pslld, 0x0ff2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) +ALT(DEF_ASM_OP2(pslld, 0x0f72, 6, OPC_MODRM, OPT_IM8, OPT_MMX )) + DEF_ASM_OP2(psllq, 0x0ff3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) +ALT(DEF_ASM_OP2(psllq, 0x0f73, 6, OPC_MODRM, OPT_IM8, OPT_MMX )) + DEF_ASM_OP2(psraw, 0x0fe1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) +ALT(DEF_ASM_OP2(psraw, 0x0f71, 4, OPC_MODRM, OPT_IM8, OPT_MMX )) + DEF_ASM_OP2(psrad, 0x0fe2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) +ALT(DEF_ASM_OP2(psrad, 0x0f72, 4, OPC_MODRM, OPT_IM8, OPT_MMX )) + DEF_ASM_OP2(psrlw, 0x0fd1, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) +ALT(DEF_ASM_OP2(psrlw, 0x0f71, 2, OPC_MODRM, OPT_IM8, OPT_MMX )) + DEF_ASM_OP2(psrld, 0x0fd2, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) +ALT(DEF_ASM_OP2(psrld, 0x0f72, 2, OPC_MODRM, OPT_IM8, OPT_MMX )) + DEF_ASM_OP2(psrlq, 0x0fd3, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) +ALT(DEF_ASM_OP2(psrlq, 0x0f73, 2, OPC_MODRM, OPT_IM8, OPT_MMX )) + DEF_ASM_OP2(psubb, 0x0ff8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(psubw, 0x0ff9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(psubd, 0x0ffa, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(psubsb, 0x0fe8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(psubsw, 0x0fe9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(psubusb, 0x0fd8, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(psubusw, 0x0fd9, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(punpckhbw, 0x0f68, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(punpckhwd, 0x0f69, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(punpckhdq, 0x0f6a, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(punpcklbw, 0x0f60, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(punpcklwd, 0x0f61, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(punpckldq, 0x0f62, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + DEF_ASM_OP2(pxor, 0x0fef, 0, OPC_MODRM, OPT_EA | OPT_MMX, OPT_MMX ) + +#undef ALT +#undef DEF_ASM_OP0 +#undef DEF_ASM_OP0L +#undef DEF_ASM_OP1 +#undef DEF_ASM_OP2 +#undef DEF_ASM_OP3 diff --git a/05/tcc-0.9.25/i386-gen.c b/05/tcc-0.9.25/i386-gen.c new file mode 100644 index 0000000..f958ab5 --- /dev/null +++ b/05/tcc-0.9.25/i386-gen.c @@ -0,0 +1,1034 @@ +/* + * X86 code generator for TCC + * + * Copyright (c) 2001-2004 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* number of available registers */ +#define NB_REGS 4 + +/* a register can belong to several classes. The classes must be + sorted from more general to more precise (see gv2() code which does + assumptions on it). */ +#define RC_INT 0x0001 /* generic integer register */ +#define RC_FLOAT 0x0002 /* generic float register */ +#define RC_EAX 0x0004 +#define RC_ST0 0x0008 +#define RC_ECX 0x0010 +#define RC_EDX 0x0020 +#define RC_IRET RC_EAX /* function return: integer register */ +#define RC_LRET RC_EDX /* function return: second integer register */ +#define RC_FRET RC_ST0 /* function return: float register */ + +/* pretty names for the registers */ +enum { + TREG_EAX = 0, + TREG_ECX, + TREG_EDX, + TREG_ST0, +}; + +int reg_classes[NB_REGS] = { + /* eax */ RC_INT | RC_EAX, + /* ecx */ RC_INT | RC_ECX, + /* edx */ RC_INT | RC_EDX, + /* st0 */ RC_FLOAT | RC_ST0, +}; + +/* return registers for function */ +#define REG_IRET TREG_EAX /* single word int return register */ +#define REG_LRET TREG_EDX /* second word return register (for long long) */ +#define REG_FRET TREG_ST0 /* float return register */ + +/* defined if function parameters must be evaluated in reverse order */ +#define INVERT_FUNC_PARAMS + +/* defined if structures are passed as pointers. Otherwise structures + are directly pushed on stack. */ +//#define FUNC_STRUCT_PARAM_AS_PTR + +/* pointer size, in bytes */ +#define PTR_SIZE 4 + +/* long double size and alignment, in bytes */ +#define LDOUBLE_SIZE 12 +#define LDOUBLE_ALIGN 4 +/* maximum alignment (for aligned attribute support) */ +#define MAX_ALIGN 8 + +/******************************************************/ +/* ELF defines */ + +#define EM_TCC_TARGET EM_386 + +/* relocation type for 32 bit data relocation */ +#define R_DATA_32 R_386_32 +#define R_JMP_SLOT R_386_JMP_SLOT +#define R_COPY R_386_COPY + +#define ELF_START_ADDR 0x08048000 +#define ELF_PAGE_SIZE 0x1000 + +/******************************************************/ + +static unsigned long func_sub_sp_offset; +static unsigned long func_bound_offset; +static int func_ret_sub; + +/* XXX: make it faster ? */ +void g(int c) +{ + int ind1; + ind1 = ind + 1; + if (ind1 > cur_text_section->data_allocated) + section_realloc(cur_text_section, ind1); + cur_text_section->data[ind] = c; + ind = ind1; +} + +void o(unsigned int c) +{ + while (c) { + g(c); + c = c >> 8; + } +} + +void gen_le32(int c) +{ + g(c); + g(c >> 8); + g(c >> 16); + g(c >> 24); +} + +/* output a symbol and patch all calls to it */ +void gsym_addr(int t, int a) +{ + int n, *ptr; + while (t) { + ptr = (int *)(cur_text_section->data + t); + n = *ptr; /* next value */ + *ptr = a - t - 4; + t = n; + } +} + +void gsym(int t) +{ + gsym_addr(t, ind); +} + +/* psym is used to put an instruction with a data field which is a + reference to a symbol. It is in fact the same as oad ! */ +#define psym oad + +/* instruction + 4 bytes data. Return the address of the data */ +static int oad(int c, int s) +{ + int ind1; + + o(c); + ind1 = ind + 4; + if (ind1 > cur_text_section->data_allocated) + section_realloc(cur_text_section, ind1); + *(int *)(cur_text_section->data + ind) = s; + s = ind; + ind = ind1; + return s; +} + +/* output constant with relocation if 'r & VT_SYM' is true */ +static void gen_addr32(int r, Sym *sym, int c) +{ + if (r & VT_SYM) + greloc(cur_text_section, sym, ind, R_386_32); + gen_le32(c); +} + +/* generate a modrm reference. 'op_reg' contains the addtionnal 3 + opcode bits */ +static void gen_modrm(int op_reg, int r, Sym *sym, int c) +{ + op_reg = op_reg << 3; + if ((r & VT_VALMASK) == VT_CONST) { + /* constant memory reference */ + o(0x05 | op_reg); + gen_addr32(r, sym, c); + } else if ((r & VT_VALMASK) == VT_LOCAL) { + /* currently, we use only ebp as base */ + if (c == (char)c) { + /* short reference */ + o(0x45 | op_reg); + g(c); + } else { + oad(0x85 | op_reg, c); + } + } else { + g(0x00 | op_reg | (r & VT_VALMASK)); + } +} + + +/* load 'r' from value 'sv' */ +void load(int r, SValue *sv) +{ + int v, t, ft, fc, fr; + SValue v1; + + fr = sv->r; + ft = sv->type.t; + fc = sv->c.ul; + + v = fr & VT_VALMASK; + if (fr & VT_LVAL) { + if (v == VT_LLOCAL) { + v1.type.t = VT_INT; + v1.r = VT_LOCAL | VT_LVAL; + v1.c.ul = fc; + load(r, &v1); + fr = r; + } + if ((ft & VT_BTYPE) == VT_FLOAT) { + o(0xd9); /* flds */ + r = 0; + } else if ((ft & VT_BTYPE) == VT_DOUBLE) { + o(0xdd); /* fldl */ + r = 0; + } else if ((ft & VT_BTYPE) == VT_LDOUBLE) { + o(0xdb); /* fldt */ + r = 5; + } else if ((ft & VT_TYPE) == VT_BYTE) { + o(0xbe0f); /* movsbl */ + } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) { + o(0xb60f); /* movzbl */ + } else if ((ft & VT_TYPE) == VT_SHORT) { + o(0xbf0f); /* movswl */ + } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) { + o(0xb70f); /* movzwl */ + } else { + o(0x8b); /* movl */ + } + gen_modrm(r, fr, sv->sym, fc); + } else { + if (v == VT_CONST) { + o(0xb8 + r); /* mov $xx, r */ + gen_addr32(fr, sv->sym, fc); + } else if (v == VT_LOCAL) { + o(0x8d); /* lea xxx(%ebp), r */ + gen_modrm(r, VT_LOCAL, sv->sym, fc); + } else if (v == VT_CMP) { + oad(0xb8 + r, 0); /* mov $0, r */ + o(0x0f); /* setxx %br */ + o(fc); + o(0xc0 + r); + } else if (v == VT_JMP || v == VT_JMPI) { + t = v & 1; + oad(0xb8 + r, t); /* mov $1, r */ + o(0x05eb); /* jmp after */ + gsym(fc); + oad(0xb8 + r, t ^ 1); /* mov $0, r */ + } else if (v != r) { + o(0x89); + o(0xc0 + r + v * 8); /* mov v, r */ + } + } +} + +/* store register 'r' in lvalue 'v' */ +void store(int r, SValue *v) +{ + int fr, bt, ft, fc; + + ft = v->type.t; + fc = v->c.ul; + fr = v->r & VT_VALMASK; + bt = ft & VT_BTYPE; + /* XXX: incorrect if float reg to reg */ + if (bt == VT_FLOAT) { + o(0xd9); /* fsts */ + r = 2; + } else if (bt == VT_DOUBLE) { + o(0xdd); /* fstpl */ + r = 2; + } else if (bt == VT_LDOUBLE) { + o(0xc0d9); /* fld %st(0) */ + o(0xdb); /* fstpt */ + r = 7; + } else { + if (bt == VT_SHORT) + o(0x66); + if (bt == VT_BYTE || bt == VT_BOOL) + o(0x88); + else + o(0x89); + } + if (fr == VT_CONST || + fr == VT_LOCAL || + (v->r & VT_LVAL)) { + gen_modrm(r, v->r, v->sym, fc); + } else if (fr != r) { + o(0xc0 + fr + r * 8); /* mov r, fr */ + } +} + +static void gadd_sp(int val) +{ + if (val == (char)val) { + o(0xc483); + g(val); + } else { + oad(0xc481, val); /* add $xxx, %esp */ + } +} + +/* 'is_jmp' is '1' if it is a jump */ +static void gcall_or_jmp(int is_jmp) +{ + int r; + if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { + /* constant case */ + if (vtop->r & VT_SYM) { + /* relocation case */ + greloc(cur_text_section, vtop->sym, + ind + 1, R_386_PC32); + } else { + /* put an empty PC32 relocation */ + put_elf_reloc(symtab_section, cur_text_section, + ind + 1, R_386_PC32, 0); + } + oad(0xe8 + is_jmp, vtop->c.ul - 4); /* call/jmp im */ + } else { + /* otherwise, indirect call */ + r = gv(RC_INT); + o(0xff); /* call/jmp *r */ + o(0xd0 + r + (is_jmp << 4)); + } +} + +static uint8_t fastcall_regs[3] = { TREG_EAX, TREG_EDX, TREG_ECX }; +static uint8_t fastcallw_regs[2] = { TREG_ECX, TREG_EDX }; + +/* Generate function call. The function address is pushed first, then + all the parameters in call order. This functions pops all the + parameters and the function address. */ +void gfunc_call(int nb_args) +{ + int size, align, r, args_size, i, func_call; + Sym *func_sym; + + args_size = 0; + for(i = 0;i < nb_args; i++) { + if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { + size = type_size(&vtop->type, &align); + /* align to stack align size */ + size = (size + 3) & ~3; + /* allocate the necessary size on stack */ + oad(0xec81, size); /* sub $xxx, %esp */ + /* generate structure store */ + r = get_reg(RC_INT); + o(0x89); /* mov %esp, r */ + o(0xe0 + r); + vset(&vtop->type, r | VT_LVAL, 0); + vswap(); + vstore(); + args_size += size; + } else if (is_float(vtop->type.t)) { + gv(RC_FLOAT); /* only one float register */ + if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) + size = 4; + else if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) + size = 8; + else + size = 12; + oad(0xec81, size); /* sub $xxx, %esp */ + if (size == 12) + o(0x7cdb); + else + o(0x5cd9 + size - 4); /* fstp[s|l] 0(%esp) */ + g(0x24); + g(0x00); + args_size += size; + } else { + /* simple type (currently always same size) */ + /* XXX: implicit cast ? */ + r = gv(RC_INT); + if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { + size = 8; + o(0x50 + vtop->r2); /* push r */ + } else { + size = 4; + } + o(0x50 + r); /* push r */ + args_size += size; + } + vtop--; + } + save_regs(0); /* save used temporary registers */ + func_sym = vtop->type.ref; + func_call = FUNC_CALL(func_sym->r); + /* fast call case */ + if ((func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) || + func_call == FUNC_FASTCALLW) { + int fastcall_nb_regs; + uint8_t *fastcall_regs_ptr; + if (func_call == FUNC_FASTCALLW) { + fastcall_regs_ptr = fastcallw_regs; + fastcall_nb_regs = 2; + } else { + fastcall_regs_ptr = fastcall_regs; + fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1; + } + for(i = 0;i < fastcall_nb_regs; i++) { + if (args_size <= 0) + break; + o(0x58 + fastcall_regs_ptr[i]); /* pop r */ + /* XXX: incorrect for struct/floats */ + args_size -= 4; + } + } + gcall_or_jmp(0); + if (args_size && func_call != FUNC_STDCALL) + gadd_sp(args_size); + vtop--; +} + +#ifdef TCC_TARGET_PE +#define FUNC_PROLOG_SIZE 10 +#else +#define FUNC_PROLOG_SIZE 9 +#endif + +/* generate function prolog of type 't' */ +void gfunc_prolog(CType *func_type) +{ + int addr, align, size, func_call, fastcall_nb_regs; + int param_index, param_addr; + uint8_t *fastcall_regs_ptr; + Sym *sym; + CType *type; + + sym = func_type->ref; + func_call = FUNC_CALL(sym->r); + addr = 8; + loc = 0; + if (func_call >= FUNC_FASTCALL1 && func_call <= FUNC_FASTCALL3) { + fastcall_nb_regs = func_call - FUNC_FASTCALL1 + 1; + fastcall_regs_ptr = fastcall_regs; + } else if (func_call == FUNC_FASTCALLW) { + fastcall_nb_regs = 2; + fastcall_regs_ptr = fastcallw_regs; + } else { + fastcall_nb_regs = 0; + fastcall_regs_ptr = NULL; + } + param_index = 0; + + ind += FUNC_PROLOG_SIZE; + func_sub_sp_offset = ind; + /* if the function returns a structure, then add an + implicit pointer parameter */ + func_vt = sym->type; + if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { + /* XXX: fastcall case ? */ + func_vc = addr; + addr += 4; + param_index++; + } + /* define parameters */ + while ((sym = sym->next) != NULL) { + type = &sym->type; + size = type_size(type, &align); + size = (size + 3) & ~3; +#ifdef FUNC_STRUCT_PARAM_AS_PTR + /* structs are passed as pointer */ + if ((type->t & VT_BTYPE) == VT_STRUCT) { + size = 4; + } +#endif + if (param_index < fastcall_nb_regs) { + /* save FASTCALL register */ + loc -= 4; + o(0x89); /* movl */ + gen_modrm(fastcall_regs_ptr[param_index], VT_LOCAL, NULL, loc); + param_addr = loc; + } else { + param_addr = addr; + addr += size; + } + sym_push(sym->v & ~SYM_FIELD, type, + VT_LOCAL | lvalue_type(type->t), param_addr); + param_index++; + } + func_ret_sub = 0; + /* pascal type call ? */ + if (func_call == FUNC_STDCALL) + func_ret_sub = addr - 8; + + /* leave some room for bound checking code */ + if (tcc_state->do_bounds_check) { + oad(0xb8, 0); /* lbound section pointer */ + oad(0xb8, 0); /* call to function */ + func_bound_offset = lbounds_section->data_offset; + } +} + +/* generate function epilog */ +void gfunc_epilog(void) +{ + int v, saved_ind; + +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check + && func_bound_offset != lbounds_section->data_offset) { + int saved_ind; + int *bounds_ptr; + Sym *sym, *sym_data; + /* add end of table info */ + bounds_ptr = section_ptr_add(lbounds_section, sizeof(int)); + *bounds_ptr = 0; + /* generate bound local allocation */ + saved_ind = ind; + ind = func_sub_sp_offset; + sym_data = get_sym_ref(&char_pointer_type, lbounds_section, + func_bound_offset, lbounds_section->data_offset); + greloc(cur_text_section, sym_data, + ind + 1, R_386_32); + oad(0xb8, 0); /* mov %eax, xxx */ + sym = external_global_sym(TOK___bound_local_new, &func_old_type, 0); + greloc(cur_text_section, sym, + ind + 1, R_386_PC32); + oad(0xe8, -4); + ind = saved_ind; + /* generate bound check local freeing */ + o(0x5250); /* save returned value, if any */ + greloc(cur_text_section, sym_data, + ind + 1, R_386_32); + oad(0xb8, 0); /* mov %eax, xxx */ + sym = external_global_sym(TOK___bound_local_delete, &func_old_type, 0); + greloc(cur_text_section, sym, + ind + 1, R_386_PC32); + oad(0xe8, -4); + o(0x585a); /* restore returned value, if any */ + } +#endif + o(0xc9); /* leave */ + if (func_ret_sub == 0) { + o(0xc3); /* ret */ + } else { + o(0xc2); /* ret n */ + g(func_ret_sub); + g(func_ret_sub >> 8); + } + /* align local size to word & save local variables */ + + v = (-loc + 3) & -4; + saved_ind = ind; + ind = func_sub_sp_offset - FUNC_PROLOG_SIZE; +#ifdef TCC_TARGET_PE + if (v >= 4096) { + Sym *sym = external_global_sym(TOK___chkstk, &func_old_type, 0); + oad(0xb8, v); /* mov stacksize, %eax */ + oad(0xe8, -4); /* call __chkstk, (does the stackframe too) */ + greloc(cur_text_section, sym, ind-4, R_386_PC32); + } else +#endif + { + o(0xe58955); /* push %ebp, mov %esp, %ebp */ + o(0xec81); /* sub esp, stacksize */ + gen_le32(v); +#if FUNC_PROLOG_SIZE == 10 + o(0x90); /* adjust to FUNC_PROLOG_SIZE */ +#endif + } + ind = saved_ind; +} + +/* generate a jump to a label */ +int gjmp(int t) +{ + return psym(0xe9, t); +} + +/* generate a jump to a fixed address */ +void gjmp_addr(int a) +{ + int r; + r = a - ind - 2; + if (r == (char)r) { + g(0xeb); + g(r); + } else { + oad(0xe9, a - ind - 5); + } +} + +/* generate a test. set 'inv' to invert test. Stack entry is popped */ +int gtst(int inv, int t) +{ + int v, *p; + + v = vtop->r & VT_VALMASK; + if (v == VT_CMP) { + /* fast case : can jump directly since flags are set */ + g(0x0f); + t = psym((vtop->c.i - 16) ^ inv, t); + } else if (v == VT_JMP || v == VT_JMPI) { + /* && or || optimization */ + if ((v & 1) == inv) { + /* insert vtop->c jump list in t */ + p = &vtop->c.i; + while (*p != 0) + p = (int *)(cur_text_section->data + *p); + *p = t; + t = vtop->c.i; + } else { + t = gjmp(t); + gsym(vtop->c.i); + } + } else { + if (is_float(vtop->type.t) || + (vtop->type.t & VT_BTYPE) == VT_LLONG) { + vpushi(0); + gen_op(TOK_NE); + } + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + /* constant jmp optimization */ + if ((vtop->c.i != 0) != inv) + t = gjmp(t); + } else { + v = gv(RC_INT); + o(0x85); + o(0xc0 + v * 9); + g(0x0f); + t = psym(0x85 ^ inv, t); + } + } + vtop--; + return t; +} + +/* generate an integer binary operation */ +void gen_opi(int op) +{ + int r, fr, opc, c; + + switch(op) { + case '+': + case TOK_ADDC1: /* add with carry generation */ + opc = 0; + gen_op8: + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + /* constant case */ + vswap(); + r = gv(RC_INT); + vswap(); + c = vtop->c.i; + if (c == (char)c) { + /* XXX: generate inc and dec for smaller code ? */ + o(0x83); + o(0xc0 | (opc << 3) | r); + g(c); + } else { + o(0x81); + oad(0xc0 | (opc << 3) | r, c); + } + } else { + gv2(RC_INT, RC_INT); + r = vtop[-1].r; + fr = vtop[0].r; + o((opc << 3) | 0x01); + o(0xc0 + r + fr * 8); + } + vtop--; + if (op >= TOK_ULT && op <= TOK_GT) { + vtop->r = VT_CMP; + vtop->c.i = op; + } + break; + case '-': + case TOK_SUBC1: /* sub with carry generation */ + opc = 5; + goto gen_op8; + case TOK_ADDC2: /* add with carry use */ + opc = 2; + goto gen_op8; + case TOK_SUBC2: /* sub with carry use */ + opc = 3; + goto gen_op8; + case '&': + opc = 4; + goto gen_op8; + case '^': + opc = 6; + goto gen_op8; + case '|': + opc = 1; + goto gen_op8; + case '*': + gv2(RC_INT, RC_INT); + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + o(0xaf0f); /* imul fr, r */ + o(0xc0 + fr + r * 8); + break; + case TOK_SHL: + opc = 4; + goto gen_shift; + case TOK_SHR: + opc = 5; + goto gen_shift; + case TOK_SAR: + opc = 7; + gen_shift: + opc = 0xc0 | (opc << 3); + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + /* constant case */ + vswap(); + r = gv(RC_INT); + vswap(); + c = vtop->c.i & 0x1f; + o(0xc1); /* shl/shr/sar $xxx, r */ + o(opc | r); + g(c); + } else { + /* we generate the shift in ecx */ + gv2(RC_INT, RC_ECX); + r = vtop[-1].r; + o(0xd3); /* shl/shr/sar %cl, r */ + o(opc | r); + } + vtop--; + break; + case '/': + case TOK_UDIV: + case TOK_PDIV: + case '%': + case TOK_UMOD: + case TOK_UMULL: + /* first operand must be in eax */ + /* XXX: need better constraint for second operand */ + gv2(RC_EAX, RC_ECX); + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + save_reg(TREG_EDX); + if (op == TOK_UMULL) { + o(0xf7); /* mul fr */ + o(0xe0 + fr); + vtop->r2 = TREG_EDX; + r = TREG_EAX; + } else { + if (op == TOK_UDIV || op == TOK_UMOD) { + o(0xf7d231); /* xor %edx, %edx, div fr, %eax */ + o(0xf0 + fr); + } else { + o(0xf799); /* cltd, idiv fr, %eax */ + o(0xf8 + fr); + } + if (op == '%' || op == TOK_UMOD) + r = TREG_EDX; + else + r = TREG_EAX; + } + vtop->r = r; + break; + default: + opc = 7; + goto gen_op8; + } +} + +/* generate a floating point operation 'v = t1 op t2' instruction. The + two operands are guaranted to have the same floating point type */ +/* XXX: need to use ST1 too */ +void gen_opf(int op) +{ + int a, ft, fc, swapped, r; + + /* convert constants to memory references */ + if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { + vswap(); + gv(RC_FLOAT); + vswap(); + } + if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) + gv(RC_FLOAT); + + /* must put at least one value in the floating point register */ + if ((vtop[-1].r & VT_LVAL) && + (vtop[0].r & VT_LVAL)) { + vswap(); + gv(RC_FLOAT); + vswap(); + } + swapped = 0; + /* swap the stack if needed so that t1 is the register and t2 is + the memory reference */ + if (vtop[-1].r & VT_LVAL) { + vswap(); + swapped = 1; + } + if (op >= TOK_ULT && op <= TOK_GT) { + /* load on stack second operand */ + load(TREG_ST0, vtop); + save_reg(TREG_EAX); /* eax is used by FP comparison code */ + if (op == TOK_GE || op == TOK_GT) + swapped = !swapped; + else if (op == TOK_EQ || op == TOK_NE) + swapped = 0; + if (swapped) + o(0xc9d9); /* fxch %st(1) */ + o(0xe9da); /* fucompp */ + o(0xe0df); /* fnstsw %ax */ + if (op == TOK_EQ) { + o(0x45e480); /* and $0x45, %ah */ + o(0x40fC80); /* cmp $0x40, %ah */ + } else if (op == TOK_NE) { + o(0x45e480); /* and $0x45, %ah */ + o(0x40f480); /* xor $0x40, %ah */ + op = TOK_NE; + } else if (op == TOK_GE || op == TOK_LE) { + o(0x05c4f6); /* test $0x05, %ah */ + op = TOK_EQ; + } else { + o(0x45c4f6); /* test $0x45, %ah */ + op = TOK_EQ; + } + vtop--; + vtop->r = VT_CMP; + vtop->c.i = op; + } else { + /* no memory reference possible for long double operations */ + if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { + load(TREG_ST0, vtop); + swapped = !swapped; + } + + switch(op) { + default: + case '+': + a = 0; + break; + case '-': + a = 4; + if (swapped) + a++; + break; + case '*': + a = 1; + break; + case '/': + a = 6; + if (swapped) + a++; + break; + } + ft = vtop->type.t; + fc = vtop->c.ul; + if ((ft & VT_BTYPE) == VT_LDOUBLE) { + o(0xde); /* fxxxp %st, %st(1) */ + o(0xc1 + (a << 3)); + } else { + /* if saved lvalue, then we must reload it */ + r = vtop->r; + if ((r & VT_VALMASK) == VT_LLOCAL) { + SValue v1; + r = get_reg(RC_INT); + v1.type.t = VT_INT; + v1.r = VT_LOCAL | VT_LVAL; + v1.c.ul = fc; + load(r, &v1); + fc = 0; + } + + if ((ft & VT_BTYPE) == VT_DOUBLE) + o(0xdc); + else + o(0xd8); + gen_modrm(a, r, vtop->sym, fc); + } + vtop--; + } +} + +/* convert integers to fp 't' type. Must handle 'int', 'unsigned int' + and 'long long' cases. */ +void gen_cvt_itof(int t) +{ + save_reg(TREG_ST0); + gv(RC_INT); + if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { + /* signed long long to float/double/long double (unsigned case + is handled generically) */ + o(0x50 + vtop->r2); /* push r2 */ + o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ + o(0x242cdf); /* fildll (%esp) */ + o(0x08c483); /* add $8, %esp */ + } else if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == + (VT_INT | VT_UNSIGNED)) { + /* unsigned int to float/double/long double */ + o(0x6a); /* push $0 */ + g(0x00); + o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ + o(0x242cdf); /* fildll (%esp) */ + o(0x08c483); /* add $8, %esp */ + } else { + /* int to float/double/long double */ + o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ + o(0x2404db); /* fildl (%esp) */ + o(0x04c483); /* add $4, %esp */ + } + vtop->r = TREG_ST0; +} + +/* convert fp to int 't' type */ +/* XXX: handle long long case */ +void gen_cvt_ftoi(int t) +{ + int r, r2, size; + Sym *sym; + CType ushort_type; + + ushort_type.t = VT_SHORT | VT_UNSIGNED; + + gv(RC_FLOAT); + if (t != VT_INT) + size = 8; + else + size = 4; + + o(0x2dd9); /* ldcw xxx */ + sym = external_global_sym(TOK___tcc_int_fpu_control, + &ushort_type, VT_LVAL); + greloc(cur_text_section, sym, + ind, R_386_32); + gen_le32(0); + + oad(0xec81, size); /* sub $xxx, %esp */ + if (size == 4) + o(0x1cdb); /* fistpl */ + else + o(0x3cdf); /* fistpll */ + o(0x24); + o(0x2dd9); /* ldcw xxx */ + sym = external_global_sym(TOK___tcc_fpu_control, + &ushort_type, VT_LVAL); + greloc(cur_text_section, sym, + ind, R_386_32); + gen_le32(0); + + r = get_reg(RC_INT); + o(0x58 + r); /* pop r */ + if (size == 8) { + if (t == VT_LLONG) { + vtop->r = r; /* mark reg as used */ + r2 = get_reg(RC_INT); + o(0x58 + r2); /* pop r2 */ + vtop->r2 = r2; + } else { + o(0x04c483); /* add $4, %esp */ + } + } + vtop->r = r; +} + +/* convert from one floating point type to another */ +void gen_cvt_ftof(int t) +{ + /* all we have to do on i386 is to put the float in a register */ + gv(RC_FLOAT); +} + +/* computed goto support */ +void ggoto(void) +{ + gcall_or_jmp(1); + vtop--; +} + +/* bound check support functions */ +#ifdef CONFIG_TCC_BCHECK + +/* generate a bounded pointer addition */ +void gen_bounded_ptr_add(void) +{ + Sym *sym; + + /* prepare fast i386 function call (args in eax and edx) */ + gv2(RC_EAX, RC_EDX); + /* save all temporary registers */ + vtop -= 2; + save_regs(0); + /* do a fast function call */ + sym = external_global_sym(TOK___bound_ptr_add, &func_old_type, 0); + greloc(cur_text_section, sym, + ind + 1, R_386_PC32); + oad(0xe8, -4); + /* returned pointer is in eax */ + vtop++; + vtop->r = TREG_EAX | VT_BOUNDED; + /* address of bounding function call point */ + vtop->c.ul = (cur_text_section->reloc->data_offset - sizeof(Elf32_Rel)); +} + +/* patch pointer addition in vtop so that pointer dereferencing is + also tested */ +void gen_bounded_ptr_deref(void) +{ + int func; + int size, align; + Elf32_Rel *rel; + Sym *sym; + + size = 0; + /* XXX: put that code in generic part of tcc */ + if (!is_float(vtop->type.t)) { + if (vtop->r & VT_LVAL_BYTE) + size = 1; + else if (vtop->r & VT_LVAL_SHORT) + size = 2; + } + if (!size) + size = type_size(&vtop->type, &align); + switch(size) { + case 1: func = TOK___bound_ptr_indir1; break; + case 2: func = TOK___bound_ptr_indir2; break; + case 4: func = TOK___bound_ptr_indir4; break; + case 8: func = TOK___bound_ptr_indir8; break; + case 12: func = TOK___bound_ptr_indir12; break; + case 16: func = TOK___bound_ptr_indir16; break; + default: + error("unhandled size when derefencing bounded pointer"); + func = 0; + break; + } + + /* patch relocation */ + /* XXX: find a better solution ? */ + rel = (Elf32_Rel *)(cur_text_section->reloc->data + vtop->c.ul); + sym = external_global_sym(func, &func_old_type, 0); + if (!sym->c) + put_extern_sym(sym, NULL, 0, 0); + rel->r_info = ELF32_R_INFO(sym->c, ELF32_R_TYPE(rel->r_info)); +} +#endif + +/* end of X86 code generator */ +/*************************************************************/ + diff --git a/05/tcc-0.9.25/il-gen.c b/05/tcc-0.9.25/il-gen.c new file mode 100644 index 0000000..29f0526 --- /dev/null +++ b/05/tcc-0.9.25/il-gen.c @@ -0,0 +1,667 @@ +/* + * CIL code generator for TCC + * + * Copyright (c) 2002 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* number of available registers */ +#define NB_REGS 3 + +/* a register can belong to several classes. The classes must be + sorted from more general to more precise (see gv2() code which does + assumptions on it). */ +#define RC_ST 0x0001 /* any stack entry */ +#define RC_ST0 0x0002 /* top of stack */ +#define RC_ST1 0x0004 /* top - 1 */ + +#define RC_INT RC_ST +#define RC_FLOAT RC_ST +#define RC_IRET RC_ST0 /* function return: integer register */ +#define RC_LRET RC_ST0 /* function return: second integer register */ +#define RC_FRET RC_ST0 /* function return: float register */ + +/* pretty names for the registers */ +enum { + REG_ST0 = 0, + REG_ST1, + REG_ST2, +}; + +int reg_classes[NB_REGS] = { + /* ST0 */ RC_ST | RC_ST0, + /* ST1 */ RC_ST | RC_ST1, + /* ST2 */ RC_ST, +}; + +/* return registers for function */ +#define REG_IRET REG_ST0 /* single word int return register */ +#define REG_LRET REG_ST0 /* second word return register (for long long) */ +#define REG_FRET REG_ST0 /* float return register */ + +/* defined if function parameters must be evaluated in reverse order */ +//#define INVERT_FUNC_PARAMS + +/* defined if structures are passed as pointers. Otherwise structures + are directly pushed on stack. */ +//#define FUNC_STRUCT_PARAM_AS_PTR + +/* pointer size, in bytes */ +#define PTR_SIZE 4 + +/* long double size and alignment, in bytes */ +#define LDOUBLE_SIZE 8 +#define LDOUBLE_ALIGN 8 + +/* function call context */ +typedef struct GFuncContext { + int func_call; /* func call type (FUNC_STDCALL or FUNC_CDECL) */ +} GFuncContext; + +/******************************************************/ +/* opcode definitions */ + +#define IL_OP_PREFIX 0xFE + +enum ILOPCodes { +#define OP(name, str, n) IL_OP_ ## name = n, +#include "il-opcodes.h" +#undef OP +}; + +char *il_opcodes_str[] = { +#define OP(name, str, n) [n] = str, +#include "il-opcodes.h" +#undef OP +}; + +/******************************************************/ + +/* arguments variable numbers start from there */ +#define ARG_BASE 0x70000000 + +static FILE *il_outfile; + +static void out_byte(int c) +{ + *(char *)ind++ = c; +} + +static void out_le32(int c) +{ + out_byte(c); + out_byte(c >> 8); + out_byte(c >> 16); + out_byte(c >> 24); +} + +static void init_outfile(void) +{ + if (!il_outfile) { + il_outfile = stdout; + fprintf(il_outfile, + ".assembly extern mscorlib\n" + "{\n" + ".ver 1:0:2411:0\n" + "}\n\n"); + } +} + +static void out_op1(int op) +{ + if (op & 0x100) + out_byte(IL_OP_PREFIX); + out_byte(op & 0xff); +} + +/* output an opcode with prefix */ +static void out_op(int op) +{ + out_op1(op); + fprintf(il_outfile, " %s\n", il_opcodes_str[op]); +} + +static void out_opb(int op, int c) +{ + out_op1(op); + out_byte(c); + fprintf(il_outfile, " %s %d\n", il_opcodes_str[op], c); +} + +static void out_opi(int op, int c) +{ + out_op1(op); + out_le32(c); + fprintf(il_outfile, " %s 0x%x\n", il_opcodes_str[op], c); +} + +/* XXX: not complete */ +static void il_type_to_str(char *buf, int buf_size, + int t, const char *varstr) +{ + int bt; + Sym *s, *sa; + char buf1[256]; + const char *tstr; + + t = t & VT_TYPE; + bt = t & VT_BTYPE; + buf[0] = '\0'; + if (t & VT_UNSIGNED) + pstrcat(buf, buf_size, "unsigned "); + switch(bt) { + case VT_VOID: + tstr = "void"; + goto add_tstr; + case VT_BOOL: + tstr = "bool"; + goto add_tstr; + case VT_BYTE: + tstr = "int8"; + goto add_tstr; + case VT_SHORT: + tstr = "int16"; + goto add_tstr; + case VT_ENUM: + case VT_INT: + case VT_LONG: + tstr = "int32"; + goto add_tstr; + case VT_LLONG: + tstr = "int64"; + goto add_tstr; + case VT_FLOAT: + tstr = "float32"; + goto add_tstr; + case VT_DOUBLE: + case VT_LDOUBLE: + tstr = "float64"; + add_tstr: + pstrcat(buf, buf_size, tstr); + break; + case VT_STRUCT: + error("structures not handled yet"); + break; + case VT_FUNC: + s = sym_find((unsigned)t >> VT_STRUCT_SHIFT); + il_type_to_str(buf, buf_size, s->t, varstr); + pstrcat(buf, buf_size, "("); + sa = s->next; + while (sa != NULL) { + il_type_to_str(buf1, sizeof(buf1), sa->t, NULL); + pstrcat(buf, buf_size, buf1); + sa = sa->next; + if (sa) + pstrcat(buf, buf_size, ", "); + } + pstrcat(buf, buf_size, ")"); + goto no_var; + case VT_PTR: + s = sym_find((unsigned)t >> VT_STRUCT_SHIFT); + pstrcpy(buf1, sizeof(buf1), "*"); + if (varstr) + pstrcat(buf1, sizeof(buf1), varstr); + il_type_to_str(buf, buf_size, s->t, buf1); + goto no_var; + } + if (varstr) { + pstrcat(buf, buf_size, " "); + pstrcat(buf, buf_size, varstr); + } + no_var: ; +} + + +/* patch relocation entry with value 'val' */ +void greloc_patch1(Reloc *p, int val) +{ +} + +/* output a symbol and patch all calls to it */ +void gsym_addr(t, a) +{ +} + +/* output jump and return symbol */ +static int out_opj(int op, int c) +{ + out_op1(op); + out_le32(0); + if (c == 0) { + c = ind - (int)cur_text_section->data; + } + fprintf(il_outfile, " %s L%d\n", il_opcodes_str[op], c); + return c; +} + +void gsym(int t) +{ + fprintf(il_outfile, "L%d:\n", t); +} + +/* load 'r' from value 'sv' */ +void load(int r, SValue *sv) +{ + int v, fc, ft; + + v = sv->r & VT_VALMASK; + fc = sv->c.i; + ft = sv->t; + + if (sv->r & VT_LVAL) { + if (v == VT_LOCAL) { + if (fc >= ARG_BASE) { + fc -= ARG_BASE; + if (fc >= 0 && fc <= 4) { + out_op(IL_OP_LDARG_0 + fc); + } else if (fc <= 0xff) { + out_opb(IL_OP_LDARG_S, fc); + } else { + out_opi(IL_OP_LDARG, fc); + } + } else { + if (fc >= 0 && fc <= 4) { + out_op(IL_OP_LDLOC_0 + fc); + } else if (fc <= 0xff) { + out_opb(IL_OP_LDLOC_S, fc); + } else { + out_opi(IL_OP_LDLOC, fc); + } + } + } else if (v == VT_CONST) { + /* XXX: handle globals */ + out_opi(IL_OP_LDSFLD, 0); + } else { + if ((ft & VT_BTYPE) == VT_FLOAT) { + out_op(IL_OP_LDIND_R4); + } else if ((ft & VT_BTYPE) == VT_DOUBLE) { + out_op(IL_OP_LDIND_R8); + } else if ((ft & VT_BTYPE) == VT_LDOUBLE) { + out_op(IL_OP_LDIND_R8); + } else if ((ft & VT_TYPE) == VT_BYTE) + out_op(IL_OP_LDIND_I1); + else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) + out_op(IL_OP_LDIND_U1); + else if ((ft & VT_TYPE) == VT_SHORT) + out_op(IL_OP_LDIND_I2); + else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) + out_op(IL_OP_LDIND_U2); + else + out_op(IL_OP_LDIND_I4); + } + } else { + if (v == VT_CONST) { + /* XXX: handle globals */ + if (fc >= -1 && fc <= 8) { + out_op(IL_OP_LDC_I4_M1 + fc + 1); + } else { + out_opi(IL_OP_LDC_I4, fc); + } + } else if (v == VT_LOCAL) { + if (fc >= ARG_BASE) { + fc -= ARG_BASE; + if (fc <= 0xff) { + out_opb(IL_OP_LDARGA_S, fc); + } else { + out_opi(IL_OP_LDARGA, fc); + } + } else { + if (fc <= 0xff) { + out_opb(IL_OP_LDLOCA_S, fc); + } else { + out_opi(IL_OP_LDLOCA, fc); + } + } + } else { + /* XXX: do it */ + } + } +} + +/* store register 'r' in lvalue 'v' */ +void store(int r, SValue *sv) +{ + int v, fc, ft; + + v = sv->r & VT_VALMASK; + fc = sv->c.i; + ft = sv->t; + if (v == VT_LOCAL) { + if (fc >= ARG_BASE) { + fc -= ARG_BASE; + /* XXX: check IL arg store semantics */ + if (fc <= 0xff) { + out_opb(IL_OP_STARG_S, fc); + } else { + out_opi(IL_OP_STARG, fc); + } + } else { + if (fc >= 0 && fc <= 4) { + out_op(IL_OP_STLOC_0 + fc); + } else if (fc <= 0xff) { + out_opb(IL_OP_STLOC_S, fc); + } else { + out_opi(IL_OP_STLOC, fc); + } + } + } else if (v == VT_CONST) { + /* XXX: handle globals */ + out_opi(IL_OP_STSFLD, 0); + } else { + if ((ft & VT_BTYPE) == VT_FLOAT) + out_op(IL_OP_STIND_R4); + else if ((ft & VT_BTYPE) == VT_DOUBLE) + out_op(IL_OP_STIND_R8); + else if ((ft & VT_BTYPE) == VT_LDOUBLE) + out_op(IL_OP_STIND_R8); + else if ((ft & VT_BTYPE) == VT_BYTE) + out_op(IL_OP_STIND_I1); + else if ((ft & VT_BTYPE) == VT_SHORT) + out_op(IL_OP_STIND_I2); + else + out_op(IL_OP_STIND_I4); + } +} + +/* start function call and return function call context */ +void gfunc_start(GFuncContext *c, int func_call) +{ + c->func_call = func_call; +} + +/* push function parameter which is in (vtop->t, vtop->c). Stack entry + is then popped. */ +void gfunc_param(GFuncContext *c) +{ + if ((vtop->t & VT_BTYPE) == VT_STRUCT) { + error("structures passed as value not handled yet"); + } else { + /* simply push on stack */ + gv(RC_ST0); + } + vtop--; +} + +/* generate function call with address in (vtop->t, vtop->c) and free function + context. Stack entry is popped */ +void gfunc_call(GFuncContext *c) +{ + char buf[1024]; + + if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { + /* XXX: more info needed from tcc */ + il_type_to_str(buf, sizeof(buf), vtop->t, "xxx"); + fprintf(il_outfile, " call %s\n", buf); + } else { + /* indirect call */ + gv(RC_INT); + il_type_to_str(buf, sizeof(buf), vtop->t, NULL); + fprintf(il_outfile, " calli %s\n", buf); + } + vtop--; +} + +/* generate function prolog of type 't' */ +void gfunc_prolog(int t) +{ + int addr, u, func_call; + Sym *sym; + char buf[1024]; + + init_outfile(); + + /* XXX: pass function name to gfunc_prolog */ + il_type_to_str(buf, sizeof(buf), t, funcname); + fprintf(il_outfile, ".method static %s il managed\n", buf); + fprintf(il_outfile, "{\n"); + /* XXX: cannot do better now */ + fprintf(il_outfile, " .maxstack %d\n", NB_REGS); + fprintf(il_outfile, " .locals (int32, int32, int32, int32, int32, int32, int32, int32)\n"); + + if (!strcmp(funcname, "main")) + fprintf(il_outfile, " .entrypoint\n"); + + sym = sym_find((unsigned)t >> VT_STRUCT_SHIFT); + func_call = sym->r; + + addr = ARG_BASE; + /* if the function returns a structure, then add an + implicit pointer parameter */ + func_vt = sym->t; + if ((func_vt & VT_BTYPE) == VT_STRUCT) { + func_vc = addr; + addr++; + } + /* define parameters */ + while ((sym = sym->next) != NULL) { + u = sym->t; + sym_push(sym->v & ~SYM_FIELD, u, + VT_LOCAL | lvalue_type(sym->type.t), addr); + addr++; + } +} + +/* generate function epilog */ +void gfunc_epilog(void) +{ + out_op(IL_OP_RET); + fprintf(il_outfile, "}\n\n"); +} + +/* generate a jump to a label */ +int gjmp(int t) +{ + return out_opj(IL_OP_BR, t); +} + +/* generate a jump to a fixed address */ +void gjmp_addr(int a) +{ + /* XXX: handle syms */ + out_opi(IL_OP_BR, a); +} + +/* generate a test. set 'inv' to invert test. Stack entry is popped */ +int gtst(int inv, int t) +{ + int v, *p, c; + + v = vtop->r & VT_VALMASK; + if (v == VT_CMP) { + c = vtop->c.i ^ inv; + switch(c) { + case TOK_EQ: + c = IL_OP_BEQ; + break; + case TOK_NE: + c = IL_OP_BNE_UN; + break; + case TOK_LT: + c = IL_OP_BLT; + break; + case TOK_LE: + c = IL_OP_BLE; + break; + case TOK_GT: + c = IL_OP_BGT; + break; + case TOK_GE: + c = IL_OP_BGE; + break; + case TOK_ULT: + c = IL_OP_BLT_UN; + break; + case TOK_ULE: + c = IL_OP_BLE_UN; + break; + case TOK_UGT: + c = IL_OP_BGT_UN; + break; + case TOK_UGE: + c = IL_OP_BGE_UN; + break; + } + t = out_opj(c, t); + } else if (v == VT_JMP || v == VT_JMPI) { + /* && or || optimization */ + if ((v & 1) == inv) { + /* insert vtop->c jump list in t */ + p = &vtop->c.i; + while (*p != 0) + p = (int *)*p; + *p = t; + t = vtop->c.i; + } else { + t = gjmp(t); + gsym(vtop->c.i); + } + } else { + if (is_float(vtop->t)) { + vpushi(0); + gen_op(TOK_NE); + } + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_FORWARD)) == VT_CONST) { + /* constant jmp optimization */ + if ((vtop->c.i != 0) != inv) + t = gjmp(t); + } else { + v = gv(RC_INT); + t = out_opj(IL_OP_BRTRUE - inv, t); + } + } + vtop--; + return t; +} + +/* generate an integer binary operation */ +void gen_opi(int op) +{ + gv2(RC_ST1, RC_ST0); + switch(op) { + case '+': + out_op(IL_OP_ADD); + goto std_op; + case '-': + out_op(IL_OP_SUB); + goto std_op; + case '&': + out_op(IL_OP_AND); + goto std_op; + case '^': + out_op(IL_OP_XOR); + goto std_op; + case '|': + out_op(IL_OP_OR); + goto std_op; + case '*': + out_op(IL_OP_MUL); + goto std_op; + case TOK_SHL: + out_op(IL_OP_SHL); + goto std_op; + case TOK_SHR: + out_op(IL_OP_SHR_UN); + goto std_op; + case TOK_SAR: + out_op(IL_OP_SHR); + goto std_op; + case '/': + case TOK_PDIV: + out_op(IL_OP_DIV); + goto std_op; + case TOK_UDIV: + out_op(IL_OP_DIV_UN); + goto std_op; + case '%': + out_op(IL_OP_REM); + goto std_op; + case TOK_UMOD: + out_op(IL_OP_REM_UN); + std_op: + vtop--; + vtop[0].r = REG_ST0; + break; + case TOK_EQ: + case TOK_NE: + case TOK_LT: + case TOK_LE: + case TOK_GT: + case TOK_GE: + case TOK_ULT: + case TOK_ULE: + case TOK_UGT: + case TOK_UGE: + vtop--; + vtop[0].r = VT_CMP; + vtop[0].c.i = op; + break; + } +} + +/* generate a floating point operation 'v = t1 op t2' instruction. The + two operands are guaranted to have the same floating point type */ +void gen_opf(int op) +{ + /* same as integer */ + gen_opi(op); +} + +/* convert integers to fp 't' type. Must handle 'int', 'unsigned int' + and 'long long' cases. */ +void gen_cvt_itof(int t) +{ + gv(RC_ST0); + if (t == VT_FLOAT) + out_op(IL_OP_CONV_R4); + else + out_op(IL_OP_CONV_R8); +} + +/* convert fp to int 't' type */ +/* XXX: handle long long case */ +void gen_cvt_ftoi(int t) +{ + gv(RC_ST0); + switch(t) { + case VT_INT | VT_UNSIGNED: + out_op(IL_OP_CONV_U4); + break; + case VT_LLONG: + out_op(IL_OP_CONV_I8); + break; + case VT_LLONG | VT_UNSIGNED: + out_op(IL_OP_CONV_U8); + break; + default: + out_op(IL_OP_CONV_I4); + break; + } +} + +/* convert from one floating point type to another */ +void gen_cvt_ftof(int t) +{ + gv(RC_ST0); + if (t == VT_FLOAT) { + out_op(IL_OP_CONV_R4); + } else { + out_op(IL_OP_CONV_R8); + } +} + +/* end of CIL code generator */ +/*************************************************************/ + diff --git a/05/tcc-0.9.25/il-opcodes.h b/05/tcc-0.9.25/il-opcodes.h new file mode 100644 index 0000000..d53ffb2 --- /dev/null +++ b/05/tcc-0.9.25/il-opcodes.h @@ -0,0 +1,251 @@ +/* + * CIL opcode definition + * + * Copyright (c) 2002 Fabrice Bellard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +OP(NOP, "nop", 0x00) +OP(BREAK, "break", 0x01) +OP(LDARG_0, "ldarg.0", 0x02) +OP(LDARG_1, "ldarg.1", 0x03) +OP(LDARG_2, "ldarg.2", 0x04) +OP(LDARG_3, "ldarg.3", 0x05) +OP(LDLOC_0, "ldloc.0", 0x06) +OP(LDLOC_1, "ldloc.1", 0x07) +OP(LDLOC_2, "ldloc.2", 0x08) +OP(LDLOC_3, "ldloc.3", 0x09) +OP(STLOC_0, "stloc.0", 0x0a) +OP(STLOC_1, "stloc.1", 0x0b) +OP(STLOC_2, "stloc.2", 0x0c) +OP(STLOC_3, "stloc.3", 0x0d) +OP(LDARG_S, "ldarg.s", 0x0e) +OP(LDARGA_S, "ldarga.s", 0x0f) +OP(STARG_S, "starg.s", 0x10) +OP(LDLOC_S, "ldloc.s", 0x11) +OP(LDLOCA_S, "ldloca.s", 0x12) +OP(STLOC_S, "stloc.s", 0x13) +OP(LDNULL, "ldnull", 0x14) +OP(LDC_I4_M1, "ldc.i4.m1", 0x15) +OP(LDC_I4_0, "ldc.i4.0", 0x16) +OP(LDC_I4_1, "ldc.i4.1", 0x17) +OP(LDC_I4_2, "ldc.i4.2", 0x18) +OP(LDC_I4_3, "ldc.i4.3", 0x19) +OP(LDC_I4_4, "ldc.i4.4", 0x1a) +OP(LDC_I4_5, "ldc.i4.5", 0x1b) +OP(LDC_I4_6, "ldc.i4.6", 0x1c) +OP(LDC_I4_7, "ldc.i4.7", 0x1d) +OP(LDC_I4_8, "ldc.i4.8", 0x1e) +OP(LDC_I4_S, "ldc.i4.s", 0x1f) +OP(LDC_I4, "ldc.i4", 0x20) +OP(LDC_I8, "ldc.i8", 0x21) +OP(LDC_R4, "ldc.r4", 0x22) +OP(LDC_R8, "ldc.r8", 0x23) +OP(LDPTR, "ldptr", 0x24) +OP(DUP, "dup", 0x25) +OP(POP, "pop", 0x26) +OP(JMP, "jmp", 0x27) +OP(CALL, "call", 0x28) +OP(CALLI, "calli", 0x29) +OP(RET, "ret", 0x2a) +OP(BR_S, "br.s", 0x2b) +OP(BRFALSE_S, "brfalse.s", 0x2c) +OP(BRTRUE_S, "brtrue.s", 0x2d) +OP(BEQ_S, "beq.s", 0x2e) +OP(BGE_S, "bge.s", 0x2f) +OP(BGT_S, "bgt.s", 0x30) +OP(BLE_S, "ble.s", 0x31) +OP(BLT_S, "blt.s", 0x32) +OP(BNE_UN_S, "bne.un.s", 0x33) +OP(BGE_UN_S, "bge.un.s", 0x34) +OP(BGT_UN_S, "bgt.un.s", 0x35) +OP(BLE_UN_S, "ble.un.s", 0x36) +OP(BLT_UN_S, "blt.un.s", 0x37) +OP(BR, "br", 0x38) +OP(BRFALSE, "brfalse", 0x39) +OP(BRTRUE, "brtrue", 0x3a) +OP(BEQ, "beq", 0x3b) +OP(BGE, "bge", 0x3c) +OP(BGT, "bgt", 0x3d) +OP(BLE, "ble", 0x3e) +OP(BLT, "blt", 0x3f) +OP(BNE_UN, "bne.un", 0x40) +OP(BGE_UN, "bge.un", 0x41) +OP(BGT_UN, "bgt.un", 0x42) +OP(BLE_UN, "ble.un", 0x43) +OP(BLT_UN, "blt.un", 0x44) +OP(SWITCH, "switch", 0x45) +OP(LDIND_I1, "ldind.i1", 0x46) +OP(LDIND_U1, "ldind.u1", 0x47) +OP(LDIND_I2, "ldind.i2", 0x48) +OP(LDIND_U2, "ldind.u2", 0x49) +OP(LDIND_I4, "ldind.i4", 0x4a) +OP(LDIND_U4, "ldind.u4", 0x4b) +OP(LDIND_I8, "ldind.i8", 0x4c) +OP(LDIND_I, "ldind.i", 0x4d) +OP(LDIND_R4, "ldind.r4", 0x4e) +OP(LDIND_R8, "ldind.r8", 0x4f) +OP(LDIND_REF, "ldind.ref", 0x50) +OP(STIND_REF, "stind.ref", 0x51) +OP(STIND_I1, "stind.i1", 0x52) +OP(STIND_I2, "stind.i2", 0x53) +OP(STIND_I4, "stind.i4", 0x54) +OP(STIND_I8, "stind.i8", 0x55) +OP(STIND_R4, "stind.r4", 0x56) +OP(STIND_R8, "stind.r8", 0x57) +OP(ADD, "add", 0x58) +OP(SUB, "sub", 0x59) +OP(MUL, "mul", 0x5a) +OP(DIV, "div", 0x5b) +OP(DIV_UN, "div.un", 0x5c) +OP(REM, "rem", 0x5d) +OP(REM_UN, "rem.un", 0x5e) +OP(AND, "and", 0x5f) +OP(OR, "or", 0x60) +OP(XOR, "xor", 0x61) +OP(SHL, "shl", 0x62) +OP(SHR, "shr", 0x63) +OP(SHR_UN, "shr.un", 0x64) +OP(NEG, "neg", 0x65) +OP(NOT, "not", 0x66) +OP(CONV_I1, "conv.i1", 0x67) +OP(CONV_I2, "conv.i2", 0x68) +OP(CONV_I4, "conv.i4", 0x69) +OP(CONV_I8, "conv.i8", 0x6a) +OP(CONV_R4, "conv.r4", 0x6b) +OP(CONV_R8, "conv.r8", 0x6c) +OP(CONV_U4, "conv.u4", 0x6d) +OP(CONV_U8, "conv.u8", 0x6e) +OP(CALLVIRT, "callvirt", 0x6f) +OP(CPOBJ, "cpobj", 0x70) +OP(LDOBJ, "ldobj", 0x71) +OP(LDSTR, "ldstr", 0x72) +OP(NEWOBJ, "newobj", 0x73) +OP(CASTCLASS, "castclass", 0x74) +OP(ISINST, "isinst", 0x75) +OP(CONV_R_UN, "conv.r.un", 0x76) +OP(ANN_DATA_S, "ann.data.s", 0x77) +OP(UNBOX, "unbox", 0x79) +OP(THROW, "throw", 0x7a) +OP(LDFLD, "ldfld", 0x7b) +OP(LDFLDA, "ldflda", 0x7c) +OP(STFLD, "stfld", 0x7d) +OP(LDSFLD, "ldsfld", 0x7e) +OP(LDSFLDA, "ldsflda", 0x7f) +OP(STSFLD, "stsfld", 0x80) +OP(STOBJ, "stobj", 0x81) +OP(CONV_OVF_I1_UN, "conv.ovf.i1.un", 0x82) +OP(CONV_OVF_I2_UN, "conv.ovf.i2.un", 0x83) +OP(CONV_OVF_I4_UN, "conv.ovf.i4.un", 0x84) +OP(CONV_OVF_I8_UN, "conv.ovf.i8.un", 0x85) +OP(CONV_OVF_U1_UN, "conv.ovf.u1.un", 0x86) +OP(CONV_OVF_U2_UN, "conv.ovf.u2.un", 0x87) +OP(CONV_OVF_U4_UN, "conv.ovf.u4.un", 0x88) +OP(CONV_OVF_U8_UN, "conv.ovf.u8.un", 0x89) +OP(CONV_OVF_I_UN, "conv.ovf.i.un", 0x8a) +OP(CONV_OVF_U_UN, "conv.ovf.u.un", 0x8b) +OP(BOX, "box", 0x8c) +OP(NEWARR, "newarr", 0x8d) +OP(LDLEN, "ldlen", 0x8e) +OP(LDELEMA, "ldelema", 0x8f) +OP(LDELEM_I1, "ldelem.i1", 0x90) +OP(LDELEM_U1, "ldelem.u1", 0x91) +OP(LDELEM_I2, "ldelem.i2", 0x92) +OP(LDELEM_U2, "ldelem.u2", 0x93) +OP(LDELEM_I4, "ldelem.i4", 0x94) +OP(LDELEM_U4, "ldelem.u4", 0x95) +OP(LDELEM_I8, "ldelem.i8", 0x96) +OP(LDELEM_I, "ldelem.i", 0x97) +OP(LDELEM_R4, "ldelem.r4", 0x98) +OP(LDELEM_R8, "ldelem.r8", 0x99) +OP(LDELEM_REF, "ldelem.ref", 0x9a) +OP(STELEM_I, "stelem.i", 0x9b) +OP(STELEM_I1, "stelem.i1", 0x9c) +OP(STELEM_I2, "stelem.i2", 0x9d) +OP(STELEM_I4, "stelem.i4", 0x9e) +OP(STELEM_I8, "stelem.i8", 0x9f) +OP(STELEM_R4, "stelem.r4", 0xa0) +OP(STELEM_R8, "stelem.r8", 0xa1) +OP(STELEM_REF, "stelem.ref", 0xa2) +OP(CONV_OVF_I1, "conv.ovf.i1", 0xb3) +OP(CONV_OVF_U1, "conv.ovf.u1", 0xb4) +OP(CONV_OVF_I2, "conv.ovf.i2", 0xb5) +OP(CONV_OVF_U2, "conv.ovf.u2", 0xb6) +OP(CONV_OVF_I4, "conv.ovf.i4", 0xb7) +OP(CONV_OVF_U4, "conv.ovf.u4", 0xb8) +OP(CONV_OVF_I8, "conv.ovf.i8", 0xb9) +OP(CONV_OVF_U8, "conv.ovf.u8", 0xba) +OP(REFANYVAL, "refanyval", 0xc2) +OP(CKFINITE, "ckfinite", 0xc3) +OP(MKREFANY, "mkrefany", 0xc6) +OP(ANN_CALL, "ann.call", 0xc7) +OP(ANN_CATCH, "ann.catch", 0xc8) +OP(ANN_DEAD, "ann.dead", 0xc9) +OP(ANN_HOISTED, "ann.hoisted", 0xca) +OP(ANN_HOISTED_CALL, "ann.hoisted.call", 0xcb) +OP(ANN_LAB, "ann.lab", 0xcc) +OP(ANN_DEF, "ann.def", 0xcd) +OP(ANN_REF_S, "ann.ref.s", 0xce) +OP(ANN_PHI, "ann.phi", 0xcf) +OP(LDTOKEN, "ldtoken", 0xd0) +OP(CONV_U2, "conv.u2", 0xd1) +OP(CONV_U1, "conv.u1", 0xd2) +OP(CONV_I, "conv.i", 0xd3) +OP(CONV_OVF_I, "conv.ovf.i", 0xd4) +OP(CONV_OVF_U, "conv.ovf.u", 0xd5) +OP(ADD_OVF, "add.ovf", 0xd6) +OP(ADD_OVF_UN, "add.ovf.un", 0xd7) +OP(MUL_OVF, "mul.ovf", 0xd8) +OP(MUL_OVF_UN, "mul.ovf.un", 0xd9) +OP(SUB_OVF, "sub.ovf", 0xda) +OP(SUB_OVF_UN, "sub.ovf.un", 0xdb) +OP(ENDFINALLY, "endfinally", 0xdc) +OP(LEAVE, "leave", 0xdd) +OP(LEAVE_S, "leave.s", 0xde) +OP(STIND_I, "stind.i", 0xdf) +OP(CONV_U, "conv.u", 0xe0) + +/* prefix instructions. we use an opcode >= 256 to ease coding */ + +OP(ARGLIST, "arglist", 0x100) +OP(CEQ, "ceq", 0x101) +OP(CGT, "cgt", 0x102) +OP(CGT_UN, "cgt.un", 0x103) +OP(CLT, "clt", 0x104) +OP(CLT_UN, "clt.un", 0x105) +OP(LDFTN, "ldftn", 0x106) +OP(LDVIRTFTN, "ldvirtftn", 0x107) +OP(JMPI, "jmpi", 0x108) +OP(LDARG, "ldarg", 0x109) +OP(LDARGA, "ldarga", 0x10a) +OP(STARG, "starg", 0x10b) +OP(LDLOC, "ldloc", 0x10c) +OP(LDLOCA, "ldloca", 0x10d) +OP(STLOC, "stloc", 0x10e) +OP(LOCALLOC, "localloc", 0x10f) +OP(ENDFILTER, "endfilter", 0x111) +OP(UNALIGNED, "unaligned", 0x112) +OP(VOLATILE, "volatile", 0x113) +OP(TAIL, "tail", 0x114) +OP(INITOBJ, "initobj", 0x115) +OP(ANN_LIVE, "ann.live", 0x116) +OP(CPBLK, "cpblk", 0x117) +OP(INITBLK, "initblk", 0x118) +OP(ANN_REF, "ann.ref", 0x119) +OP(RETHROW, "rethrow", 0x11a) +OP(SIZEOF, "sizeof", 0x11c) +OP(REFANYTYPE, "refanytype", 0x11d) +OP(ANN_DATA, "ann.data", 0x122) +OP(ANN_ARG, "ann.arg", 0x123) diff --git a/05/tcc-0.9.25/include/float.h b/05/tcc-0.9.25/include/float.h new file mode 100644 index 0000000..5f1c6f7 --- /dev/null +++ b/05/tcc-0.9.25/include/float.h @@ -0,0 +1,57 @@ +#ifndef _FLOAT_H_ +#define _FLOAT_H_ + +#define FLT_RADIX 2 + +/* IEEE float */ +#define FLT_MANT_DIG 24 +#define FLT_DIG 6 +#define FLT_ROUNDS 1 +#define FLT_EPSILON 1.19209290e-07F +#define FLT_MIN_EXP (-125) +#define FLT_MIN 1.17549435e-38F +#define FLT_MIN_10_EXP (-37) +#define FLT_MAX_EXP 128 +#define FLT_MAX 3.40282347e+38F +#define FLT_MAX_10_EXP 38 + +/* IEEE double */ +#define DBL_MANT_DIG 53 +#define DBL_DIG 15 +#define DBL_EPSILON 2.2204460492503131e-16 +#define DBL_MIN_EXP (-1021) +#define DBL_MIN 2.2250738585072014e-308 +#define DBL_MIN_10_EXP (-307) +#define DBL_MAX_EXP 1024 +#define DBL_MAX 1.7976931348623157e+308 +#define DBL_MAX_10_EXP 308 + +/* horrible intel long double */ +#ifdef __i386__ + +#define LDBL_MANT_DIG 64 +#define LDBL_DIG 18 +#define LDBL_EPSILON 1.08420217248550443401e-19L +#define LDBL_MIN_EXP (-16381) +#define LDBL_MIN 3.36210314311209350626e-4932L +#define LDBL_MIN_10_EXP (-4931) +#define LDBL_MAX_EXP 16384 +#define LDBL_MAX 1.18973149535723176502e+4932L +#define LDBL_MAX_10_EXP 4932 + +#else + +/* same as IEEE double */ +#define LDBL_MANT_DIG 53 +#define LDBL_DIG 15 +#define LDBL_EPSILON 2.2204460492503131e-16 +#define LDBL_MIN_EXP (-1021) +#define LDBL_MIN 2.2250738585072014e-308 +#define LDBL_MIN_10_EXP (-307) +#define LDBL_MAX_EXP 1024 +#define LDBL_MAX 1.7976931348623157e+308 +#define LDBL_MAX_10_EXP 308 + +#endif + +#endif /* _FLOAT_H_ */ diff --git a/05/tcc-0.9.25/include/stdarg.h b/05/tcc-0.9.25/include/stdarg.h new file mode 100644 index 0000000..86e556c --- /dev/null +++ b/05/tcc-0.9.25/include/stdarg.h @@ -0,0 +1,67 @@ +#ifndef _STDARG_H +#define _STDARG_H + +#ifdef __x86_64__ +#include + +/* GCC compatible definition of va_list. */ +struct __va_list_struct { + unsigned int gp_offset; + unsigned int fp_offset; + union { + unsigned int overflow_offset; + char *overflow_arg_area; + }; + char *reg_save_area; +}; + +typedef struct __va_list_struct *va_list; + +/* we use __builtin_(malloc|free) to avoid #define malloc tcc_malloc */ +/* XXX: this lacks the support of aggregated types. */ +#define va_start(ap, last) \ + (ap = (va_list)__builtin_malloc(sizeof(struct __va_list_struct)), \ + *ap = *(struct __va_list_struct*)( \ + (char*)__builtin_frame_address(0) - 16), \ + ap->overflow_arg_area = ((char *)__builtin_frame_address(0) + \ + ap->overflow_offset), \ + ap->reg_save_area = (char *)__builtin_frame_address(0) - 176 - 16 \ + ) +#define va_arg(ap, type) \ + (*(type*)(__builtin_types_compatible_p(type, long double) \ + ? (ap->overflow_arg_area += 16, \ + ap->overflow_arg_area - 16) \ + : __builtin_types_compatible_p(type, double) \ + ? (ap->fp_offset < 128 + 48 \ + ? (ap->fp_offset += 16, \ + ap->reg_save_area + ap->fp_offset - 16) \ + : (ap->overflow_arg_area += 8, \ + ap->overflow_arg_area - 8)) \ + : (ap->gp_offset < 48 \ + ? (ap->gp_offset += 8, \ + ap->reg_save_area + ap->gp_offset - 8) \ + : (ap->overflow_arg_area += 8, \ + ap->overflow_arg_area - 8)) \ + )) +#define va_copy(dest, src) \ + ((dest) = (va_list)malloc(sizeof(struct __va_list_struct)), \ + *(dest) = *(src)) +#define va_end(ap) __builtin_free(ap) + +#else + +typedef char *va_list; + +/* only correct for i386 */ +#define va_start(ap,last) ap = ((char *)&(last)) + ((sizeof(last)+3)&~3) +#define va_arg(ap,type) (ap += (sizeof(type)+3)&~3, *(type *)(ap - ((sizeof(type)+3)&~3))) +#define va_copy(dest, src) (dest) = (src) +#define va_end(ap) + +#endif + +/* fix a buggy dependency on GCC in libio.h */ +typedef va_list __gnuc_va_list; +#define _VA_LIST_DEFINED + +#endif /* _STDARG_H */ diff --git a/05/tcc-0.9.25/include/stdbool.h b/05/tcc-0.9.25/include/stdbool.h new file mode 100644 index 0000000..6ed13a6 --- /dev/null +++ b/05/tcc-0.9.25/include/stdbool.h @@ -0,0 +1,10 @@ +#ifndef _STDBOOL_H +#define _STDBOOL_H + +/* ISOC99 boolean */ + +#define bool _Bool +#define true 1 +#define false 0 + +#endif /* _STDBOOL_H */ diff --git a/05/tcc-0.9.25/include/stddef.h b/05/tcc-0.9.25/include/stddef.h new file mode 100644 index 0000000..aef5b39 --- /dev/null +++ b/05/tcc-0.9.25/include/stddef.h @@ -0,0 +1,20 @@ +#ifndef _STDDEF_H +#define _STDDEF_H + +#define NULL ((void *)0) +typedef __SIZE_TYPE__ size_t; +typedef __WCHAR_TYPE__ wchar_t; +typedef __PTRDIFF_TYPE__ ptrdiff_t; +#define offsetof(type, field) ((size_t) &((type *)0)->field) + +#ifndef __int8_t_defined +#define __int8_t_defined +typedef char int8_t; +typedef short int int16_t; +typedef int int32_t; +typedef long long int int64_t; +#endif + +void *alloca(size_t size); + +#endif diff --git a/05/tcc-0.9.25/include/tcclib.h b/05/tcc-0.9.25/include/tcclib.h new file mode 100644 index 0000000..42f8f3f --- /dev/null +++ b/05/tcc-0.9.25/include/tcclib.h @@ -0,0 +1,78 @@ +/* Simple libc header for TCC + * + * Add any function you want from the libc there. This file is here + * only for your convenience so that you do not need to put the whole + * glibc include files on your floppy disk + */ +#ifndef _TCCLIB_H +#define _TCCLIB_H + +#include +#include + +/* stdlib.h */ +void *calloc(size_t nmemb, size_t size); +void *malloc(size_t size); +void free(void *ptr); +void *realloc(void *ptr, size_t size); +int atoi(const char *nptr); +long int strtol(const char *nptr, char **endptr, int base); +unsigned long int strtoul(const char *nptr, char **endptr, int base); +void exit(int); + +/* stdio.h */ +typedef struct __FILE FILE; +#define EOF (-1) +extern FILE *stdin; +extern FILE *stdout; +extern FILE *stderr; +FILE *fopen(const char *path, const char *mode); +FILE *fdopen(int fildes, const char *mode); +FILE *freopen(const char *path, const char *mode, FILE *stream); +int fclose(FILE *stream); +size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); +size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream); +int fgetc(FILE *stream); +char *fgets(char *s, int size, FILE *stream); +int getc(FILE *stream); +int getchar(void); +char *gets(char *s); +int ungetc(int c, FILE *stream); +int fflush(FILE *stream); + +int printf(const char *format, ...); +int fprintf(FILE *stream, const char *format, ...); +int sprintf(char *str, const char *format, ...); +int snprintf(char *str, size_t size, const char *format, ...); +int asprintf(char **strp, const char *format, ...); +int dprintf(int fd, const char *format, ...); +int vprintf(const char *format, va_list ap); +int vfprintf(FILE *stream, const char *format, va_list ap); +int vsprintf(char *str, const char *format, va_list ap); +int vsnprintf(char *str, size_t size, const char *format, va_list ap); +int vasprintf(char **strp, const char *format, va_list ap); +int vdprintf(int fd, const char *format, va_list ap); + +void perror(const char *s); + +/* string.h */ +char *strcat(char *dest, const char *src); +char *strchr(const char *s, int c); +char *strrchr(const char *s, int c); +char *strcpy(char *dest, const char *src); +void *memcpy(void *dest, const void *src, size_t n); +void *memmove(void *dest, const void *src, size_t n); +void *memset(void *s, int c, size_t n); +char *strdup(const char *s); + +/* dlfcn.h */ +#define RTLD_LAZY 0x001 +#define RTLD_NOW 0x002 +#define RTLD_GLOBAL 0x100 + +void *dlopen(const char *filename, int flag); +const char *dlerror(void); +void *dlsym(void *handle, char *symbol); +int dlclose(void *handle); + +#endif /* _TCCLIB_H */ diff --git a/05/tcc-0.9.25/include/varargs.h b/05/tcc-0.9.25/include/varargs.h new file mode 100644 index 0000000..daee29e --- /dev/null +++ b/05/tcc-0.9.25/include/varargs.h @@ -0,0 +1,11 @@ +#ifndef _VARARGS_H +#define _VARARGS_H + +#include + +#define va_dcl +#define va_alist __va_alist +#undef va_start +#define va_start(ap) ap = __builtin_varargs_start + +#endif diff --git a/05/tcc-0.9.25/lib/alloca86-bt.S b/05/tcc-0.9.25/lib/alloca86-bt.S new file mode 100644 index 0000000..994da20 --- /dev/null +++ b/05/tcc-0.9.25/lib/alloca86-bt.S @@ -0,0 +1,45 @@ +/* ---------------------------------------------- */ +/* alloca86b.S */ + +#include "../config.h" + +.globl __bound_alloca + +__bound_alloca: + pop %edx + pop %eax + mov %eax, %ecx + add $3,%eax + and $-4,%eax + jz p6 + +#ifdef TCC_TARGET_PE +p4: + cmp $4096,%eax + jle p5 + sub $4096,%esp + sub $4096,%eax + test %eax,(%esp) + jmp p4 + +p5: +#endif + + sub %eax,%esp + mov %esp,%eax + + push %edx + push %eax + push %ecx + push %eax + call __bound_new_region + add $8, %esp + pop %eax + pop %edx + +p6: + push %edx + push %edx + ret + +/* ---------------------------------------------- */ diff --git a/05/tcc-0.9.25/lib/alloca86.S b/05/tcc-0.9.25/lib/alloca86.S new file mode 100644 index 0000000..fb208a0 --- /dev/null +++ b/05/tcc-0.9.25/lib/alloca86.S @@ -0,0 +1,33 @@ +/* ---------------------------------------------- */ +/* alloca86.S */ + +#include "../config.h" + +.globl alloca + +alloca: + pop %edx + pop %eax + add $3,%eax + and $-4,%eax + jz p3 + +#ifdef TCC_TARGET_PE +p1: + cmp $4096,%eax + jle p2 + sub $4096,%esp + sub $4096,%eax + test %eax,(%esp) + jmp p1 +p2: +#endif + + sub %eax,%esp + mov %esp,%eax +p3: + push %edx + push %edx + ret + +/* ---------------------------------------------- */ diff --git a/05/tcc-0.9.25/lib/bcheck.c b/05/tcc-0.9.25/lib/bcheck.c new file mode 100644 index 0000000..0ec2a4b --- /dev/null +++ b/05/tcc-0.9.25/lib/bcheck.c @@ -0,0 +1,868 @@ +/* + * Tiny C Memory and bounds checker + * + * Copyright (c) 2002 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#if !defined(__FreeBSD__) && !defined(__DragonFly__) && !defined(__OpenBSD__) +#include +#endif + +//#define BOUND_DEBUG + +/* define so that bound array is static (faster, but use memory if + bound checking not used) */ +//#define BOUND_STATIC + +/* use malloc hooks. Currently the code cannot be reliable if no hooks */ +#define CONFIG_TCC_MALLOC_HOOKS + +#define HAVE_MEMALIGN + +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__dietlibc__) \ + || defined(__UCLIBC__) || defined(__OpenBSD__) +#warning Bound checking not fully supported in this environment. +#undef CONFIG_TCC_MALLOC_HOOKS +#undef HAVE_MEMALIGN +#endif + +#define BOUND_T1_BITS 13 +#define BOUND_T2_BITS 11 +#define BOUND_T3_BITS (32 - BOUND_T1_BITS - BOUND_T2_BITS) + +#define BOUND_T1_SIZE (1 << BOUND_T1_BITS) +#define BOUND_T2_SIZE (1 << BOUND_T2_BITS) +#define BOUND_T3_SIZE (1 << BOUND_T3_BITS) +#define BOUND_E_BITS 4 + +#define BOUND_T23_BITS (BOUND_T2_BITS + BOUND_T3_BITS) +#define BOUND_T23_SIZE (1 << BOUND_T23_BITS) + + +/* this pointer is generated when bound check is incorrect */ +#define INVALID_POINTER ((void *)(-2)) +/* size of an empty region */ +#define EMPTY_SIZE 0xffffffff +/* size of an invalid region */ +#define INVALID_SIZE 0 + +typedef struct BoundEntry { + unsigned long start; + unsigned long size; + struct BoundEntry *next; + unsigned long is_invalid; /* true if pointers outside region are invalid */ +} BoundEntry; + +/* external interface */ +void __bound_init(void); +void __bound_new_region(void *p, unsigned long size); +int __bound_delete_region(void *p); + +#define FASTCALL __attribute__((regparm(3))) + +void *__bound_malloc(size_t size, const void *caller); +void *__bound_memalign(size_t size, size_t align, const void *caller); +void __bound_free(void *ptr, const void *caller); +void *__bound_realloc(void *ptr, size_t size, const void *caller); +static void *libc_malloc(size_t size); +static void libc_free(void *ptr); +static void install_malloc_hooks(void); +static void restore_malloc_hooks(void); + +#ifdef CONFIG_TCC_MALLOC_HOOKS +static void *saved_malloc_hook; +static void *saved_free_hook; +static void *saved_realloc_hook; +static void *saved_memalign_hook; +#endif + +/* linker definitions */ +extern char _end; + +/* TCC definitions */ +extern char __bounds_start; /* start of static bounds table */ +/* error message, just for TCC */ +const char *__bound_error_msg; + +/* runtime error output */ +extern void rt_error(unsigned long pc, const char *fmt, ...); + +#ifdef BOUND_STATIC +static BoundEntry *__bound_t1[BOUND_T1_SIZE]; /* page table */ +#else +static BoundEntry **__bound_t1; /* page table */ +#endif +static BoundEntry *__bound_empty_t2; /* empty page, for unused pages */ +static BoundEntry *__bound_invalid_t2; /* invalid page, for invalid pointers */ + +static BoundEntry *__bound_find_region(BoundEntry *e1, void *p) +{ + unsigned long addr, tmp; + BoundEntry *e; + + e = e1; + while (e != NULL) { + addr = (unsigned long)p; + addr -= e->start; + if (addr <= e->size) { + /* put region at the head */ + tmp = e1->start; + e1->start = e->start; + e->start = tmp; + tmp = e1->size; + e1->size = e->size; + e->size = tmp; + return e1; + } + e = e->next; + } + /* no entry found: return empty entry or invalid entry */ + if (e1->is_invalid) + return __bound_invalid_t2; + else + return __bound_empty_t2; +} + +/* print a bound error message */ +static void bound_error(const char *fmt, ...) +{ + __bound_error_msg = fmt; + *(int *)0 = 0; /* force a runtime error */ +} + +static void bound_alloc_error(void) +{ + bound_error("not enough memory for bound checking code"); +} + +/* currently, tcc cannot compile that because we use GNUC extensions */ +#if !defined(__TINYC__) + +/* return '(p + offset)' for pointer arithmetic (a pointer can reach + the end of a region in this case */ +void * FASTCALL __bound_ptr_add(void *p, int offset) +{ + unsigned long addr = (unsigned long)p; + BoundEntry *e; +#if defined(BOUND_DEBUG) + printf("add: 0x%x %d\n", (int)p, offset); +#endif + + e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)]; + e = (BoundEntry *)((char *)e + + ((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) & + ((BOUND_T2_SIZE - 1) << BOUND_E_BITS))); + addr -= e->start; + if (addr > e->size) { + e = __bound_find_region(e, p); + addr = (unsigned long)p - e->start; + } + addr += offset; + if (addr > e->size) + return INVALID_POINTER; /* return an invalid pointer */ + return p + offset; +} + +/* return '(p + offset)' for pointer indirection (the resulting must + be strictly inside the region */ +#define BOUND_PTR_INDIR(dsize) \ +void * FASTCALL __bound_ptr_indir ## dsize (void *p, int offset) \ +{ \ + unsigned long addr = (unsigned long)p; \ + BoundEntry *e; \ + \ + e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)]; \ + e = (BoundEntry *)((char *)e + \ + ((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) & \ + ((BOUND_T2_SIZE - 1) << BOUND_E_BITS))); \ + addr -= e->start; \ + if (addr > e->size) { \ + e = __bound_find_region(e, p); \ + addr = (unsigned long)p - e->start; \ + } \ + addr += offset + dsize; \ + if (addr > e->size) \ + return INVALID_POINTER; /* return an invalid pointer */ \ + return p + offset; \ +} + +#ifdef __i386__ +/* return the frame pointer of the caller */ +#define GET_CALLER_FP(fp)\ +{\ + unsigned long *fp1;\ + __asm__ __volatile__ ("movl %%ebp,%0" :"=g" (fp1));\ + fp = fp1[0];\ +} +#else +#error put code to extract the calling frame pointer +#endif + +/* called when entering a function to add all the local regions */ +void FASTCALL __bound_local_new(void *p1) +{ + unsigned long addr, size, fp, *p = p1; + GET_CALLER_FP(fp); + for(;;) { + addr = p[0]; + if (addr == 0) + break; + addr += fp; + size = p[1]; + p += 2; + __bound_new_region((void *)addr, size); + } +} + +/* called when leaving a function to delete all the local regions */ +void FASTCALL __bound_local_delete(void *p1) +{ + unsigned long addr, fp, *p = p1; + GET_CALLER_FP(fp); + for(;;) { + addr = p[0]; + if (addr == 0) + break; + addr += fp; + p += 2; + __bound_delete_region((void *)addr); + } +} + +#else + +void __bound_local_new(void *p) +{ +} +void __bound_local_delete(void *p) +{ +} + +void *__bound_ptr_add(void *p, int offset) +{ + return p + offset; +} + +#define BOUND_PTR_INDIR(dsize) \ +void *__bound_ptr_indir ## dsize (void *p, int offset) \ +{ \ + return p + offset; \ +} +#endif + +BOUND_PTR_INDIR(1) +BOUND_PTR_INDIR(2) +BOUND_PTR_INDIR(4) +BOUND_PTR_INDIR(8) +BOUND_PTR_INDIR(12) +BOUND_PTR_INDIR(16) + +static BoundEntry *__bound_new_page(void) +{ + BoundEntry *page; + int i; + + page = libc_malloc(sizeof(BoundEntry) * BOUND_T2_SIZE); + if (!page) + bound_alloc_error(); + for(i=0;i> BOUND_T3_BITS; + if (end != 0) + t2_end = end >> BOUND_T3_BITS; + else + t2_end = 1 << (BOUND_T1_BITS + BOUND_T2_BITS); + +#if 0 + printf("mark_invalid: start = %x %x\n", t2_start, t2_end); +#endif + + /* first we handle full pages */ + t1_start = (t2_start + BOUND_T2_SIZE - 1) >> BOUND_T2_BITS; + t1_end = t2_end >> BOUND_T2_BITS; + + i = t2_start & (BOUND_T2_SIZE - 1); + j = t2_end & (BOUND_T2_SIZE - 1); + + if (t1_start == t1_end) { + page = get_page(t2_start >> BOUND_T2_BITS); + for(; i < j; i++) { + page[i].size = INVALID_SIZE; + page[i].is_invalid = 1; + } + } else { + if (i > 0) { + page = get_page(t2_start >> BOUND_T2_BITS); + for(; i < BOUND_T2_SIZE; i++) { + page[i].size = INVALID_SIZE; + page[i].is_invalid = 1; + } + } + for(i = t1_start; i < t1_end; i++) { + __bound_t1[i] = __bound_invalid_t2; + } + if (j != 0) { + page = get_page(t1_end); + for(i = 0; i < j; i++) { + page[i].size = INVALID_SIZE; + page[i].is_invalid = 1; + } + } + } +} + +void __bound_init(void) +{ + int i; + BoundEntry *page; + unsigned long start, size; + int *p; + + /* save malloc hooks and install bound check hooks */ + install_malloc_hooks(); + +#ifndef BOUND_STATIC + __bound_t1 = libc_malloc(BOUND_T1_SIZE * sizeof(BoundEntry *)); + if (!__bound_t1) + bound_alloc_error(); +#endif + __bound_empty_t2 = __bound_new_page(); + for(i=0;istart == 0) { + /* no region : add it */ + e->start = start; + e->size = size; + } else { + /* already regions in the list: add it at the head */ + e1 = bound_new_entry(); + e1->start = e->start; + e1->size = e->size; + e1->next = e->next; + e->start = start; + e->size = size; + e->next = e1; + } +} + +/* create a new region. It should not already exist in the region list */ +void __bound_new_region(void *p, unsigned long size) +{ + unsigned long start, end; + BoundEntry *page, *e, *e2; + int t1_start, t1_end, i, t2_start, t2_end; + + start = (unsigned long)p; + end = start + size; + t1_start = start >> (BOUND_T2_BITS + BOUND_T3_BITS); + t1_end = end >> (BOUND_T2_BITS + BOUND_T3_BITS); + + /* start */ + page = get_page(t1_start); + t2_start = (start >> (BOUND_T3_BITS - BOUND_E_BITS)) & + ((BOUND_T2_SIZE - 1) << BOUND_E_BITS); + t2_end = (end >> (BOUND_T3_BITS - BOUND_E_BITS)) & + ((BOUND_T2_SIZE - 1) << BOUND_E_BITS); +#ifdef BOUND_DEBUG + printf("new %lx %lx %x %x %x %x\n", + start, end, t1_start, t1_end, t2_start, t2_end); +#endif + + e = (BoundEntry *)((char *)page + t2_start); + add_region(e, start, size); + + if (t1_end == t1_start) { + /* same ending page */ + e2 = (BoundEntry *)((char *)page + t2_end); + if (e2 > e) { + e++; + for(;estart = start; + e->size = size; + } + add_region(e, start, size); + } + } else { + /* mark until end of page */ + e2 = page + BOUND_T2_SIZE; + e++; + for(;estart = start; + e->size = size; + } + /* mark intermediate pages, if any */ + for(i=t1_start+1;istart = start; + e->size = size; + } + } + /* last page */ + page = get_page(t1_end); + e2 = (BoundEntry *)((char *)page + t2_end); + for(e=page;estart = start; + e->size = size; + } + add_region(e, start, size); + } +} + +/* delete a region */ +static inline void delete_region(BoundEntry *e, + void *p, unsigned long empty_size) +{ + unsigned long addr; + BoundEntry *e1; + + addr = (unsigned long)p; + addr -= e->start; + if (addr <= e->size) { + /* region found is first one */ + e1 = e->next; + if (e1 == NULL) { + /* no more region: mark it empty */ + e->start = 0; + e->size = empty_size; + } else { + /* copy next region in head */ + e->start = e1->start; + e->size = e1->size; + e->next = e1->next; + bound_free_entry(e1); + } + } else { + /* find the matching region */ + for(;;) { + e1 = e; + e = e->next; + /* region not found: do nothing */ + if (e == NULL) + break; + addr = (unsigned long)p - e->start; + if (addr <= e->size) { + /* found: remove entry */ + e1->next = e->next; + bound_free_entry(e); + break; + } + } + } +} + +/* WARNING: 'p' must be the starting point of the region. */ +/* return non zero if error */ +int __bound_delete_region(void *p) +{ + unsigned long start, end, addr, size, empty_size; + BoundEntry *page, *e, *e2; + int t1_start, t1_end, t2_start, t2_end, i; + + start = (unsigned long)p; + t1_start = start >> (BOUND_T2_BITS + BOUND_T3_BITS); + t2_start = (start >> (BOUND_T3_BITS - BOUND_E_BITS)) & + ((BOUND_T2_SIZE - 1) << BOUND_E_BITS); + + /* find region size */ + page = __bound_t1[t1_start]; + e = (BoundEntry *)((char *)page + t2_start); + addr = start - e->start; + if (addr > e->size) + e = __bound_find_region(e, p); + /* test if invalid region */ + if (e->size == EMPTY_SIZE || (unsigned long)p != e->start) + return -1; + /* compute the size we put in invalid regions */ + if (e->is_invalid) + empty_size = INVALID_SIZE; + else + empty_size = EMPTY_SIZE; + size = e->size; + end = start + size; + + /* now we can free each entry */ + t1_end = end >> (BOUND_T2_BITS + BOUND_T3_BITS); + t2_end = (end >> (BOUND_T3_BITS - BOUND_E_BITS)) & + ((BOUND_T2_SIZE - 1) << BOUND_E_BITS); + + delete_region(e, p, empty_size); + if (t1_end == t1_start) { + /* same ending page */ + e2 = (BoundEntry *)((char *)page + t2_end); + if (e2 > e) { + e++; + for(;estart = 0; + e->size = empty_size; + } + delete_region(e, p, empty_size); + } + } else { + /* mark until end of page */ + e2 = page + BOUND_T2_SIZE; + e++; + for(;estart = 0; + e->size = empty_size; + } + /* mark intermediate pages, if any */ + /* XXX: should free them */ + for(i=t1_start+1;istart = 0; + e->size = empty_size; + } + } + /* last page */ + page = get_page(t2_end); + e2 = (BoundEntry *)((char *)page + t2_end); + for(e=page;estart = 0; + e->size = empty_size; + } + delete_region(e, p, empty_size); + } + return 0; +} + +/* return the size of the region starting at p, or EMPTY_SIZE if non + existant region. */ +static unsigned long get_region_size(void *p) +{ + unsigned long addr = (unsigned long)p; + BoundEntry *e; + + e = __bound_t1[addr >> (BOUND_T2_BITS + BOUND_T3_BITS)]; + e = (BoundEntry *)((char *)e + + ((addr >> (BOUND_T3_BITS - BOUND_E_BITS)) & + ((BOUND_T2_SIZE - 1) << BOUND_E_BITS))); + addr -= e->start; + if (addr > e->size) + e = __bound_find_region(e, p); + if (e->start != (unsigned long)p) + return EMPTY_SIZE; + return e->size; +} + +/* patched memory functions */ + +static void install_malloc_hooks(void) +{ +#ifdef CONFIG_TCC_MALLOC_HOOKS + saved_malloc_hook = __malloc_hook; + saved_free_hook = __free_hook; + saved_realloc_hook = __realloc_hook; + saved_memalign_hook = __memalign_hook; + __malloc_hook = __bound_malloc; + __free_hook = __bound_free; + __realloc_hook = __bound_realloc; + __memalign_hook = __bound_memalign; +#endif +} + +static void restore_malloc_hooks(void) +{ +#ifdef CONFIG_TCC_MALLOC_HOOKS + __malloc_hook = saved_malloc_hook; + __free_hook = saved_free_hook; + __realloc_hook = saved_realloc_hook; + __memalign_hook = saved_memalign_hook; +#endif +} + +static void *libc_malloc(size_t size) +{ + void *ptr; + restore_malloc_hooks(); + ptr = malloc(size); + install_malloc_hooks(); + return ptr; +} + +static void libc_free(void *ptr) +{ + restore_malloc_hooks(); + free(ptr); + install_malloc_hooks(); +} + +/* XXX: we should use a malloc which ensure that it is unlikely that + two malloc'ed data have the same address if 'free' are made in + between. */ +void *__bound_malloc(size_t size, const void *caller) +{ + void *ptr; + + /* we allocate one more byte to ensure the regions will be + separated by at least one byte. With the glibc malloc, it may + be in fact not necessary */ + ptr = libc_malloc(size + 1); + + if (!ptr) + return NULL; + __bound_new_region(ptr, size); + return ptr; +} + +void *__bound_memalign(size_t size, size_t align, const void *caller) +{ + void *ptr; + + restore_malloc_hooks(); + +#ifndef HAVE_MEMALIGN + if (align > 4) { + /* XXX: handle it ? */ + ptr = NULL; + } else { + /* we suppose that malloc aligns to at least four bytes */ + ptr = malloc(size + 1); + } +#else + /* we allocate one more byte to ensure the regions will be + separated by at least one byte. With the glibc malloc, it may + be in fact not necessary */ + ptr = memalign(size + 1, align); +#endif + + install_malloc_hooks(); + + if (!ptr) + return NULL; + __bound_new_region(ptr, size); + return ptr; +} + +void __bound_free(void *ptr, const void *caller) +{ + if (ptr == NULL) + return; + if (__bound_delete_region(ptr) != 0) + bound_error("freeing invalid region"); + + libc_free(ptr); +} + +void *__bound_realloc(void *ptr, size_t size, const void *caller) +{ + void *ptr1; + int old_size; + + if (size == 0) { + __bound_free(ptr, caller); + return NULL; + } else { + ptr1 = __bound_malloc(size, caller); + if (ptr == NULL || ptr1 == NULL) + return ptr1; + old_size = get_region_size(ptr); + if (old_size == EMPTY_SIZE) + bound_error("realloc'ing invalid pointer"); + memcpy(ptr1, ptr, old_size); + __bound_free(ptr, caller); + return ptr1; + } +} + +#ifndef CONFIG_TCC_MALLOC_HOOKS +void *__bound_calloc(size_t nmemb, size_t size) +{ + void *ptr; + size = size * nmemb; + ptr = __bound_malloc(size, NULL); + if (!ptr) + return NULL; + memset(ptr, 0, size); + return ptr; +} +#endif + +#if 0 +static void bound_dump(void) +{ + BoundEntry *page, *e; + int i, j; + + printf("region dump:\n"); + for(i=0;isize != EMPTY_SIZE && e->start != 0) { + printf("%08x:", + (i << (BOUND_T2_BITS + BOUND_T3_BITS)) + + (j << BOUND_T3_BITS)); + do { + printf(" %08lx:%08lx", e->start, e->start + e->size); + e = e->next; + } while (e != NULL); + printf("\n"); + } + } + } +} +#endif + +/* some useful checked functions */ + +/* check that (p ... p + size - 1) lies inside 'p' region, if any */ +static void __bound_check(const void *p, size_t size) +{ + if (size == 0) + return; + p = __bound_ptr_add((void *)p, size); + if (p == INVALID_POINTER) + bound_error("invalid pointer"); +} + +void *__bound_memcpy(void *dst, const void *src, size_t size) +{ + __bound_check(dst, size); + __bound_check(src, size); + /* check also region overlap */ + if (src >= dst && src < dst + size) + bound_error("overlapping regions in memcpy()"); + return memcpy(dst, src, size); +} + +void *__bound_memmove(void *dst, const void *src, size_t size) +{ + __bound_check(dst, size); + __bound_check(src, size); + return memmove(dst, src, size); +} + +void *__bound_memset(void *dst, int c, size_t size) +{ + __bound_check(dst, size); + return memset(dst, c, size); +} + +/* XXX: could be optimized */ +int __bound_strlen(const char *s) +{ + const char *p; + int len; + + len = 0; + for(;;) { + p = __bound_ptr_indir1((char *)s, len); + if (p == INVALID_POINTER) + bound_error("bad pointer in strlen()"); + if (*p == '\0') + break; + len++; + } + return len; +} + +char *__bound_strcpy(char *dst, const char *src) +{ + int len; + len = __bound_strlen(src); + return __bound_memcpy(dst, src, len + 1); +} + diff --git a/05/tcc-0.9.25/lib/libtcc1.c b/05/tcc-0.9.25/lib/libtcc1.c new file mode 100644 index 0000000..b079477 --- /dev/null +++ b/05/tcc-0.9.25/lib/libtcc1.c @@ -0,0 +1,607 @@ +/* TCC runtime library. + Parts of this code are (c) 2002 Fabrice Bellard + + Copyright (C) 1987, 1988, 1992, 1994, 1995 Free Software Foundation, Inc. + +This file is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2, or (at your option) any +later version. + +In addition to the permissions in the GNU General Public License, the +Free Software Foundation gives you unlimited permission to link the +compiled version of this file into combinations with other programs, +and to distribute those combinations without any restriction coming +from the use of this file. (The General Public License restrictions +do apply in other respects; for example, they cover modification of +the file, and distribution when not linked into a combine +executable.) + +This file is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; see the file COPYING. If not, write to +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. +*/ + +#define W_TYPE_SIZE 32 +#define BITS_PER_UNIT 8 + +typedef int Wtype; +typedef unsigned int UWtype; +typedef unsigned int USItype; +typedef long long DWtype; +typedef unsigned long long UDWtype; + +struct DWstruct { + Wtype low, high; +}; + +typedef union +{ + struct DWstruct s; + DWtype ll; +} DWunion; + +typedef long double XFtype; +#define WORD_SIZE (sizeof (Wtype) * BITS_PER_UNIT) +#define HIGH_WORD_COEFF (((UDWtype) 1) << WORD_SIZE) + +/* the following deal with IEEE single-precision numbers */ +#define EXCESS 126 +#define SIGNBIT 0x80000000 +#define HIDDEN (1 << 23) +#define SIGN(fp) ((fp) & SIGNBIT) +#define EXP(fp) (((fp) >> 23) & 0xFF) +#define MANT(fp) (((fp) & 0x7FFFFF) | HIDDEN) +#define PACK(s,e,m) ((s) | ((e) << 23) | (m)) + +/* the following deal with IEEE double-precision numbers */ +#define EXCESSD 1022 +#define HIDDEND (1 << 20) +#define EXPD(fp) (((fp.l.upper) >> 20) & 0x7FF) +#define SIGND(fp) ((fp.l.upper) & SIGNBIT) +#define MANTD(fp) (((((fp.l.upper) & 0xFFFFF) | HIDDEND) << 10) | \ + (fp.l.lower >> 22)) +#define HIDDEND_LL ((long long)1 << 52) +#define MANTD_LL(fp) ((fp.ll & (HIDDEND_LL-1)) | HIDDEND_LL) +#define PACKD_LL(s,e,m) (((long long)((s)+((e)<<20))<<32)|(m)) + +/* the following deal with x86 long double-precision numbers */ +#define EXCESSLD 16382 +#define EXPLD(fp) (fp.l.upper & 0x7fff) +#define SIGNLD(fp) ((fp.l.upper) & 0x8000) + +/* only for x86 */ +union ldouble_long { + long double ld; + struct { + unsigned long long lower; + unsigned short upper; + } l; +}; + +union double_long { + double d; +#if 1 + struct { + unsigned int lower; + int upper; + } l; +#else + struct { + int upper; + unsigned int lower; + } l; +#endif + long long ll; +}; + +union float_long { + float f; + long l; +}; + +/* XXX: we don't support several builtin supports for now */ +#ifndef __x86_64__ + +/* XXX: use gcc/tcc intrinsic ? */ +#if defined(__i386__) +#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ + __asm__ ("subl %5,%1\n\tsbbl %3,%0" \ + : "=r" ((USItype) (sh)), \ + "=&r" ((USItype) (sl)) \ + : "0" ((USItype) (ah)), \ + "g" ((USItype) (bh)), \ + "1" ((USItype) (al)), \ + "g" ((USItype) (bl))) +#define umul_ppmm(w1, w0, u, v) \ + __asm__ ("mull %3" \ + : "=a" ((USItype) (w0)), \ + "=d" ((USItype) (w1)) \ + : "%0" ((USItype) (u)), \ + "rm" ((USItype) (v))) +#define udiv_qrnnd(q, r, n1, n0, dv) \ + __asm__ ("divl %4" \ + : "=a" ((USItype) (q)), \ + "=d" ((USItype) (r)) \ + : "0" ((USItype) (n0)), \ + "1" ((USItype) (n1)), \ + "rm" ((USItype) (dv))) +#define count_leading_zeros(count, x) \ + do { \ + USItype __cbtmp; \ + __asm__ ("bsrl %1,%0" \ + : "=r" (__cbtmp) : "rm" ((USItype) (x))); \ + (count) = __cbtmp ^ 31; \ + } while (0) +#else +#error unsupported CPU type +#endif + +/* most of this code is taken from libgcc2.c from gcc */ + +static UDWtype __udivmoddi4 (UDWtype n, UDWtype d, UDWtype *rp) +{ + DWunion ww; + DWunion nn, dd; + DWunion rr; + UWtype d0, d1, n0, n1, n2; + UWtype q0, q1; + UWtype b, bm; + + nn.ll = n; + dd.ll = d; + + d0 = dd.s.low; + d1 = dd.s.high; + n0 = nn.s.low; + n1 = nn.s.high; + +#if !UDIV_NEEDS_NORMALIZATION + if (d1 == 0) + { + if (d0 > n1) + { + /* 0q = nn / 0D */ + + udiv_qrnnd (q0, n0, n1, n0, d0); + q1 = 0; + + /* Remainder in n0. */ + } + else + { + /* qq = NN / 0d */ + + if (d0 == 0) + d0 = 1 / d0; /* Divide intentionally by zero. */ + + udiv_qrnnd (q1, n1, 0, n1, d0); + udiv_qrnnd (q0, n0, n1, n0, d0); + + /* Remainder in n0. */ + } + + if (rp != 0) + { + rr.s.low = n0; + rr.s.high = 0; + *rp = rr.ll; + } + } + +#else /* UDIV_NEEDS_NORMALIZATION */ + + if (d1 == 0) + { + if (d0 > n1) + { + /* 0q = nn / 0D */ + + count_leading_zeros (bm, d0); + + if (bm != 0) + { + /* Normalize, i.e. make the most significant bit of the + denominator set. */ + + d0 = d0 << bm; + n1 = (n1 << bm) | (n0 >> (W_TYPE_SIZE - bm)); + n0 = n0 << bm; + } + + udiv_qrnnd (q0, n0, n1, n0, d0); + q1 = 0; + + /* Remainder in n0 >> bm. */ + } + else + { + /* qq = NN / 0d */ + + if (d0 == 0) + d0 = 1 / d0; /* Divide intentionally by zero. */ + + count_leading_zeros (bm, d0); + + if (bm == 0) + { + /* From (n1 >= d0) /\ (the most significant bit of d0 is set), + conclude (the most significant bit of n1 is set) /\ (the + leading quotient digit q1 = 1). + + This special case is necessary, not an optimization. + (Shifts counts of W_TYPE_SIZE are undefined.) */ + + n1 -= d0; + q1 = 1; + } + else + { + /* Normalize. */ + + b = W_TYPE_SIZE - bm; + + d0 = d0 << bm; + n2 = n1 >> b; + n1 = (n1 << bm) | (n0 >> b); + n0 = n0 << bm; + + udiv_qrnnd (q1, n1, n2, n1, d0); + } + + /* n1 != d0... */ + + udiv_qrnnd (q0, n0, n1, n0, d0); + + /* Remainder in n0 >> bm. */ + } + + if (rp != 0) + { + rr.s.low = n0 >> bm; + rr.s.high = 0; + *rp = rr.ll; + } + } +#endif /* UDIV_NEEDS_NORMALIZATION */ + + else + { + if (d1 > n1) + { + /* 00 = nn / DD */ + + q0 = 0; + q1 = 0; + + /* Remainder in n1n0. */ + if (rp != 0) + { + rr.s.low = n0; + rr.s.high = n1; + *rp = rr.ll; + } + } + else + { + /* 0q = NN / dd */ + + count_leading_zeros (bm, d1); + if (bm == 0) + { + /* From (n1 >= d1) /\ (the most significant bit of d1 is set), + conclude (the most significant bit of n1 is set) /\ (the + quotient digit q0 = 0 or 1). + + This special case is necessary, not an optimization. */ + + /* The condition on the next line takes advantage of that + n1 >= d1 (true due to program flow). */ + if (n1 > d1 || n0 >= d0) + { + q0 = 1; + sub_ddmmss (n1, n0, n1, n0, d1, d0); + } + else + q0 = 0; + + q1 = 0; + + if (rp != 0) + { + rr.s.low = n0; + rr.s.high = n1; + *rp = rr.ll; + } + } + else + { + UWtype m1, m0; + /* Normalize. */ + + b = W_TYPE_SIZE - bm; + + d1 = (d1 << bm) | (d0 >> b); + d0 = d0 << bm; + n2 = n1 >> b; + n1 = (n1 << bm) | (n0 >> b); + n0 = n0 << bm; + + udiv_qrnnd (q0, n1, n2, n1, d1); + umul_ppmm (m1, m0, q0, d0); + + if (m1 > n1 || (m1 == n1 && m0 > n0)) + { + q0--; + sub_ddmmss (m1, m0, m1, m0, d1, d0); + } + + q1 = 0; + + /* Remainder in (n1n0 - m1m0) >> bm. */ + if (rp != 0) + { + sub_ddmmss (n1, n0, n1, n0, m1, m0); + rr.s.low = (n1 << b) | (n0 >> bm); + rr.s.high = n1 >> bm; + *rp = rr.ll; + } + } + } + } + + ww.s.low = q0; + ww.s.high = q1; + return ww.ll; +} + +#define __negdi2(a) (-(a)) + +long long __divdi3(long long u, long long v) +{ + int c = 0; + DWunion uu, vv; + DWtype w; + + uu.ll = u; + vv.ll = v; + + if (uu.s.high < 0) { + c = ~c; + uu.ll = __negdi2 (uu.ll); + } + if (vv.s.high < 0) { + c = ~c; + vv.ll = __negdi2 (vv.ll); + } + w = __udivmoddi4 (uu.ll, vv.ll, (UDWtype *) 0); + if (c) + w = __negdi2 (w); + return w; +} + +long long __moddi3(long long u, long long v) +{ + int c = 0; + DWunion uu, vv; + DWtype w; + + uu.ll = u; + vv.ll = v; + + if (uu.s.high < 0) { + c = ~c; + uu.ll = __negdi2 (uu.ll); + } + if (vv.s.high < 0) + vv.ll = __negdi2 (vv.ll); + + __udivmoddi4 (uu.ll, vv.ll, (UDWtype *) &w); + if (c) + w = __negdi2 (w); + return w; +} + +unsigned long long __udivdi3(unsigned long long u, unsigned long long v) +{ + return __udivmoddi4 (u, v, (UDWtype *) 0); +} + +unsigned long long __umoddi3(unsigned long long u, unsigned long long v) +{ + UDWtype w; + + __udivmoddi4 (u, v, &w); + return w; +} + +/* XXX: fix tcc's code generator to do this instead */ +long long __ashrdi3(long long a, int b) +{ +#ifdef __TINYC__ + DWunion u; + u.ll = a; + if (b >= 32) { + u.s.low = u.s.high >> (b - 32); + u.s.high = u.s.high >> 31; + } else if (b != 0) { + u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b)); + u.s.high = u.s.high >> b; + } + return u.ll; +#else + return a >> b; +#endif +} + +/* XXX: fix tcc's code generator to do this instead */ +unsigned long long __lshrdi3(unsigned long long a, int b) +{ +#ifdef __TINYC__ + DWunion u; + u.ll = a; + if (b >= 32) { + u.s.low = (unsigned)u.s.high >> (b - 32); + u.s.high = 0; + } else if (b != 0) { + u.s.low = ((unsigned)u.s.low >> b) | (u.s.high << (32 - b)); + u.s.high = (unsigned)u.s.high >> b; + } + return u.ll; +#else + return a >> b; +#endif +} + +/* XXX: fix tcc's code generator to do this instead */ +long long __ashldi3(long long a, int b) +{ +#ifdef __TINYC__ + DWunion u; + u.ll = a; + if (b >= 32) { + u.s.high = (unsigned)u.s.low << (b - 32); + u.s.low = 0; + } else if (b != 0) { + u.s.high = ((unsigned)u.s.high << b) | ((unsigned)u.s.low >> (32 - b)); + u.s.low = (unsigned)u.s.low << b; + } + return u.ll; +#else + return a << b; +#endif +} + +#if defined(__i386__) +/* FPU control word for rounding to nearest mode */ +unsigned short __tcc_fpu_control = 0x137f; +/* FPU control word for round to zero mode for int conversion */ +unsigned short __tcc_int_fpu_control = 0x137f | 0x0c00; +#endif + +#endif /* !__x86_64__ */ + +/* XXX: fix tcc's code generator to do this instead */ +float __floatundisf(unsigned long long a) +{ + DWunion uu; + XFtype r; + + uu.ll = a; + if (uu.s.high >= 0) { + return (float)uu.ll; + } else { + r = (XFtype)uu.ll; + r += 18446744073709551616.0; + return (float)r; + } +} + +double __floatundidf(unsigned long long a) +{ + DWunion uu; + XFtype r; + + uu.ll = a; + if (uu.s.high >= 0) { + return (double)uu.ll; + } else { + r = (XFtype)uu.ll; + r += 18446744073709551616.0; + return (double)r; + } +} + +long double __floatundixf(unsigned long long a) +{ + DWunion uu; + XFtype r; + + uu.ll = a; + if (uu.s.high >= 0) { + return (long double)uu.ll; + } else { + r = (XFtype)uu.ll; + r += 18446744073709551616.0; + return (long double)r; + } +} + +unsigned long long __fixunssfdi (float a1) +{ + register union float_long fl1; + register int exp; + register unsigned long l; + + fl1.f = a1; + + if (fl1.l == 0) + return (0); + + exp = EXP (fl1.l) - EXCESS - 24; + + l = MANT(fl1.l); + if (exp >= 41) + return (unsigned long long)-1; + else if (exp >= 0) + return (unsigned long long)l << exp; + else if (exp >= -23) + return l >> -exp; + else + return 0; +} + +unsigned long long __fixunsdfdi (double a1) +{ + register union double_long dl1; + register int exp; + register unsigned long long l; + + dl1.d = a1; + + if (dl1.ll == 0) + return (0); + + exp = EXPD (dl1) - EXCESSD - 53; + + l = MANTD_LL(dl1); + + if (exp >= 12) + return (unsigned long long)-1; + else if (exp >= 0) + return l << exp; + else if (exp >= -52) + return l >> -exp; + else + return 0; +} + +unsigned long long __fixunsxfdi (long double a1) +{ + register union ldouble_long dl1; + register int exp; + register unsigned long long l; + + dl1.ld = a1; + + if (dl1.l.lower == 0 && dl1.l.upper == 0) + return (0); + + exp = EXPLD (dl1) - EXCESSLD - 64; + + l = dl1.l.lower; + + if (exp > 0) + return (unsigned long long)-1; + else if (exp >= -63) + return l >> -exp; + else + return 0; +} + diff --git a/05/tcc-0.9.25/libtcc.c b/05/tcc-0.9.25/libtcc.c new file mode 100644 index 0000000..f842f27 --- /dev/null +++ b/05/tcc-0.9.25/libtcc.c @@ -0,0 +1,2276 @@ +/* + * TCC - Tiny C Compiler + * + * Copyright (c) 2001-2004 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "tcc.h" + +/********************************************************/ +/* global variables */ + +/* display benchmark infos */ +int total_lines; +int total_bytes; + +/* parser */ +static struct BufferedFile *file; +static int ch, tok; +static CValue tokc; +static CString tokcstr; /* current parsed string, if any */ +/* additional informations about token */ +static int tok_flags; +#define TOK_FLAG_BOL 0x0001 /* beginning of line before */ +#define TOK_FLAG_BOF 0x0002 /* beginning of file before */ +#define TOK_FLAG_ENDIF 0x0004 /* a endif was found matching starting #ifdef */ +#define TOK_FLAG_EOF 0x0008 /* end of file */ + +static int *macro_ptr, *macro_ptr_allocated; +static int *unget_saved_macro_ptr; +static int unget_saved_buffer[TOK_MAX_SIZE + 1]; +static int unget_buffer_enabled; +static int parse_flags; +#define PARSE_FLAG_PREPROCESS 0x0001 /* activate preprocessing */ +#define PARSE_FLAG_TOK_NUM 0x0002 /* return numbers instead of TOK_PPNUM */ +#define PARSE_FLAG_LINEFEED 0x0004 /* line feed is returned as a + token. line feed is also + returned at eof */ +#define PARSE_FLAG_ASM_COMMENTS 0x0008 /* '#' can be used for line comment */ +#define PARSE_FLAG_SPACES 0x0010 /* next() returns space tokens (for -E) */ + +static Section *text_section, *data_section, *bss_section; /* predefined sections */ +static Section *cur_text_section; /* current section where function code is + generated */ +#ifdef CONFIG_TCC_ASM +static Section *last_text_section; /* to handle .previous asm directive */ +#endif +/* bound check related sections */ +static Section *bounds_section; /* contains global data bound description */ +static Section *lbounds_section; /* contains local data bound description */ +/* symbol sections */ +static Section *symtab_section, *strtab_section; + +/* debug sections */ +static Section *stab_section, *stabstr_section; + +/* loc : local variable index + ind : output code index + rsym: return symbol + anon_sym: anonymous symbol index +*/ +static int rsym, anon_sym, ind, loc; +/* expression generation modifiers */ +static int const_wanted; /* true if constant wanted */ +static int nocode_wanted; /* true if no code generation wanted for an expression */ +static int global_expr; /* true if compound literals must be allocated + globally (used during initializers parsing */ +static CType func_vt; /* current function return type (used by return + instruction) */ +static int func_vc; +static int last_line_num, last_ind, func_ind; /* debug last line number and pc */ +static int tok_ident; +static TokenSym **table_ident; +static TokenSym *hash_ident[TOK_HASH_SIZE]; +static char token_buf[STRING_MAX_SIZE + 1]; +static char *funcname; +static Sym *global_stack, *local_stack; +static Sym *define_stack; +static Sym *global_label_stack, *local_label_stack; +/* symbol allocator */ +#define SYM_POOL_NB (8192 / sizeof(Sym)) +static Sym *sym_free_first; +static void **sym_pools; +static int nb_sym_pools; + +static SValue vstack[VSTACK_SIZE], *vtop; +/* some predefined types */ +static CType char_pointer_type, func_old_type, int_type; + +/* use GNU C extensions */ +static int gnu_ext = 1; + +/* use Tiny C extensions */ +static int tcc_ext = 1; + +/* max number of callers shown if error */ +#ifdef CONFIG_TCC_BACKTRACE +int num_callers = 6; +const char **rt_bound_error_msg; +#endif + +/* XXX: get rid of this ASAP */ +static struct TCCState *tcc_state; + +/********************************************************/ +/* function prototypes */ + +/* tccpp.c */ +static void next(void); +char *get_tok_str(int v, CValue *cv); + +/* tccgen.c */ +static void parse_expr_type(CType *type); +static void expr_type(CType *type); +static void unary_type(CType *type); +static void block(int *bsym, int *csym, int *case_sym, int *def_sym, + int case_reg, int is_expr); +static int expr_const(void); +static void expr_eq(void); +static void gexpr(void); +static void gen_inline_functions(void); +static void decl(int l); +static void decl_initializer(CType *type, Section *sec, unsigned long c, + int first, int size_only); +static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, + int has_init, int v, int scope); +int gv(int rc); +void gv2(int rc1, int rc2); +void move_reg(int r, int s); +void save_regs(int n); +void save_reg(int r); +void vpop(void); +void vswap(void); +void vdup(void); +int get_reg(int rc); +int get_reg_ex(int rc,int rc2); + +void gen_op(int op); +void force_charshort_cast(int t); +static void gen_cast(CType *type); +void vstore(void); +static Sym *sym_find(int v); +static Sym *sym_push(int v, CType *type, int r, int c); + +/* type handling */ +static int type_size(CType *type, int *a); +static inline CType *pointed_type(CType *type); +static int pointed_size(CType *type); +static int lvalue_type(int t); +static int parse_btype(CType *type, AttributeDef *ad); +static void type_decl(CType *type, AttributeDef *ad, int *v, int td); +static int compare_types(CType *type1, CType *type2, int unqualified); +static int is_compatible_types(CType *type1, CType *type2); +static int is_compatible_parameter_types(CType *type1, CType *type2); + +int ieee_finite(double d); +void vpushi(int v); +void vpushll(long long v); +void vrott(int n); +void vnrott(int n); +void lexpand_nr(void); +static void vpush_global_sym(CType *type, int v); +void vset(CType *type, int r, int v); +void type_to_str(char *buf, int buf_size, + CType *type, const char *varstr); +static Sym *get_sym_ref(CType *type, Section *sec, + unsigned long offset, unsigned long size); +static Sym *external_global_sym(int v, CType *type, int r); + +/* section generation */ +static void section_realloc(Section *sec, unsigned long new_size); +static void *section_ptr_add(Section *sec, unsigned long size); +static void put_extern_sym(Sym *sym, Section *section, + unsigned long value, unsigned long size); +static void greloc(Section *s, Sym *sym, unsigned long addr, int type); +static int put_elf_str(Section *s, const char *sym); +static int put_elf_sym(Section *s, + unsigned long value, unsigned long size, + int info, int other, int shndx, const char *name); +static int add_elf_sym(Section *s, unsigned long value, unsigned long size, + int info, int other, int sh_num, const char *name); +static void put_elf_reloc(Section *symtab, Section *s, unsigned long offset, + int type, int symbol); +static void put_stabs(const char *str, int type, int other, int desc, + unsigned long value); +static void put_stabs_r(const char *str, int type, int other, int desc, + unsigned long value, Section *sec, int sym_index); +static void put_stabn(int type, int other, int desc, int value); +static void put_stabd(int type, int other, int desc); +static int tcc_add_dll(TCCState *s, const char *filename, int flags); + +#define AFF_PRINT_ERROR 0x0001 /* print error if file not found */ +#define AFF_REFERENCED_DLL 0x0002 /* load a referenced dll from another dll */ +#define AFF_PREPROCESS 0x0004 /* preprocess file */ +static int tcc_add_file_internal(TCCState *s, const char *filename, int flags); + +/* tcccoff.c */ +int tcc_output_coff(TCCState *s1, FILE *f); + +/* tccpe.c */ +void *resolve_sym(TCCState *s1, const char *sym, int type); +int pe_load_def_file(struct TCCState *s1, int fd); +int pe_test_res_file(void *v, int size); +int pe_load_res_file(struct TCCState *s1, int fd); +void pe_add_runtime(struct TCCState *s1); +void pe_guess_outfile(char *objfilename, int output_type); +int pe_output_file(struct TCCState *s1, const char *filename); + +/* tccasm.c */ +#ifdef CONFIG_TCC_ASM +static void asm_expr(TCCState *s1, ExprValue *pe); +static int asm_int_expr(TCCState *s1); +static int find_constraint(ASMOperand *operands, int nb_operands, + const char *name, const char **pp); + +static int tcc_assemble(TCCState *s1, int do_preprocess); +#endif + +static void asm_instr(void); +static void asm_global_instr(void); + +/********************************************************/ +/* global variables */ + +#ifdef TCC_TARGET_I386 +#include "i386-gen.c" +#endif + +#ifdef TCC_TARGET_ARM +#include "arm-gen.c" +#endif + +#ifdef TCC_TARGET_C67 +#include "c67-gen.c" +#endif + +#ifdef TCC_TARGET_X86_64 +#include "x86_64-gen.c" +#endif + +#ifdef CONFIG_TCC_STATIC + +#define RTLD_LAZY 0x001 +#define RTLD_NOW 0x002 +#define RTLD_GLOBAL 0x100 +#define RTLD_DEFAULT NULL + +/* dummy function for profiling */ +void *dlopen(const char *filename, int flag) +{ + return NULL; +} + +void dlclose(void *p) +{ +} + +const char *dlerror(void) +{ + return "error"; +} + +typedef struct TCCSyms { + char *str; + void *ptr; +} TCCSyms; + +#define TCCSYM(a) { #a, &a, }, + +/* add the symbol you want here if no dynamic linking is done */ +static TCCSyms tcc_syms[] = { + {"printf", 0}, + {"fprintf", 0}, + {"fopen", 0}, + {"fclose", 0}, + { NULL, NULL } +}; + +void _init_tcc_syms(void) { + tcc_syms[0].ptr = printf; + tcc_syms[1].ptr = fprintf; + tcc_syms[2].ptr = fopen; + tcc_syms[3].ptr = fclose; +} + +void *resolve_sym(TCCState *s1, const char *symbol, int type) +{ + TCCSyms *p; + p = tcc_syms; + while (p->str != NULL) { + if (!strcmp(p->str, symbol)) + return p->ptr; + p++; + } + return NULL; +} + +#elif !defined(_WIN32) + +#include + +void *resolve_sym(TCCState *s1, const char *sym, int type) +{ + return dlsym(RTLD_DEFAULT, sym); +} + +#endif + +/********************************************************/ + +/* we use our own 'finite' function to avoid potential problems with + non standard math libs */ +/* XXX: endianness dependent */ +int ieee_finite(double d) +{ + int *p = (int *)&d; + return ((unsigned)((p[1] | 0x800fffff) + 1)) >> 31; +} + +/* copy a string and truncate it. */ +char *pstrcpy(char *buf, int buf_size, const char *s) +{ + char *q, *q_end; + int c; + + if (buf_size > 0) { + q = buf; + q_end = buf + buf_size - 1; + while (q < q_end) { + c = *s++; + if (c == '\0') + break; + *q++ = c; + } + *q = '\0'; + } + return buf; +} + +/* strcat and truncate. */ +char *pstrcat(char *buf, int buf_size, const char *s) +{ + int len; + len = strlen(buf); + if (len < buf_size) + pstrcpy(buf + len, buf_size - len, s); + return buf; +} + +/* extract the basename of a file */ +char *tcc_basename(const char *name) +{ + char *p = strchr(name, 0); + while (p > name && !IS_PATHSEP(p[-1])) + --p; + return p; +} + +char *tcc_fileextension (const char *name) +{ + char *b = tcc_basename(name); + char *e = strrchr(b, '.'); + return e ? e : strchr(b, 0); +} + +#ifdef _WIN32 +char *normalize_slashes(char *path) +{ + char *p; + for (p = path; *p; ++p) + if (*p == '\\') + *p = '/'; + return path; +} + +void tcc_set_lib_path_w32(TCCState *s) +{ + /* on win32, we suppose the lib and includes are at the location + of 'tcc.exe' */ + char path[1024], *p; + GetModuleFileNameA(NULL, path, sizeof path); + p = tcc_basename(normalize_slashes(strlwr(path))); + if (p - 5 > path && 0 == strncmp(p - 5, "/bin/", 5)) + p -= 5; + else if (p > path) + p--; + *p = 0; + tcc_set_lib_path(s, path); +} +#endif + +void set_pages_executable(void *ptr, unsigned long length) +{ +#ifdef _WIN32 + unsigned long old_protect; + VirtualProtect(ptr, length, PAGE_EXECUTE_READWRITE, &old_protect); +#else + unsigned long start, end; + start = (unsigned long)ptr & ~(PAGESIZE - 1); + end = (unsigned long)ptr + length; + end = (end + PAGESIZE - 1) & ~(PAGESIZE - 1); + mprotect((void *)start, end - start, PROT_READ | PROT_WRITE | PROT_EXEC); +#endif +} + +/* memory management */ +#ifdef MEM_DEBUG +int mem_cur_size; +int mem_max_size; +unsigned malloc_usable_size(void*); +#endif + +void tcc_free(void *ptr) +{ +#ifdef MEM_DEBUG + mem_cur_size -= malloc_usable_size(ptr); +#endif + free(ptr); +} + +void *tcc_malloc(unsigned long size) +{ + void *ptr; + ptr = malloc(size); + if (!ptr && size) + error("memory full"); +#ifdef MEM_DEBUG + mem_cur_size += malloc_usable_size(ptr); + if (mem_cur_size > mem_max_size) + mem_max_size = mem_cur_size; +#endif + return ptr; +} + +void *tcc_mallocz(unsigned long size) +{ + void *ptr; + ptr = tcc_malloc(size); + memset(ptr, 0, size); + return ptr; +} + +void *tcc_realloc(void *ptr, unsigned long size) +{ + void *ptr1; +#ifdef MEM_DEBUG + mem_cur_size -= malloc_usable_size(ptr); +#endif + ptr1 = realloc(ptr, size); +#ifdef MEM_DEBUG + /* NOTE: count not correct if alloc error, but not critical */ + mem_cur_size += malloc_usable_size(ptr1); + if (mem_cur_size > mem_max_size) + mem_max_size = mem_cur_size; +#endif + return ptr1; +} + +char *tcc_strdup(const char *str) +{ + char *ptr; + ptr = tcc_malloc(strlen(str) + 1); + strcpy(ptr, str); + return ptr; +} + +#define free(p) use_tcc_free(p) +#define malloc(s) use_tcc_malloc(s) +#define realloc(p, s) use_tcc_realloc(p, s) + +void dynarray_add(void ***ptab, int *nb_ptr, void *data) +{ + int nb, nb_alloc; + void **pp; + + nb = *nb_ptr; + pp = *ptab; + /* every power of two we double array size */ + if ((nb & (nb - 1)) == 0) { + if (!nb) + nb_alloc = 1; + else + nb_alloc = nb * 2; + pp = tcc_realloc(pp, nb_alloc * sizeof(void *)); + if (!pp) + error("memory full"); + *ptab = pp; + } + pp[nb++] = data; + *nb_ptr = nb; +} + +void dynarray_reset(void *pp, int *n) +{ + void **p; + for (p = *(void***)pp; *n; ++p, --*n) + if (*p) + tcc_free(*p); + tcc_free(*(void**)pp); + *(void**)pp = NULL; +} + +/* symbol allocator */ +static Sym *__sym_malloc(void) +{ + Sym *sym_pool, *sym, *last_sym; + int i; + + sym_pool = tcc_malloc(SYM_POOL_NB * sizeof(Sym)); + dynarray_add(&sym_pools, &nb_sym_pools, sym_pool); + + last_sym = sym_free_first; + sym = sym_pool; + for(i = 0; i < SYM_POOL_NB; i++) { + sym->next = last_sym; + last_sym = sym; + sym++; + } + sym_free_first = last_sym; + return last_sym; +} + +static inline Sym *sym_malloc(void) +{ + Sym *sym; + sym = sym_free_first; + if (!sym) + sym = __sym_malloc(); + sym_free_first = sym->next; + return sym; +} + +static inline void sym_free(Sym *sym) +{ + sym->next = sym_free_first; + sym_free_first = sym; +} + +Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags) +{ + Section *sec; + + sec = tcc_mallocz(sizeof(Section) + strlen(name)); + strcpy(sec->name, name); + sec->sh_type = sh_type; + sec->sh_flags = sh_flags; + switch(sh_type) { + case SHT_HASH: + case SHT_REL: + case SHT_RELA: + case SHT_DYNSYM: + case SHT_SYMTAB: + case SHT_DYNAMIC: + sec->sh_addralign = 4; + break; + case SHT_STRTAB: + sec->sh_addralign = 1; + break; + default: + sec->sh_addralign = 32; /* default conservative alignment */ + break; + } + + if (sh_flags & SHF_PRIVATE) { + dynarray_add((void ***)&s1->priv_sections, &s1->nb_priv_sections, sec); + } else { + sec->sh_num = s1->nb_sections; + dynarray_add((void ***)&s1->sections, &s1->nb_sections, sec); + } + + return sec; +} + +static void free_section(Section *s) +{ + tcc_free(s->data); +} + +/* realloc section and set its content to zero */ +static void section_realloc(Section *sec, unsigned long new_size) +{ + unsigned long size; + unsigned char *data; + + size = sec->data_allocated; + if (size == 0) + size = 1; + while (size < new_size) + size = size * 2; + data = tcc_realloc(sec->data, size); + if (!data) + error("memory full"); + memset(data + sec->data_allocated, 0, size - sec->data_allocated); + sec->data = data; + sec->data_allocated = size; +} + +/* reserve at least 'size' bytes in section 'sec' from + sec->data_offset. */ +static void *section_ptr_add(Section *sec, unsigned long size) +{ + unsigned long offset, offset1; + + offset = sec->data_offset; + offset1 = offset + size; + if (offset1 > sec->data_allocated) + section_realloc(sec, offset1); + sec->data_offset = offset1; + return sec->data + offset; +} + +/* return a reference to a section, and create it if it does not + exists */ +Section *find_section(TCCState *s1, const char *name) +{ + Section *sec; + int i; + for(i = 1; i < s1->nb_sections; i++) { + sec = s1->sections[i]; + if (!strcmp(name, sec->name)) + return sec; + } + /* sections are created as PROGBITS */ + return new_section(s1, name, SHT_PROGBITS, SHF_ALLOC); +} + +/* update sym->c so that it points to an external symbol in section + 'section' with value 'value' */ +static void put_extern_sym2(Sym *sym, Section *section, + unsigned long value, unsigned long size, + int can_add_underscore) +{ + int sym_type, sym_bind, sh_num, info, other, attr; + ElfW(Sym) *esym; + const char *name; + char buf1[256]; + + if (section == NULL) + sh_num = SHN_UNDEF; + else if (section == SECTION_ABS) + sh_num = SHN_ABS; + else + sh_num = section->sh_num; + + other = attr = 0; + + if ((sym->type.t & VT_BTYPE) == VT_FUNC) { + sym_type = STT_FUNC; +#ifdef TCC_TARGET_PE + if (sym->type.ref) + attr = sym->type.ref->r; + if (FUNC_EXPORT(attr)) + other |= 1; + if (FUNC_CALL(attr) == FUNC_STDCALL) + other |= 2; +#endif + } else { + sym_type = STT_OBJECT; + } + + if (sym->type.t & VT_STATIC) + sym_bind = STB_LOCAL; + else + sym_bind = STB_GLOBAL; + + if (!sym->c) { + name = get_tok_str(sym->v, NULL); +#ifdef CONFIG_TCC_BCHECK + if (tcc_state->do_bounds_check) { + char buf[32]; + + /* XXX: avoid doing that for statics ? */ + /* if bound checking is activated, we change some function + names by adding the "__bound" prefix */ + switch(sym->v) { +#if 0 + /* XXX: we rely only on malloc hooks */ + case TOK_malloc: + case TOK_free: + case TOK_realloc: + case TOK_memalign: + case TOK_calloc: +#endif + case TOK_memcpy: + case TOK_memmove: + case TOK_memset: + case TOK_strlen: + case TOK_strcpy: + case TOK_alloca: + strcpy(buf, "__bound_"); + strcat(buf, name); + name = buf; + break; + } + } +#endif + +#ifdef TCC_TARGET_PE + if ((other & 2) && can_add_underscore) { + sprintf(buf1, "_%s@%d", name, FUNC_ARGS(attr)); + name = buf1; + } else +#endif + if (tcc_state->leading_underscore && can_add_underscore) { + buf1[0] = '_'; + pstrcpy(buf1 + 1, sizeof(buf1) - 1, name); + name = buf1; + } + info = ELF64_ST_INFO(sym_bind, sym_type); + sym->c = add_elf_sym(symtab_section, value, size, info, other, sh_num, name); + } else { + esym = &((ElfW(Sym) *)symtab_section->data)[sym->c]; + esym->st_value = value; + esym->st_size = size; + esym->st_shndx = sh_num; + esym->st_other |= other; + } +} + +static void put_extern_sym(Sym *sym, Section *section, + unsigned long value, unsigned long size) +{ + put_extern_sym2(sym, section, value, size, 1); +} + +/* add a new relocation entry to symbol 'sym' in section 's' */ +static void greloc(Section *s, Sym *sym, unsigned long offset, int type) +{ + if (!sym->c) + put_extern_sym(sym, NULL, 0, 0); + /* now we can add ELF relocation info */ + put_elf_reloc(symtab_section, s, offset, type, sym->c); +} + +static inline int isid(int c) +{ + return (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + c == '_'; +} + +static inline int isnum(int c) +{ + return c >= '0' && c <= '9'; +} + +static inline int isoct(int c) +{ + return c >= '0' && c <= '7'; +} + +static inline int toup(int c) +{ + if (c >= 'a' && c <= 'z') + return c - 'a' + 'A'; + else + return c; +} + +static void strcat_vprintf(char *buf, int buf_size, const char *fmt, va_list ap) +{ + int len; + len = strlen(buf); + vsnprintf(buf + len, buf_size - len, fmt, ap); +} + +static void strcat_printf(char *buf, int buf_size, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + strcat_vprintf(buf, buf_size, fmt, ap); + va_end(ap); +} + +void error1(TCCState *s1, int is_warning, const char *fmt, va_list ap) +{ + char buf[2048]; + BufferedFile **f; + + buf[0] = '\0'; + if (file) { + for(f = s1->include_stack; f < s1->include_stack_ptr; f++) + strcat_printf(buf, sizeof(buf), "In file included from %s:%d:\n", + (*f)->filename, (*f)->line_num); + if (file->line_num > 0) { + strcat_printf(buf, sizeof(buf), + "%s:%d: ", file->filename, file->line_num); + } else { + strcat_printf(buf, sizeof(buf), + "%s: ", file->filename); + } + } else { + strcat_printf(buf, sizeof(buf), + "tcc: "); + } + if (is_warning) + strcat_printf(buf, sizeof(buf), "warning: "); + strcat_vprintf(buf, sizeof(buf), fmt, ap); + + if (!s1->error_func) { + /* default case: stderr */ + fprintf(stderr, "%s\n", buf); + } else { + s1->error_func(s1->error_opaque, buf); + } + if (!is_warning || s1->warn_error) + s1->nb_errors++; +} + +void tcc_set_error_func(TCCState *s, void *error_opaque, + void (*error_func)(void *opaque, const char *msg)) +{ + s->error_opaque = error_opaque; + s->error_func = error_func; +} + +/* error without aborting current compilation */ +void error_noabort(const char *fmt, ...) +{ + TCCState *s1 = tcc_state; + va_list ap; + + va_start(ap, fmt); + error1(s1, 0, fmt, ap); + va_end(ap); +} + +void error(const char *fmt, ...) +{ + TCCState *s1 = tcc_state; + va_list ap; + + va_start(ap, fmt); + error1(s1, 0, fmt, ap); + va_end(ap); + /* better than nothing: in some cases, we accept to handle errors */ + if (s1->error_set_jmp_enabled) { + longjmp(s1->error_jmp_buf, 1); + } else { + /* XXX: eliminate this someday */ + exit(1); + } +} + +void expect(const char *msg) +{ + error("%s expected", msg); +} + +void warning(const char *fmt, ...) +{ + TCCState *s1 = tcc_state; + va_list ap; + + if (s1->warn_none) + return; + + va_start(ap, fmt); + error1(s1, 1, fmt, ap); + va_end(ap); +} + +void skip(int c) +{ + if (tok != c) + error("'%c' expected", c); + next(); +} + +static void test_lvalue(void) +{ + if (!(vtop->r & VT_LVAL)) + expect("lvalue"); +} + +/* CString handling */ + +static void cstr_realloc(CString *cstr, int new_size) +{ + int size; + void *data; + + size = cstr->size_allocated; + if (size == 0) + size = 8; /* no need to allocate a too small first string */ + while (size < new_size) + size = size * 2; + data = tcc_realloc(cstr->data_allocated, size); + if (!data) + error("memory full"); + cstr->data_allocated = data; + cstr->size_allocated = size; + cstr->data = data; +} + +/* add a byte */ +static inline void cstr_ccat(CString *cstr, int ch) +{ + int size; + size = cstr->size + 1; + if (size > cstr->size_allocated) + cstr_realloc(cstr, size); + ((unsigned char *)cstr->data)[size - 1] = ch; + cstr->size = size; +} + +static void cstr_cat(CString *cstr, const char *str) +{ + int c; + for(;;) { + c = *str; + if (c == '\0') + break; + cstr_ccat(cstr, c); + str++; + } +} + +/* add a wide char */ +static void cstr_wccat(CString *cstr, int ch) +{ + int size; + size = cstr->size + sizeof(nwchar_t); + if (size > cstr->size_allocated) + cstr_realloc(cstr, size); + *(nwchar_t *)(((unsigned char *)cstr->data) + size - sizeof(nwchar_t)) = ch; + cstr->size = size; +} + +static void cstr_new(CString *cstr) +{ + memset(cstr, 0, sizeof(CString)); +} + +/* free string and reset it to NULL */ +static void cstr_free(CString *cstr) +{ + tcc_free(cstr->data_allocated); + cstr_new(cstr); +} + +#define cstr_reset(cstr) cstr_free(cstr) + +/* XXX: unicode ? */ +static void add_char(CString *cstr, int c) +{ + if (c == '\'' || c == '\"' || c == '\\') { + /* XXX: could be more precise if char or string */ + cstr_ccat(cstr, '\\'); + } + if (c >= 32 && c <= 126) { + cstr_ccat(cstr, c); + } else { + cstr_ccat(cstr, '\\'); + if (c == '\n') { + cstr_ccat(cstr, 'n'); + } else { + cstr_ccat(cstr, '0' + ((c >> 6) & 7)); + cstr_ccat(cstr, '0' + ((c >> 3) & 7)); + cstr_ccat(cstr, '0' + (c & 7)); + } + } +} + +/* push, without hashing */ +static Sym *sym_push2(Sym **ps, int v, int t, long c) +{ + Sym *s; + s = sym_malloc(); + s->v = v; + s->type.t = t; + s->c = c; + s->next = NULL; + /* add in stack */ + s->prev = *ps; + *ps = s; + return s; +} + +/* find a symbol and return its associated structure. 's' is the top + of the symbol stack */ +static Sym *sym_find2(Sym *s, int v) +{ + while (s) { + if (s->v == v) + return s; + s = s->prev; + } + return NULL; +} + +/* structure lookup */ +static inline Sym *struct_find(int v) +{ + v -= TOK_IDENT; + if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT)) + return NULL; + return table_ident[v]->sym_struct; +} + +/* find an identifier */ +static inline Sym *sym_find(int v) +{ + v -= TOK_IDENT; + if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT)) + return NULL; + return table_ident[v]->sym_identifier; +} + +/* push a given symbol on the symbol stack */ +static Sym *sym_push(int v, CType *type, int r, int c) +{ + Sym *s, **ps; + TokenSym *ts; + + if (local_stack) + ps = &local_stack; + else + ps = &global_stack; + s = sym_push2(ps, v, type->t, c); + s->type.ref = type->ref; + s->r = r; + /* don't record fields or anonymous symbols */ + /* XXX: simplify */ + if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) { + /* record symbol in token array */ + ts = table_ident[(v & ~SYM_STRUCT) - TOK_IDENT]; + if (v & SYM_STRUCT) + ps = &ts->sym_struct; + else + ps = &ts->sym_identifier; + s->prev_tok = *ps; + *ps = s; + } + return s; +} + +/* push a global identifier */ +static Sym *global_identifier_push(int v, int t, int c) +{ + Sym *s, **ps; + s = sym_push2(&global_stack, v, t, c); + /* don't record anonymous symbol */ + if (v < SYM_FIRST_ANOM) { + ps = &table_ident[v - TOK_IDENT]->sym_identifier; + /* modify the top most local identifier, so that + sym_identifier will point to 's' when popped */ + while (*ps != NULL) + ps = &(*ps)->prev_tok; + s->prev_tok = NULL; + *ps = s; + } + return s; +} + +/* pop symbols until top reaches 'b' */ +static void sym_pop(Sym **ptop, Sym *b) +{ + Sym *s, *ss, **ps; + TokenSym *ts; + int v; + + s = *ptop; + while(s != b) { + ss = s->prev; + v = s->v; + /* remove symbol in token array */ + /* XXX: simplify */ + if (!(v & SYM_FIELD) && (v & ~SYM_STRUCT) < SYM_FIRST_ANOM) { + ts = table_ident[(v & ~SYM_STRUCT) - TOK_IDENT]; + if (v & SYM_STRUCT) + ps = &ts->sym_struct; + else + ps = &ts->sym_identifier; + *ps = s->prev_tok; + } + sym_free(s); + s = ss; + } + *ptop = b; +} + +/* I/O layer */ + +BufferedFile *tcc_open(TCCState *s1, const char *filename) +{ + int fd; + BufferedFile *bf; + + if (strcmp(filename, "-") == 0) + fd = 0, filename = "stdin"; + else + fd = open(filename, O_RDONLY | O_BINARY); + if ((s1->verbose == 2 && fd >= 0) || s1->verbose == 3) + printf("%s %*s%s\n", fd < 0 ? "nf":"->", + (s1->include_stack_ptr - s1->include_stack), "", filename); + if (fd < 0) + return NULL; + bf = tcc_malloc(sizeof(BufferedFile)); + bf->fd = fd; + bf->buf_ptr = bf->buffer; + bf->buf_end = bf->buffer; + bf->buffer[0] = CH_EOB; /* put eob symbol */ + pstrcpy(bf->filename, sizeof(bf->filename), filename); +#ifdef _WIN32 + normalize_slashes(bf->filename); +#endif + bf->line_num = 1; + bf->ifndef_macro = 0; + bf->ifdef_stack_ptr = s1->ifdef_stack_ptr; + // printf("opening '%s'\n", filename); + return bf; +} + +void tcc_close(BufferedFile *bf) +{ + total_lines += bf->line_num; + close(bf->fd); + tcc_free(bf); +} + +#include "tccpp.c" +#include "tccgen.c" + + +/* compile the C file opened in 'file'. Return non zero if errors. */ +static int tcc_compile(TCCState *s1) +{ + Sym *define_start; + char buf[512]; + volatile int section_sym; + +#ifdef INC_DEBUG + printf("%s: **** new file\n", file->filename); +#endif + preprocess_init(s1); + + cur_text_section = NULL; + funcname = ""; + anon_sym = SYM_FIRST_ANOM; + + /* file info: full path + filename */ + section_sym = 0; /* avoid warning */ + if (s1->do_debug) { + section_sym = put_elf_sym(symtab_section, 0, 0, + ELF64_ST_INFO(STB_LOCAL, STT_SECTION), 0, + text_section->sh_num, NULL); + getcwd(buf, sizeof(buf)); +#ifdef _WIN32 + normalize_slashes(buf); +#endif + pstrcat(buf, sizeof(buf), "/"); + put_stabs_r(buf, N_SO, 0, 0, + text_section->data_offset, text_section, section_sym); + put_stabs_r(file->filename, N_SO, 0, 0, + text_section->data_offset, text_section, section_sym); + } + /* an elf symbol of type STT_FILE must be put so that STB_LOCAL + symbols can be safely used */ + put_elf_sym(symtab_section, 0, 0, + ELF64_ST_INFO(STB_LOCAL, STT_FILE), 0, + SHN_ABS, file->filename); + + /* define some often used types */ + int_type.t = VT_INT; + + char_pointer_type.t = VT_BYTE; + mk_pointer(&char_pointer_type); + + func_old_type.t = VT_FUNC; + func_old_type.ref = sym_push(SYM_FIELD, &int_type, FUNC_CDECL, FUNC_OLD); + +#if defined(TCC_ARM_EABI) && defined(TCC_ARM_VFP) + float_type.t = VT_FLOAT; + double_type.t = VT_DOUBLE; + + func_float_type.t = VT_FUNC; + func_float_type.ref = sym_push(SYM_FIELD, &float_type, FUNC_CDECL, FUNC_OLD); + func_double_type.t = VT_FUNC; + func_double_type.ref = sym_push(SYM_FIELD, &double_type, FUNC_CDECL, FUNC_OLD); +#endif + +#if 0 + /* define 'void *alloca(unsigned int)' builtin function */ + { + Sym *s1; + + p = anon_sym++; + sym = sym_push(p, mk_pointer(VT_VOID), FUNC_CDECL, FUNC_NEW); + s1 = sym_push(SYM_FIELD, VT_UNSIGNED | VT_INT, 0, 0); + s1->next = NULL; + sym->next = s1; + sym_push(TOK_alloca, VT_FUNC | (p << VT_STRUCT_SHIFT), VT_CONST, 0); + } +#endif + + define_start = define_stack; + nocode_wanted = 1; + + if (setjmp(s1->error_jmp_buf) == 0) { + s1->nb_errors = 0; + s1->error_set_jmp_enabled = 1; + + ch = file->buf_ptr[0]; + tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF; + parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM; + next(); + decl(VT_CONST); + if (tok != TOK_EOF) + expect("declaration"); + + /* end of translation unit info */ + if (s1->do_debug) { + put_stabs_r(NULL, N_SO, 0, 0, + text_section->data_offset, text_section, section_sym); + } + } + s1->error_set_jmp_enabled = 0; + + /* reset define stack, but leave -Dsymbols (may be incorrect if + they are undefined) */ + free_defines(define_start); + + gen_inline_functions(); + + sym_pop(&global_stack, NULL); + sym_pop(&local_stack, NULL); + + return s1->nb_errors != 0 ? -1 : 0; +} + +int tcc_compile_string(TCCState *s, const char *str) +{ + BufferedFile bf1, *bf = &bf1; + int ret, len; + char *buf; + + /* init file structure */ + bf->fd = -1; + /* XXX: avoid copying */ + len = strlen(str); + buf = tcc_malloc(len + 1); + if (!buf) + return -1; + memcpy(buf, str, len); + buf[len] = CH_EOB; + bf->buf_ptr = buf; + bf->buf_end = buf + len; + pstrcpy(bf->filename, sizeof(bf->filename), ""); + bf->line_num = 1; + file = bf; + ret = tcc_compile(s); + file = NULL; + tcc_free(buf); + + /* currently, no need to close */ + return ret; +} + +/* define a preprocessor symbol. A value can also be provided with the '=' operator */ +void tcc_define_symbol(TCCState *s1, const char *sym, const char *value) +{ + BufferedFile bf1, *bf = &bf1; + + pstrcpy(bf->buffer, IO_BUF_SIZE, sym); + pstrcat(bf->buffer, IO_BUF_SIZE, " "); + /* default value */ + if (!value) + value = "1"; + pstrcat(bf->buffer, IO_BUF_SIZE, value); + + /* init file structure */ + bf->fd = -1; + bf->buf_ptr = bf->buffer; + bf->buf_end = bf->buffer + strlen(bf->buffer); + *bf->buf_end = CH_EOB; + bf->filename[0] = '\0'; + bf->line_num = 1; + file = bf; + + s1->include_stack_ptr = s1->include_stack; + + /* parse with define parser */ + ch = file->buf_ptr[0]; + next_nomacro(); + parse_define(); + file = NULL; +} + +/* undefine a preprocessor symbol */ +void tcc_undefine_symbol(TCCState *s1, const char *sym) +{ + TokenSym *ts; + Sym *s; + ts = tok_alloc(sym, strlen(sym)); + s = define_find(ts->tok); + /* undefine symbol by putting an invalid name */ + if (s) + define_undef(s); +} + +#ifdef CONFIG_TCC_ASM + +#ifdef TCC_TARGET_I386 +#include "i386-asm.c" +#endif +#include "tccasm.c" + +#else +static void asm_instr(void) +{ + error("inline asm() not supported"); +} +static void asm_global_instr(void) +{ + error("inline asm() not supported"); +} +#endif + +#include "tccelf.c" + +#ifdef TCC_TARGET_COFF +#include "tcccoff.c" +#endif + +#ifdef TCC_TARGET_PE +#include "tccpe.c" +#endif + +#ifdef CONFIG_TCC_BACKTRACE +/* print the position in the source file of PC value 'pc' by reading + the stabs debug information */ +static void rt_printline(unsigned long wanted_pc) +{ + Stab_Sym *sym, *sym_end; + char func_name[128], last_func_name[128]; + unsigned long func_addr, last_pc, pc; + const char *incl_files[INCLUDE_STACK_SIZE]; + int incl_index, len, last_line_num, i; + const char *str, *p; + + fprintf(stderr, "0x%08lx:", wanted_pc); + + func_name[0] = '\0'; + func_addr = 0; + incl_index = 0; + last_func_name[0] = '\0'; + last_pc = 0xffffffff; + last_line_num = 1; + sym = (Stab_Sym *)stab_section->data + 1; + sym_end = (Stab_Sym *)(stab_section->data + stab_section->data_offset); + while (sym < sym_end) { + switch(sym->n_type) { + /* function start or end */ + case N_FUN: + if (sym->n_strx == 0) { + /* we test if between last line and end of function */ + pc = sym->n_value + func_addr; + if (wanted_pc >= last_pc && wanted_pc < pc) + goto found; + func_name[0] = '\0'; + func_addr = 0; + } else { + str = stabstr_section->data + sym->n_strx; + p = strchr(str, ':'); + if (!p) { + pstrcpy(func_name, sizeof(func_name), str); + } else { + len = p - str; + if (len > sizeof(func_name) - 1) + len = sizeof(func_name) - 1; + memcpy(func_name, str, len); + func_name[len] = '\0'; + } + func_addr = sym->n_value; + } + break; + /* line number info */ + case N_SLINE: + pc = sym->n_value + func_addr; + if (wanted_pc >= last_pc && wanted_pc < pc) + goto found; + last_pc = pc; + last_line_num = sym->n_desc; + /* XXX: slow! */ + strcpy(last_func_name, func_name); + break; + /* include files */ + case N_BINCL: + str = stabstr_section->data + sym->n_strx; + add_incl: + if (incl_index < INCLUDE_STACK_SIZE) { + incl_files[incl_index++] = str; + } + break; + case N_EINCL: + if (incl_index > 1) + incl_index--; + break; + case N_SO: + if (sym->n_strx == 0) { + incl_index = 0; /* end of translation unit */ + } else { + str = stabstr_section->data + sym->n_strx; + /* do not add path */ + len = strlen(str); + if (len > 0 && str[len - 1] != '/') + goto add_incl; + } + break; + } + sym++; + } + + /* second pass: we try symtab symbols (no line number info) */ + incl_index = 0; + { + ElfW(Sym) *sym, *sym_end; + int type; + + sym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset); + for(sym = (ElfW(Sym) *)symtab_section->data + 1; + sym < sym_end; + sym++) { + type = ELF64_ST_TYPE(sym->st_info); + if (type == STT_FUNC) { + if (wanted_pc >= sym->st_value && + wanted_pc < sym->st_value + sym->st_size) { + pstrcpy(last_func_name, sizeof(last_func_name), + strtab_section->data + sym->st_name); + goto found; + } + } + } + } + /* did not find any info: */ + fprintf(stderr, " ???\n"); + return; + found: + if (last_func_name[0] != '\0') { + fprintf(stderr, " %s()", last_func_name); + } + if (incl_index > 0) { + fprintf(stderr, " (%s:%d", + incl_files[incl_index - 1], last_line_num); + for(i = incl_index - 2; i >= 0; i--) + fprintf(stderr, ", included from %s", incl_files[i]); + fprintf(stderr, ")"); + } + fprintf(stderr, "\n"); +} + +#ifdef __i386__ +/* fix for glibc 2.1 */ +#ifndef REG_EIP +#define REG_EIP EIP +#define REG_EBP EBP +#endif + +/* return the PC at frame level 'level'. Return non zero if not found */ +static int rt_get_caller_pc(unsigned long *paddr, + ucontext_t *uc, int level) +{ + unsigned long fp; + int i; + + if (level == 0) { +#if defined(__FreeBSD__) + *paddr = uc->uc_mcontext.mc_eip; +#elif defined(__dietlibc__) + *paddr = uc->uc_mcontext.eip; +#else + *paddr = uc->uc_mcontext.gregs[REG_EIP]; +#endif + return 0; + } else { +#if defined(__FreeBSD__) + fp = uc->uc_mcontext.mc_ebp; +#elif defined(__dietlibc__) + fp = uc->uc_mcontext.ebp; +#else + fp = uc->uc_mcontext.gregs[REG_EBP]; +#endif + for(i=1;i= 0xc0000000) + return -1; + fp = ((unsigned long *)fp)[0]; + } + *paddr = ((unsigned long *)fp)[1]; + return 0; + } +} +#elif 1 +/* return the PC at frame level 'level'. Return non zero if not found */ +static int rt_get_caller_pc(unsigned long *paddr, + ucontext_t *uc, int level) +{ + unsigned long fp; + int i; + + if (level == 0) { + /* XXX: only support linux */ + *paddr = uc->uc_mcontext.gregs[REG_RIP]; + return 0; + } else { + fp = uc->uc_mcontext.gregs[REG_RBP]; + for(i=1;isi_code) { + case FPE_INTDIV: + case FPE_FLTDIV: + rt_error(uc, "division by zero"); + break; + default: + rt_error(uc, "floating point exception"); + break; + } + break; + case SIGBUS: + case SIGSEGV: + if (rt_bound_error_msg && *rt_bound_error_msg) + rt_error(uc, *rt_bound_error_msg); + else + rt_error(uc, "dereferencing invalid pointer"); + break; + case SIGILL: + rt_error(uc, "illegal instruction"); + break; + case SIGABRT: + rt_error(uc, "abort() called"); + break; + default: + rt_error(uc, "caught signal %d", signum); + break; + } + exit(255); +} + +#endif + +/* copy code into memory passed in by the caller and do all relocations + (needed before using tcc_get_symbol()). + returns -1 on error and required size if ptr is NULL */ +int tcc_relocate(TCCState *s1, void *ptr) +{ + Section *s; + unsigned long offset, length, mem; + int i; + + if (0 == s1->runtime_added) { + s1->runtime_added = 1; + s1->nb_errors = 0; +#ifdef TCC_TARGET_PE + pe_add_runtime(s1); + relocate_common_syms(); + tcc_add_linker_symbols(s1); +#else + tcc_add_runtime(s1); + relocate_common_syms(); + tcc_add_linker_symbols(s1); + build_got_entries(s1); +#endif + } + + offset = 0, mem = (unsigned long)ptr; + for(i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; + if (0 == (s->sh_flags & SHF_ALLOC)) + continue; + length = s->data_offset; + s->sh_addr = mem ? (mem + offset + 15) & ~15 : 0; + offset = (offset + length + 15) & ~15; + } + + /* relocate symbols */ + relocate_syms(s1, 1); + if (s1->nb_errors) + return -1; + +#ifdef TCC_TARGET_X86_64 + s1->runtime_plt_and_got_offset = 0; + s1->runtime_plt_and_got = (char *)(mem + offset); + /* double the size of the buffer for got and plt entries + XXX: calculate exact size for them? */ + offset *= 2; +#endif + + if (0 == mem) + return offset + 15; + + /* relocate each section */ + for(i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; + if (s->reloc) + relocate_section(s1, s); + } + + for(i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; + if (0 == (s->sh_flags & SHF_ALLOC)) + continue; + length = s->data_offset; + // printf("%-12s %08x %04x\n", s->name, s->sh_addr, length); + ptr = (void*)s->sh_addr; + if (NULL == s->data || s->sh_type == SHT_NOBITS) + memset(ptr, 0, length); + else + memcpy(ptr, s->data, length); + /* mark executable sections as executable in memory */ + if (s->sh_flags & SHF_EXECINSTR) + set_pages_executable(ptr, length); + } +#ifdef TCC_TARGET_X86_64 + set_pages_executable(s1->runtime_plt_and_got, + s1->runtime_plt_and_got_offset); +#endif + return 0; +} + +/* launch the compiled program with the given arguments */ +int tcc_run(TCCState *s1, int argc, char **argv) +{ + int (*prog_main)(int, char **); + void *ptr; + int ret; + + ret = tcc_relocate(s1, NULL); + if (ret < 0) + return -1; + ptr = tcc_malloc(ret); + tcc_relocate(s1, ptr); + + prog_main = tcc_get_symbol_err(s1, "main"); + + if (s1->do_debug) { +#ifdef CONFIG_TCC_BACKTRACE + struct sigaction sigact; + /* install TCC signal handlers to print debug info on fatal + runtime errors */ + sigact.sa_flags = SA_SIGINFO | SA_RESETHAND; + sigact.sa_sigaction = sig_error; + sigemptyset(&sigact.sa_mask); + sigaction(SIGFPE, &sigact, NULL); + sigaction(SIGILL, &sigact, NULL); + sigaction(SIGSEGV, &sigact, NULL); + sigaction(SIGBUS, &sigact, NULL); + sigaction(SIGABRT, &sigact, NULL); +#else + error("debug mode not available"); +#endif + } + +#ifdef CONFIG_TCC_BCHECK + if (s1->do_bounds_check) { + void (*bound_init)(void); + + /* set error function */ + rt_bound_error_msg = tcc_get_symbol_err(s1, "__bound_error_msg"); + + /* XXX: use .init section so that it also work in binary ? */ + bound_init = (void *)tcc_get_symbol_err(s1, "__bound_init"); + bound_init(); + } +#endif + ret = (*prog_main)(argc, argv); + tcc_free(ptr); + return ret; +} + +void tcc_memstats(void) +{ +#ifdef MEM_DEBUG + printf("memory in use: %d\n", mem_cur_size); +#endif +} + +static void tcc_cleanup(void) +{ + int i, n; + + if (NULL == tcc_state) + return; + tcc_state = NULL; + + /* free -D defines */ + free_defines(NULL); + + /* free tokens */ + n = tok_ident - TOK_IDENT; + for(i = 0; i < n; i++) + tcc_free(table_ident[i]); + tcc_free(table_ident); + + /* free sym_pools */ + dynarray_reset(&sym_pools, &nb_sym_pools); + /* string buffer */ + cstr_free(&tokcstr); + /* reset symbol stack */ + sym_free_first = NULL; + /* cleanup from error/setjmp */ + macro_ptr = NULL; +} + +TCCState *tcc_new(void) +{ + TCCState *s; + + tcc_cleanup(); + + s = tcc_mallocz(sizeof(TCCState)); + if (!s) + return NULL; + tcc_state = s; + s->output_type = TCC_OUTPUT_MEMORY; + s->tcc_lib_path = CONFIG_TCCDIR; + + preprocess_new(); + + /* we add dummy defines for some special macros to speed up tests + and to have working defined() */ + define_push(TOK___LINE__, MACRO_OBJ, NULL, NULL); + define_push(TOK___FILE__, MACRO_OBJ, NULL, NULL); + define_push(TOK___DATE__, MACRO_OBJ, NULL, NULL); + define_push(TOK___TIME__, MACRO_OBJ, NULL, NULL); + + /* standard defines */ + tcc_define_symbol(s, "__STDC__", NULL); + tcc_define_symbol(s, "__STDC_VERSION__", "199901L"); +#if defined(TCC_TARGET_I386) + tcc_define_symbol(s, "__i386__", NULL); +#endif +#if defined(TCC_TARGET_X86_64) + tcc_define_symbol(s, "__x86_64__", NULL); +#endif +#if defined(TCC_TARGET_ARM) + tcc_define_symbol(s, "__ARM_ARCH_4__", NULL); + tcc_define_symbol(s, "__arm_elf__", NULL); + tcc_define_symbol(s, "__arm_elf", NULL); + tcc_define_symbol(s, "arm_elf", NULL); + tcc_define_symbol(s, "__arm__", NULL); + tcc_define_symbol(s, "__arm", NULL); + tcc_define_symbol(s, "arm", NULL); + tcc_define_symbol(s, "__APCS_32__", NULL); +#endif +#ifdef TCC_TARGET_PE + tcc_define_symbol(s, "_WIN32", NULL); +#else + tcc_define_symbol(s, "__unix__", NULL); + tcc_define_symbol(s, "__unix", NULL); +#if defined(__linux) + tcc_define_symbol(s, "__linux__", NULL); + tcc_define_symbol(s, "__linux", NULL); +#endif +#endif + /* tiny C specific defines */ + tcc_define_symbol(s, "__TINYC__", NULL); + + /* tiny C & gcc defines */ + tcc_define_symbol(s, "__SIZE_TYPE__", "unsigned int"); + tcc_define_symbol(s, "__PTRDIFF_TYPE__", "int"); +#ifdef TCC_TARGET_PE + tcc_define_symbol(s, "__WCHAR_TYPE__", "unsigned short"); +#else + tcc_define_symbol(s, "__WCHAR_TYPE__", "int"); +#endif + +#ifndef TCC_TARGET_PE + /* default library paths */ + tcc_add_library_path(s, CONFIG_SYSROOT "/usr/local/lib"); + tcc_add_library_path(s, CONFIG_SYSROOT "/usr/lib"); + tcc_add_library_path(s, CONFIG_SYSROOT "/lib"); +#endif + + /* no section zero */ + dynarray_add((void ***)&s->sections, &s->nb_sections, NULL); + + /* create standard sections */ + text_section = new_section(s, ".text", SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR); + data_section = new_section(s, ".data", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); + bss_section = new_section(s, ".bss", SHT_NOBITS, SHF_ALLOC | SHF_WRITE); + + /* symbols are always generated for linking stage */ + symtab_section = new_symtab(s, ".symtab", SHT_SYMTAB, 0, + ".strtab", + ".hashtab", SHF_PRIVATE); + strtab_section = symtab_section->link; + + /* private symbol table for dynamic symbols */ + s->dynsymtab_section = new_symtab(s, ".dynsymtab", SHT_SYMTAB, SHF_PRIVATE, + ".dynstrtab", + ".dynhashtab", SHF_PRIVATE); + s->alacarte_link = 1; + +#ifdef CHAR_IS_UNSIGNED + s->char_is_unsigned = 1; +#endif +#if defined(TCC_TARGET_PE) && 0 + /* XXX: currently the PE linker is not ready to support that */ + s->leading_underscore = 1; +#endif + return s; +} + +void tcc_delete(TCCState *s1) +{ + int i; + + tcc_cleanup(); + + /* free all sections */ + for(i = 1; i < s1->nb_sections; i++) + free_section(s1->sections[i]); + dynarray_reset(&s1->sections, &s1->nb_sections); + + for(i = 0; i < s1->nb_priv_sections; i++) + free_section(s1->priv_sections[i]); + dynarray_reset(&s1->priv_sections, &s1->nb_priv_sections); + + /* free any loaded DLLs */ + for ( i = 0; i < s1->nb_loaded_dlls; i++) { + DLLReference *ref = s1->loaded_dlls[i]; + if ( ref->handle ) + dlclose(ref->handle); + } + + /* free loaded dlls array */ + dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls); + + /* free library paths */ + dynarray_reset(&s1->library_paths, &s1->nb_library_paths); + + /* free include paths */ + dynarray_reset(&s1->cached_includes, &s1->nb_cached_includes); + dynarray_reset(&s1->include_paths, &s1->nb_include_paths); + dynarray_reset(&s1->sysinclude_paths, &s1->nb_sysinclude_paths); + + tcc_free(s1); +} + +int tcc_add_include_path(TCCState *s1, const char *pathname) +{ + char *pathname1; + + pathname1 = tcc_strdup(pathname); + dynarray_add((void ***)&s1->include_paths, &s1->nb_include_paths, pathname1); + return 0; +} + +int tcc_add_sysinclude_path(TCCState *s1, const char *pathname) +{ + char *pathname1; + + pathname1 = tcc_strdup(pathname); + dynarray_add((void ***)&s1->sysinclude_paths, &s1->nb_sysinclude_paths, pathname1); + return 0; +} + +static int tcc_add_file_internal(TCCState *s1, const char *filename, int flags) +{ + const char *ext; + ElfW(Ehdr) ehdr; + int fd, ret; + BufferedFile *saved_file; + + /* find source file type with extension */ + ext = tcc_fileextension(filename); + if (ext[0]) + ext++; + + /* open the file */ + saved_file = file; + file = tcc_open(s1, filename); + if (!file) { + if (flags & AFF_PRINT_ERROR) { + error_noabort("file '%s' not found", filename); + } + ret = -1; + goto fail1; + } + + if (flags & AFF_PREPROCESS) { + ret = tcc_preprocess(s1); + } else if (!ext[0] || !PATHCMP(ext, "c")) { + /* C file assumed */ + ret = tcc_compile(s1); + } else +#ifdef CONFIG_TCC_ASM + if (!strcmp(ext, "S")) { + /* preprocessed assembler */ + ret = tcc_assemble(s1, 1); + } else if (!strcmp(ext, "s")) { + /* non preprocessed assembler */ + ret = tcc_assemble(s1, 0); + } else +#endif +#ifdef TCC_TARGET_PE + if (!PATHCMP(ext, "def")) { + ret = pe_load_def_file(s1, file->fd); + } else +#endif + { + fd = file->fd; + /* assume executable format: auto guess file type */ + ret = read(fd, &ehdr, sizeof(ehdr)); + lseek(fd, 0, SEEK_SET); + if (ret <= 0) { + error_noabort("could not read header"); + goto fail; + } else if (ret != sizeof(ehdr)) { + goto try_load_script; + } + + if (ehdr.e_ident[0] == ELFMAG0 && + ehdr.e_ident[1] == ELFMAG1 && + ehdr.e_ident[2] == ELFMAG2 && + ehdr.e_ident[3] == ELFMAG3) { + file->line_num = 0; /* do not display line number if error */ + if (ehdr.e_type == ET_REL) { + ret = tcc_load_object_file(s1, fd, 0); + } else if (ehdr.e_type == ET_DYN) { + if (s1->output_type == TCC_OUTPUT_MEMORY) { +#ifdef TCC_TARGET_PE + ret = -1; +#else + void *h; + h = dlopen(filename, RTLD_GLOBAL | RTLD_LAZY); + if (h) + ret = 0; + else + ret = -1; +#endif + } else { + ret = tcc_load_dll(s1, fd, filename, + (flags & AFF_REFERENCED_DLL) != 0); + } + } else { + error_noabort("unrecognized ELF file"); + goto fail; + } + } else if (memcmp((char *)&ehdr, ARMAG, 8) == 0) { + file->line_num = 0; /* do not display line number if error */ + ret = tcc_load_archive(s1, fd); + } else +#ifdef TCC_TARGET_COFF + if (*(uint16_t *)(&ehdr) == COFF_C67_MAGIC) { + ret = tcc_load_coff(s1, fd); + } else +#endif +#ifdef TCC_TARGET_PE + if (pe_test_res_file(&ehdr, ret)) { + ret = pe_load_res_file(s1, fd); + } else +#endif + { + /* as GNU ld, consider it is an ld script if not recognized */ + try_load_script: + ret = tcc_load_ldscript(s1); + if (ret < 0) { + error_noabort("unrecognized file type"); + goto fail; + } + } + } + the_end: + tcc_close(file); + fail1: + file = saved_file; + return ret; + fail: + ret = -1; + goto the_end; +} + +int tcc_add_file(TCCState *s, const char *filename) +{ + if (s->output_type == TCC_OUTPUT_PREPROCESS) + return tcc_add_file_internal(s, filename, AFF_PRINT_ERROR | AFF_PREPROCESS); + else + return tcc_add_file_internal(s, filename, AFF_PRINT_ERROR); +} + +int tcc_add_library_path(TCCState *s, const char *pathname) +{ + char *pathname1; + + pathname1 = tcc_strdup(pathname); + dynarray_add((void ***)&s->library_paths, &s->nb_library_paths, pathname1); + return 0; +} + +/* find and load a dll. Return non zero if not found */ +/* XXX: add '-rpath' option support ? */ +static int tcc_add_dll(TCCState *s, const char *filename, int flags) +{ + char buf[1024]; + int i; + + for(i = 0; i < s->nb_library_paths; i++) { + snprintf(buf, sizeof(buf), "%s/%s", + s->library_paths[i], filename); + if (tcc_add_file_internal(s, buf, flags) == 0) + return 0; + } + return -1; +} + +/* the library name is the same as the argument of the '-l' option */ +int tcc_add_library(TCCState *s, const char *libraryname) +{ + char buf[1024]; + int i; + + /* first we look for the dynamic library if not static linking */ + if (!s->static_link) { +#ifdef TCC_TARGET_PE + snprintf(buf, sizeof(buf), "%s.def", libraryname); +#else + snprintf(buf, sizeof(buf), "lib%s.so", libraryname); +#endif + if (tcc_add_dll(s, buf, 0) == 0) + return 0; + } + + /* then we look for the static library */ + for(i = 0; i < s->nb_library_paths; i++) { + snprintf(buf, sizeof(buf), "%s/lib%s.a", + s->library_paths[i], libraryname); + if (tcc_add_file_internal(s, buf, 0) == 0) + return 0; + } + return -1; +} + +int tcc_add_symbol(TCCState *s, const char *name, void *val) +{ + add_elf_sym(symtab_section, (unsigned long)val, 0, + ELF64_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, + SHN_ABS, name); + return 0; +} + +int tcc_set_output_type(TCCState *s, int output_type) +{ + char buf[1024]; + + s->output_type = output_type; + + if (!s->nostdinc) { + /* default include paths */ + /* XXX: reverse order needed if -isystem support */ +#ifndef TCC_TARGET_PE + tcc_add_sysinclude_path(s, CONFIG_SYSROOT "/usr/local/include"); + tcc_add_sysinclude_path(s, CONFIG_SYSROOT "/usr/include"); +#endif + snprintf(buf, sizeof(buf), "%s/include", s->tcc_lib_path); + tcc_add_sysinclude_path(s, buf); +#ifdef TCC_TARGET_PE + snprintf(buf, sizeof(buf), "%s/include/winapi", s->tcc_lib_path); + tcc_add_sysinclude_path(s, buf); +#endif + } + + /* if bound checking, then add corresponding sections */ +#ifdef CONFIG_TCC_BCHECK + if (s->do_bounds_check) { + /* define symbol */ + tcc_define_symbol(s, "__BOUNDS_CHECKING_ON", NULL); + /* create bounds sections */ + bounds_section = new_section(s, ".bounds", + SHT_PROGBITS, SHF_ALLOC); + lbounds_section = new_section(s, ".lbounds", + SHT_PROGBITS, SHF_ALLOC); + } +#endif + + if (s->char_is_unsigned) { + tcc_define_symbol(s, "__CHAR_UNSIGNED__", NULL); + } + + /* add debug sections */ + if (s->do_debug) { + /* stab symbols */ + stab_section = new_section(s, ".stab", SHT_PROGBITS, 0); + stab_section->sh_entsize = sizeof(Stab_Sym); + stabstr_section = new_section(s, ".stabstr", SHT_STRTAB, 0); + put_elf_str(stabstr_section, ""); + stab_section->link = stabstr_section; + /* put first entry */ + put_stabs("", 0, 0, 0, 0); + } + + /* add libc crt1/crti objects */ +#ifndef TCC_TARGET_PE + if ((output_type == TCC_OUTPUT_EXE || output_type == TCC_OUTPUT_DLL) && + !s->nostdlib) { + if (output_type != TCC_OUTPUT_DLL) + tcc_add_file(s, CONFIG_TCC_CRT_PREFIX "/crt1.o"); + tcc_add_file(s, CONFIG_TCC_CRT_PREFIX "/crti.o"); + } +#endif + +#ifdef TCC_TARGET_PE + snprintf(buf, sizeof(buf), "%s/lib", s->tcc_lib_path); + tcc_add_library_path(s, buf); +#endif + + return 0; +} + +#define WD_ALL 0x0001 /* warning is activated when using -Wall */ +#define FD_INVERT 0x0002 /* invert value before storing */ + +typedef struct FlagDef { + uint16_t offset; + uint16_t flags; + const char *name; +} FlagDef; + +static const FlagDef warning_defs[] = { + { 0, 0, "unsupported" }, + { 0, 0, "write-strings" }, + { 0, 0, "error" }, + { 0, WD_ALL, "implicit-function-declaration" } +}; + +void _init_warning_defs(void) { + warning_defs[0].offset = offsetof(TCCState, warn_unsupported); + warning_defs[1].offset = offsetof(TCCState, warn_write_strings); + warning_defs[2].offset = offsetof(TCCState, warn_error); + warning_defs[3].offset = offsetof(TCCState, warn_implicit_function_declaration); +} + +static int set_flag(TCCState *s, const FlagDef *flags, int nb_flags, + const char *name, int value) +{ + int i; + const FlagDef *p; + const char *r; + + r = name; + if (r[0] == 'n' && r[1] == 'o' && r[2] == '-') { + r += 3; + value = !value; + } + for(i = 0, p = flags; i < nb_flags; i++, p++) { + if (!strcmp(r, p->name)) + goto found; + } + return -1; + found: + if (p->flags & FD_INVERT) + value = !value; + *(int *)((uint8_t *)s + p->offset) = value; + return 0; +} + + +/* set/reset a warning */ +int tcc_set_warning(TCCState *s, const char *warning_name, int value) +{ + int i; + const FlagDef *p; + + if (!strcmp(warning_name, "all")) { + for(i = 0, p = warning_defs; i < countof(warning_defs); i++, p++) { + if (p->flags & WD_ALL) + *(int *)((uint8_t *)s + p->offset) = 1; + } + return 0; + } else { + return set_flag(s, warning_defs, countof(warning_defs), + warning_name, value); + } +} + +static const FlagDef flag_defs[] = { + { 0, 0, "unsigned-char" }, + { 0, FD_INVERT, "signed-char" }, + { 0, FD_INVERT, "common" }, + { 0, 0, "leading-underscore" } +}; +void _init_flag_defs(void) { + flag_defs[0].offset = offsetof(TCCState, char_is_unsigned); + flag_defs[1].offset = offsetof(TCCState, char_is_unsigned); + flag_defs[2].offset = offsetof(TCCState, nocommon); + flag_defs[3].offset = offsetof(TCCState, leading_underscore); +} + +/* set/reset a flag */ +int tcc_set_flag(TCCState *s, const char *flag_name, int value) +{ + return set_flag(s, flag_defs, countof(flag_defs), + flag_name, value); +} + +/* set CONFIG_TCCDIR at runtime */ +void tcc_set_lib_path(TCCState *s, const char *path) +{ + s->tcc_lib_path = tcc_strdup(path); +} + +void tcc_print_stats(TCCState *s, int64_t total_time) +{ + double tt; + tt = (double)total_time / 1000000.0; + if (tt < 0.001) + tt = 0.001; + if (total_bytes < 1) + total_bytes = 1; + printf("%d idents, %d lines, %d bytes, %0.3f s, %d lines/s, %0.1f MB/s\n", + tok_ident - TOK_IDENT, total_lines, total_bytes, + tt, (int)(total_lines / tt), + total_bytes / tt / 1000000.0); +} diff --git a/05/tcc-0.9.25/libtcc.h b/05/tcc-0.9.25/libtcc.h new file mode 100644 index 0000000..96070e2 --- /dev/null +++ b/05/tcc-0.9.25/libtcc.h @@ -0,0 +1,108 @@ +#ifndef LIBTCC_H +#define LIBTCC_H + +#ifdef LIBTCC_AS_DLL +#define LIBTCCAPI __declspec(dllexport) +#else +#define LIBTCCAPI +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +struct TCCState; + +typedef struct TCCState TCCState; + +/* create a new TCC compilation context */ +LIBTCCAPI TCCState *tcc_new(void); + +/* free a TCC compilation context */ +LIBTCCAPI void tcc_delete(TCCState *s); + +/* add debug information in the generated code */ +LIBTCCAPI void tcc_enable_debug(TCCState *s); + +/* set error/warning display callback */ +LIBTCCAPI void tcc_set_error_func(TCCState *s, void *error_opaque, + void (*error_func)(void *opaque, const char *msg)); + +/* set/reset a warning */ +LIBTCCAPI int tcc_set_warning(TCCState *s, const char *warning_name, int value); + +/*****************************/ +/* preprocessor */ + +/* add include path */ +LIBTCCAPI int tcc_add_include_path(TCCState *s, const char *pathname); + +/* add in system include path */ +LIBTCCAPI int tcc_add_sysinclude_path(TCCState *s, const char *pathname); + +/* define preprocessor symbol 'sym'. Can put optional value */ +LIBTCCAPI void tcc_define_symbol(TCCState *s, const char *sym, const char *value); + +/* undefine preprocess symbol 'sym' */ +LIBTCCAPI void tcc_undefine_symbol(TCCState *s, const char *sym); + +/*****************************/ +/* compiling */ + +/* add a file (either a C file, dll, an object, a library or an ld + script). Return -1 if error. */ +LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename); + +/* compile a string containing a C source. Return non zero if + error. */ +LIBTCCAPI int tcc_compile_string(TCCState *s, const char *buf); + +/*****************************/ +/* linking commands */ + +/* set output type. MUST BE CALLED before any compilation */ +#define TCC_OUTPUT_MEMORY 0 /* output will be ran in memory (no + output file) (default) */ +#define TCC_OUTPUT_EXE 1 /* executable file */ +#define TCC_OUTPUT_DLL 2 /* dynamic library */ +#define TCC_OUTPUT_OBJ 3 /* object file */ +#define TCC_OUTPUT_PREPROCESS 4 /* preprocessed file (used internally) */ +LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type); + +#define TCC_OUTPUT_FORMAT_ELF 0 /* default output format: ELF */ +#define TCC_OUTPUT_FORMAT_BINARY 1 /* binary image output */ +#define TCC_OUTPUT_FORMAT_COFF 2 /* COFF */ + +/* equivalent to -Lpath option */ +LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname); + +/* the library name is the same as the argument of the '-l' option */ +LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname); + +/* add a symbol to the compiled program */ +LIBTCCAPI int tcc_add_symbol(TCCState *s, const char *name, void *val); + +/* output an executable, library or object file. DO NOT call + tcc_relocate() before. */ +LIBTCCAPI int tcc_output_file(TCCState *s, const char *filename); + +/* link and run main() function and return its value. DO NOT call + tcc_relocate() before. */ +LIBTCCAPI int tcc_run(TCCState *s, int argc, char **argv); + +/* copy code into memory passed in by the caller and do all relocations + (needed before using tcc_get_symbol()). + returns -1 on error and required size if ptr is NULL */ +LIBTCCAPI int tcc_relocate(TCCState *s1, void *ptr); + +/* return symbol value or NULL if not found */ +LIBTCCAPI void *tcc_get_symbol(TCCState *s, const char *name); + +/* set CONFIG_TCCDIR at runtime */ +LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/05/tcc-0.9.25/limits.h b/05/tcc-0.9.25/limits.h new file mode 100644 index 0000000..633e9ff --- /dev/null +++ b/05/tcc-0.9.25/limits.h @@ -0,0 +1,6 @@ +#ifndef _LIMITS_H +#define _LIMITS_H + +#include // we define all the relevant constants here + +#endif // _LIMITS_H diff --git a/05/tcc-0.9.25/locale.h b/05/tcc-0.9.25/locale.h new file mode 100644 index 0000000..af90729 --- /dev/null +++ b/05/tcc-0.9.25/locale.h @@ -0,0 +1,60 @@ +#ifndef _LOCALE_H +#define _LOCALE_H + +#include + +struct lconv { + char *decimal_point; /* "." */ + char *thousands_sep; /* "" */ + char *grouping; /* "" */ + char *int_curr_symbol; /* "" */ + char *currency_symbol; /* "" */ + char *mon_decimal_point; /* "" */ + char *mon_thousands_sep; /* "" */ + char *mon_grouping; /* "" */ + char *positive_sign; /* "" */ + char *negative_sign; /* "" */ + char int_frac_digits; /* CHAR_MAX */ + char frac_digits; /* CHAR_MAX */ + char p_cs_precedes; /* CHAR_MAX */ + char p_sep_by_space; /* CHAR_MAX */ + char n_cs_precedes; /* CHAR_MAX */ + char n_sep_by_space; /* CHAR_MAX */ + char p_sign_posn; /* CHAR_MAX */ + char n_sign_posn; /* CHAR_MAX */ +}; + +// these are GCC's constants, but it doesn't really matter which constants we use. +#define LC_ALL 6 +#define LC_COLLATE 3 +#define LC_CTYPE 0 +#define LC_MONETARY 4 +#define LC_NUMERIC 1 +#define LC_TIME 2 + +char *setlocale(int category, char *locale) { + if (!locale) return "C"; + if (*locale == 'C' && !locale[1]) { + // yep + return "C"; + } + + // we only support the C locale + return NULL; + +} + +struct lconv *localeconv(void) { + static struct lconv conv = { + ".", + "", "", "", + "", "", "", + "", "", "", + CHAR_MAX, CHAR_MAX, CHAR_MAX, + CHAR_MAX, CHAR_MAX, CHAR_MAX, + CHAR_MAX, CHAR_MAX + }; + return &conv; +} + +#endif // _LOCALE_H diff --git a/05/tcc-0.9.25/math.h b/05/tcc-0.9.25/math.h new file mode 100644 index 0000000..7b029ac --- /dev/null +++ b/05/tcc-0.9.25/math.h @@ -0,0 +1,409 @@ +#ifndef _MATH_H +#define _MATH_H + +#include +#define HUGE_VAL _INFINITY // glibc defines HUGE_VAL as infinity (the C standard only requires it to be positive, funnily enough) +#define _NAN (-(_INFINITY-_INFINITY)) +#define _PI 3.141592653589793 +#define _2PI 6.283185307179586 +#define _HALF_PI 1.5707963267948966 +#define _THREE_HALVES_PI 4.71238898038469 + +// NOTE: these functions are not IEEE 754-compliant (the C standard doesn't require them to be), but they're pretty good + +double frexp(double value, int *exp) { + if (value == 0) { + *exp = 0; + return 0; + } + unsigned long u = *(unsigned long *)&value, significand; + *exp = ((u >> 52) & 0x7ff) - 1022; + // replace exponent with 1022 + u &= 0x800fffffffffffff; + u |= 0x3fe0000000000000; + return *(double *)&u; +} + +double ldexp(double x, int exp) { + int e; + double y = frexp(x, &e); + // since x = y * 2^e, x * 2^exp = y * 2^(e+exp) + exp += e; + if (exp < -1022) return 0; + if (exp > 1023) return _INFINITY; + unsigned long pow2 = (unsigned long)(exp + 1023) << 52; + return y * *(double *)&pow2; +} + +double floor(double x) { + if (x >= 0.0) { + if (x > 1073741824.0 * 1073741824.0) + return x; // floats this big must be integers + return (unsigned long)x; + } else { + if (x < -1073741824.0 * 1073741824.0) + return x; // floats this big must be integers + double i = (long)x; + if (x == i) return x; + return i - 1.0; + } +} + +double ceil(double x) { + double f = floor(x); + if (x == f) return f; + return f + 1.; +} + +double fabs(double x) { + // this is better than x >= 0 ? x : -x because it deals with -0 properly + unsigned long u = *(unsigned long *)&x; + u &= 0x7fffffffffffffff; + return *(double *)&u; +} + +double fmod(double x, double y) { + if (y == 0.0) { + errno = EDOM; + return 0.0; + } + return x - (floor(x / y) * y); +} + +double _sin_taylor(double x) { + double i; + double term = x; + // taylor expansion for sin: x - x³/3! + x⁵/5! - ... + + // https://en.wikipedia.org/wiki/Kahan_summation_algorithm + double prev = -1.0; + double sum = 0.0; + double c = 0.0; + for (i = 0.0; i < 100.0 && sum != prev; ++i) { + prev = sum; + double y = term - c; + double t = sum + y; + c = (t - sum) - y; + sum = t; + term *= -(x * x) / ((2.0*i+2.0)*(2.0*i+3.0)); + } + return sum; +} + +double _cos_taylor(double x) { + double i; + double term = 1.0; + // taylor expansion for cos: 1 - x²/2! + x⁴/4! - ... + + // https://en.wikipedia.org/wiki/Kahan_summation_algorithm + double prev = -1.0; + double sum = 0.0; + double c = 0.0; + for (i = 0.0; i < 100.0 && sum != prev; ++i) { + prev = sum; + double y = term - c; + double t = sum + y; + c = (t - sum) - y; + sum = t; + term *= -(x * x) / ((2.0*i+1.0)*(2.0*i+2.0)); + } + return sum; +} + +double sin(double x) { + x = fmod(x, 2.0*_PI); + // the Taylor series works best for small inputs. so, provide _sin_taylor with a value in the range [0,π/2] + if (x < _HALF_PI) + return _sin_taylor(x); + if (x < _PI) + return _sin_taylor(_PI - x); + if (x < _THREE_HALVES_PI) + return -_sin_taylor(x - _PI); + return -_sin_taylor(_2PI - x); +} + +double cos(double x) { + x = fmod(x, 2.0*_PI); + // the Taylor series works best for small inputs. so, provide _cos_taylor with a value in the range [0,π/2] + if (x < _HALF_PI) + return _cos_taylor(x); + if (x < _PI) + return -_cos_taylor(_PI - x); + if (x < _THREE_HALVES_PI) + return -_cos_taylor(x - _PI); + return _cos_taylor(_2PI - x); +} + +double tan(double x) { + return sin(x)/cos(x); +} + +// for sqrt and the inverse trigonometric functions, we use Newton's method +// https://en.wikipedia.org/wiki/Newton%27s_method + +double sqrt(double x) { + if (x < 0.0) { + errno = EDOM; + return _NAN; + } + if (x == 0.0) return 0.0; + if (x == _INFINITY) return _INFINITY; + // we want to find the root of: f(t) = t² - x + // f'(t) = 2t + int exp; + double y = frexp(x, &exp); + if (exp & 1) { + y *= 2; + --exp; + } + // newton's method will be slow for very small or very large numbers. + // so we have ensured that + // 0.5 ≤ y < 2 + // and also x = y * 2^exp; sqrt(x) = sqrt(y) * 2^(exp/2) NB: we've ensured that exp is even + + // 7 iterations seems to be more than enough for any number + double t = y; + t = (y / t + t) * 0.5; + t = (y / t + t) * 0.5; + t = (y / t + t) * 0.5; + t = (y / t + t) * 0.5; + t = (y / t + t) * 0.5; + t = (y / t + t) * 0.5; + t = (y / t + t) * 0.5; + + return ldexp(t, exp>>1); + +} + +double _acos_newton(double x) { + // we want to find the root of: f(t) = cos(t) - x + // f'(t) = -sin(t) + double t = _HALF_PI - x; // reasonably good first approximation + double prev_t = -100.0; + int i; + + for (i = 0; i < 100 && prev_t != t; ++i) { + prev_t = t; + t += (cos(t) - x) / sin(t); + } + return t; +} + +double _asin_newton(double x) { + // we want to find the root of: f(t) = sin(t) - x + // f'(t) = cos(t) + double t = x; // reasonably good first approximation + double prev_t = -100.0; + int i; + + for (i = 0; i < 100 && prev_t != t; ++i) { + prev_t = t; + t += (x - sin(t)) / cos(t); + } + return t; +} + +double acos(double x) { + if (x > 1.0 || x < -1.0) { + errno = EDOM; + return _NAN; + } + // Newton's method doesn't work well near -1 and 1, because f(x) / f'(x) is very large. + if (x > 0.8) + return _asin_newton(sqrt(1-x*x)); + if (x < -0.8) + return _PI-_asin_newton(sqrt(1-x*x)); + + return _acos_newton(x); +} + +double asin(double x) { + if (x > 1.0 || x < -1.0) { + errno = EDOM; + return _NAN; + } + // Newton's method doesn't work well near -1 and 1, because f(x) / f'(x) is very large. + if (x > 0.8) + return _acos_newton(sqrt(1.0-x*x)); + if (x < -0.8) + return -_acos_newton(sqrt(1.0-x*x)); + + return _asin_newton(x); +} + +double atan(double x) { + // the formula below breaks for really large inputs; tan(10^20) as a double is indistinguishable from pi/2 anyways + if (x > 1e20) return _HALF_PI; + if (x < -1e20) return -_HALF_PI; + + // we can use a nice trigonometric identity here + return asin(x / sqrt(1+x*x)); +} + +double atan2(double y, double x) { + if (x == 0.0) { + if (y > 0.0) return _HALF_PI; + if (y < 0.0) return -_HALF_PI; + return 0.0; // this is what IEEE 754 does + } + + double a = atan(y/x); + if (x > 0.0) { + return a; + } else if (y > 0.0) { + return a + _PI; + } else { + return a - _PI; + } +} + +double _exp_taylor(double x) { + double i; + double term = 1.0; + // taylor expansion for exp: 1 + x/1! + x²/2! + ... + + // https://en.wikipedia.org/wiki/Kahan_summation_algorithm + double prev = -1.0; + double sum = 0.0; + double c = 0.0; + for (i = 1.0; i < 100.0 && sum != prev; ++i) { + prev = sum; + double y = term - c; + double t = sum + y; + c = (t - sum) - y; + sum = t; + term *= x / i; + } + return sum; +} + +double exp(double x) { + if (x > 709.782712893384) { + errno = ERANGE; + return _INFINITY; + } + if (x == 0.0) return 1; + if (x < -744.4400719213812) + return 0; + int i, e; + double y = frexp(x, &e); + if (e < 1.0) return _exp_taylor(x); + // the taylor series doesn't work well for large x (positive or negative), + // so we use the fact that exp(y*2^e) = exp(y)^(2^e) + double value = _exp_taylor(y); + for (i = 0; i < e; ++i) + value *= value; + return value; +} + +#define _LOG2 0.6931471805599453 + +double log(double x) { + if (x < 0.0) { + errno = EDOM; + return _NAN; + } + if (x == 0.0) return -_INFINITY; + if (x == 1.0) return 0.0; + int e; + double sum; + double a = frexp(x, &e); + // since x = a * 2^e, log(x) = log(a) + log(2^e) = log(a) + e log(2) + sum = e * _LOG2; + // now that a is in [1/2,1), the series log(a) = (a-1) - (a-1)²/2 + (a-1)³/3 - ... converges quickly + + a -= 1; + // https://en.wikipedia.org/wiki/Kahan_summation_algorithm + double prev = HUGE_VAL; + double c = 0; + double term = a; + double i; + for (i = 1.0; i < 100.0 && sum != prev; ++i) { + prev = sum; + double y = term / i - c; + double t = sum + y; + c = (t - sum) - y; + sum = t; + term *= -a; + } + return sum; +} + +#define _INVLOG10 0.43429448190325176 // = 1/log(10) +double log10(double x) { + return log(x) * _INVLOG10; +} + +double modf(double value, double *iptr) { + double m = fmod(value, 1.0); + if (value >= 0.0) { + *iptr = value - m; + return m; + } else if (m == 0.0) { + *iptr = value; + return 0.0; + } else { + *iptr = value - m + 1.0; + return m - 1.0; + } +} + +// double raised to the power of an integer +double _dpowi(double x, unsigned long y) { + double result = 1.0; + if (y & 1) { + --y; + result *= x; + } + if (y > 0) { + double p = _dpowi(x, y >> 1); + result *= p * p; + } + return result; +} + +double pow(double x, double y) { + if (x > 0.0) { + return exp(y * log(x)); + } else if (x < 0.0) { + if (fmod(y, 1.0) != 0) { + errno = EDOM; + return _NAN; + } + if (y > 1.6602069666338597e+19) + return x < -1. ? -_INFINITY : 0.; + if (y < -1.6602069666338597e+19) + return x < -1. ? 0. : -_INFINITY; + return _dpowi(x, (unsigned long)y); + } else { + if (y < 0) { + errno = EDOM; + return _NAN; + } + if (y > 0) { + // 0^x = 0 for x>0 + return 0.; + } + // 0^0 = 1 + return 1.; + } +} + +double cosh(double x) { + double e = exp(x); + return (e + 1./e) * 0.5; +} + +double sinh(double x) { + double e = exp(x); + return (e - 1./e) * 0.5; +} + +double tanh(double x) { + if (x > 20.0) return 1.; + if (x < -20.0) return -1.; + double e = exp(x); + double f = 1./e; + return (e - f) / (e + f); +} +#endif // _MATH_H diff --git a/05/tcc-0.9.25/setjmp.h b/05/tcc-0.9.25/setjmp.h new file mode 100644 index 0000000..b3ffce1 --- /dev/null +++ b/05/tcc-0.9.25/setjmp.h @@ -0,0 +1,21 @@ +#ifndef _SETJMP_H +#define _SETJMP_H + +#include + +typedef long jmp_buf[3]; + +// @NONSTANDARD: we don't actually support setjmp + +int setjmp(jmp_buf env) { + return 0; +} + +void __longjmp(jmp_buf env, int val, const char *filename, int line) { + fprintf(stderr, "Error: Tried to longjmp from %s:%d with value %d\n", filename, line, val); + _Exit(-1); +} + +#define longjmp(env, val) __longjmp(env, val, __FILE__, __LINE__) + +#endif diff --git a/05/tcc-0.9.25/signal.h b/05/tcc-0.9.25/signal.h new file mode 100644 index 0000000..ded960c --- /dev/null +++ b/05/tcc-0.9.25/signal.h @@ -0,0 +1,253 @@ +#ifndef _SIGNAL_H +#define _SIGNAL_H + + +#include + +typedef long sig_atomic_t; // there are no "asynchronous interrupts" + +#define SIG_DFL ((void *)0) +#define SIG_IGN _sig_ign +#define SIG_ERR ((void *)-1) + +typedef void (*_Sighandler)(int); + +struct sigaction { + void (*sa_handler)(int); + #define sa_sigaction sa_handler + unsigned long sa_flags; + void (*sa_restorer)(void); + unsigned long sa_mask; +}; + +unsigned char _signal_restorer[] = { + 0x48,0xb8,15,0,0,0,0,0,0,0, // mov rax, 15 (sigreturn) + 0x0f,0x05 // syscall +}; + +#define _SIGNAL_HANDLERS 0xfff000 +#define _LE64(x) (x)&0xff, ((x)>> 8)&0xff, ((x)>>16)&0xff, ((x)>>24)&0xff, \ + ((x)>>32)&0xff, ((x)>>40)&0xff, ((x)>>48)&0xff, (x)>>56 + +// we need to do this weird indirection because linux has a different +// calling convention from us. + +unsigned char _signal_handler[] = { + // signal # passed in rdi + 0x48,0x89,0xf8, // mov rax, rdi (signal #) + 0x50, // push rax + 0x50, // push rax (allocate space for return value) + 0x48,0xb8,_LE64(_SIGNAL_HANDLERS), // mov rax, _SIGNAL_HANDLERS + 0x48,0x89,0xc3, // mov rbx, rax + 0x48,0x89,0xf8, // mov rax, rdi (signal #) + 0x48,0xc1,0xe0,0x03, // shl rax, 3 + 0x48,0x01,0xd8, // add rax, rbx + 0x48,0x89,0xc3, // mov rbx, rax + 0x48,0x8b,0x03, // mov rax, [rbx] + 0xff,0xd0, // call rax + 0x48,0x81,0xc4,16,0,0,0, // add rsp, 16 + 0xc3 // ret +}; + +#define _SA_RESTORER 0x04000000 +#define SA_SIGINFO 4 +#define SA_RESETHAND 0x80000000 + +int __sigaction(int signum, const struct sigaction *act, struct sigaction *oldact) { + return __syscall(13, signum, act, oldact, 8, 0, 0); +} + +void sigemptyset(unsigned long *set) { + *set = 0; +} + +void _sig_ign(int signal) { + return; +} + +static unsigned long _sig_mask = 0; + +_Sighandler signal(int sig, _Sighandler func) { + void **handlers = _SIGNAL_HANDLERS; + _Sighandler ret = handlers[sig]; + if (func == SIG_IGN) { + func = _sig_ign; + } + handlers[sig] = func; + + if (func == SIG_DFL) { + _sig_mask &= ~(1ul << (sig-1)); + } else { + _sig_mask |= 1ul << (sig-1); + } + struct sigaction act = {0}; + act.sa_handler = func == SIG_DFL ? SIG_DFL : (void*)_signal_handler; + act.sa_mask = _sig_mask; + act.sa_flags = _SA_RESTORER; + act.sa_restorer = _signal_restorer; + __sigaction(sig, &act, NULL); + return ret; +} + +int raise(int signal) { + return kill(getpid(), signal); +} + +#define FPE_INTDIV 1 +#define FPE_FLTDIV 3 + +#define __SI_MAX_SIZE 128 +#if __WORDSIZE == 64 +# define __SI_PAD_SIZE ((__SI_MAX_SIZE / sizeof (int)) - 4) +#else +# define __SI_PAD_SIZE ((__SI_MAX_SIZE / sizeof (int)) - 3) +#endif + +#ifndef __SI_ALIGNMENT +# define __SI_ALIGNMENT /* nothing */ +#endif +#ifndef __SI_BAND_TYPE +# define __SI_BAND_TYPE long int +#endif +#ifndef __SI_CLOCK_T +# define __SI_CLOCK_T __clock_t +#endif +#ifndef __SI_ERRNO_THEN_CODE +# define __SI_ERRNO_THEN_CODE 1 +#endif +#ifndef __SI_HAVE_SIGSYS +# define __SI_HAVE_SIGSYS 1 +#endif +#ifndef __SI_SIGFAULT_ADDL +# define __SI_SIGFAULT_ADDL /* nothing */ +#endif + +typedef int __pid_t; +typedef unsigned __uid_t; + +union __sigval +{ + int __sival_int; + void *__sival_ptr; +}; + +typedef union __sigval __sigval_t; +typedef long __clock_t; + +typedef struct + { + int si_signo; /* Signal number. */ +#if __SI_ERRNO_THEN_CODE + int si_errno; /* If non-zero, an errno value associated with + this signal, as defined in . */ + int si_code; /* Signal code. */ +#else + int si_code; + int si_errno; +#endif +#if __WORDSIZE == 64 + int __pad0; /* Explicit padding. */ +#endif + + union + { + int _pad[__SI_PAD_SIZE]; + + /* kill(). */ + struct + { + __pid_t si_pid; /* Sending process ID. */ + __uid_t si_uid; /* Real user ID of sending process. */ + } _kill; + + /* POSIX.1b timers. */ + struct + { + int si_tid; /* Timer ID. */ + int si_overrun; /* Overrun count. */ + __sigval_t si_sigval; /* Signal value. */ + } _timer; + + /* POSIX.1b signals. */ + struct + { + __pid_t si_pid; /* Sending process ID. */ + __uid_t si_uid; /* Real user ID of sending process. */ + __sigval_t si_sigval; /* Signal value. */ + } _rt; + + /* SIGCHLD. */ + struct + { + __pid_t si_pid; /* Which child. */ + __uid_t si_uid; /* Real user ID of sending process. */ + int si_status; /* Exit value or signal. */ + __SI_CLOCK_T si_utime; + __SI_CLOCK_T si_stime; + } _sigchld; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS. */ + struct + { + void *si_addr; /* Faulting insn/memory ref. */ + __SI_SIGFAULT_ADDL + short int si_addr_lsb; /* Valid LSB of the reported address. */ + union + { + /* used when si_code=SEGV_BNDERR */ + struct + { + void *_lower; + void *_upper; + } _addr_bnd; + /* used when si_code=SEGV_PKUERR */ + uint32_t _pkey; + } _bounds; + } _sigfault; + + /* SIGPOLL. */ + struct + { + __SI_BAND_TYPE si_band; /* Band event for SIGPOLL. */ + int si_fd; + } _sigpoll; + + /* SIGSYS. */ +#if __SI_HAVE_SIGSYS + struct + { + void *_call_addr; /* Calling user insn. */ + int _syscall; /* Triggering system call number. */ + unsigned int _arch; /* AUDIT_ARCH_* of syscall. */ + } _sigsys; +#endif + } _sifields; + } siginfo_t __SI_ALIGNMENT; + + +/* X/Open requires some more fields with fixed names. */ +#define si_pid _sifields._kill.si_pid +#define si_uid _sifields._kill.si_uid +#define si_timerid _sifields._timer.si_tid +#define si_overrun _sifields._timer.si_overrun +#define si_status _sifields._sigchld.si_status +#define si_utime _sifields._sigchld.si_utime +#define si_stime _sifields._sigchld.si_stime +#define si_value _sifields._rt.si_sigval +#define si_int _sifields._rt.si_sigval.sival_int +#define si_ptr _sifields._rt.si_sigval.sival_ptr +#define si_addr _sifields._sigfault.si_addr +#define si_addr_lsb _sifields._sigfault.si_addr_lsb +#define si_lower _sifields._sigfault._bounds._addr_bnd._lower +#define si_upper _sifields._sigfault._bounds._addr_bnd._upper +#define si_pkey _sifields._sigfault._bounds._pkey +#define si_band _sifields._sigpoll.si_band +#define si_fd _sifields._sigpoll.si_fd +#if __SI_HAVE_SIGSYS +# define si_call_addr _sifields._sigsys._call_addr +# define si_syscall _sifields._sigsys._syscall +# define si_arch _sifields._sigsys._arch +#endif + + +#endif // _SIGNAL_H diff --git a/05/tcc-0.9.25/stab.def b/05/tcc-0.9.25/stab.def new file mode 100644 index 0000000..3d10ca6 --- /dev/null +++ b/05/tcc-0.9.25/stab.def @@ -0,0 +1,238 @@ +/* Table of DBX symbol codes for the GNU system. + Copyright (C) 1988, 1997 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with the GNU C Library; see the file COPYING.LIB. If not, + write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* This contains contribution from Cygnus Support. */ +#if 0 + // random page break character for some reason +#endif +/* Global variable. Only the name is significant. + To find the address, look in the corresponding external symbol. */ +__define_stab (N_GSYM, 0x20, "GSYM") + +/* Function name for BSD Fortran. Only the name is significant. + To find the address, look in the corresponding external symbol. */ +__define_stab (N_FNAME, 0x22, "FNAME") + +/* Function name or text-segment variable for C. Value is its address. + Desc is supposedly starting line number, but GCC doesn't set it + and DBX seems not to miss it. */ +__define_stab (N_FUN, 0x24, "FUN") + +/* Data-segment variable with internal linkage. Value is its address. + "Static Sym". */ +__define_stab (N_STSYM, 0x26, "STSYM") + +/* BSS-segment variable with internal linkage. Value is its address. */ +__define_stab (N_LCSYM, 0x28, "LCSYM") + +/* Name of main routine. Only the name is significant. + This is not used in C. */ +__define_stab (N_MAIN, 0x2a, "MAIN") + +/* Global symbol in Pascal. + Supposedly the value is its line number; I'm skeptical. */ +__define_stab (N_PC, 0x30, "PC") + +/* Number of symbols: 0, files,,funcs,lines according to Ultrix V4.0. */ +__define_stab (N_NSYMS, 0x32, "NSYMS") + +/* "No DST map for sym: name, ,0,type,ignored" according to Ultrix V4.0. */ +__define_stab (N_NOMAP, 0x34, "NOMAP") + +/* New stab from Solaris. I don't know what it means, but it + don't seem to contain useful information. */ +__define_stab (N_OBJ, 0x38, "OBJ") + +/* New stab from Solaris. I don't know what it means, but it + don't seem to contain useful information. Possibly related to the + optimization flags used in this module. */ +__define_stab (N_OPT, 0x3c, "OPT") + +/* Register variable. Value is number of register. */ +__define_stab (N_RSYM, 0x40, "RSYM") + +/* Modula-2 compilation unit. Can someone say what info it contains? */ +__define_stab (N_M2C, 0x42, "M2C") + +/* Line number in text segment. Desc is the line number; + value is corresponding address. */ +__define_stab (N_SLINE, 0x44, "SLINE") + +/* Similar, for data segment. */ +__define_stab (N_DSLINE, 0x46, "DSLINE") + +/* Similar, for bss segment. */ +__define_stab (N_BSLINE, 0x48, "BSLINE") + +/* Sun's source-code browser stabs. ?? Don't know what the fields are. + Supposedly the field is "path to associated .cb file". THIS VALUE + OVERLAPS WITH N_BSLINE! */ +__define_stab (N_BROWS, 0x48, "BROWS") + +/* GNU Modula-2 definition module dependency. Value is the modification time + of the definition file. Other is non-zero if it is imported with the + GNU M2 keyword %INITIALIZE. Perhaps N_M2C can be used if there + are enough empty fields? */ +__define_stab(N_DEFD, 0x4a, "DEFD") + +/* THE FOLLOWING TWO STAB VALUES CONFLICT. Happily, one is for Modula-2 + and one is for C++. Still,... */ +/* GNU C++ exception variable. Name is variable name. */ +__define_stab (N_EHDECL, 0x50, "EHDECL") +/* Modula2 info "for imc": name,,0,0,0 according to Ultrix V4.0. */ +__define_stab (N_MOD2, 0x50, "MOD2") + +/* GNU C++ `catch' clause. Value is its address. Desc is nonzero if + this entry is immediately followed by a CAUGHT stab saying what exception + was caught. Multiple CAUGHT stabs means that multiple exceptions + can be caught here. If Desc is 0, it means all exceptions are caught + here. */ +__define_stab (N_CATCH, 0x54, "CATCH") + +/* Structure or union element. Value is offset in the structure. */ +__define_stab (N_SSYM, 0x60, "SSYM") + +/* Name of main source file. + Value is starting text address of the compilation. */ +__define_stab (N_SO, 0x64, "SO") + +/* Automatic variable in the stack. Value is offset from frame pointer. + Also used for type descriptions. */ +__define_stab (N_LSYM, 0x80, "LSYM") + +/* Beginning of an include file. Only Sun uses this. + In an object file, only the name is significant. + The Sun linker puts data into some of the other fields. */ +__define_stab (N_BINCL, 0x82, "BINCL") + +/* Name of sub-source file (#include file). + Value is starting text address of the compilation. */ +__define_stab (N_SOL, 0x84, "SOL") + +/* Parameter variable. Value is offset from argument pointer. + (On most machines the argument pointer is the same as the frame pointer. */ +__define_stab (N_PSYM, 0xa0, "PSYM") + +/* End of an include file. No name. + This and N_BINCL act as brackets around the file's output. + In an object file, there is no significant data in this entry. + The Sun linker puts data into some of the fields. */ +__define_stab (N_EINCL, 0xa2, "EINCL") + +/* Alternate entry point. Value is its address. */ +__define_stab (N_ENTRY, 0xa4, "ENTRY") + +/* Beginning of lexical block. + The desc is the nesting level in lexical blocks. + The value is the address of the start of the text for the block. + The variables declared inside the block *precede* the N_LBRAC symbol. */ +__define_stab (N_LBRAC, 0xc0, "LBRAC") + +/* Place holder for deleted include file. Replaces a N_BINCL and everything + up to the corresponding N_EINCL. The Sun linker generates these when + it finds multiple identical copies of the symbols from an include file. + This appears only in output from the Sun linker. */ +__define_stab (N_EXCL, 0xc2, "EXCL") + +/* Modula-2 scope information. Can someone say what info it contains? */ +__define_stab (N_SCOPE, 0xc4, "SCOPE") + +/* End of a lexical block. Desc matches the N_LBRAC's desc. + The value is the address of the end of the text for the block. */ +__define_stab (N_RBRAC, 0xe0, "RBRAC") + +/* Begin named common block. Only the name is significant. */ +__define_stab (N_BCOMM, 0xe2, "BCOMM") + +/* End named common block. Only the name is significant + (and it should match the N_BCOMM). */ +__define_stab (N_ECOMM, 0xe4, "ECOMM") + +/* End common (local name): value is address. + I'm not sure how this is used. */ +__define_stab (N_ECOML, 0xe8, "ECOML") + +/* These STAB's are used on Gould systems for Non-Base register symbols + or something like that. FIXME. I have assigned the values at random + since I don't have a Gould here. Fixups from Gould folk welcome... */ +__define_stab (N_NBTEXT, 0xF0, "NBTEXT") +__define_stab (N_NBDATA, 0xF2, "NBDATA") +__define_stab (N_NBBSS, 0xF4, "NBBSS") +__define_stab (N_NBSTS, 0xF6, "NBSTS") +__define_stab (N_NBLCS, 0xF8, "NBLCS") + +/* Second symbol entry containing a length-value for the preceding entry. + The value is the length. */ +__define_stab (N_LENG, 0xfe, "LENG") +#if 0 + // random page break character for some reason +#endif +/* The above information, in matrix format. + + STAB MATRIX + _________________________________________________ + | 00 - 1F are not dbx stab symbols | + | In most cases, the low bit is the EXTernal bit| + + | 00 UNDEF | 02 ABS | 04 TEXT | 06 DATA | + | 01 |EXT | 03 |EXT | 05 |EXT | 07 |EXT | + + | 08 BSS | 0A INDR | 0C FN_SEQ | 0E | + | 09 |EXT | 0B | 0D | 0F | + + | 10 | 12 COMM | 14 SETA | 16 SETT | + | 11 | 13 | 15 | 17 | + + | 18 SETD | 1A SETB | 1C SETV | 1E WARNING| + | 19 | 1B | 1D | 1F FN | + + |_______________________________________________| + | Debug entries with bit 01 set are unused. | + | 20 GSYM | 22 FNAME | 24 FUN | 26 STSYM | + | 28 LCSYM | 2A MAIN | 2C | 2E | + | 30 PC | 32 NSYMS | 34 NOMAP | 36 | + | 38 OBJ | 3A | 3C OPT | 3E | + | 40 RSYM | 42 M2C | 44 SLINE | 46 DSLINE | + | 48 BSLINE*| 4A DEFD | 4C | 4E | + | 50 EHDECL*| 52 | 54 CATCH | 56 | + | 58 | 5A | 5C | 5E | + | 60 SSYM | 62 | 64 SO | 66 | + | 68 | 6A | 6C | 6E | + | 70 | 72 | 74 | 76 | + | 78 | 7A | 7C | 7E | + | 80 LSYM | 82 BINCL | 84 SOL | 86 | + | 88 | 8A | 8C | 8E | + | 90 | 92 | 94 | 96 | + | 98 | 9A | 9C | 9E | + | A0 PSYM | A2 EINCL | A4 ENTRY | A6 | + | A8 | AA | AC | AE | + | B0 | B2 | B4 | B6 | + | B8 | BA | BC | BE | + | C0 LBRAC | C2 EXCL | C4 SCOPE | C6 | + | C8 | CA | CC | CE | + | D0 | D2 | D4 | D6 | + | D8 | DA | DC | DE | + | E0 RBRAC | E2 BCOMM | E4 ECOMM | E6 | + | E8 ECOML | EA | EC | EE | + | F0 | F2 | F4 | F6 | + | F8 | FA | FC | FE LENG | + +-----------------------------------------------+ + * 50 EHDECL is also MOD2. + * 48 BSLINE is also BROWS. + */ diff --git a/05/tcc-0.9.25/stab.h b/05/tcc-0.9.25/stab.h new file mode 100644 index 0000000..80bd594 --- /dev/null +++ b/05/tcc-0.9.25/stab.h @@ -0,0 +1,17 @@ +#ifndef __GNU_STAB__ + +/* Indicate the GNU stab.h is in use. */ + +#define __GNU_STAB__ + +#define __define_stab(NAME, CODE, STRING) NAME=CODE, + +enum __stab_debug_code +{ +#include "stab.def" +LAST_UNUSED_STAB_CODE +}; + +#undef __define_stab + +#endif /* __GNU_STAB_ */ diff --git a/05/tcc-0.9.25/stdarg.h b/05/tcc-0.9.25/stdarg.h new file mode 100644 index 0000000..4df996f --- /dev/null +++ b/05/tcc-0.9.25/stdarg.h @@ -0,0 +1,10 @@ +#ifndef _STDARG_H +#define _STDARG_H + +typedef unsigned long va_list; + +#define va_start(list, arg) ((list) = (unsigned long)&arg) +#define va_arg(list, type) (*((type *)(list += ((sizeof(type) + 7) & 0xfffffffffffffff8)))) +#define va_end(list) + +#endif // _STDARG_H diff --git a/05/tcc-0.9.25/stdc_common.h b/05/tcc-0.9.25/stdc_common.h new file mode 100644 index 0000000..1540f19 --- /dev/null +++ b/05/tcc-0.9.25/stdc_common.h @@ -0,0 +1,683 @@ +#ifndef _STDC_COMMON_H +#define _STDC_COMMON_H + +#define signed +#define volatile +#define register +#define const +#define NULL ((void*)0) + +typedef unsigned char uint8_t; +typedef char int8_t; +typedef unsigned short uint16_t; +typedef short int16_t; +typedef unsigned int uint32_t; +typedef int int32_t; +typedef unsigned long uint64_t; +typedef long int64_t; +typedef unsigned long size_t; +typedef long ptrdiff_t; +typedef unsigned long uintptr_t; +typedef long intptr_t; + +#define INT8_MAX 0x7f +#define INT8_MIN (-0x80) +#define INT16_MAX 0x7fff +#define INT16_MIN (-0x8000) +#define INT32_MAX 0x7fffffff +#define INT32_MIN (-0x80000000) +#define INT64_MAX 0x7fffffffffffffff +#define INT64_MIN (-0x8000000000000000) +#define UINT8_MAX 0xff +#define UINT16_MAX 0xffff +#define UINT32_MAX 0xffffffff +#define UINT64_MAX 0xffffffffffffffff +#define CHAR_BIT 8 +#define MB_LEN_MAX 4 +#define CHAR_MIN INT8_MIN +#define CHAR_MAX INT8_MAX +#define SCHAR_MIN INT8_MIN +#define SCHAR_MAX INT8_MAX +#define INT_MIN INT32_MIN +#define INT_MAX INT32_MAX +#define LONG_MIN INT64_MIN +#define LONG_MAX INT64_MAX +#define SHRT_MIN INT16_MIN +#define SHRT_MAX INT16_MAX +#define UCHAR_MAX UINT8_MAX +#define USHRT_MAX UINT16_MAX +#define UINT_MAX UINT32_MAX +#define ULONG_MAX UINT64_MAX + +static unsigned char __syscall_data[] = { + // mov rax, [rsp+24] + 0x48, 0x8b, 0x84, 0x24, 24, 0, 0, 0, + // mov rdi, rax + 0x48, 0x89, 0xc7, + // mov rax, [rsp+32] + 0x48, 0x8b, 0x84, 0x24, 32, 0, 0, 0, + // mov rsi, rax + 0x48, 0x89, 0xc6, + // mov rax, [rsp+40] + 0x48, 0x8b, 0x84, 0x24, 40, 0, 0, 0, + // mov rdx, rax + 0x48, 0x89, 0xc2, + // mov rax, [rsp+48] + 0x48, 0x8b, 0x84, 0x24, 48, 0, 0, 0, + // mov r10, rax + 0x49, 0x89, 0xc2, + // mov rax, [rsp+56] + 0x48, 0x8b, 0x84, 0x24, 56, 0, 0, 0, + // mov r8, rax + 0x49, 0x89, 0xc0, + // mov rax, [rsp+64] + 0x48, 0x8b, 0x84, 0x24, 64, 0, 0, 0, + // mov r9, rax + 0x49, 0x89, 0xc1, + // mov rax, [rsp+16] + 0x48, 0x8b, 0x84, 0x24, 16, 0, 0, 0, + // syscall + 0x0f, 0x05, + // mov [rsp+8], rax + 0x48, 0x89, 0x84, 0x24, 8, 0, 0, 0, + // ret + 0xc3 +}; + +#define __syscall(no, arg1, arg2, arg3, arg4, arg5, arg6)\ + (((unsigned long (*)(unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long))__syscall_data)\ + (no, arg1, arg2, arg3, arg4, arg5, arg6)) + +// we need to define ucontext_t +# define __ctx(fld) fld +typedef long long int greg_t; +#define __NGREG 23 +typedef greg_t gregset_t[__NGREG]; +#define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int))) +typedef struct +{ + unsigned long int __val[_SIGSET_NWORDS]; +} __sigset_t, sigset_t; +typedef struct +{ + void *ss_sp; + int ss_flags; + size_t ss_size; +} stack_t; +enum +{ + REG_R8 = 0, +# define REG_R8 REG_R8 + REG_R9, +# define REG_R9 REG_R9 + REG_R10, +# define REG_R10 REG_R10 + REG_R11, +# define REG_R11 REG_R11 + REG_R12, +# define REG_R12 REG_R12 + REG_R13, +# define REG_R13 REG_R13 + REG_R14, +# define REG_R14 REG_R14 + REG_R15, +# define REG_R15 REG_R15 + REG_RDI, +# define REG_RDI REG_RDI + REG_RSI, +# define REG_RSI REG_RSI + REG_RBP, +# define REG_RBP REG_RBP + REG_RBX, +# define REG_RBX REG_RBX + REG_RDX, +# define REG_RDX REG_RDX + REG_RAX, +# define REG_RAX REG_RAX + REG_RCX, +# define REG_RCX REG_RCX + REG_RSP, +# define REG_RSP REG_RSP + REG_RIP, +# define REG_RIP REG_RIP + REG_EFL, +# define REG_EFL REG_EFL + REG_CSGSFS, /* Actually short cs, gs, fs, __pad0. */ +# define REG_CSGSFS REG_CSGSFS + REG_ERR, +# define REG_ERR REG_ERR + REG_TRAPNO, +# define REG_TRAPNO REG_TRAPNO + REG_OLDMASK, +# define REG_OLDMASK REG_OLDMASK + REG_CR2 +# define REG_CR2 REG_CR2 +}; +struct _libc_fpxreg +{ + unsigned short int __ctx(significand)[4]; + unsigned short int __ctx(exponent); + unsigned short int __glibc_reserved1[3]; +}; +struct _libc_xmmreg +{ + uint32_t __ctx(element)[4]; +}; +struct _libc_fpstate +{ + uint16_t __ctx(cwd); + uint16_t __ctx(swd); + uint16_t __ctx(ftw); + uint16_t __ctx(fop); + uint64_t __ctx(rip); + uint64_t __ctx(rdp); + uint32_t __ctx(mxcsr); + uint32_t __ctx(mxcr_mask); + struct _libc_fpxreg _st[8]; + struct _libc_xmmreg _xmm[16]; + uint32_t __glibc_reserved1[24]; +}; +typedef struct _libc_fpstate *fpregset_t; +typedef struct { + gregset_t __ctx(gregs); + fpregset_t __ctx(fpregs); + unsigned long long __reserved1 [8]; +} mcontext_t; +typedef struct ucontext_t { + unsigned long int __ctx(uc_flags); + struct ucontext_t *uc_link; + stack_t uc_stack; + mcontext_t uc_mcontext; + sigset_t uc_sigmask; + struct _libc_fpstate __fpregs_mem; + unsigned long long int __ssp[4]; +} ucontext_t; + +long read(int fd, void *buf, size_t count) { + return __syscall(0, fd, buf, count, 0, 0, 0); +} + +long write(int fd, void *buf, size_t count) { + return __syscall(1, fd, buf, count, 0, 0, 0); +} + +void _Exit(int status) { + return __syscall(60, status, 0, 0, 0, 0, 0); +} + +int kill(int pid, int sig) { + return __syscall(62, pid, sig, 0, 0, 0, 0); +} + +int getpid(void) { + return __syscall(39, 0, 0, 0, 0, 0, 0); +} + +int fork(void) { + return __syscall(57, 0, 0, 0, 0, 0, 0); +} + +int execve(const char *pathname, char *const argv[], char *const envp[]) { + return __syscall(59, pathname, argv, envp, 0, 0, 0); +} + +int gettimeofday(struct timeval *tv, struct timezone *tz) { + return __syscall(96, tv, tz, 0, 0, 0, 0); +} + +typedef long time_t; + +struct timespec { + time_t tv_sec; + long tv_nsec; +}; + +struct timeval { + time_t tv_sec; + long tv_usec; +}; + +struct timezone { + int tz_minuteswest; + int tz_dsttime; +}; + +char *getcwd(char *buf, size_t size) { + long n = __syscall(79, buf, size, 0, 0, 0, 0); + if (n < 0) return NULL; + return buf; +} + +#define _WEXITSTATUS(status) (((status) & 0xff00) >> 8) +#define _WIFEXITED(status) (__WTERMSIG(status) == 0) +#define _WIFSIGNALED(status) \ + (((signed char) (((status) & 0x7f) + 1) >> 1) > 0) +int wait4(int pid, int *status, int options, struct rusage *rusage) { + return __syscall(61, pid, status, options, rusage, 0, 0); +} + +#define SIGABRT 6 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGILL 4 +#define SIGINT 2 +#define SIGSEGV 11 +#define SIGTERM 15 +#define SIGBUS 7 +void abort(void) { + kill(getpid(), SIGABRT); +} + + +#define CLOCK_REALTIME 0 +#define CLOCK_MONOTONIC 1 +int clock_gettime(int clock, struct timespec *tp) { + return __syscall(228, clock, tp, 0, 0, 0, 0); +} + +#define F_OK 0 +#define R_OK 4 +#define W_OK 2 +#define X_OK 1 +int access(const char *pathname, int mode) { + return __syscall(21, pathname, mode, 0, 0, 0, 0); +} + +typedef struct { + int fd; + unsigned char eof; + unsigned char err; + unsigned char has_ungetc; + char ungetc; // character which was pushed by ungetc() +} FILE; + +int errno; +int printf(char *, ...); +int fprintf(FILE *, char *, ...); // needed now for assert() + +FILE _stdin = {0}, *stdin; +FILE _stdout = {1}, *stdout; +FILE _stderr = {2}, *stderr; + +#ifdef NDEBUG +#define assert(x) ((void)0) +#else +int __assert_failed(const char *file, int line, const char *expr) { + fprintf(stderr, "Assertion failed at %s:%d: %s\n", file, line, expr); + abort(); +} +#define assert(x) (void)((x) || __assert_failed(__FILE__, __LINE__, #x)) +#endif + + +int _clamp_long_to_int(long x) { + if (x < INT_MIN) return INT_MIN; + if (x > INT_MAX) return INT_MAX; + return x; +} + +short _clamp_long_to_short(long x) { + if (x < SHRT_MIN) return SHRT_MIN; + if (x > SHRT_MAX) return SHRT_MAX; + return x; +} + +unsigned _clamp_ulong_to_uint(unsigned long x) { + if (x > UINT_MAX) return UINT_MAX; + return x; +} + +unsigned short _clamp_ulong_to_ushort(unsigned long x) { + if (x > USHRT_MAX) return USHRT_MAX; + return x; +} + +#define EIO 5 +#define EDOM 33 +#define ERANGE 34 + +#define PROT_READ 1 +#define PROT_WRITE 2 +#define PROT_EXEC 4 +#define MAP_SHARED 0x01 +#define MAP_ANONYMOUS 0x20 +#define MAP_PRIVATE 0x02 +void *mmap(void *addr, size_t length, int prot, int flags, int fd, long offset) { + return __syscall(9, addr, length, prot, flags, fd, offset); +} + +int munmap(void *addr, size_t length) { + return __syscall(11, addr, length, 0, 0, 0, 0); +} + +int mprotect(void *addr, size_t len, int prot) { + return __syscall(10, addr, len, prot, 0, 0, 0); +} + +#define MREMAP_MAYMOVE 1 +void *_mremap(void *addr, size_t old_size, size_t new_size, int flags) { + return __syscall(25, addr, old_size, new_size, flags, 0, 0); +} + +void *malloc(size_t n) { + if (!n) return NULL; + void *memory; + size_t bytes = n + 16; + memory = mmap(0, bytes, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); + if ((uint64_t)memory > 0xffffffffffff0000) return NULL; + *(uint64_t *)memory = bytes; + return (char *)memory + 16; +} + +void free(void *ptr) { + if (!ptr) return; + uint64_t *memory = (char *)ptr - 16; + uint64_t size = *memory; + munmap(memory, size); +} + + +size_t strlen(char *s) { + char *t = s; + while (*t) ++t; + return t - s; +} + +void *memcpy(void *s1, const void *s2, size_t n) { + char *p = s1, *end = p + n, *q = s2; + while (p < end) + *p++ = *q++; + return s1; +} + +int isspace(int c) { + return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v'; +} + +int isdigit(int c) { + return c >= '0' && c <= '9'; +} + +int _isdigit_in_base(int c, int base) { + if (c >= '0' && c <= '9') { + return c - '0' < base; + } else if (c >= 'a' && c <= 'z') { + return c - 'a' + 10 < base; + } else if (c >= 'A' && c <= 'Z') { + return c - 'A' + 10 < base; + } + return 0; +} + +void *memset(void *s, int c, size_t n) { + char *p = s, *end = p + n; + while (p < end) + *p++ = c; + return s; +} + +unsigned long strtoul(const char *nptr, char **endptr, int base) { + unsigned long value = 0, newvalue; + int overflow = 0; + + while (isspace(*nptr)) ++nptr; + if (*nptr == '+') ++nptr; + if (base == 0) { + if (*nptr == '0') { + ++nptr; + switch (*nptr) { + case 'x': + case 'X': + base = 16; + ++nptr; + break; + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': + base = 8; + break; + default: + // this must just be the number 0. + if (endptr) *endptr = nptr; + return 0; + } + } else { + base = 10; + } + } + + while (1) { + int c = *nptr; + unsigned v; + if (c >= '0' && c <= '9') + v = c - '0'; + else if (c >= 'a' && c <= 'z') + v = c - 'a' + 10; + else if (c >= 'A' && c <= 'Z') + v = c - 'A' + 10; + else break; + if (v >= base) break; + unsigned long newvalue = value * base + v; + if (newvalue < value) overflow = 1; + value = newvalue; + ++nptr; + } + if (endptr) *endptr = nptr; + if (overflow) { + errno = ERANGE; + return ULONG_MAX; + } else { + return value; + } +} + +long strtol(const char *nptr, char **endptr, int base) { + int sign = 1; + while (isspace(*nptr)) ++nptr; + if (*nptr == '-') { + sign = -1; + ++nptr; + } + unsigned long mag = strtoul(nptr, endptr, base); + if (sign > 0) { + if (mag > LONG_MAX) { + errno = ERANGE; + return LONG_MAX; + } + return (long)mag; + } else { + if (mag > (unsigned long)LONG_MAX + 1) { + errno = ERANGE; + return LONG_MIN; + } + return -(long)mag; + } +} + +long _strtol_clamped(const char *nptr, char **endptr, int base, int min, int max) { + long l = strtol(nptr, endptr, base); + if (l < min) return min; + if (l > max) return max; + return l; +} + +#define _NPOW10 310 +#define _INFINITY 1e1000 +// non-negative floating-point number with more precision than a double +// its value is equal to fraction * 2^exponent +typedef struct { + unsigned long fraction; + int exponent; +} _Float; + +// ensure that f->fraction >= 2^64 / 2 +static void _normalize_float(_Float *f) { + if (!f->fraction) return; + while (f->fraction < 0x8000000000000000) { + f->exponent -= 1; + f->fraction <<= 1; + } +} + +static double _Float_to_double(_Float f) { + unsigned long dbl_fraction; + int dbl_exponent; + unsigned long dbl_value; + if (f.fraction == 0) return 0; + _normalize_float(&f); + f.fraction &= 0x7fffffffffffffff; // remove the "1." in 1.01101110111... to get 63-bit significand + dbl_fraction = (f.fraction + 0x3ff) >> 11; + dbl_exponent = f.exponent + 63; + if (dbl_exponent < -1022) return 0; + if (dbl_exponent > 1023) return _INFINITY; + dbl_exponent += 1023; + dbl_value = (unsigned long)dbl_exponent << 52 | dbl_fraction; + return *(double *)&dbl_value; +} + +static _Float _powers_of_10_dat[2*_NPOW10+1]; +static _Float *_powers_of_10; +static _Float _Float_ZERO = {0, 1}; +static _Float _Float_INFINITY = {0x8000000000000000, 100000}; + + +_Float _int_pow10(int x) { + if (x <= -_NPOW10) return _Float_ZERO; + if (x >= _NPOW10) return _Float_INFINITY; + return _powers_of_10[x]; +} + +double strtod(const char *nptr, char **endptr) { + const char *flt, *dot, *p, *number_end; + double sign = 1; + int exponent = 0; + while (isspace(*nptr)) ++nptr; + + flt = nptr; // start of float + if (*flt == '+') ++flt; + else if (*flt == '-') sign = -1, ++flt; + + if (*flt != '.' && (*flt < '0' || *flt > '9')) { + // this isn't a float + *endptr = nptr; + return 0; + } + + // find the decimal point, if any + dot = flt; + while (*dot >= '0' && *dot <= '9') ++dot; + + nptr = dot + (*dot == '.'); + // skip digits after the dot + while (*nptr >= '0' && *nptr <= '9') ++nptr; + number_end = nptr; + + if (*nptr == 'e') { + ++nptr; + exponent = 1; + if (*nptr == '+') ++nptr; + else if (*nptr == '-') ++nptr, exponent = -1; + exponent *= _strtol_clamped(nptr, &nptr, 10, -10000, 10000); // use _strtol_clamped to prevent problems with -LONG_MIN + } + + // construct the value using the Kahan summation algorithm (https://en.wikipedia.org/wiki/Kahan_summation_algorithm) + double sum = 0; + double c = 0; + for (p = flt; p < number_end; ++p) { + if (*p == '.') continue; + int n = *p - '0'; + assert(n >= 0 && n <= 9); + int pow10 = dot - p; + pow10 -= pow10 > 0; + pow10 += exponent; + _Float f_val = _int_pow10(pow10); + f_val.fraction >>= 4; + f_val.exponent += 4; + f_val.fraction *= n; + double value = _Float_to_double(f_val); + if (value == _INFINITY || sum == _INFINITY) { + sum = _INFINITY; + break; + } + double y = value - c; + double t = sum + y; + c = (t - sum) - y; + sum = t; + } + + if (sum == _INFINITY) errno = ERANGE; + if (endptr) *endptr = nptr; + return sum * sign; +} + +float strtof(const char *nptr, char **endptr) { + return strtod(nptr, endptr); +} + +long double strtold(const char *nptr, char **endptr) { + return strtod(nptr, endptr); +} + +char *strerror(int errnum) { + switch (errnum) { + case ERANGE: return "Range error"; + case EDOM: return "Domain error"; + case EIO: return "I/O error"; + } + return "Other error"; +} + +typedef void (*_ExitHandler)(void); +_ExitHandler _exit_handlers[33]; +int _n_exit_handlers; + +void exit(int status) { + int i; + for (i = _n_exit_handlers - 1; i >= 0; --i) + _exit_handlers[i](); + _Exit(status); +} + +int main(); + +static char **_envp; +static uint64_t _rand_seed; + +int _main(int argc, char **argv) { + int i; + _Float p = {1, 0}; + + _envp = argv + argc + 1; // this is where the environment variables will be + + stdin = &_stdin; + stdout = &_stdout; + stderr = &_stderr; + + /* + "If rand is called before any calls to srand have been made, + the same sequence shall be generated as when srand is first + called with a seed value of 1." C89 § 4.10.2.2 + */ + _rand_seed = 1; + // initialize powers of 10 + _powers_of_10 = _powers_of_10_dat + _NPOW10; + for (i = 0; i < _NPOW10; ++i) { + _normalize_float(&p); + _powers_of_10[i] = p; + p.exponent += 4; + p.fraction >>= 4; + p.fraction *= 10; + } + + p.fraction = 1; + p.exponent = 0; + for (i = 0; i > -_NPOW10; --i) { + _normalize_float(&p); + _powers_of_10[i] = p; + p.fraction /= 5; + p.exponent -= 1; + } + + exit(main(argc, argv)); +} + + +#endif // _STDC_COMMON_H diff --git a/05/tcc-0.9.25/stddef.h b/05/tcc-0.9.25/stddef.h new file mode 100644 index 0000000..f012d95 --- /dev/null +++ b/05/tcc-0.9.25/stddef.h @@ -0,0 +1,8 @@ +#ifndef _STDDEF_H +#define _STDDEF_H + +#include +#define offsetof(struct, member) ((size_t)(&((struct *)NULL)->member)) +// @NONSTANDARD: we don't have wchar_t + +#endif // _STDDEF_H diff --git a/05/tcc-0.9.25/stdio.h b/05/tcc-0.9.25/stdio.h new file mode 100644 index 0000000..436dd62 --- /dev/null +++ b/05/tcc-0.9.25/stdio.h @@ -0,0 +1,2270 @@ +#ifndef _STDIO_H +#define _STDIO_H + +#include +#include + +int printf(const char *, ...); + +/* --- snprintf, adapted from github.com/nothings/stb stb_sprintf.h */ + +#ifndef STB_SPRINTF_MIN +#define STB_SPRINTF_MIN 512 // how many characters per callback +#endif +typedef char *_STBSP_SPRINTFCB(const char *buf, void *user, int len); + +// internal float utility functions +static int32_t _stbsp__real_to_str(char const **start, uint32_t *len, char *out, int32_t *decimal_pos, double value, uint32_t frac_digits); +static int32_t _stbsp__real_to_parts(int64_t *bits, int32_t *expo, double value); +#define STBSP__SPECIAL 0x7000 + +static char _stbsp__period = '.'; +static char _stbsp__comma = ','; +static struct +{ + short temp; // force next field to be 2-byte aligned + char pair[201]; +} _stbsp__digitpair = +{ + 0, + "00010203040506070809101112131415161718192021222324" + "25262728293031323334353637383940414243444546474849" + "50515253545556575859606162636465666768697071727374" + "75767778798081828384858687888990919293949596979899" +}; + +#define STBSP__LEFTJUST 1 +#define STBSP__LEADINGPLUS 2 +#define STBSP__LEADINGSPACE 4 +#define STBSP__LEADING_0X 8 +#define STBSP__LEADINGZERO 16 +#define STBSP__INTMAX 32 +#define STBSP__TRIPLET_COMMA 64 +#define STBSP__NEGATIVE 128 +#define STBSP__METRIC_SUFFIX 256 +#define STBSP__HALFWIDTH 512 +#define STBSP__METRIC_NOSPACE 1024 +#define STBSP__METRIC_1024 2048 +#define STBSP__METRIC_JEDEC 4096 + +static void _stbsp__lead_sign(uint32_t fl, char *sign) +{ + sign[0] = 0; + if (fl & STBSP__NEGATIVE) { + sign[0] = 1; + sign[1] = '-'; + } else if (fl & STBSP__LEADINGSPACE) { + sign[0] = 1; + sign[1] = ' '; + } else if (fl & STBSP__LEADINGPLUS) { + sign[0] = 1; + sign[1] = '+'; + } +} + +static uint32_t _stbsp__strlen_limited(char const *s, uint32_t limit) +{ + char const * sn = s; + + // get up to 4-byte alignment + for (;;) { + if (((uintptr_t)sn & 3) == 0) + break; + + if (!limit || *sn == 0) + return (uint32_t)(sn - s); + + ++sn; + --limit; + } + + // scan over 4 bytes at a time to find terminating 0 + // this will intentionally scan up to 3 bytes past the end of buffers, + // but becase it works 4B aligned, it will never cross page boundaries + // (hence the STBSP__ASAN markup; the over-read here is intentional + // and harmless) + while (limit >= 4) { + uint32_t v = *(uint32_t *)sn; + // bit hack to find if there's a 0 byte in there + if ((v - 0x01010101) & (~v) & 0x80808080UL) + break; + + sn += 4; + limit -= 4; + } + + // handle the last few characters to find actual size + while (limit && *sn) { + ++sn; + --limit; + } + + return (uint32_t)(sn - s); +} + +int __vsprintfcb(_STBSP_SPRINTFCB *callback, void *user, char *buf, char const *fmt, va_list va) +{ + static char hex[] = "0123456789abcdefxp"; + static char hexu[] = "0123456789ABCDEFXP"; + char *bf; + char const *f; + int tlen = 0; + + bf = buf; + f = fmt; + for (;;) { + int32_t fw, pr, tz; + uint32_t fl; + + // macros for the callback buffer stuff + #define stbsp__chk_cb_bufL(bytes) \ + { \ + int len = (int)(bf - buf); \ + if ((len + (bytes)) >= STB_SPRINTF_MIN) { \ + tlen += len; \ + if (0 == (bf = buf = callback(buf, user, len))) \ + goto done; \ + } \ + } + #define stbsp__chk_cb_buf(bytes) \ + { \ + if (callback) { \ + stbsp__chk_cb_bufL(bytes); \ + } \ + } + #define stbsp__flush_cb() \ + { \ + stbsp__chk_cb_bufL(STB_SPRINTF_MIN - 1); \ + } // flush if there is even one byte in the buffer + #define stbsp__cb_buf_clamp(cl, v) \ + cl = v; \ + if (callback) { \ + int lg = STB_SPRINTF_MIN - (int)(bf - buf); \ + if (cl > lg) \ + cl = lg; \ + } + + // fast copy everything up to the next % (or end of string) + for (;;) { + while (((uintptr_t)f) & 3) { + schk1: + if (f[0] == '%') + goto scandd; + schk2: + if (f[0] == 0) + goto endfmt; + stbsp__chk_cb_buf(1); + *bf++ = f[0]; + ++f; + } + for (;;) { + // Check if the next 4 bytes contain %(0x25) or end of string. + // Using the 'hasless' trick: + // https://graphics.stanford.edu/~seander/bithacks.html#HasLessInWord + uint32_t v, c; + v = *(uint32_t *)f; + c = (~v) & 0x80808080; + if (((v ^ 0x25252525) - 0x01010101) & c) + goto schk1; + if ((v - 0x01010101) & c) + goto schk2; + if (callback) + if ((STB_SPRINTF_MIN - (int)(bf - buf)) < 4) + goto schk1; + { + *(uint32_t *)bf = v; + } + bf += 4; + f += 4; + } + } + scandd: + + ++f; + + // ok, we have a percent, read the modifiers first + fw = 0; + pr = -1; + fl = 0; + tz = 0; + + // flags + for (;;) { + switch (f[0]) { + // if we have left justify + case '-': + fl |= STBSP__LEFTJUST; + ++f; + continue; + // if we have leading plus + case '+': + fl |= STBSP__LEADINGPLUS; + ++f; + continue; + // if we have leading space + case ' ': + fl |= STBSP__LEADINGSPACE; + ++f; + continue; + // if we have leading 0x + case '#': + fl |= STBSP__LEADING_0X; + ++f; + continue; + // if we have thousand commas + case '\'': + fl |= STBSP__TRIPLET_COMMA; + ++f; + continue; + // if we have kilo marker (none->kilo->kibi->jedec) + case '$': + if (fl & STBSP__METRIC_SUFFIX) { + if (fl & STBSP__METRIC_1024) { + fl |= STBSP__METRIC_JEDEC; + } else { + fl |= STBSP__METRIC_1024; + } + } else { + fl |= STBSP__METRIC_SUFFIX; + } + ++f; + continue; + // if we don't want space between metric suffix and number + case '_': + fl |= STBSP__METRIC_NOSPACE; + ++f; + continue; + // if we have leading zero + case '0': + fl |= STBSP__LEADINGZERO; + ++f; + goto flags_done; + default: goto flags_done; + } + } + flags_done: + + // get the field width + if (f[0] == '*') { + fw = va_arg(va, uint32_t); + ++f; + } else { + while ((f[0] >= '0') && (f[0] <= '9')) { + fw = fw * 10 + f[0] - '0'; + f++; + } + } + // get the precision + if (f[0] == '.') { + ++f; + if (f[0] == '*') { + pr = va_arg(va, uint32_t); + ++f; + } else { + pr = 0; + while ((f[0] >= '0') && (f[0] <= '9')) { + pr = pr * 10 + f[0] - '0'; + f++; + } + } + } + + // handle integer size overrides + switch (f[0]) { + // are we halfwidth? + case 'h': + fl |= STBSP__HALFWIDTH; + ++f; + if (f[0] == 'h') + ++f; // QUARTERWIDTH + break; + // are we 64-bit (unix style) + case 'l': + fl |= ((sizeof(long) == 8) ? STBSP__INTMAX : 0); + ++f; + if (f[0] == 'l') { + fl |= STBSP__INTMAX; + ++f; + } + break; + // are we 64-bit on intmax? (c99) + case 'j': + fl |= (sizeof(size_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit on size_t or ptrdiff_t? (c99) + case 'z': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + case 't': + fl |= (sizeof(ptrdiff_t) == 8) ? STBSP__INTMAX : 0; + ++f; + break; + // are we 64-bit (msft style) + case 'I': + if ((f[1] == '6') && (f[2] == '4')) { + fl |= STBSP__INTMAX; + f += 3; + } else if ((f[1] == '3') && (f[2] == '2')) { + f += 3; + } else { + fl |= ((sizeof(void *) == 8) ? STBSP__INTMAX : 0); + ++f; + } + break; + default: break; + } + + // handle each replacement + switch (f[0]) { + #define STBSP__NUMSZ 512 // big enough for e308 (with commas) or e-307 + char num[STBSP__NUMSZ]; + char lead[8]; + char tail[8]; + char *s; + char const *h; + uint32_t l, n, cs; + uint64_t n64; + double fv; + int32_t dp; + char const *sn; + + case 's': + // get the string + s = va_arg(va, char *); + if (s == 0) + s = (char *)"null"; + // get the length, limited to desired precision + // always limit to ~0u chars since our counts are 32b + l = _stbsp__strlen_limited(s, (pr >= 0) ? pr : ~0u); + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + // copy the string in + goto scopy; + + case 'c': // char + // get the character + s = num + STBSP__NUMSZ - 1; + *s = (char)va_arg(va, int); + l = 1; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + + case 'n': // weird write-bytes specifier + { + int *d = va_arg(va, int *); + *d = tlen + (int)(bf - buf); + } break; + + case 'A': // hex float + case 'a': // hex float + h = (f[0] == 'A') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (_stbsp__real_to_parts((int64_t *)&n64, &dp, fv)) + fl |= STBSP__NEGATIVE; + + s = num + 64; + + _stbsp__lead_sign(fl, lead); + + if (dp == -1023) + dp = (n64) ? -1022 : 0; + else + n64 |= (((uint64_t)1) << 52); + n64 <<= (64 - 56); + if (pr < 15) + n64 += ((((uint64_t)8) << 56) >> (pr * 4)); +// add leading chars + lead[1 + lead[0]] = '0'; + lead[2 + lead[0]] = 'x'; + lead[0] += 2; + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + if (pr) + *s++ = _stbsp__period; + sn = s; + + // print the bits + n = pr; + if (n > 13) + n = 13; + if (pr > (int32_t)n) + tz = pr - n; + pr = 0; + while (n--) { + *s++ = h[(n64 >> 60) & 15]; + n64 <<= 4; + } + + // print the expo + tail[1] = h[17]; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; + n = (dp >= 1000) ? 6 : ((dp >= 100) ? 5 : ((dp >= 10) ? 4 : 3)); + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + + dp = (int)(s - sn); + l = (int)(s - (num + 64)); + s = num + 64; + cs = 1 + (3 << 24); + goto scopy; + + case 'G': // float + case 'g': // float + h = (f[0] == 'G') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; + else if (pr == 0) + pr = 1; // default is 6 + // read the double into a string + if (_stbsp__real_to_str(&sn, &l, num, &dp, fv, (pr - 1) | 0x80000000)) + fl |= STBSP__NEGATIVE; + + // clamp the precision and delete extra zeros after clamp + n = pr; + if (l > (uint32_t)pr) + l = pr; + while ((l > 1) && (pr) && (sn[l - 1] == '0')) { + --pr; + --l; + } + + // should we use %e + if ((dp <= -4) || (dp > (int32_t)n)) { + if (pr > (int32_t)l) + pr = l - 1; + else if (pr) + --pr; // when using %e, there is one digit before the decimal + goto doexpfromg; + } + // this is the insane action to get the pr to match %g semantics for %f + if (dp > 0) { + pr = (dp < (int32_t)l) ? l - dp : 0; + } else { + pr = -dp + ((pr > (int32_t)l) ? (int32_t) l : pr); + } + goto dofloatfromg; + + case 'E': // float + case 'e': // float + h = (f[0] == 'E') ? hexu : hex; + fv = va_arg(va, double); + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (_stbsp__real_to_str(&sn, &l, num, &dp, fv, pr | 0x80000000)) + fl |= STBSP__NEGATIVE; + doexpfromg: + tail[0] = 0; + _stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + // handle leading chars + *s++ = sn[0]; + + if (pr) + *s++ = _stbsp__period; + + // handle after decimal + if ((l - 1) > (uint32_t)pr) + l = pr + 1; + for (n = 1; n < l; n++) + *s++ = sn[n]; + // trailing zeros + tz = pr - (l - 1); + pr = 0; + // dump expo + tail[1] = h[0xe]; + dp -= 1; + if (dp < 0) { + tail[2] = '-'; + dp = -dp; + } else + tail[2] = '+'; + n = (dp >= 100) ? 5 : 4; + tail[0] = (char)n; + for (;;) { + tail[n] = '0' + dp % 10; + if (n <= 3) + break; + --n; + dp /= 10; + } + cs = 1 + (3 << 24); // how many tens + goto flt_lead; + + case 'f': // float + fv = va_arg(va, double); + doafloat: + // do kilos + if (fl & STBSP__METRIC_SUFFIX) { + double divisor; + divisor = 1000.0f; + if (fl & STBSP__METRIC_1024) + divisor = 1024.0; + while (fl < 0x4000000) { + if ((fv < divisor) && (fv > -divisor)) + break; + fv /= divisor; + fl += 0x1000000; + } + } + if (pr == -1) + pr = 6; // default is 6 + // read the double into a string + if (_stbsp__real_to_str(&sn, &l, num, &dp, fv, pr)) + fl |= STBSP__NEGATIVE; + dofloatfromg: + tail[0] = 0; + _stbsp__lead_sign(fl, lead); + if (dp == STBSP__SPECIAL) { + s = (char *)sn; + cs = 0; + pr = 0; + goto scopy; + } + s = num + 64; + + // handle the three decimal varieties + if (dp <= 0) { + int32_t i; + // handle 0.000*000xxxx + *s++ = '0'; + if (pr) + *s++ = _stbsp__period; + n = -dp; + if ((int32_t)n > pr) + n = pr; + i = n; + while (i) { + if ((((uintptr_t)s) & 3) == 0) + break; + *s++ = '0'; + --i; + } + while (i >= 4) { + *(uint32_t *)s = 0x30303030; + s += 4; + i -= 4; + } + while (i) { + *s++ = '0'; + --i; + } + if ((int32_t)(l + n) > pr) + l = pr - n; + i = l; + while (i) { + *s++ = *sn++; + --i; + } + tz = pr - (n + l); + cs = 1 + (3 << 24); // how many tens did we write (for commas below) + } else { + cs = (fl & STBSP__TRIPLET_COMMA) ? ((600 - (uint32_t)dp) % 3) : 0; + if ((uint32_t)dp >= l) { + // handle xxxx000*000.0 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = _stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= l) + break; + } + } + if (n < (uint32_t)dp) { + n = dp - n; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (n) { + if ((((uintptr_t)s) & 3) == 0) + break; + *s++ = '0'; + --n; + } + while (n >= 4) { + *(uint32_t *)s = 0x30303030; + s += 4; + n -= 4; + } + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = _stbsp__comma; + } else { + *s++ = '0'; + --n; + } + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) { + *s++ = _stbsp__period; + tz = pr; + } + } else { + // handle xxxxx.xxxx000*000 + n = 0; + for (;;) { + if ((fl & STBSP__TRIPLET_COMMA) && (++cs == 4)) { + cs = 0; + *s++ = _stbsp__comma; + } else { + *s++ = sn[n]; + ++n; + if (n >= (uint32_t)dp) + break; + } + } + cs = (int)(s - (num + 64)) + (3 << 24); // cs is how many tens + if (pr) + *s++ = _stbsp__period; + if ((l - dp) > (uint32_t)pr) + l = pr + dp; + while (n < l) { + *s++ = sn[n]; + ++n; + } + tz = pr - (l - dp); + } + } + pr = 0; + + // handle k,m,g,t + if (fl & STBSP__METRIC_SUFFIX) { + char idx; + idx = 1; + if (fl & STBSP__METRIC_NOSPACE) + idx = 0; + tail[0] = idx; + tail[1] = ' '; + { + if (fl >> 24) { // SI kilo is 'k', JEDEC and SI kibits are 'K'. + if (fl & STBSP__METRIC_1024) + tail[idx + 1] = "_KMGT"[fl >> 24]; + else + tail[idx + 1] = "_kMGT"[fl >> 24]; + idx++; + // If printing kibits and not in jedec, add the 'i'. + if (fl & STBSP__METRIC_1024 && !(fl & STBSP__METRIC_JEDEC)) { + tail[idx + 1] = 'i'; + idx++; + } + tail[0] = idx; + } + } + }; + + flt_lead: + // get the length that we copied + l = (uint32_t)(s - (num + 64)); + s = num + 64; + goto scopy; + + case 'B': // upper binary + case 'b': // lower binary + h = (f[0] == 'B') ? hexu : hex; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[0xb]; + } + l = (8 << 4) | (1 << 8); + goto radixnum; + + case 'o': // octal + h = hexu; + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 1; + lead[1] = '0'; + } + l = (3 << 4) | (3 << 8); + goto radixnum; + + case 'p': // pointer + fl |= (sizeof(void *) == 8) ? STBSP__INTMAX : 0; + pr = sizeof(void *) * 2; + fl &= ~STBSP__LEADINGZERO; // 'p' only prints the pointer with zeros + // fall through - to X + + case 'X': // upper hex + case 'x': // lower hex + h = (f[0] == 'X') ? hexu : hex; + l = (4 << 4) | (4 << 8); + lead[0] = 0; + if (fl & STBSP__LEADING_0X) { + lead[0] = 2; + lead[1] = '0'; + lead[2] = h[16]; + } + radixnum: + // get the number + if (fl & STBSP__INTMAX) + n64 = va_arg(va, uint64_t); + else + n64 = va_arg(va, uint32_t); + + s = num + STBSP__NUMSZ; + dp = 0; + // clear tail, and clear leading if value is zero + tail[0] = 0; + if (n64 == 0) { + lead[0] = 0; + if (pr == 0) { + l = 0; + cs = 0; + goto scopy; + } + } + // convert to string + for (;;) { + *--s = h[n64 & ((1 << (l >> 8)) - 1)]; + n64 >>= (l >> 8); + if (!((n64) || ((int32_t)((num + STBSP__NUMSZ) - s) < pr))) + break; + if (fl & STBSP__TRIPLET_COMMA) { + ++l; + if ((l & 15) == ((l >> 4) & 15)) { + l &= ~15; + *--s = _stbsp__comma; + } + } + }; + // get the tens and the comma pos + cs = (uint32_t)((num + STBSP__NUMSZ) - s) + ((((l >> 4) & 15)) << 24); + // get the length that we copied + l = (uint32_t)((num + STBSP__NUMSZ) - s); + // copy it + goto scopy; + + case 'u': // unsigned + case 'i': + case 'd': // integer + // get the integer and abs it + if (fl & STBSP__INTMAX) { + int64_t i64 = va_arg(va, int64_t); + n64 = (uint64_t)i64; + if ((f[0] != 'u') && (i64 < 0)) { + n64 = (uint64_t)-i64; + fl |= STBSP__NEGATIVE; + } + } else { + int32_t i = va_arg(va, int32_t); + n64 = (uint32_t)i; + if ((f[0] != 'u') && (i < 0)) { + n64 = (uint32_t)-i; + fl |= STBSP__NEGATIVE; + } + } + + if (fl & STBSP__METRIC_SUFFIX) { + if (n64 < 1024) + pr = 0; + else if (pr == -1) + pr = 1; + fv = (double)(int64_t)n64; + goto doafloat; + } + + // convert to string + s = num + STBSP__NUMSZ; + l = 0; + + for (;;) { + // do in 32-bit chunks (avoid lots of 64-bit divides even with constant denominators) + char *o = s - 8; + if (n64 >= 100000000) { + n = (uint32_t)(n64 % 100000000); + n64 /= 100000000; + } else { + n = (uint32_t)n64; + n64 = 0; + } + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + do { + s -= 2; + *(uint16_t *)s = *(uint16_t *)&_stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + } while (n); + } + while (n) { + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = _stbsp__comma; + --o; + } else { + *--s = (char)(n % 10) + '0'; + n /= 10; + } + } + if (n64 == 0) { + if ((s[0] == '0') && (s != (num + STBSP__NUMSZ))) + ++s; + break; + } + while (s != o) + if ((fl & STBSP__TRIPLET_COMMA) && (l++ == 3)) { + l = 0; + *--s = _stbsp__comma; + --o; + } else { + *--s = '0'; + } + } + + tail[0] = 0; + _stbsp__lead_sign(fl, lead); + + // get the length that we copied + l = (uint32_t)((num + STBSP__NUMSZ) - s); + if (l == 0) { + *--s = '0'; + l = 1; + } + cs = l + (3 << 24); + if (pr < 0) + pr = 0; + + scopy: + // get fw=leading/trailing space, pr=leading zeros + if (pr < (int32_t)l) + pr = l; + n = pr + lead[0] + tail[0] + tz; + if (fw < (int32_t)n) + fw = n; + fw -= n; + pr -= l; + + // handle right justify and leading zeros + if ((fl & STBSP__LEFTJUST) == 0) { + if (fl & STBSP__LEADINGZERO) // if leading zeros, everything is in pr + { + pr = (fw > pr) ? fw : pr; + fw = 0; + } else { + fl &= ~STBSP__TRIPLET_COMMA; // if no leading zeros, then no commas + } + } + + // copy the spaces and/or zeros + if (fw + pr) { + int32_t i; + uint32_t c; + + // copy leading spaces (or when doing %8.4d stuff) + if ((fl & STBSP__LEFTJUST) == 0) + while (fw > 0) { + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((uintptr_t)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(uint32_t *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = ' '; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leader + sn = lead + 1; + while (lead[0]) { + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy leading zeros + c = cs >> 24; + cs &= 0xffffff; + cs = (fl & STBSP__TRIPLET_COMMA) ? ((uint32_t)(c - ((pr + cs) % (c + 1)))) : 0; + while (pr > 0) { + stbsp__cb_buf_clamp(i, pr); + pr -= i; + if ((fl & STBSP__TRIPLET_COMMA) == 0) { + while (i) { + if ((((uintptr_t)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(uint32_t *)bf = 0x30303030; + bf += 4; + i -= 4; + } + } + while (i) { + if ((fl & STBSP__TRIPLET_COMMA) && (cs++ == c)) { + cs = 0; + *bf++ = _stbsp__comma; + } else + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + } + + // copy leader if there is still one + sn = lead + 1; + while (lead[0]) { + int32_t i; + stbsp__cb_buf_clamp(i, lead[0]); + lead[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy the string + n = l; + while (n) { + int32_t i; + stbsp__cb_buf_clamp(i, n); + n -= i; + while (i >= 4) { + *(uint32_t volatile *)bf = *(uint32_t volatile *)s; + bf += 4; + s += 4; + i -= 4; + } + while (i) { + *bf++ = *s++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy trailing zeros + while (tz) { + int32_t i; + stbsp__cb_buf_clamp(i, tz); + tz -= i; + while (i) { + if ((((uintptr_t)bf) & 3) == 0) + break; + *bf++ = '0'; + --i; + } + while (i >= 4) { + *(uint32_t *)bf = 0x30303030; + bf += 4; + i -= 4; + } + while (i) { + *bf++ = '0'; + --i; + } + stbsp__chk_cb_buf(1); + } + + // copy tail if there is one + sn = tail + 1; + while (tail[0]) { + int32_t i; + stbsp__cb_buf_clamp(i, tail[0]); + tail[0] -= (char)i; + while (i) { + *bf++ = *sn++; + --i; + } + stbsp__chk_cb_buf(1); + } + + // handle the left justify + if (fl & STBSP__LEFTJUST) + if (fw > 0) { + while (fw) { + int32_t i; + stbsp__cb_buf_clamp(i, fw); + fw -= i; + while (i) { + if ((((uintptr_t)bf) & 3) == 0) + break; + *bf++ = ' '; + --i; + } + while (i >= 4) { + *(uint32_t *)bf = 0x20202020; + bf += 4; + i -= 4; + } + while (i--) + *bf++ = ' '; + stbsp__chk_cb_buf(1); + } + } + break; + + default: // unknown, just copy code + s = num + STBSP__NUMSZ - 1; + *s = f[0]; + l = 1; + fw = fl = 0; + lead[0] = 0; + tail[0] = 0; + pr = 0; + dp = 0; + cs = 0; + goto scopy; + } + ++f; + } +endfmt: + + if (!callback) + *bf = 0; + else + stbsp__flush_cb(); + +done: + return tlen + (int)(bf - buf); +} + +// cleanup +#undef STBSP__LEFTJUST +#undef STBSP__LEADINGPLUS +#undef STBSP__LEADINGSPACE +#undef STBSP__LEADING_0X +#undef STBSP__LEADINGZERO +#undef STBSP__INTMAX +#undef STBSP__TRIPLET_COMMA +#undef STBSP__NEGATIVE +#undef STBSP__METRIC_SUFFIX +#undef STBSP__NUMSZ +#undef stbsp__chk_cb_bufL +#undef stbsp__chk_cb_buf +#undef stbsp__flush_cb +#undef stbsp__cb_buf_clamp + +// ============================================================================ +// wrapper functions + +int sprintf(char *buf, char const *fmt, ...) +{ + int result; + va_list va; + va_start(va, fmt); + result = __vsprintfcb(0, 0, buf, fmt, va); + va_end(va); + return result; +} + +typedef struct stbsp__context { + char *buf; + int count; + int length; + char tmp[STB_SPRINTF_MIN]; +} stbsp__context; + +static char *stbsp__clamp_callback(const char *buf, void *user, int len) +{ + stbsp__context *c = (stbsp__context *)user; + + c->length += len; + + if (len > c->count) + len = c->count; + + if (len) { + if (buf != c->buf) { + const char *s, *se; + char *d; + d = c->buf; + s = buf; + se = buf + len; + do { + *d++ = *s++; + } while (s < se); + } + c->buf += len; + c->count -= len; + } + + if (c->count <= 0) + return c->tmp; + return (c->count >= STB_SPRINTF_MIN) ? c->buf : c->tmp; // go direct into buffer if you can +} + +char * stbsp__count_clamp_callback( const char * buf, void * user, int len ) +{ + stbsp__context * c = (stbsp__context*)user; + (void) sizeof(buf); + + c->length += len; + return c->tmp; // go direct into buffer if you can +} + +int vsnprintf( char * buf, int count, char const * fmt, va_list va ) +{ + stbsp__context c; + if ( (count == 0) && !buf ) + { + c.length = 0; + + __vsprintfcb( stbsp__count_clamp_callback, &c, c.tmp, fmt, va ); + } + else + { + int l; + + c.buf = buf; + c.count = count; + c.length = 0; + + __vsprintfcb( stbsp__clamp_callback, &c, stbsp__clamp_callback(0,&c,0), fmt, va ); + + // zero-terminate + l = (int)( c.buf - buf ); + if ( l >= count ) // should never be greater, only equal (or less) than count + l = count - 1; + buf[l] = 0; + } + + return c.length; +} + +int snprintf(char *buf, int count, char const *fmt, ...) +{ + int result; + va_list va; + va_start(va, fmt); + + result = vsnprintf(buf, count, fmt, va); + va_end(va); + + return result; +} + +int vsprintf(char *buf, char const *fmt, va_list va) +{ + return __vsprintfcb(0, 0, buf, fmt, va); +} + +// ======================================================================= +// low level float utility functions + +// copies d to bits w/ strict aliasing (this compiles to nothing on /Ox) +#define STBSP__COPYFP(dest, src) { *(long *)&dest = *(long *)&src; } +/* + \ + { \ + int cn; \ + for (cn = 0; cn < 8; cn++) \ + ((char *)&dest)[cn] = ((char *)&src)[cn]; \ + } +*/ + +// get float info +int32_t _stbsp__real_to_parts(int64_t *bits, int32_t *expo, double value) +{ + double d; + int64_t b = 0; + + // load value and round at the frac_digits + d = value; + + STBSP__COPYFP(b, d); + + *bits = b & ((((uint64_t)1) << 52) - 1); + *expo = (int32_t)(((b >> 52) & 2047) - 1023); + + return (int32_t)((uint64_t) b >> 63); +} + +static double const stbsp__bot[23] = { + 1e+000, 1e+001, 1e+002, 1e+003, 1e+004, 1e+005, 1e+006, 1e+007, 1e+008, 1e+009, 1e+010, 1e+011, + 1e+012, 1e+013, 1e+014, 1e+015, 1e+016, 1e+017, 1e+018, 1e+019, 1e+020, 1e+021, 1e+022 +}; +static double const stbsp__negbot[22] = { + 1e-001, 1e-002, 1e-003, 1e-004, 1e-005, 1e-006, 1e-007, 1e-008, 1e-009, 1e-010, 1e-011, + 1e-012, 1e-013, 1e-014, 1e-015, 1e-016, 1e-017, 1e-018, 1e-019, 1e-020, 1e-021, 1e-022 +}; +static double const stbsp__negboterr[22] = { + -5.551115123125783e-018, -2.0816681711721684e-019, -2.0816681711721686e-020, -4.7921736023859299e-021, -8.1803053914031305e-022, 4.5251888174113741e-023, + 4.5251888174113739e-024, -2.0922560830128471e-025, -6.2281591457779853e-026, -3.6432197315497743e-027, 6.0503030718060191e-028, 2.0113352370744385e-029, + -3.0373745563400371e-030, 1.1806906454401013e-032, -7.7705399876661076e-032, 2.0902213275965398e-033, -7.1542424054621921e-034, -7.1542424054621926e-035, + 2.4754073164739869e-036, 5.4846728545790429e-037, 9.2462547772103625e-038, -4.8596774326570872e-039 +}; +static double const stbsp__top[13] = { + 1e+023, 1e+046, 1e+069, 1e+092, 1e+115, 1e+138, 1e+161, 1e+184, 1e+207, 1e+230, 1e+253, 1e+276, 1e+299 +}; +static double const stbsp__negtop[13] = { + 1e-023, 1e-046, 1e-069, 1e-092, 1e-115, 1e-138, 1e-161, 1e-184, 1e-207, 1e-230, 1e-253, 1e-276, 1e-299 +}; +static double const stbsp__toperr[13] = { + 8388608., + 6.8601809640529717e+028, + -7.253143638152921e+052, + -4.3377296974619174e+075, + -1.5559416129466825e+098, + -3.2841562489204913e+121, + -3.7745893248228135e+144, + -1.7356668416969134e+167, + -3.8893577551088374e+190, + -9.9566444326005119e+213, + 6.3641293062232429e+236, + -5.2069140800249813e+259, + -5.2504760255204387e+282 +}; +static double const stbsp__negtoperr[13] = { + 3.9565301985100693e-040, -2.299904345391321e-063, 3.6506201437945798e-086, 1.1875228833981544e-109, + -5.0644902316928607e-132, -6.7156837247865426e-155, -2.812077463003139e-178, -5.7778912386589953e-201, + 7.4997100559334532e-224, -4.6439668915134491e-247, -6.3691100762962136e-270, -9.436808465446358e-293, + 8.0970921678014997e-317 +}; + +static uint64_t const stbsp__powten[20] = { + 1, + 10, + 100, + 1000, + 10000, + 100000, + 1000000, + 10000000, + 100000000, + 1000000000, + 10000000000ULL, + 100000000000ULL, + 1000000000000ULL, + 10000000000000ULL, + 100000000000000ULL, + 1000000000000000ULL, + 10000000000000000ULL, + 100000000000000000ULL, + 1000000000000000000ULL, + 10000000000000000000ULL +}; +#define stbsp__tento19th (1000000000000000000ULL) + +#define stbsp__ddmulthi(oh, ol, xh, yh) \ + { \ + double ahi = 0, alo, bhi = 0, blo; \ + int64_t bt; \ + oh = xh * yh; \ + STBSP__COPYFP(bt, xh); \ + bt &= ((~(uint64_t)0) << 27); \ + STBSP__COPYFP(ahi, bt); \ + alo = xh - ahi; \ + STBSP__COPYFP(bt, yh); \ + bt &= ((~(uint64_t)0) << 27); \ + STBSP__COPYFP(bhi, bt); \ + blo = yh - bhi; \ + ol = ((ahi * bhi - oh) + ahi * blo + alo * bhi) + alo * blo; \ + } + +#define stbsp__ddtoS64(ob, xh, xl) \ + { \ + double ahi = 0, alo, vh, t; \ + ob = (int64_t)xh; \ + vh = (double)ob; \ + ahi = (xh - vh); \ + t = (ahi - xh); \ + alo = (xh - (ahi - t)) - (vh + t); \ + ob += (int64_t)(ahi + alo + xl); \ + } + +#define stbsp__ddrenorm(oh, ol) \ + { \ + double s; \ + s = oh + ol; \ + ol = ol - (s - oh); \ + oh = s; \ + } + +#define stbsp__ddmultlo(oh, ol, xh, xl, yh, yl) ol = ol + (xh * yl + xl * yh); + +#define stbsp__ddmultlos(oh, ol, xh, yl) ol = ol + (xh * yl); + +static void stbsp__raise_to_power10(double *ohi, double *olo, double d, int32_t power) // power can be -323 to +350 +{ + double ph, pl; + if ((power >= 0) && (power <= 22)) { + stbsp__ddmulthi(ph, pl, d, stbsp__bot[power]); + } else { + int32_t e, et, eb; + double p2h, p2l; + + e = power; + if (power < 0) + e = -e; + et = (e * 0x2c9) >> 14; /* %23 */ + if (et > 13) + et = 13; + eb = e - (et * 23); + + ph = d; + pl = 0.0; + if (power < 0) { + if (eb) { + --eb; + stbsp__ddmulthi(ph, pl, d, stbsp__negbot[eb]); + stbsp__ddmultlos(ph, pl, d, stbsp__negboterr[eb]); + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__negtop[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__negtop[et], stbsp__negtoperr[et]); + ph = p2h; + pl = p2l; + } + } else { + if (eb) { + e = eb; + if (eb > 22) + eb = 22; + e -= eb; + stbsp__ddmulthi(ph, pl, d, stbsp__bot[eb]); + if (e) { + stbsp__ddrenorm(ph, pl); + stbsp__ddmulthi(p2h, p2l, ph, stbsp__bot[e]); + stbsp__ddmultlos(p2h, p2l, stbsp__bot[e], pl); + ph = p2h; + pl = p2l; + } + } + if (et) { + stbsp__ddrenorm(ph, pl); + --et; + stbsp__ddmulthi(p2h, p2l, ph, stbsp__top[et]); + stbsp__ddmultlo(p2h, p2l, ph, pl, stbsp__top[et], stbsp__toperr[et]); + ph = p2h; + pl = p2l; + } + } + } + stbsp__ddrenorm(ph, pl); + *ohi = ph; + *olo = pl; +} + +// given a float value, returns the significant bits in bits, and the position of the +// decimal point in decimal_pos. +/-INF and NAN are specified by special values +// returned in the decimal_pos parameter. +// frac_digits is absolute normally, but if you want from first significant digits (got %g and %e), or in 0x80000000 +static int32_t _stbsp__real_to_str(char const **start, uint32_t *len, char *out, int32_t *decimal_pos, double value, uint32_t frac_digits) +{ + double d; + int64_t bits = 0; + int32_t expo, e, ng, tens; + + d = value; + STBSP__COPYFP(bits, d); + expo = (int32_t)((bits >> 52) & 2047); + ng = (int32_t)((uint64_t) bits >> 63); + if (ng) + d = -d; + + if (expo == 2047) // is nan or inf? + { + *start = (bits & ((((uint64_t)1) << 52) - 1)) ? "NaN" : "Inf"; + *decimal_pos = STBSP__SPECIAL; + *len = 3; + return ng; + } + + if (expo == 0) // is zero or denormal + { + if (((uint64_t) bits << 1) == 0) // do zero + { + *decimal_pos = 1; + *start = out; + out[0] = '0'; + *len = 1; + return ng; + } + // find the right expo for denormals + { + int64_t v = ((uint64_t)1) << 51; + while ((bits & v) == 0) { + --expo; + v >>= 1; + } + } + } + + // find the decimal exponent as well as the decimal bits of the value + { + double ph, pl; + + // log10 estimate - very specifically tweaked to hit or undershoot by no more than 1 of log10 of all expos 1..2046 + tens = expo - 1023; + tens = (tens < 0) ? ((tens * 617) / 2048) : (((tens * 1233) / 4096) + 1); + + // move the significant bits into position and stick them into an int + stbsp__raise_to_power10(&ph, &pl, d, 18 - tens); + + // get full as much precision from double-double as possible + stbsp__ddtoS64(bits, ph, pl); + + // check if we undershot + if (((uint64_t)bits) >= stbsp__tento19th) + ++tens; + } + + // now do the rounding in integer land + frac_digits = (frac_digits & 0x80000000) ? ((frac_digits & 0x7ffffff) + 1) : (tens + frac_digits); + if ((frac_digits < 24)) { + uint32_t dg = 1; + if ((uint64_t)bits >= stbsp__powten[9]) + dg = 10; + while ((uint64_t)bits >= stbsp__powten[dg]) { + ++dg; + if (dg == 20) + goto noround; + } + if (frac_digits < dg) { + uint64_t r; + // add 0.5 at the right position and round + e = dg - frac_digits; + if ((uint32_t)e >= 24) + goto noround; + r = stbsp__powten[e]; + bits = bits + (r / 2); + if ((uint64_t)bits >= stbsp__powten[dg]) + ++tens; + bits /= r; + } + noround:; + } + + // kill long trailing runs of zeros + if (bits) { + uint32_t n; + for (;;) { + if (bits <= 0xffffffff) + break; + if (bits % 1000) + goto donez; + bits /= 1000; + } + n = (uint32_t)bits; + while ((n % 1000) == 0) + n /= 1000; + bits = n; + donez:; + } + + // convert to string + out += 64; + e = 0; + for (;;) { + uint32_t n; + char *o = out - 8; + // do the conversion in chunks of U32s (avoid most 64-bit divides, worth it, constant denomiators be damned) + if (bits >= 100000000) { + n = (uint32_t)(bits % 100000000); + bits /= 100000000; + } else { + n = (uint32_t)bits; + bits = 0; + } + while (n) { + out -= 2; + *(uint16_t *)out = *(uint16_t *)&_stbsp__digitpair.pair[(n % 100) * 2]; + n /= 100; + e += 2; + } + if (bits == 0) { + if ((e) && (out[0] == '0')) { + ++out; + --e; + } + break; + } + while (out != o) { + *--out = '0'; + ++e; + } + } + + *decimal_pos = tens; + *start = out; + *len = e; + return ng; +} + +#undef stbsp__ddmulthi +#undef stbsp__ddrenorm +#undef stbsp__ddmultlo +#undef stbsp__ddmultlos +#undef STBSP__SPECIAL +#undef STBSP__COPYFP + +// these are the constants that gnu uses, but they don't really matter for us +#define _IOFBF 0 +#define _IOLBF 1 +#define _IONBF 2 +#define BUFSIZ 8192 + + +#define EOF (-1) +#define FILENAME_MAX 4096 +#define FOPEN_MAX 16 +typedef long fpos_t; +#define L_tmpnam 20 +#define SEEK_CUR 1 +#define SEEK_END 2 +#define SEEK_SET 0 +#define TMP_MAX 500 + +long lseek(int fd, long offset, int whence) { + return __syscall(8, fd, offset, whence, 0, 0, 0); +} + +size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) { + size_t count; + if (nmemb > 0xffffffffffffffff / size) { + stream->err = 1; + return 0; + } + count = size * nmemb; + while (count > 0) { + long n = write(stream->fd, ptr, count); + if (n <= 0) break; + count -= n; + ptr = (char *)ptr + n; + } + if (count > 0) stream->err = 1; + return nmemb - count / size; +} + +size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream) { + size_t count; + long n = 1; + if (nmemb > 0xffffffffffffffff / size) { + stream->err = 1; + return 0; + } + if (size == 0 || nmemb == 0) return 0; + + count = size * nmemb; + + if (stream->has_ungetc) { + *(char *)ptr = stream->ungetc; + stream->has_ungetc = 0; + ptr = (char *)ptr + 1; + --count; + } + if (stream->eof) return 0; + + while (count > 0) { + n = read(stream->fd, ptr, count); + if (n <= 0) break; + count -= n; + ptr = (char *)ptr + n; + } + if (n == 0) stream->eof = 1; + if (n < 0) stream->err = 1; + return nmemb - count / size; +} + +static char *__fprintf_callback(const char *buf, void *user, int len) { + FILE *fp = user; + fwrite(buf, 1, len, fp); + return buf; +} + +int vfprintf(FILE *fp, const char *fmt, va_list args) { + char buf[STB_SPRINTF_MIN]; + return __vsprintfcb(__fprintf_callback, fp, buf, fmt, args); +} + +int fprintf(FILE *fp, const char *fmt, ...) { + va_list args; + int ret; + va_start(args, fmt); + ret = vfprintf(fp, fmt, args); + va_end(args); + return ret; +} + +int vprintf(const char *fmt, va_list args) { + return vfprintf(stdout, fmt, args); +} + +int printf(const char *fmt, ...) { + va_list args; + int ret; + va_start(args, fmt); + ret = vfprintf(stdout, fmt, args); + va_end(args); + return ret; +} + +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#define O_CREAT 0100 +#define O_TRUNC 01000 +#define O_APPEND 02000 +#define O_DIRECTORY 0200000 +#define __O_TMPFILE 020000000 +#define O_TMPFILE (__O_TMPFILE | O_DIRECTORY) +int open(const char *path, int flags, int mode) { + return __syscall(2, path, flags, mode, 0, 0, 0); +} + +int close(int fd) { + return __syscall(3, fd, 0, 0, 0, 0, 0); +} + + +int _fopen_flags_from_mode(const char *mode) { + int flags; + if (mode[1] == '+' || (mode[1] && mode[2] == '+')) { + // open for updating + flags = O_RDWR; + switch (mode[0]) { + case 'r': break; + case 'w': flags |= O_TRUNC | O_CREAT; break; + case 'a': flags |= O_APPEND | O_CREAT; break; + default: return -1; + } + } else { + switch (mode[0]) { + case 'r': flags = O_RDONLY; break; + case 'w': flags = O_WRONLY | O_TRUNC | O_CREAT; break; + case 'a': flags = O_WRONLY | O_APPEND | O_CREAT; break; + default: return -1; + } + } + return flags; +} + +FILE *_FILE_from_fd(int fd) { + FILE *fp = malloc(sizeof(FILE)); + // NB: our malloc implementation returns zeroed memory + fp->fd = fd; + return fp; +} + +FILE *fopen(const char *filename, const char *mode) { + int flags = _fopen_flags_from_mode(mode); + if (flags < 0) return NULL; + int fd; + + fd = open(filename, flags, 0644); + if (fd < 0) return NULL; + return _FILE_from_fd(fd); +} + + +FILE *fdopen(int fd, const char *mode) { + // mode doesn't matter, hopefully + return _FILE_from_fd(fd); +} + +int fclose(FILE *stream) { + int ret = close(stream->fd); + free(stream); + return ret; +} + +int fflush(FILE *stream) { + // we don't buffer anything + return 0; +} + +FILE *freopen(const char *filename, const char *mode, FILE *stream) { + int flags = _fopen_flags_from_mode(mode); + close(stream->fd); + if (flags < 0) return NULL; + stream->eof = stream->err = 0; + stream->fd = open(filename, flags, 0644); + return stream; +} + +int unlink(const char *pathname) { + return __syscall(87, pathname, 0, 0, 0, 0, 0); +} + +int rmdir(const char *pathname) { + return __syscall(84, pathname, 0, 0, 0, 0, 0); +} + +int remove(const char *filename) { + return rmdir(filename) + ? unlink(filename) + : 0; +} + +int rename(const char *old, const char *new) { + return __syscall(82, old, new, 0, 0, 0, 0); +} + +char *tmpnam(char *s) { + struct timespec t = {0}; + do { + // NB: we can't use rand() here because + // "The implementation shall behave as if no library function calls the rand function." C89 § 4.10.2.1 + clock_gettime(CLOCK_MONOTONIC, &t); // use clock as a source of randomness + sprintf(s, "/tmp/C_%06u", t.tv_nsec % 1000000); + } while (access(s, F_OK) == 0); // if file exists, generate a new name + return s; +} + +FILE *tmpfile(void) { + int fd = open("/tmp", O_TMPFILE | O_RDWR, 0600); + if (fd < 0) return NULL; + return _FILE_from_fd(fd); +} + +int getc(FILE *stream) { + unsigned char c; + long n; + if (stream->eof) return EOF; + n = fread(&c, 1, 1, stream); + if (n != 1) return EOF; + return c; +} + +int fgetc(FILE *stream) { + return getc(stream); +} + +char *fgets(char *s, int n, FILE *stream) { + char *p = s, *end = p + (n-1); + + if (stream->eof) return NULL; + + while (p < end) { + size_t n = fread(p, 1, 1, stream); + if (n != 1) { + if (p == s) { + // end of file reached, and no characters were read + return NULL; + } + break; + } + if (*p == '\n') { + ++p; + break; + } + ++p; + } + *p = '\0'; + return s; +} + +int putc(int c, FILE *stream) { + size_t n = fwrite(&c, 1, 1, stream); + if (n == 1) return c; + return EOF; +} + +int fputc(int c, FILE *stream) { + return putc(c, stream); +} + +int fputs(const char *s, FILE *stream) { + size_t n = strlen(s); + if (fwrite(s, 1, n, stream) == n) + return n; + return EOF; +} + +int getchar(void) { + return getc(stdin); +} + +char *gets(char *s) { + char *p; + fgets(s, 1l<<20, stdin); + if (*s) { + p = s + strlen(s) - 1; + // remove newline + if (*p == '\n') + *p = '\0'; + } + return s; +} + +int putchar(int c) { + return putc(c, stdout); +} + +int puts(const char *s) { + fputs(s, stdout); + putchar('\n'); +} + +int ungetc(int c, FILE *stream) { + if (c == EOF || stream->has_ungetc) return EOF; + stream->has_ungetc = 1; + stream->ungetc = c; + stream->eof = 0; + return c; +} + + +int fgetpos(FILE *stream, fpos_t *pos) { + long off = lseek(stream->fd, 0, SEEK_CUR); + if (off < 0) { + errno = EIO; + return EIO; + } + *pos = off; + return 0; +} + +int fsetpos(FILE *stream, const fpos_t *pos) { + long off = lseek(stream->fd, *pos, SEEK_SET); + if (off < 0) { + errno = EIO; + return EIO; + } + stream->eof = 0; + return 0; +} + +int fseek(FILE *stream, long int offset, int whence) { + long off = lseek(stream->fd, offset, whence); + if (off < 0) { + return EIO; + } + stream->eof = 0; + return 0; +} + +long int ftell(FILE *stream) { + long off = lseek(stream->fd, 0, SEEK_CUR); + if (off < 0) { + errno = EIO; + return -1L; + } + return off; +} + +void rewind(FILE *stream) { + fseek(stream, 0, SEEK_SET); + stream->err = 0; +} + +void clearerr(FILE *stream) { + stream->err = 0; +} + +int feof(FILE *stream) { + return stream->eof; +} + +int ferror(FILE *stream) { + return stream->err; +} + +// we don't buffer anything +// we're allowed to do this: "The contents of the array at any time are indeterminate." C89 § 4.9.5.6 +void setbuf(FILE *stream, char *buf) { +} + +int setvbuf(FILE *stream, char *buf, int mode, size_t size) { + return 0; +} + +typedef int _VscanfNextChar(void *, long *); +typedef int _VscanfPeekChar(void *); +int _str_next_char(void *dat, long *pos) { + const char **s = dat; + int c = **s; + if (c == '\0') return c; + ++*pos; + ++*s; + return c; +} +int _file_next_char(void *dat, long *pos) { + int c = getc(dat); + if (c == EOF) return c; + ++*pos; + return c; +} +int _str_peek_char(void *dat) { + const char **s = dat; + return **s; +} +int _file_peek_char(void *dat) { + int c = getc(dat); + ungetc(c, dat); + return c; +} + +void _bad_scanf(void) { + fprintf(stderr, "bad scanf format.\n"); + abort(); +} + +char _parse_escape_sequence(char **p_str) { + char *str = *p_str; + if (*str == '\\') { + ++str; + switch (*str) { + case 'n': *p_str = str + 1; return '\n'; + case 'v': *p_str = str + 1; return '\v'; + case 't': *p_str = str + 1; return '\t'; + case 'a': *p_str = str + 1; return '\a'; + case 'f': *p_str = str + 1; return '\f'; + case 'r': *p_str = str + 1; return '\r'; + case 'b': *p_str = str + 1; return '\b'; + case 'x': + ++str; + return (char)strtoul(str, p_str, 16); + case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7': { + int c = *str++ - '0'; + if (_isdigit_in_base(*str, 8)) c = (c << 3) + *str - '0', ++str; + if (_isdigit_in_base(*str, 8)) c = (c << 3) + *str - '0', ++str; + return c; + } break; + default: *p_str = str + 1; return *str; + } + } else { + *p_str += 1; + return *str; + } +} + +int _vscanf(_VscanfNextChar *__next_char, _VscanfPeekChar *__peek_char, int terminator, void *data, const char *fmt, va_list args) { + long pos = 0; // position in file/string (needed for %n) + int assignments = 0; + char number[128], *p_number; + unsigned char charset[256]; + int i; + + #define _next_char() (__next_char(data, &pos)) + #define _peek_char() (__peek_char(data)) + while (*fmt) { + if (*fmt == '%') { + int base = 10; + int assign = 1; + long field_width = LONG_MAX; + int modifier = 0; + char *end; + + ++fmt; + if (*fmt == '*') assign = 0, ++fmt; // assignment suppression + if (*fmt >= '0' && *fmt <= '9') + field_width = strtol(fmt, &fmt, 10); // field width + if (*fmt == 'l' || *fmt == 'L' || *fmt == 'h') + modifier = *fmt, ++fmt; + switch (*fmt) { + case 'd': { + while (isspace(_peek_char())) _next_char(); + // signed decimal integer + ++fmt; + if (field_width > 100) field_width = 100; // max number length + if (field_width == 0) goto vscanf_done; // number can't have size 0 + int c = _peek_char(); + p_number = number; + if (c == '-' || c == '+') { + if (field_width == 1) goto vscanf_done; + *p_number++ = _next_char(); + } + while ((p_number - number) < field_width && isdigit(_peek_char())) + *p_number++ = _next_char(); + *p_number = 0; + long value = strtol(number, &end, 10); + if (end == number) goto vscanf_done; // bad number + if (assign) { + switch (modifier) { + case 0: *va_arg(args, int*) = _clamp_long_to_int(value); break; + case 'h': *va_arg(args, short*) = _clamp_long_to_short(value); break; + case 'l': *va_arg(args, long*) = value; break; + default: _bad_scanf(); break; + } + ++assignments; + } + } break; + case 'i': { + while (isspace(_peek_char())) _next_char(); + // signed integer + long value = 0; + ++fmt; + if (field_width > 100) field_width = 100; // max number length + if (field_width == 0) goto vscanf_done; // number can't have size 0 + int c = _peek_char(); + p_number = number; + if (c == '-' || c == '+') { + if (field_width == 1) goto vscanf_done; + *p_number++ = _next_char(); + c = _peek_char(); + } + if (c == '0') { + *p_number++ = _next_char(); + if ((p_number - number) < field_width) { + c = _peek_char(); + if (c == 'x') { + if ((p_number - number) < field_width-1) + *p_number++ = _next_char(), base = 16; + else + goto emit_value; // e.g. 0x... width field width 2 + } else { + base = 8; + } + } else goto emit_value; + } + while ((p_number - number) < field_width && _isdigit_in_base(_peek_char(), base)) + *p_number++ = _next_char(); + *p_number = 0; + value = strtol(number, &end, 0); + if (end == number) goto vscanf_done; // bad number + emit_value: + if (assign) { + switch (modifier) { + case 0: *va_arg(args, int*) = _clamp_long_to_int(value); break; + case 'h': *va_arg(args, short*) = _clamp_long_to_short(value); break; + case 'l': *va_arg(args, long*) = value; break; + default: _bad_scanf(); break; + } + ++assignments; + } + } break; + case 'o': base = 8; goto vscanf_unsigned; + case 'u': goto vscanf_unsigned; + case 'p': modifier = 'l', base = 16; goto vscanf_unsigned; + case 'x': case 'X': base = 16; goto vscanf_unsigned; + vscanf_unsigned:{ + while (isspace(_peek_char())) _next_char(); + // unsigned integers + ++fmt; + if (field_width > 100) field_width = 100; // max number length + if (field_width == 0) goto vscanf_done; + int c = _peek_char(); + p_number = number; + if (c == '+') *p_number++ = _next_char(); + while ((p_number - number) < field_width && _isdigit_in_base(_peek_char(), base)) + *p_number++ = _next_char(); + *p_number = 0; + unsigned long value = strtoul(number, &end, base); + if (end == number) goto vscanf_done; // bad number + if (assign) { + switch (modifier) { + case 0: *va_arg(args, unsigned*) = _clamp_ulong_to_uint(value); break; + case 'h': *va_arg(args, unsigned short*) = _clamp_ulong_to_ushort(value); break; + case 'l': *va_arg(args, unsigned long*) = value; break; + default: _bad_scanf(); break; + } + ++assignments; + } + } break; + case 'e': + case 'f': + case 'g': + case 'E': + case 'G': { + while (isspace(_peek_char())) _next_char(); + // floats + ++fmt; + if (field_width > 100) field_width = 100; // max number length + if (field_width == 0) goto vscanf_done; + int c = _peek_char(); + p_number = number; + if (c == '-' || c == '+') { + if (field_width == 1) goto vscanf_done; + *p_number++ = _next_char(); + c = _peek_char(); + } + if (c != '.' && !isdigit(c)) + goto vscanf_done; + while ((p_number - number) < field_width && isdigit(_peek_char())) + *p_number++ = _next_char(); + if ((p_number - number) < field_width && _peek_char() == '.') { + *p_number++ = _next_char(); + while ((p_number - number) < field_width && isdigit(_peek_char())) + *p_number++ = _next_char(); + } + c = _peek_char(); + if ((p_number - number) < field_width && c == 'e' || c == 'E') { + *p_number++ = _next_char(); + c = _peek_char(); + if ((p_number - number) < field_width && c == '+') + *p_number++ = _next_char(); + else if ((p_number - number) < field_width && c == '-') + *p_number++ = _next_char(); + + while ((p_number - number) < field_width && isdigit(_peek_char())) + *p_number++ = _next_char(); + } + double value = strtod(number, &end); + if (end == number) goto vscanf_done; // bad number + if (assign) { + switch (modifier) { + case 0: *va_arg(args, float*) = value; break; + case 'l': case 'L': *va_arg(args, double*) = value; break; + default: _bad_scanf(); break; + } + + ++assignments; + } + } break; + case 's': { + while (isspace(_peek_char())) _next_char(); + // string of non-whitespace characters + ++fmt; + char *str = assign ? va_arg(args, char*) : NULL, *p = str; + for (i = 0; i < field_width && !isspace(_peek_char()); ++i) { + int c = _next_char(); + if (c == terminator) break; + if (p) *p++ = c; + } + if (i == 0) goto vscanf_done; // empty sequence + if (p) { + *p = 0; + ++assignments; + } + } break; + case '[': { + // string of characters in charset + int complement = 0; + ++fmt; + if (*fmt == '^') { + complement = 1; + ++fmt; + } + memset(charset, complement, sizeof charset); + do { // NB: this is a do-while loop and not a while loop, because []] matches strings of ]'s. + charset[(unsigned char)_parse_escape_sequence(&fmt)] = !complement; + } while (*fmt != ']'); + ++fmt; // skip ] + char *str = assign ? va_arg(args, char*) : NULL, *p = str; + for (i = 0; i < field_width && charset[(unsigned char)_peek_char()]; ++i) { + int c = _next_char(); + if (c == terminator) break; + if (p) *p++ = c; + } + if (i == 0) goto vscanf_done; // empty sequence + if (p) { + *p = 0; + ++assignments; + } + } break; + case 'c': { + // string of characters + ++fmt; + char *str = assign ? va_arg(args, char*) : NULL, *p = str; + if (field_width == LONG_MAX) field_width = 1; + for (i = 0; i < field_width; ++i) { + int c = _next_char(); + if (c == terminator) break; + if (p) *p++ = c; + } + if (i < field_width) goto vscanf_done; // end of file encountered + if (p) { + ++assignments; + } + } break; + case 'n': + ++fmt; + switch (modifier) { + case 0: *va_arg(args, int *) = pos; break; + case 'h': *va_arg(args, short *) = pos; break; + case 'l': *va_arg(args, long *) = pos; break; + default: _bad_scanf(); break; + } + break; + default: + _bad_scanf(); + break; + } + } else if (isspace(*fmt)) { + // skip spaces in input + ++fmt; + while (isspace(_peek_char())) _next_char(); + } else { + if (_peek_char() == *fmt) { + // format character matches input character + ++fmt; + _next_char(); + } else { + // format character doesn't match input character; stop parsing + break; + } + } + } + vscanf_done: + if (_peek_char() == terminator && assignments == 0) return EOF; + return assignments; + #undef _next_char + #undef _peek_char +} + +int fscanf(FILE *stream, const char *format, ...) { + va_list args; + va_start(args, format); + int ret = _vscanf(_file_next_char, _file_peek_char, EOF, stream, format, args); + va_end(args); + return ret; +} + +int sscanf(const char *s, const char *format, ...) { + va_list args; + va_start(args, format); + int ret = _vscanf(_str_next_char, _str_peek_char, 0, &s, format, args); + va_end(args); + return ret; +} + +int scanf(const char *format, ...) { + va_list args; + va_start(args, format); + int ret = _vscanf(_file_next_char, _file_peek_char, EOF, stdin, format, args); + va_end(args); + return ret; +} + + +void perror(const char *s) { + printf("%s: %s\n", s, strerror(errno)); +} + +#undef STB_SPRINTF_MIN + +#endif // _STDIO_H diff --git a/05/tcc-0.9.25/stdlib.h b/05/tcc-0.9.25/stdlib.h new file mode 100644 index 0000000..c2f83eb --- /dev/null +++ b/05/tcc-0.9.25/stdlib.h @@ -0,0 +1,193 @@ +#ifndef _STDLIB_H +#define _STDLIB_H + +#include + +#define EXIT_FAILURE (-1) +#define EXIT_SUCCESS 0 +#define RAND_MAX 2147483647 +// @NONSTANDARD: we don't define MB_CUR_MAX or any of the mbtowc functions + +typedef struct { + int quot; + int rem; +} div_t; + +typedef struct { + long quot; + long rem; +} ldiv_t; + +char *getenv(const char *name) { + int i, j; + for (i = 0; _envp[i]; ++i) { + char *key = _envp[i]; + for (j = 0; key[j] != '=' && name[j]; ++j) + if (name[j] != key[j]) + break; + if (key[j] == '=' && !name[j]) + return key + (j+1); + } + return NULL; +} + +double atof(const char *nptr) { + return strtod(nptr, NULL); +} + +int atoi(const char *nptr) { + return _clamp_long_to_int(strtol(nptr, NULL, 10)); +} + +long atol(const char *nptr) { + return strtol(nptr, NULL, 10); +} + +int rand(void) { + // https://en.wikipedia.org/wiki/Linear_congruential_generator + // we're using musl/newlib's constants + _rand_seed = 6364136223846793005 * _rand_seed + 1; + return _rand_seed >> 33; +} + +void srand(unsigned seed) { + _rand_seed = seed; +} + +void *calloc(size_t nmemb, size_t size) { + if (nmemb > 0xffffffffffffffff / size) + return NULL; + // NB: our malloc implementation returns zeroed memory + return malloc(nmemb * size); +} + +void *realloc(void *ptr, size_t size) { + if (!ptr) return malloc(size); + if (!size) { + free(ptr); + return NULL; + } + uint64_t *memory = (char *)ptr - 16; + uint64_t old_size = *memory; + uint64_t *new_memory = _mremap(memory, old_size, size, MREMAP_MAYMOVE); + if ((uint64_t)new_memory > 0xffffffffffff0000) return NULL; + *new_memory = size; + return (char *)new_memory + 16; +} + + +int atexit(void (*func)(void)) { + if (_n_exit_handlers >= 32) return -1; + _exit_handlers[_n_exit_handlers++] = func; + return 0; +} + +int system(const char *string) { + if (!string) return 1; + + int pid = fork(); + if (pid < 0) { + return -1; + } else if (pid == 0) { + // child + char *argv[] = { + "/bin/sh", + "-c", + 0, + 0 + }; + argv[2] = string; + execve("/bin/sh", argv, _envp); + // on success, execve does not return. + _Exit(-1); + } else { + // parent + int status = 0; + int ret = wait4(pid, &status, 0, NULL); + if (ret != pid) return -1; + if (_WIFSIGNALED(status)) return -1; + return _WEXITSTATUS(status); + } + +} + +void *bsearch(const void *key, const void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)) { + size_t lo = 0; + size_t hi = nmemb; + while (lo < hi) { + size_t mid = (lo + hi) >> 1; + void *elem = (char *)base + mid * size; + int cmp = compar(key, elem); + if (cmp < 0) { + // key < elem + hi = mid; + } else if (cmp) { + // key > elem + lo = mid + 1; + } else { + return elem; + } + } + return NULL; +} + +void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)) { + // quicksort + if (nmemb < 2) return; + + void *temp = malloc(size); + void *mid = (char *)base + ((nmemb >> 1) * size); // choose middle element to speed up sorting an already-sorted array + size_t pivot_index = 0, i; + for (i = 0; i < nmemb; ++i) { + void *elem = (char *)base + i * size; + if (compar(elem, mid) < 0) + ++pivot_index; + } + void *pivot = (char *)base + pivot_index * size; + memcpy(temp, pivot, size); + memcpy(pivot, mid, size); + memcpy(mid, temp, size); + + char *l, *r = (char *)base + (nmemb-1) * size; + for (l = base; l < r;) { + if (compar(l, pivot) > 0) { + // swap l and r + memcpy(temp, l, size); + memcpy(l, r, size); + memcpy(r, temp, size); + r -= size; + } else { + // l is already in the right place + l += size; + } + } + + qsort(base, pivot_index, size, compar); + qsort((char *)pivot + size, nmemb - 1 - pivot_index, size, compar); + + free(temp); +} + +int abs(int x) { + return x >= 0 ? x : -x; +} + +long labs(long x) { + return x >= 0 ? x : -x; +} + +div_t div(int numer, int denom) { + div_t d; + d.quot = numer / denom; + d.rem = numer % denom; + return d; +} + +ldiv_t ldiv(long numer, long denom) { + ldiv_t d; + d.quot = numer / denom; + d.rem = numer % denom; + return d; +} + +#endif // _STDLIB_H diff --git a/05/tcc-0.9.25/string.h b/05/tcc-0.9.25/string.h new file mode 100644 index 0000000..4b73101 --- /dev/null +++ b/05/tcc-0.9.25/string.h @@ -0,0 +1,185 @@ +#ifndef _STRING_H +#define _STRING_H + +#include + + +void *memmove(void *s1, const void *s2, size_t n) { + if (s1 < s2) return memcpy(s1, s2, n); // our memcpy does a forwards copy + // backwards copy + char *p = (char*)s1 + n, *q = (char*)s2 + n; + while (p > s1) + *--p = *--q; + return s1; +} + +char *strcpy(char *s1, const char *s2) { + char *p = s1 - 1, *q = s2 - 1; + while ((*++p = *++q)); + return s1; +} + +char *strncpy(char *s1, const char *s2, size_t n) { + char *p = s1 - 1, *q = s2 - 1; + size_t i; + for (i = 0; i < n; ++i) + if (!(*++p = *++q)) + break; + for (; i < n; ++i) + *++p = 0; + return s1; +} + +char *strcat(char *s1, const char *s2) { + return strcpy(s1 + strlen(s1), s2); +} + +char *strncat(char *s1, const char *s2, size_t n) { + // oddly, not equivalent to strncpy(s1 + strlen(s1), s2, n) + char *p = s1 + strlen(s1) - 1, *q = s2 - 1; + size_t i; + for (i = 0; i < n; ++i) + if (!(*++p = *++q)) + break; + *++p = 0; + return s1; +} + +int memcmp(const void *s1, const void *s2, size_t n) { + char *p = s1, *q = s2; + size_t i; + for (i = 0; i < n; ++i, ++p, ++q) { + if (*p > *q) + return 1; + if (*p < *q) + return -1; + } + return 0; +} + +int strcmp(const char *s1, const char *s2) { + char *p = s1, *q = s2; + for (; ; ++p, ++q) { + if (*p > *q) + return 1; + if (*p < *q) + return -1; + if (!*p) break; + } + return 0; +} + +int strcoll(const char *s1, const char *s2) { + // we only support the C locale + return strcmp(s1, s2); +} + +int strncmp(const char *s1, const char *s2, size_t n) { + char *p = s1, *q = s2; + size_t i; + for (i = 0; i < n; ++i, ++p, ++q) { + if (*p > *q) + return 1; + if (*p < *q) + return -1; + if (!*p) break; + } + return 0; +} + +size_t strxfrm(char *s1, const char *s2, size_t n) { + // we only support the C locale + size_t l = strlen(s2); + if (l >= n) return l; + strcpy(s1, s2); + return l; +} + +void *memchr(const void *s, int c, size_t n) { + char *p = s, *end = p + n; + while (p < end) { + if ((unsigned char)*p == c) + return p; + ++p; + } + return NULL; +} + +char *strchr(const char *s, int c) { + return memchr(s, c, strlen(s)+1); +} + + +size_t strcspn(const char *s1, const char *s2) { + const char *p, *q; + for (p = s1; *p; ++p) { + for (q = s2; *q; ++q) { + if (*p == *q) + goto ret; + } + } + ret: + return p - s1; +} + +char *strpbrk(const char *s1, const char *s2) { + const char *p, *q; + for (p = s1; *p; ++p) { + for (q = s2; *q; ++q) { + if (*p == *q) + return p; + } + } + return NULL; +} + +char *strrchr(const char *s, int c) { + char *p; + for (p = s + strlen(s); p >= s; --p) { + if (*p == c) + return p; + } + return NULL; +} + +size_t strspn(const char *s1, const char *s2) { + const char *p, *q; + for (p = s1; *p; ++p) { + for (q = s2; *q; ++q) { + if (*p == *q) break; + } + if (!*q) break; + } + return p - s1; +} + +char *strstr(const char *s1, const char *s2) { + char *p; + size_t l = strlen(s2); + for (p = s1; *p; ++p) { + if (memcmp(p, s2, l) == 0) + return p; + } + return NULL; +} + +char *strtok(char *s1, const char *s2) { + static char *str; + if (s1) str = s1; + if (!str) return NULL; + char *p = str + strspn(str, s2); + if (!*p) { + str = NULL; + return NULL; + } + char *q = strpbrk(p, s2); + if (q) { + *q = 0; + str = q + 1; + } else { + str = NULL; + } + return p; +} + +#endif // _STRING_H diff --git a/05/tcc-0.9.25/tcc-doc.html b/05/tcc-0.9.25/tcc-doc.html new file mode 100644 index 0000000..e40532e --- /dev/null +++ b/05/tcc-0.9.25/tcc-doc.html @@ -0,0 +1,2241 @@ + + + + + +Tiny C Compiler Reference Documentation + + + + + + + + + + + + + + + + + + + + + +
[Top][Contents][Index][ ? ]
+

Tiny C Compiler Reference Documentation

+ +

This manual documents version of the Tiny C Compiler. +

+ + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

1. Introduction

+ +

TinyCC (aka TCC) is a small but hyper fast C compiler. Unlike other C +compilers, it is meant to be self-relying: you do not need an +external assembler or linker because TCC does that for you. +

+

TCC compiles so fast that even for big projects Makefiles may +not be necessary. +

+

TCC not only supports ANSI C, but also most of the new ISO C99 +standard and many GNUC extensions including inline assembly. +

+

TCC can also be used to make C scripts, i.e. pieces of C source +that you run as a Perl or Python script. Compilation is so fast that +your script will be as fast as if it was an executable. +

+

TCC can also automatically generate memory and bound checks +(see section TinyCC Memory and Bound checks) while allowing all C pointers operations. TCC can do +these checks even if non patched libraries are used. +

+

With libtcc, you can use TCC as a backend for dynamic code +generation (see section The libtcc library). +

+

TCC mainly supports the i386 target on Linux and Windows. There are alpha +ports for the ARM (arm-tcc) and the TMS320C67xx targets +(c67-tcc). More information about the ARM port is available at +http://lists.gnu.org/archive/html/tinycc-devel/2003-10/msg00044.html. +

+

For usage on Windows, see also tcc-win32.txt. +

+
+ + + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

2. Command line invocation

+ +
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

2.1 Quick start

+ +
 
usage: tcc [options] [infile1 infile2…] [‘-runinfile args…]
+
+ +

TCC options are a very much like gcc options. The main difference is that TCC +can also execute directly the resulting program and give it runtime +arguments. +

+

Here are some examples to understand the logic: +

+
+
tcc -run a.c
+

Compile ‘a.c’ and execute it directly +

+
+
tcc -run a.c arg1
+

Compile a.c and execute it directly. arg1 is given as first argument to +the main() of a.c. +

+
+
tcc a.c -run b.c arg1
+

Compile ‘a.c’ and ‘b.c’, link them together and execute them. arg1 is given +as first argument to the main() of the resulting program. +

+
+
tcc -o myprog a.c b.c
+

Compile ‘a.c’ and ‘b.c’, link them and generate the executable ‘myprog’. +

+
+
tcc -o myprog a.o b.o
+

link ‘a.o’ and ‘b.o’ together and generate the executable ‘myprog’. +

+
+
tcc -c a.c
+

Compile ‘a.c’ and generate object file ‘a.o’. +

+
+
tcc -c asmfile.S
+

Preprocess with C preprocess and assemble ‘asmfile.S’ and generate +object file ‘asmfile.o’. +

+
+
tcc -c asmfile.s
+

Assemble (but not preprocess) ‘asmfile.s’ and generate object file +‘asmfile.o’. +

+
+
tcc -r -o ab.o a.c b.c
+

Compile ‘a.c’ and ‘b.c’, link them together and generate the object file ‘ab.o’. +

+
+
+ +

Scripting: +

+

TCC can be invoked from scripts, just as shell scripts. You just +need to add #!/usr/local/bin/tcc -run at the start of your C source: +

+
 
#!/usr/local/bin/tcc -run
+#include <stdio.h>
+
+int main() 
+{
+    printf("Hello World\n");
+    return 0;
+}
+
+ +

TCC can read C source code from standard input when ‘-’ is used in +place of ‘infile’. Example: +

+
 
echo 'main(){puts("hello");}' | tcc -run -
+
+ +
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

2.2 Option summary

+ +

General Options: +

+
+
-v
+

Display current TCC version, increase verbosity. +

+
+
-c
+

Generate an object file (‘-o’ option must also be given). +

+
+
-o outfile
+

Put object file, executable, or dll into output file ‘outfile’. +

+
+
-Bdir
+

Set the path where the tcc internal libraries can be found (default is +‘PREFIX/lib/tcc’). +

+
+
-bench
+

Output compilation statistics. +

+
+
-run source [args...]
+

Compile file source and run it with the command line arguments +args. In order to be able to give more than one argument to a +script, several TCC options can be given after the +‘-run’ option, separated by spaces. Example: +

+
 
tcc "-run -L/usr/X11R6/lib -lX11" ex4.c
+
+ +

In a script, it gives the following header: +

+
 
#!/usr/local/bin/tcc -run -L/usr/X11R6/lib -lX11
+#include <stdlib.h>
+int main(int argc, char **argv)
+{
+    ...
+}
+
+ +
+
+ +

Preprocessor options: +

+
+
-Idir
+

Specify an additional include path. Include paths are searched in the +order they are specified. +

+

System include paths are always searched after. The default system +include paths are: ‘/usr/local/include’, ‘/usr/include’ +and ‘PREFIX/lib/tcc/include’. (‘PREFIX’ is usually +‘/usr’ or ‘/usr/local’). +

+
+
-Dsym[=val]
+

Define preprocessor symbol ‘sym’ to +val. If val is not present, its value is ‘1’. Function-like macros can +also be defined: ‘-DF(a)=a+1’ +

+
+
-Usym
+

Undefine preprocessor symbol ‘sym’. +

+
+ +

Compilation flags: +

+

Note: each of the following warning options has a negative form beginning with +‘-fno-’. +

+
+
-funsigned-char
+

Let the char type be unsigned. +

+
+
-fsigned-char
+

Let the char type be signed. +

+
+
-fno-common
+

Do not generate common symbols for uninitialized data. +

+
+
-fleading-underscore
+

Add a leading underscore at the beginning of each C symbol. +

+
+
+ +

Warning options: +

+
+
-w
+

Disable all warnings. +

+
+
+ +

Note: each of the following warning options has a negative form beginning with +‘-Wno-’. +

+
+
-Wimplicit-function-declaration
+

Warn about implicit function declaration. +

+
+
-Wunsupported
+

Warn about unsupported GCC features that are ignored by TCC. +

+
+
-Wwrite-strings
+

Make string constants be of type const char * instead of char +*. +

+
+
-Werror
+

Abort compilation if warnings are issued. +

+
+
-Wall
+

Activate all warnings, except ‘-Werror’, ‘-Wunusupported’ and +‘-Wwrite-strings’. +

+
+
+ +

Linker options: +

+
+
-Ldir
+

Specify an additional static library path for the ‘-l’ option. The +default library paths are ‘/usr/local/lib’, ‘/usr/lib’ and ‘/lib’. +

+
+
-lxxx
+

Link your program with dynamic library libxxx.so or static library +libxxx.a. The library is searched in the paths specified by the +‘-L’ option. +

+
+
-shared
+

Generate a shared library instead of an executable (‘-o’ option +must also be given). +

+
+
-static
+

Generate a statically linked executable (default is a shared linked +executable) (‘-o’ option must also be given). +

+
+
-rdynamic
+

Export global symbols to the dynamic linker. It is useful when a library +opened with dlopen() needs to access executable symbols. +

+
+
-r
+

Generate an object file combining all input files (‘-o’ option must +also be given). +

+
+
-Wl,-Ttext,address
+

Set the start of the .text section to address. +

+
+
-Wl,--oformat,fmt
+

Use fmt as output format. The supported output formats are: +

+
elf32-i386
+

ELF output format (default) +

+
binary
+

Binary image (only for executable output) +

+
coff
+

COFF output format (only for executable output for TMS320C67xx target) +

+
+ +
+
+ +

Debugger options: +

+
+
-g
+

Generate run time debug information so that you get clear run time +error messages: test.c:68: in function 'test5()': dereferencing +invalid pointer instead of the laconic Segmentation +fault. +

+
+
-b
+

Generate additional support code to check +memory allocations and array/pointer bounds. ‘-g’ is implied. Note +that the generated code is slower and bigger in this case. +

+
+
-bt N
+

Display N callers in stack traces. This is useful with ‘-g’ or +‘-b’. +

+
+
+ +

Note: GCC options ‘-Ox’, ‘-fx’ and ‘-mx’ are +ignored. +

+ +
+ + + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

3. C language support

+ +
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

3.1 ANSI C

+ +

TCC implements all the ANSI C standard, including structure bit fields +and floating point numbers (long double, double, and +float fully supported). +

+
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

3.2 ISOC99 extensions

+ +

TCC implements many features of the new C standard: ISO C99. Currently +missing items are: complex and imaginary numbers and variable length +arrays. +

+

Currently implemented ISOC99 features: +

+
    +
  • 64 bit long long types are fully supported. + +
  • The boolean type _Bool is supported. + +
  • __func__ is a string variable containing the current +function name. + +
  • Variadic macros: __VA_ARGS__ can be used for + function-like macros: +
     
        #define dprintf(level, __VA_ARGS__) printf(__VA_ARGS__)
    +
    + +

    dprintf can then be used with a variable number of parameters. +

    +
  • Declarations can appear anywhere in a block (as in C++). + +
  • Array and struct/union elements can be initialized in any order by + using designators: +
     
        struct { int x, y; } st[10] = { [0].x = 1, [0].y = 2 };
    +
    +    int tab[10] = { 1, 2, [5] = 5, [9] = 9};
    +
    + +
  • Compound initializers are supported: +
     
        int *p = (int []){ 1, 2, 3 };
    +
    +

    to initialize a pointer pointing to an initialized array. The same +works for structures and strings. +

    +
  • Hexadecimal floating point constants are supported: +
     
              double d = 0x1234p10;
    +
    + +

    is the same as writing +

     
              double d = 4771840.0;
    +
    + +
  • inline keyword is ignored. + +
  • restrict keyword is ignored. +
+ +
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

3.3 GNU C extensions

+ +

TCC implements some GNU C extensions: +

+
    +
  • array designators can be used without '=': +
     
        int a[10] = { [0] 1, [5] 2, 3, 4 };
    +
    + +
  • Structure field designators can be a label: +
     
        struct { int x, y; } st = { x: 1, y: 1};
    +
    +

    instead of +

     
        struct { int x, y; } st = { .x = 1, .y = 1};
    +
    + +
  • \e is ASCII character 27. + +
  • case ranges : ranges can be used in cases: +
     
        switch(a) {
    +    case 1 … 9:
    +          printf("range 1 to 9\n");
    +          break;
    +    default:
    +          printf("unexpected\n");
    +          break;
    +    }
    +
    + + + + + + + + + + +
  • The keyword __attribute__ is handled to specify variable or +function attributes. The following attributes are supported: + +
      +
    • aligned(n): align a variable or a structure field to n bytes +(must be a power of two). + +
    • packed: force alignment of a variable or a structure field to + 1. + +
    • section(name): generate function or data in assembly section +name (name is a string containing the section name) instead of the default +section. + +
    • unused: specify that the variable or the function is unused. + +
    • cdecl: use standard C calling convention (default). + +
    • stdcall: use Pascal-like calling convention. + +
    • regparm(n): use fast i386 calling convention. n must be +between 1 and 3. The first n function parameters are respectively put in +registers %eax, %edx and %ecx. + +
    • dllexport: export function from dll/executable (win32 only) + +
    + +

    Here are some examples: +

     
        int a __attribute__ ((aligned(8), section(".mysection")));
    +
    + +

    align variable a to 8 bytes and put it in section .mysection. +

    +
     
        int my_add(int a, int b) __attribute__ ((section(".mycodesection"))) 
    +    {
    +        return a + b;
    +    }
    +
    + +

    generate function my_add in section .mycodesection. +

    +
  • GNU style variadic macros: +
     
        #define dprintf(fmt, args…) printf(fmt, ## args)
    +
    +    dprintf("no arg\n");
    +    dprintf("one arg %d\n", 1);
    +
    + +
  • __FUNCTION__ is interpreted as C99 __func__ +(so it has not exactly the same semantics as string literal GNUC +where it is a string literal). + +
  • The __alignof__ keyword can be used as sizeof +to get the alignment of a type or an expression. + +
  • The typeof(x) returns the type of x. +x is an expression or a type. + +
  • Computed gotos: &&label returns a pointer of type +void * on the goto label label. goto *expr can be +used to jump on the pointer resulting from expr. + +
  • Inline assembly with asm instruction: + + + +
     
    static inline void * my_memcpy(void * to, const void * from, size_t n)
    +{
    +int d0, d1, d2;
    +__asm__ __volatile__(
    +        "rep ; movsl\n\t"
    +        "testb $2,%b4\n\t"
    +        "je 1f\n\t"
    +        "movsw\n"
    +        "1:\ttestb $1,%b4\n\t"
    +        "je 2f\n\t"
    +        "movsb\n"
    +        "2:"
    +        : "=&c" (d0), "=&D" (d1), "=&S" (d2)
    +        :"0" (n/4), "q" (n),"1" ((long) to),"2" ((long) from)
    +        : "memory");
    +return (to);
    +}
    +
    + + +

    TCC includes its own x86 inline assembler with a gas-like (GNU +assembler) syntax. No intermediate files are generated. GCC 3.x named +operands are supported. +

    +
  • __builtin_types_compatible_p() and __builtin_constant_p() +are supported. + +
  • #pragma pack is supported for win32 compatibility. + +
+ +
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

3.4 TinyCC extensions

+ +
    +
  • __TINYC__ is a predefined macro to 1 to +indicate that you use TCC. + +
  • #! at the start of a line is ignored to allow scripting. + +
  • Binary digits can be entered (0b101 instead of +5). + +
  • __BOUNDS_CHECKING_ON is defined if bound checking is activated. + +
+ +
+ + + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

4. TinyCC Assembler

+ +

Since version 0.9.16, TinyCC integrates its own assembler. TinyCC +assembler supports a gas-like syntax (GNU assembler). You can +desactivate assembler support if you want a smaller TinyCC executable +(the C compiler does not rely on the assembler). +

+

TinyCC Assembler is used to handle files with ‘.S’ (C +preprocessed assembler) and ‘.s’ extensions. It is also used to +handle the GNU inline assembler with the asm keyword. +

+
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

4.1 Syntax

+ +

TinyCC Assembler supports most of the gas syntax. The tokens are the +same as C. +

+
    +
  • C and C++ comments are supported. + +
  • Identifiers are the same as C, so you cannot use '.' or '$'. + +
  • Only 32 bit integer numbers are supported. + +
+ +
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

4.2 Expressions

+ +
    +
  • Integers in decimal, octal and hexa are supported. + +
  • Unary operators: +, -, ~. + +
  • Binary operators in decreasing priority order: + +
      +
    1. *, /, % +
    2. &, |, ^ +
    3. +, - +
    + +
  • A value is either an absolute number or a label plus an offset. +All operators accept absolute values except '+' and '-'. '+' or '-' can be +used to add an offset to a label. '-' supports two labels only if they +are the same or if they are both defined and in the same section. + +
+ +
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

4.3 Labels

+ +
    +
  • All labels are considered as local, except undefined ones. + +
  • Numeric labels can be used as local gas-like labels. +They can be defined several times in the same source. Use 'b' +(backward) or 'f' (forward) as suffix to reference them: + +
     
     1:
    +      jmp 1b /* jump to '1' label before */
    +      jmp 1f /* jump to '1' label after */
    + 1:
    +
    + +
+ +
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

4.4 Directives

+ +

All directives are preceeded by a '.'. The following directives are +supported: +

+
    +
  • .align n[,value] +
  • .skip n[,value] +
  • .space n[,value] +
  • .byte value1[,...] +
  • .word value1[,...] +
  • .short value1[,...] +
  • .int value1[,...] +
  • .long value1[,...] +
  • .quad immediate_value1[,...] +
  • .globl symbol +
  • .global symbol +
  • .section section +
  • .text +
  • .data +
  • .bss +
  • .fill repeat[,size[,value]] +
  • .org n +
  • .previous +
  • .string string[,...] +
  • .asciz string[,...] +
  • .ascii string[,...] +
+ +
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

4.5 X86 Assembler

+ +

All X86 opcodes are supported. Only ATT syntax is supported (source +then destination operand order). If no size suffix is given, TinyCC +tries to guess it from the operand sizes. +

+

Currently, MMX opcodes are supported but not SSE ones. +

+
+ + + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

5. TinyCC Linker

+ +
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

5.1 ELF file generation

+ +

TCC can directly output relocatable ELF files (object files), +executable ELF files and dynamic ELF libraries without relying on an +external linker. +

+

Dynamic ELF libraries can be output but the C compiler does not generate +position independent code (PIC). It means that the dynamic library +code generated by TCC cannot be factorized among processes yet. +

+

TCC linker eliminates unreferenced object code in libraries. A single pass is +done on the object and library list, so the order in which object files and +libraries are specified is important (same constraint as GNU ld). No grouping +options (‘--start-group’ and ‘--end-group’) are supported. +

+
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

5.2 ELF file loader

+ +

TCC can load ELF object files, archives (.a files) and dynamic +libraries (.so). +

+
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

5.3 PE-i386 file generation

+ +

TCC for Windows supports the native Win32 executable file format (PE-i386). It +generates EXE files (console and gui) and DLL files. +

+

For usage on Windows, see also tcc-win32.txt. +

+
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

5.4 GNU Linker Scripts

+ +

Because on many Linux systems some dynamic libraries (such as +‘/usr/lib/libc.so’) are in fact GNU ld link scripts (horrible!), +the TCC linker also supports a subset of GNU ld scripts. +

+

The GROUP and FILE commands are supported. OUTPUT_FORMAT +and TARGET are ignored. +

+

Example from ‘/usr/lib/libc.so’: +

 
/* GNU ld script
+   Use the shared library, but some functions are only in
+   the static library, so try that secondarily.  */
+GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a )
+
+ +
+ + + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

6. TinyCC Memory and Bound checks

+ +

This feature is activated with the ‘-b’ (see section Command line invocation). +

+

Note that pointer size is unchanged and that code generated +with bound checks is fully compatible with unchecked +code. When a pointer comes from unchecked code, it is assumed to be +valid. Even very obscure C code with casts should work correctly. +

+

For more information about the ideas behind this method, see +http://www.doc.ic.ac.uk/~phjk/BoundsChecking.html. +

+

Here are some examples of caught errors: +

+
+
Invalid range with standard string function:
+
 
{
+    char tab[10];
+    memset(tab, 0, 11);
+}
+
+ +
+
Out of bounds-error in global or local arrays:
+
 
{
+    int tab[10];
+    for(i=0;i<11;i++) {
+        sum += tab[i];
+    }
+}
+
+ +
+
Out of bounds-error in malloc'ed data:
+
 
{
+    int *tab;
+    tab = malloc(20 * sizeof(int));
+    for(i=0;i<21;i++) {
+        sum += tab4[i];
+    }
+    free(tab);
+}
+
+ +
+
Access of freed memory:
+
 
{
+    int *tab;
+    tab = malloc(20 * sizeof(int));
+    free(tab);
+    for(i=0;i<20;i++) {
+        sum += tab4[i];
+    }
+}
+
+ +
+
Double free:
+
 
{
+    int *tab;
+    tab = malloc(20 * sizeof(int));
+    free(tab);
+    free(tab);
+}
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

7. The libtcc library

+ +

The libtcc library enables you to use TCC as a backend for +dynamic code generation. +

+

Read the ‘libtcc.h’ to have an overview of the API. Read +‘libtcc_test.c’ to have a very simple example. +

+

The idea consists in giving a C string containing the program you want +to compile directly to libtcc. Then you can access to any global +symbol (function or variable) defined. +

+
+ + + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

8. Developer's guide

+ +

This chapter gives some hints to understand how TCC works. You can skip +it if you do not intend to modify the TCC code. +

+
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

8.1 File reading

+ +

The BufferedFile structure contains the context needed to read a +file, including the current line number. tcc_open() opens a new +file and tcc_close() closes it. inp() returns the next +character. +

+
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

8.2 Lexer

+ +

next() reads the next token in the current +file. next_nomacro() reads the next token without macro +expansion. +

+

tok contains the current token (see TOK_xxx) +constants. Identifiers and keywords are also keywords. tokc +contains additional infos about the token (for example a constant value +if number or string token). +

+
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

8.3 Parser

+ +

The parser is hardcoded (yacc is not necessary). It does only one pass, +except: +

+
    +
  • For initialized arrays with unknown size, a first pass +is done to count the number of elements. + +
  • For architectures where arguments are evaluated in +reverse order, a first pass is done to reverse the argument order. + +
+ +
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

8.4 Types

+ +

The types are stored in a single 'int' variable. It was choosen in the +first stages of development when tcc was much simpler. Now, it may not +be the best solution. +

+
 
#define VT_INT        0  /* integer type */
+#define VT_BYTE       1  /* signed byte type */
+#define VT_SHORT      2  /* short type */
+#define VT_VOID       3  /* void type */
+#define VT_PTR        4  /* pointer */
+#define VT_ENUM       5  /* enum definition */
+#define VT_FUNC       6  /* function type */
+#define VT_STRUCT     7  /* struct/union definition */
+#define VT_FLOAT      8  /* IEEE float */
+#define VT_DOUBLE     9  /* IEEE double */
+#define VT_LDOUBLE   10  /* IEEE long double */
+#define VT_BOOL      11  /* ISOC99 boolean type */
+#define VT_LLONG     12  /* 64 bit integer */
+#define VT_LONG      13  /* long integer (NEVER USED as type, only
+                            during parsing) */
+#define VT_BTYPE      0x000f /* mask for basic type */
+#define VT_UNSIGNED   0x0010  /* unsigned type */
+#define VT_ARRAY      0x0020  /* array type (also has VT_PTR) */
+#define VT_BITFIELD   0x0040  /* bitfield modifier */
+
+#define VT_STRUCT_SHIFT 16   /* structure/enum name shift (16 bits left) */
+
+ +

When a reference to another type is needed (for pointers, functions and +structures), the 32 - VT_STRUCT_SHIFT high order bits are used to +store an identifier reference. +

+

The VT_UNSIGNED flag can be set for chars, shorts, ints and long +longs. +

+

Arrays are considered as pointers VT_PTR with the flag +VT_ARRAY set. +

+

The VT_BITFIELD flag can be set for chars, shorts, ints and long +longs. If it is set, then the bitfield position is stored from bits +VT_STRUCT_SHIFT to VT_STRUCT_SHIFT + 5 and the bit field size is stored +from bits VT_STRUCT_SHIFT + 6 to VT_STRUCT_SHIFT + 11. +

+

VT_LONG is never used except during parsing. +

+

During parsing, the storage of an object is also stored in the type +integer: +

+
 
#define VT_EXTERN  0x00000080  /* extern definition */
+#define VT_STATIC  0x00000100  /* static variable */
+#define VT_TYPEDEF 0x00000200  /* typedef definition */
+
+ +
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

8.5 Symbols

+ +

All symbols are stored in hashed symbol stacks. Each symbol stack +contains Sym structures. +

+

Sym.v contains the symbol name (remember +an idenfier is also a token, so a string is never necessary to store +it). Sym.t gives the type of the symbol. Sym.r is usually +the register in which the corresponding variable is stored. Sym.c is +usually a constant associated to the symbol. +

+

Four main symbol stacks are defined: +

+
+
define_stack
+

for the macros (#defines). +

+
+
global_stack
+

for the global variables, functions and types. +

+
+
local_stack
+

for the local variables, functions and types. +

+
+
global_label_stack
+

for the local labels (for goto). +

+
+
label_stack
+

for GCC block local labels (see the __label__ keyword). +

+
+
+ +

sym_push() is used to add a new symbol in the local symbol +stack. If no local symbol stack is active, it is added in the global +symbol stack. +

+

sym_pop(st,b) pops symbols from the symbol stack st until +the symbol b is on the top of stack. If b is NULL, the stack +is emptied. +

+

sym_find(v) return the symbol associated to the identifier +v. The local stack is searched first from top to bottom, then the +global stack. +

+
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

8.6 Sections

+ +

The generated code and datas are written in sections. The structure +Section contains all the necessary information for a given +section. new_section() creates a new section. ELF file semantics +is assumed for each section. +

+

The following sections are predefined: +

+
+
text_section
+

is the section containing the generated code. ind contains the +current position in the code section. +

+
+
data_section
+

contains initialized data +

+
+
bss_section
+

contains uninitialized data +

+
+
bounds_section
+
lbounds_section
+

are used when bound checking is activated +

+
+
stab_section
+
stabstr_section
+

are used when debugging is actived to store debug information +

+
+
symtab_section
+
strtab_section
+

contain the exported symbols (currently only used for debugging). +

+
+
+ +
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

8.7 Code generation

+ +
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

8.7.1 Introduction

+ +

The TCC code generator directly generates linked binary code in one +pass. It is rather unusual these days (see gcc for example which +generates text assembly), but it can be very fast and surprisingly +little complicated. +

+

The TCC code generator is register based. Optimization is only done at +the expression level. No intermediate representation of expression is +kept except the current values stored in the value stack. +

+

On x86, three temporary registers are used. When more registers are +needed, one register is spilled into a new temporary variable on the stack. +

+
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

8.7.2 The value stack

+ +

When an expression is parsed, its value is pushed on the value stack +(vstack). The top of the value stack is vtop. Each value +stack entry is the structure SValue. +

+

SValue.t is the type. SValue.r indicates how the value is +currently stored in the generated code. It is usually a CPU register +index (REG_xxx constants), but additional values and flags are +defined: +

+
 
#define VT_CONST     0x00f0
+#define VT_LLOCAL    0x00f1
+#define VT_LOCAL     0x00f2
+#define VT_CMP       0x00f3
+#define VT_JMP       0x00f4
+#define VT_JMPI      0x00f5
+#define VT_LVAL      0x0100
+#define VT_SYM       0x0200
+#define VT_MUSTCAST  0x0400
+#define VT_MUSTBOUND 0x0800
+#define VT_BOUNDED   0x8000
+#define VT_LVAL_BYTE     0x1000
+#define VT_LVAL_SHORT    0x2000
+#define VT_LVAL_UNSIGNED 0x4000
+#define VT_LVAL_TYPE     (VT_LVAL_BYTE | VT_LVAL_SHORT | VT_LVAL_UNSIGNED)
+
+ +
+
VT_CONST
+

indicates that the value is a constant. It is stored in the union +SValue.c, depending on its type. +

+
+
VT_LOCAL
+

indicates a local variable pointer at offset SValue.c.i in the +stack. +

+
+
VT_CMP
+

indicates that the value is actually stored in the CPU flags (i.e. the +value is the consequence of a test). The value is either 0 or 1. The +actual CPU flags used is indicated in SValue.c.i. +

+

If any code is generated which destroys the CPU flags, this value MUST be +put in a normal register. +

+
+
VT_JMP
+
VT_JMPI
+

indicates that the value is the consequence of a conditional jump. For VT_JMP, +it is 1 if the jump is taken, 0 otherwise. For VT_JMPI it is inverted. +

+

These values are used to compile the || and && logical +operators. +

+

If any code is generated, this value MUST be put in a normal +register. Otherwise, the generated code won't be executed if the jump is +taken. +

+
+
VT_LVAL
+

is a flag indicating that the value is actually an lvalue (left value of +an assignment). It means that the value stored is actually a pointer to +the wanted value. +

+

Understanding the use VT_LVAL is very important if you want to +understand how TCC works. +

+
+
VT_LVAL_BYTE
+
VT_LVAL_SHORT
+
VT_LVAL_UNSIGNED
+

if the lvalue has an integer type, then these flags give its real +type. The type alone is not enough in case of cast optimisations. +

+
+
VT_LLOCAL
+

is a saved lvalue on the stack. VT_LLOCAL should be eliminated +ASAP because its semantics are rather complicated. +

+
+
VT_MUSTCAST
+

indicates that a cast to the value type must be performed if the value +is used (lazy casting). +

+
+
VT_SYM
+

indicates that the symbol SValue.sym must be added to the constant. +

+
+
VT_MUSTBOUND
+
VT_BOUNDED
+

are only used for optional bound checking. +

+
+
+ +
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

8.7.3 Manipulating the value stack

+ +

vsetc() and vset() pushes a new value on the value +stack. If the previous vtop was stored in a very unsafe place(for +example in the CPU flags), then some code is generated to put the +previous vtop in a safe storage. +

+

vpop() pops vtop. In some cases, it also generates cleanup +code (for example if stacked floating point registers are used as on +x86). +

+

The gv(rc) function generates code to evaluate vtop (the +top value of the stack) into registers. rc selects in which +register class the value should be put. gv() is the most +important function of the code generator. +

+

gv2() is the same as gv() but for the top two stack +entries. +

+
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

8.7.4 CPU dependent code generation

+

See the ‘i386-gen.c’ file to have an example. +

+
+
load()
+

must generate the code needed to load a stack value into a register. +

+
+
store()
+

must generate the code needed to store a register into a stack value +lvalue. +

+
+
gfunc_start()
+
gfunc_param()
+
gfunc_call()
+

should generate a function call +

+
+
gfunc_prolog()
+
gfunc_epilog()
+

should generate a function prolog/epilog. +

+
+
gen_opi(op)
+

must generate the binary integer operation op on the two top +entries of the stack which are guaranted to contain integer types. +

+

The result value should be put on the stack. +

+
+
gen_opf(op)
+

same as gen_opi() for floating point operations. The two top +entries of the stack are guaranted to contain floating point values of +same types. +

+
+
gen_cvt_itof()
+

integer to floating point conversion. +

+
+
gen_cvt_ftoi()
+

floating point to integer conversion. +

+
+
gen_cvt_ftof()
+

floating point to floating point of different size conversion. +

+
+
gen_bounded_ptr_add()
+
gen_bounded_ptr_deref()
+

are only used for bounds checking. +

+
+
+ +
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

8.8 Optimizations done

+

Constant propagation is done for all operations. Multiplications and +divisions are optimized to shifts when appropriate. Comparison +operators are optimized by maintaining a special cache for the +processor flags. &&, || and ! are optimized by maintaining a special +'jump target' value. No other jump optimization is currently performed +because it would require to store the code in a more abstract fashion. +

+
+ + + + + + + + + + + + + + + + +
[ < ][ > ]   [ << ][ Up ][ >> ]         [Top][Contents][Index][ ? ]
+

Concept Index

+
Jump to:   _ +   +
+A +   +B +   +C +   +D +   +E +   +F +   +G +   +I +   +J +   +L +   +M +   +O +   +P +   +Q +   +R +   +S +   +T +   +U +   +V +   +W +   +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Index Entry Section

_
__asm__3.3 GNU C extensions

A
align directive4.4 Directives
aligned attribute3.3 GNU C extensions
ascii directive4.4 Directives
asciz directive4.4 Directives
assembler4.5 X86 Assembler
assembler directives4.4 Directives
assembly, inline3.3 GNU C extensions

B
bound checks6. TinyCC Memory and Bound checks
bss directive4.4 Directives
byte directive4.4 Directives

C
caching processor flags8.8 Optimizations done
cdecl attribute3.3 GNU C extensions
code generation8.7 Code generation
comparison operators8.8 Optimizations done
constant propagation8.8 Optimizations done
CPU dependent8.7.4 CPU dependent code generation

D
data directive4.4 Directives
directives, assembler4.4 Directives
dllexport attribute3.3 GNU C extensions

E
ELF5.1 ELF file generation

F
FILE, linker command5.4 GNU Linker Scripts
fill directive4.4 Directives
flags, caching8.8 Optimizations done

G
gas3.3 GNU C extensions
global directive4.4 Directives
globl directive4.4 Directives
GROUP, linker command5.4 GNU Linker Scripts

I
inline assembly3.3 GNU C extensions
int directive4.4 Directives

J
jump optimization8.8 Optimizations done

L
linker5. TinyCC Linker
linker scripts5.4 GNU Linker Scripts
long directive4.4 Directives

M
memory checks6. TinyCC Memory and Bound checks

O
optimizations8.8 Optimizations done
org directive4.4 Directives
OUTPUT_FORMAT, linker command5.4 GNU Linker Scripts

P
packed attribute3.3 GNU C extensions
PE-i3865.3 PE-i386 file generation
previous directive4.4 Directives

Q
quad directive4.4 Directives

R
regparm attribute3.3 GNU C extensions

S
scripts, linker5.4 GNU Linker Scripts
section attribute3.3 GNU C extensions
section directive4.4 Directives
short directive4.4 Directives
skip directive4.4 Directives
space directive4.4 Directives
stdcall attribute3.3 GNU C extensions
strength reduction8.8 Optimizations done
string directive4.4 Directives

T
TARGET, linker command5.4 GNU Linker Scripts
text directive4.4 Directives

U
unused attribute3.3 GNU C extensions

V
value stack8.7.3 Manipulating the value stack
value stack, introduction8.7.2 The value stack

W
word directive4.4 Directives

+
Jump to:   _ +   +
+A +   +B +   +C +   +D +   +E +   +F +   +G +   +I +   +J +   +L +   +M +   +O +   +P +   +Q +   +R +   +S +   +T +   +U +   +V +   +W +   +
+ +
+ + + + + + +
[Top][Contents][Index][ ? ]
+

Table of Contents

+ +
+ + + + + + +
[Top][Contents][Index][ ? ]
+

About This Document

+

+ This document was generated by gr on May, 18 2009 using texi2html 1.78. +

+

+ The buttons in the navigation panels have the following meaning: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Button Name Go to From 1.2.3 go to
[ < ] BackPrevious section in reading order1.2.2
[ > ] ForwardNext section in reading order1.2.4
[ << ] FastBackBeginning of this chapter or previous chapter1
[ Up ] UpUp section1.2
[ >> ] FastForwardNext chapter2
[Top] TopCover (top) of document  
[Contents] ContentsTable of contents  
[Index] IndexIndex  
[ ? ] AboutAbout (help)  
+ +

+ where the Example assumes that the current position is at Subsubsection One-Two-Three of a document of the following structure: +

+ +
    +
  • 1. Section One +
      +
    • 1.1 Subsection One-One +
        +
      • ...
      • +
      +
    • +
    • 1.2 Subsection One-Two +
        +
      • 1.2.1 Subsubsection One-Two-One
      • +
      • 1.2.2 Subsubsection One-Two-Two
      • +
      • 1.2.3 Subsubsection One-Two-Three     + <== Current Position
      • +
      • 1.2.4 Subsubsection One-Two-Four
      • +
      +
    • +
    • 1.3 Subsection One-Three +
        +
      • ...
      • +
      +
    • +
    • 1.4 Subsection One-Four
    • +
    +
  • +
+ +
+

+ + This document was generated by gr on May, 18 2009 using texi2html 1.78. + +
+ +

+ + diff --git a/05/tcc-0.9.25/tcc-doc.texi b/05/tcc-0.9.25/tcc-doc.texi new file mode 100644 index 0000000..7cc61bb --- /dev/null +++ b/05/tcc-0.9.25/tcc-doc.texi @@ -0,0 +1,1227 @@ +\input texinfo @c -*- texinfo -*- +@c %**start of header +@setfilename tcc-doc.info +@settitle Tiny C Compiler Reference Documentation +@c %**end of header + +@include config.texi + +@iftex +@titlepage +@afourpaper +@sp 7 +@center @titlefont{Tiny C Compiler Reference Documentation} +@sp 3 +@end titlepage +@headings double +@end iftex + +@contents + +@node Top, Introduction, (dir), (dir) +@top Tiny C Compiler Reference Documentation + +This manual documents version @value{VERSION} of the Tiny C Compiler. + +@menu +* Introduction:: Introduction to tcc. +* Invoke:: Invocation of tcc (command line, options). +* Clang:: ANSI C and extensions. +* asm:: Assembler syntax. +* linker:: Output file generation and supported targets. +* Bounds:: Automatic bounds-checking of C code. +* Libtcc:: The libtcc library. +* devel:: Guide for Developers. +@end menu + + +@node Introduction +@chapter Introduction + +TinyCC (aka TCC) is a small but hyper fast C compiler. Unlike other C +compilers, it is meant to be self-relying: you do not need an +external assembler or linker because TCC does that for you. + +TCC compiles so @emph{fast} that even for big projects @code{Makefile}s may +not be necessary. + +TCC not only supports ANSI C, but also most of the new ISO C99 +standard and many GNUC extensions including inline assembly. + +TCC can also be used to make @emph{C scripts}, i.e. pieces of C source +that you run as a Perl or Python script. Compilation is so fast that +your script will be as fast as if it was an executable. + +TCC can also automatically generate memory and bound checks +(@pxref{Bounds}) while allowing all C pointers operations. TCC can do +these checks even if non patched libraries are used. + +With @code{libtcc}, you can use TCC as a backend for dynamic code +generation (@pxref{Libtcc}). + +TCC mainly supports the i386 target on Linux and Windows. There are alpha +ports for the ARM (@code{arm-tcc}) and the TMS320C67xx targets +(@code{c67-tcc}). More information about the ARM port is available at +@url{http://lists.gnu.org/archive/html/tinycc-devel/2003-10/msg00044.html}. + +For usage on Windows, see also tcc-win32.txt. + +@node Invoke +@chapter Command line invocation + +@section Quick start + +@example +@c man begin SYNOPSIS +usage: tcc [options] [@var{infile1} @var{infile2}@dots{}] [@option{-run} @var{infile} @var{args}@dots{}] +@c man end +@end example + +@noindent +@c man begin DESCRIPTION +TCC options are a very much like gcc options. The main difference is that TCC +can also execute directly the resulting program and give it runtime +arguments. + +Here are some examples to understand the logic: + +@table @code +@item @samp{tcc -run a.c} +Compile @file{a.c} and execute it directly + +@item @samp{tcc -run a.c arg1} +Compile a.c and execute it directly. arg1 is given as first argument to +the @code{main()} of a.c. + +@item @samp{tcc a.c -run b.c arg1} +Compile @file{a.c} and @file{b.c}, link them together and execute them. arg1 is given +as first argument to the @code{main()} of the resulting program. +@ignore +Because multiple C files are specified, @option{--} are necessary to clearly +separate the program arguments from the TCC options. +@end ignore + +@item @samp{tcc -o myprog a.c b.c} +Compile @file{a.c} and @file{b.c}, link them and generate the executable @file{myprog}. + +@item @samp{tcc -o myprog a.o b.o} +link @file{a.o} and @file{b.o} together and generate the executable @file{myprog}. + +@item @samp{tcc -c a.c} +Compile @file{a.c} and generate object file @file{a.o}. + +@item @samp{tcc -c asmfile.S} +Preprocess with C preprocess and assemble @file{asmfile.S} and generate +object file @file{asmfile.o}. + +@item @samp{tcc -c asmfile.s} +Assemble (but not preprocess) @file{asmfile.s} and generate object file +@file{asmfile.o}. + +@item @samp{tcc -r -o ab.o a.c b.c} +Compile @file{a.c} and @file{b.c}, link them together and generate the object file @file{ab.o}. + +@end table + +Scripting: + +TCC can be invoked from @emph{scripts}, just as shell scripts. You just +need to add @code{#!/usr/local/bin/tcc -run} at the start of your C source: + +@example +#!/usr/local/bin/tcc -run +#include + +int main() +@{ + printf("Hello World\n"); + return 0; +@} +@end example + +TCC can read C source code from @emph{standard input} when @option{-} is used in +place of @option{infile}. Example: + +@example +echo 'main()@{puts("hello");@}' | tcc -run - +@end example +@c man end + +@section Option summary + +General Options: + +@c man begin OPTIONS +@table @option +@item -v +Display current TCC version, increase verbosity. + +@item -c +Generate an object file (@option{-o} option must also be given). + +@item -o outfile +Put object file, executable, or dll into output file @file{outfile}. + +@item -Bdir +Set the path where the tcc internal libraries can be found (default is +@file{PREFIX/lib/tcc}). + +@item -bench +Output compilation statistics. + +@item -run source [args...] +Compile file @var{source} and run it with the command line arguments +@var{args}. In order to be able to give more than one argument to a +script, several TCC options can be given @emph{after} the +@option{-run} option, separated by spaces. Example: + +@example +tcc "-run -L/usr/X11R6/lib -lX11" ex4.c +@end example + +In a script, it gives the following header: + +@example +#!/usr/local/bin/tcc -run -L/usr/X11R6/lib -lX11 +#include +int main(int argc, char **argv) +@{ + ... +@} +@end example + +@end table + +Preprocessor options: + +@table @option +@item -Idir +Specify an additional include path. Include paths are searched in the +order they are specified. + +System include paths are always searched after. The default system +include paths are: @file{/usr/local/include}, @file{/usr/include} +and @file{PREFIX/lib/tcc/include}. (@file{PREFIX} is usually +@file{/usr} or @file{/usr/local}). + +@item -Dsym[=val] +Define preprocessor symbol @samp{sym} to +val. If val is not present, its value is @samp{1}. Function-like macros can +also be defined: @option{-DF(a)=a+1} + +@item -Usym +Undefine preprocessor symbol @samp{sym}. +@end table + +Compilation flags: + +Note: each of the following warning options has a negative form beginning with +@option{-fno-}. + +@table @option +@item -funsigned-char +Let the @code{char} type be unsigned. + +@item -fsigned-char +Let the @code{char} type be signed. + +@item -fno-common +Do not generate common symbols for uninitialized data. + +@item -fleading-underscore +Add a leading underscore at the beginning of each C symbol. + +@end table + +Warning options: + +@table @option +@item -w +Disable all warnings. + +@end table + +Note: each of the following warning options has a negative form beginning with +@option{-Wno-}. + +@table @option +@item -Wimplicit-function-declaration +Warn about implicit function declaration. + +@item -Wunsupported +Warn about unsupported GCC features that are ignored by TCC. + +@item -Wwrite-strings +Make string constants be of type @code{const char *} instead of @code{char +*}. + +@item -Werror +Abort compilation if warnings are issued. + +@item -Wall +Activate all warnings, except @option{-Werror}, @option{-Wunusupported} and +@option{-Wwrite-strings}. + +@end table + +Linker options: + +@table @option +@item -Ldir +Specify an additional static library path for the @option{-l} option. The +default library paths are @file{/usr/local/lib}, @file{/usr/lib} and @file{/lib}. + +@item -lxxx +Link your program with dynamic library libxxx.so or static library +libxxx.a. The library is searched in the paths specified by the +@option{-L} option. + +@item -shared +Generate a shared library instead of an executable (@option{-o} option +must also be given). + +@item -static +Generate a statically linked executable (default is a shared linked +executable) (@option{-o} option must also be given). + +@item -rdynamic +Export global symbols to the dynamic linker. It is useful when a library +opened with @code{dlopen()} needs to access executable symbols. + +@item -r +Generate an object file combining all input files (@option{-o} option must +also be given). + +@item -Wl,-Ttext,address +Set the start of the .text section to @var{address}. + +@item -Wl,--oformat,fmt +Use @var{fmt} as output format. The supported output formats are: +@table @code +@item elf32-i386 +ELF output format (default) +@item binary +Binary image (only for executable output) +@item coff +COFF output format (only for executable output for TMS320C67xx target) +@end table + +@end table + +Debugger options: + +@table @option +@item -g +Generate run time debug information so that you get clear run time +error messages: @code{ test.c:68: in function 'test5()': dereferencing +invalid pointer} instead of the laconic @code{Segmentation +fault}. + +@item -b +Generate additional support code to check +memory allocations and array/pointer bounds. @option{-g} is implied. Note +that the generated code is slower and bigger in this case. + +@item -bt N +Display N callers in stack traces. This is useful with @option{-g} or +@option{-b}. + +@end table + +Note: GCC options @option{-Ox}, @option{-fx} and @option{-mx} are +ignored. +@c man end + +@ignore + +@setfilename tcc +@settitle Tiny C Compiler + +@c man begin SEEALSO +gcc(1) +@c man end + +@c man begin AUTHOR +Fabrice Bellard +@c man end + +@end ignore + +@node Clang +@chapter C language support + +@section ANSI C + +TCC implements all the ANSI C standard, including structure bit fields +and floating point numbers (@code{long double}, @code{double}, and +@code{float} fully supported). + +@section ISOC99 extensions + +TCC implements many features of the new C standard: ISO C99. Currently +missing items are: complex and imaginary numbers and variable length +arrays. + +Currently implemented ISOC99 features: + +@itemize + +@item 64 bit @code{long long} types are fully supported. + +@item The boolean type @code{_Bool} is supported. + +@item @code{__func__} is a string variable containing the current +function name. + +@item Variadic macros: @code{__VA_ARGS__} can be used for + function-like macros: +@example + #define dprintf(level, __VA_ARGS__) printf(__VA_ARGS__) +@end example + +@noindent +@code{dprintf} can then be used with a variable number of parameters. + +@item Declarations can appear anywhere in a block (as in C++). + +@item Array and struct/union elements can be initialized in any order by + using designators: +@example + struct @{ int x, y; @} st[10] = @{ [0].x = 1, [0].y = 2 @}; + + int tab[10] = @{ 1, 2, [5] = 5, [9] = 9@}; +@end example + +@item Compound initializers are supported: +@example + int *p = (int [])@{ 1, 2, 3 @}; +@end example +to initialize a pointer pointing to an initialized array. The same +works for structures and strings. + +@item Hexadecimal floating point constants are supported: +@example + double d = 0x1234p10; +@end example + +@noindent +is the same as writing +@example + double d = 4771840.0; +@end example + +@item @code{inline} keyword is ignored. + +@item @code{restrict} keyword is ignored. +@end itemize + +@section GNU C extensions + +TCC implements some GNU C extensions: + +@itemize + +@item array designators can be used without '=': +@example + int a[10] = @{ [0] 1, [5] 2, 3, 4 @}; +@end example + +@item Structure field designators can be a label: +@example + struct @{ int x, y; @} st = @{ x: 1, y: 1@}; +@end example +instead of +@example + struct @{ int x, y; @} st = @{ .x = 1, .y = 1@}; +@end example + +@item @code{\e} is ASCII character 27. + +@item case ranges : ranges can be used in @code{case}s: +@example + switch(a) @{ + case 1 @dots{} 9: + printf("range 1 to 9\n"); + break; + default: + printf("unexpected\n"); + break; + @} +@end example + +@cindex aligned attribute +@cindex packed attribute +@cindex section attribute +@cindex unused attribute +@cindex cdecl attribute +@cindex stdcall attribute +@cindex regparm attribute +@cindex dllexport attribute + +@item The keyword @code{__attribute__} is handled to specify variable or +function attributes. The following attributes are supported: + @itemize + + @item @code{aligned(n)}: align a variable or a structure field to n bytes +(must be a power of two). + + @item @code{packed}: force alignment of a variable or a structure field to + 1. + + @item @code{section(name)}: generate function or data in assembly section +name (name is a string containing the section name) instead of the default +section. + + @item @code{unused}: specify that the variable or the function is unused. + + @item @code{cdecl}: use standard C calling convention (default). + + @item @code{stdcall}: use Pascal-like calling convention. + + @item @code{regparm(n)}: use fast i386 calling convention. @var{n} must be +between 1 and 3. The first @var{n} function parameters are respectively put in +registers @code{%eax}, @code{%edx} and @code{%ecx}. + + @item @code{dllexport}: export function from dll/executable (win32 only) + + @end itemize + +Here are some examples: +@example + int a __attribute__ ((aligned(8), section(".mysection"))); +@end example + +@noindent +align variable @code{a} to 8 bytes and put it in section @code{.mysection}. + +@example + int my_add(int a, int b) __attribute__ ((section(".mycodesection"))) + @{ + return a + b; + @} +@end example + +@noindent +generate function @code{my_add} in section @code{.mycodesection}. + +@item GNU style variadic macros: +@example + #define dprintf(fmt, args@dots{}) printf(fmt, ## args) + + dprintf("no arg\n"); + dprintf("one arg %d\n", 1); +@end example + +@item @code{__FUNCTION__} is interpreted as C99 @code{__func__} +(so it has not exactly the same semantics as string literal GNUC +where it is a string literal). + +@item The @code{__alignof__} keyword can be used as @code{sizeof} +to get the alignment of a type or an expression. + +@item The @code{typeof(x)} returns the type of @code{x}. +@code{x} is an expression or a type. + +@item Computed gotos: @code{&&label} returns a pointer of type +@code{void *} on the goto label @code{label}. @code{goto *expr} can be +used to jump on the pointer resulting from @code{expr}. + +@item Inline assembly with asm instruction: +@cindex inline assembly +@cindex assembly, inline +@cindex __asm__ +@example +static inline void * my_memcpy(void * to, const void * from, size_t n) +@{ +int d0, d1, d2; +__asm__ __volatile__( + "rep ; movsl\n\t" + "testb $2,%b4\n\t" + "je 1f\n\t" + "movsw\n" + "1:\ttestb $1,%b4\n\t" + "je 2f\n\t" + "movsb\n" + "2:" + : "=&c" (d0), "=&D" (d1), "=&S" (d2) + :"0" (n/4), "q" (n),"1" ((long) to),"2" ((long) from) + : "memory"); +return (to); +@} +@end example + +@noindent +@cindex gas +TCC includes its own x86 inline assembler with a @code{gas}-like (GNU +assembler) syntax. No intermediate files are generated. GCC 3.x named +operands are supported. + +@item @code{__builtin_types_compatible_p()} and @code{__builtin_constant_p()} +are supported. + +@item @code{#pragma pack} is supported for win32 compatibility. + +@end itemize + +@section TinyCC extensions + +@itemize + +@item @code{__TINYC__} is a predefined macro to @code{1} to +indicate that you use TCC. + +@item @code{#!} at the start of a line is ignored to allow scripting. + +@item Binary digits can be entered (@code{0b101} instead of +@code{5}). + +@item @code{__BOUNDS_CHECKING_ON} is defined if bound checking is activated. + +@end itemize + +@node asm +@chapter TinyCC Assembler + +Since version 0.9.16, TinyCC integrates its own assembler. TinyCC +assembler supports a gas-like syntax (GNU assembler). You can +desactivate assembler support if you want a smaller TinyCC executable +(the C compiler does not rely on the assembler). + +TinyCC Assembler is used to handle files with @file{.S} (C +preprocessed assembler) and @file{.s} extensions. It is also used to +handle the GNU inline assembler with the @code{asm} keyword. + +@section Syntax + +TinyCC Assembler supports most of the gas syntax. The tokens are the +same as C. + +@itemize + +@item C and C++ comments are supported. + +@item Identifiers are the same as C, so you cannot use '.' or '$'. + +@item Only 32 bit integer numbers are supported. + +@end itemize + +@section Expressions + +@itemize + +@item Integers in decimal, octal and hexa are supported. + +@item Unary operators: +, -, ~. + +@item Binary operators in decreasing priority order: + +@enumerate +@item *, /, % +@item &, |, ^ +@item +, - +@end enumerate + +@item A value is either an absolute number or a label plus an offset. +All operators accept absolute values except '+' and '-'. '+' or '-' can be +used to add an offset to a label. '-' supports two labels only if they +are the same or if they are both defined and in the same section. + +@end itemize + +@section Labels + +@itemize + +@item All labels are considered as local, except undefined ones. + +@item Numeric labels can be used as local @code{gas}-like labels. +They can be defined several times in the same source. Use 'b' +(backward) or 'f' (forward) as suffix to reference them: + +@example + 1: + jmp 1b /* jump to '1' label before */ + jmp 1f /* jump to '1' label after */ + 1: +@end example + +@end itemize + +@section Directives +@cindex assembler directives +@cindex directives, assembler +@cindex align directive +@cindex skip directive +@cindex space directive +@cindex byte directive +@cindex word directive +@cindex short directive +@cindex int directive +@cindex long directive +@cindex quad directive +@cindex globl directive +@cindex global directive +@cindex section directive +@cindex text directive +@cindex data directive +@cindex bss directive +@cindex fill directive +@cindex org directive +@cindex previous directive +@cindex string directive +@cindex asciz directive +@cindex ascii directive + +All directives are preceeded by a '.'. The following directives are +supported: + +@itemize +@item .align n[,value] +@item .skip n[,value] +@item .space n[,value] +@item .byte value1[,...] +@item .word value1[,...] +@item .short value1[,...] +@item .int value1[,...] +@item .long value1[,...] +@item .quad immediate_value1[,...] +@item .globl symbol +@item .global symbol +@item .section section +@item .text +@item .data +@item .bss +@item .fill repeat[,size[,value]] +@item .org n +@item .previous +@item .string string[,...] +@item .asciz string[,...] +@item .ascii string[,...] +@end itemize + +@section X86 Assembler +@cindex assembler + +All X86 opcodes are supported. Only ATT syntax is supported (source +then destination operand order). If no size suffix is given, TinyCC +tries to guess it from the operand sizes. + +Currently, MMX opcodes are supported but not SSE ones. + +@node linker +@chapter TinyCC Linker +@cindex linker + +@section ELF file generation +@cindex ELF + +TCC can directly output relocatable ELF files (object files), +executable ELF files and dynamic ELF libraries without relying on an +external linker. + +Dynamic ELF libraries can be output but the C compiler does not generate +position independent code (PIC). It means that the dynamic library +code generated by TCC cannot be factorized among processes yet. + +TCC linker eliminates unreferenced object code in libraries. A single pass is +done on the object and library list, so the order in which object files and +libraries are specified is important (same constraint as GNU ld). No grouping +options (@option{--start-group} and @option{--end-group}) are supported. + +@section ELF file loader + +TCC can load ELF object files, archives (.a files) and dynamic +libraries (.so). + +@section PE-i386 file generation +@cindex PE-i386 + +TCC for Windows supports the native Win32 executable file format (PE-i386). It +generates EXE files (console and gui) and DLL files. + +For usage on Windows, see also tcc-win32.txt. + +@section GNU Linker Scripts +@cindex scripts, linker +@cindex linker scripts +@cindex GROUP, linker command +@cindex FILE, linker command +@cindex OUTPUT_FORMAT, linker command +@cindex TARGET, linker command + +Because on many Linux systems some dynamic libraries (such as +@file{/usr/lib/libc.so}) are in fact GNU ld link scripts (horrible!), +the TCC linker also supports a subset of GNU ld scripts. + +The @code{GROUP} and @code{FILE} commands are supported. @code{OUTPUT_FORMAT} +and @code{TARGET} are ignored. + +Example from @file{/usr/lib/libc.so}: +@example +/* GNU ld script + Use the shared library, but some functions are only in + the static library, so try that secondarily. */ +GROUP ( /lib/libc.so.6 /usr/lib/libc_nonshared.a ) +@end example + +@node Bounds +@chapter TinyCC Memory and Bound checks +@cindex bound checks +@cindex memory checks + +This feature is activated with the @option{-b} (@pxref{Invoke}). + +Note that pointer size is @emph{unchanged} and that code generated +with bound checks is @emph{fully compatible} with unchecked +code. When a pointer comes from unchecked code, it is assumed to be +valid. Even very obscure C code with casts should work correctly. + +For more information about the ideas behind this method, see +@url{http://www.doc.ic.ac.uk/~phjk/BoundsChecking.html}. + +Here are some examples of caught errors: + +@table @asis + +@item Invalid range with standard string function: +@example +@{ + char tab[10]; + memset(tab, 0, 11); +@} +@end example + +@item Out of bounds-error in global or local arrays: +@example +@{ + int tab[10]; + for(i=0;i<11;i++) @{ + sum += tab[i]; + @} +@} +@end example + +@item Out of bounds-error in malloc'ed data: +@example +@{ + int *tab; + tab = malloc(20 * sizeof(int)); + for(i=0;i<21;i++) @{ + sum += tab4[i]; + @} + free(tab); +@} +@end example + +@item Access of freed memory: +@example +@{ + int *tab; + tab = malloc(20 * sizeof(int)); + free(tab); + for(i=0;i<20;i++) @{ + sum += tab4[i]; + @} +@} +@end example + +@item Double free: +@example +@{ + int *tab; + tab = malloc(20 * sizeof(int)); + free(tab); + free(tab); +@} +@end example + +@end table + +@node Libtcc +@chapter The @code{libtcc} library + +The @code{libtcc} library enables you to use TCC as a backend for +dynamic code generation. + +Read the @file{libtcc.h} to have an overview of the API. Read +@file{libtcc_test.c} to have a very simple example. + +The idea consists in giving a C string containing the program you want +to compile directly to @code{libtcc}. Then you can access to any global +symbol (function or variable) defined. + +@node devel +@chapter Developer's guide + +This chapter gives some hints to understand how TCC works. You can skip +it if you do not intend to modify the TCC code. + +@section File reading + +The @code{BufferedFile} structure contains the context needed to read a +file, including the current line number. @code{tcc_open()} opens a new +file and @code{tcc_close()} closes it. @code{inp()} returns the next +character. + +@section Lexer + +@code{next()} reads the next token in the current +file. @code{next_nomacro()} reads the next token without macro +expansion. + +@code{tok} contains the current token (see @code{TOK_xxx}) +constants. Identifiers and keywords are also keywords. @code{tokc} +contains additional infos about the token (for example a constant value +if number or string token). + +@section Parser + +The parser is hardcoded (yacc is not necessary). It does only one pass, +except: + +@itemize + +@item For initialized arrays with unknown size, a first pass +is done to count the number of elements. + +@item For architectures where arguments are evaluated in +reverse order, a first pass is done to reverse the argument order. + +@end itemize + +@section Types + +The types are stored in a single 'int' variable. It was choosen in the +first stages of development when tcc was much simpler. Now, it may not +be the best solution. + +@example +#define VT_INT 0 /* integer type */ +#define VT_BYTE 1 /* signed byte type */ +#define VT_SHORT 2 /* short type */ +#define VT_VOID 3 /* void type */ +#define VT_PTR 4 /* pointer */ +#define VT_ENUM 5 /* enum definition */ +#define VT_FUNC 6 /* function type */ +#define VT_STRUCT 7 /* struct/union definition */ +#define VT_FLOAT 8 /* IEEE float */ +#define VT_DOUBLE 9 /* IEEE double */ +#define VT_LDOUBLE 10 /* IEEE long double */ +#define VT_BOOL 11 /* ISOC99 boolean type */ +#define VT_LLONG 12 /* 64 bit integer */ +#define VT_LONG 13 /* long integer (NEVER USED as type, only + during parsing) */ +#define VT_BTYPE 0x000f /* mask for basic type */ +#define VT_UNSIGNED 0x0010 /* unsigned type */ +#define VT_ARRAY 0x0020 /* array type (also has VT_PTR) */ +#define VT_BITFIELD 0x0040 /* bitfield modifier */ + +#define VT_STRUCT_SHIFT 16 /* structure/enum name shift (16 bits left) */ +@end example + +When a reference to another type is needed (for pointers, functions and +structures), the @code{32 - VT_STRUCT_SHIFT} high order bits are used to +store an identifier reference. + +The @code{VT_UNSIGNED} flag can be set for chars, shorts, ints and long +longs. + +Arrays are considered as pointers @code{VT_PTR} with the flag +@code{VT_ARRAY} set. + +The @code{VT_BITFIELD} flag can be set for chars, shorts, ints and long +longs. If it is set, then the bitfield position is stored from bits +VT_STRUCT_SHIFT to VT_STRUCT_SHIFT + 5 and the bit field size is stored +from bits VT_STRUCT_SHIFT + 6 to VT_STRUCT_SHIFT + 11. + +@code{VT_LONG} is never used except during parsing. + +During parsing, the storage of an object is also stored in the type +integer: + +@example +#define VT_EXTERN 0x00000080 /* extern definition */ +#define VT_STATIC 0x00000100 /* static variable */ +#define VT_TYPEDEF 0x00000200 /* typedef definition */ +@end example + +@section Symbols + +All symbols are stored in hashed symbol stacks. Each symbol stack +contains @code{Sym} structures. + +@code{Sym.v} contains the symbol name (remember +an idenfier is also a token, so a string is never necessary to store +it). @code{Sym.t} gives the type of the symbol. @code{Sym.r} is usually +the register in which the corresponding variable is stored. @code{Sym.c} is +usually a constant associated to the symbol. + +Four main symbol stacks are defined: + +@table @code + +@item define_stack +for the macros (@code{#define}s). + +@item global_stack +for the global variables, functions and types. + +@item local_stack +for the local variables, functions and types. + +@item global_label_stack +for the local labels (for @code{goto}). + +@item label_stack +for GCC block local labels (see the @code{__label__} keyword). + +@end table + +@code{sym_push()} is used to add a new symbol in the local symbol +stack. If no local symbol stack is active, it is added in the global +symbol stack. + +@code{sym_pop(st,b)} pops symbols from the symbol stack @var{st} until +the symbol @var{b} is on the top of stack. If @var{b} is NULL, the stack +is emptied. + +@code{sym_find(v)} return the symbol associated to the identifier +@var{v}. The local stack is searched first from top to bottom, then the +global stack. + +@section Sections + +The generated code and datas are written in sections. The structure +@code{Section} contains all the necessary information for a given +section. @code{new_section()} creates a new section. ELF file semantics +is assumed for each section. + +The following sections are predefined: + +@table @code + +@item text_section +is the section containing the generated code. @var{ind} contains the +current position in the code section. + +@item data_section +contains initialized data + +@item bss_section +contains uninitialized data + +@item bounds_section +@itemx lbounds_section +are used when bound checking is activated + +@item stab_section +@itemx stabstr_section +are used when debugging is actived to store debug information + +@item symtab_section +@itemx strtab_section +contain the exported symbols (currently only used for debugging). + +@end table + +@section Code generation +@cindex code generation + +@subsection Introduction + +The TCC code generator directly generates linked binary code in one +pass. It is rather unusual these days (see gcc for example which +generates text assembly), but it can be very fast and surprisingly +little complicated. + +The TCC code generator is register based. Optimization is only done at +the expression level. No intermediate representation of expression is +kept except the current values stored in the @emph{value stack}. + +On x86, three temporary registers are used. When more registers are +needed, one register is spilled into a new temporary variable on the stack. + +@subsection The value stack +@cindex value stack, introduction + +When an expression is parsed, its value is pushed on the value stack +(@var{vstack}). The top of the value stack is @var{vtop}. Each value +stack entry is the structure @code{SValue}. + +@code{SValue.t} is the type. @code{SValue.r} indicates how the value is +currently stored in the generated code. It is usually a CPU register +index (@code{REG_xxx} constants), but additional values and flags are +defined: + +@example +#define VT_CONST 0x00f0 +#define VT_LLOCAL 0x00f1 +#define VT_LOCAL 0x00f2 +#define VT_CMP 0x00f3 +#define VT_JMP 0x00f4 +#define VT_JMPI 0x00f5 +#define VT_LVAL 0x0100 +#define VT_SYM 0x0200 +#define VT_MUSTCAST 0x0400 +#define VT_MUSTBOUND 0x0800 +#define VT_BOUNDED 0x8000 +#define VT_LVAL_BYTE 0x1000 +#define VT_LVAL_SHORT 0x2000 +#define VT_LVAL_UNSIGNED 0x4000 +#define VT_LVAL_TYPE (VT_LVAL_BYTE | VT_LVAL_SHORT | VT_LVAL_UNSIGNED) +@end example + +@table @code + +@item VT_CONST +indicates that the value is a constant. It is stored in the union +@code{SValue.c}, depending on its type. + +@item VT_LOCAL +indicates a local variable pointer at offset @code{SValue.c.i} in the +stack. + +@item VT_CMP +indicates that the value is actually stored in the CPU flags (i.e. the +value is the consequence of a test). The value is either 0 or 1. The +actual CPU flags used is indicated in @code{SValue.c.i}. + +If any code is generated which destroys the CPU flags, this value MUST be +put in a normal register. + +@item VT_JMP +@itemx VT_JMPI +indicates that the value is the consequence of a conditional jump. For VT_JMP, +it is 1 if the jump is taken, 0 otherwise. For VT_JMPI it is inverted. + +These values are used to compile the @code{||} and @code{&&} logical +operators. + +If any code is generated, this value MUST be put in a normal +register. Otherwise, the generated code won't be executed if the jump is +taken. + +@item VT_LVAL +is a flag indicating that the value is actually an lvalue (left value of +an assignment). It means that the value stored is actually a pointer to +the wanted value. + +Understanding the use @code{VT_LVAL} is very important if you want to +understand how TCC works. + +@item VT_LVAL_BYTE +@itemx VT_LVAL_SHORT +@itemx VT_LVAL_UNSIGNED +if the lvalue has an integer type, then these flags give its real +type. The type alone is not enough in case of cast optimisations. + +@item VT_LLOCAL +is a saved lvalue on the stack. @code{VT_LLOCAL} should be eliminated +ASAP because its semantics are rather complicated. + +@item VT_MUSTCAST +indicates that a cast to the value type must be performed if the value +is used (lazy casting). + +@item VT_SYM +indicates that the symbol @code{SValue.sym} must be added to the constant. + +@item VT_MUSTBOUND +@itemx VT_BOUNDED +are only used for optional bound checking. + +@end table + +@subsection Manipulating the value stack +@cindex value stack + +@code{vsetc()} and @code{vset()} pushes a new value on the value +stack. If the previous @var{vtop} was stored in a very unsafe place(for +example in the CPU flags), then some code is generated to put the +previous @var{vtop} in a safe storage. + +@code{vpop()} pops @var{vtop}. In some cases, it also generates cleanup +code (for example if stacked floating point registers are used as on +x86). + +The @code{gv(rc)} function generates code to evaluate @var{vtop} (the +top value of the stack) into registers. @var{rc} selects in which +register class the value should be put. @code{gv()} is the @emph{most +important function} of the code generator. + +@code{gv2()} is the same as @code{gv()} but for the top two stack +entries. + +@subsection CPU dependent code generation +@cindex CPU dependent +See the @file{i386-gen.c} file to have an example. + +@table @code + +@item load() +must generate the code needed to load a stack value into a register. + +@item store() +must generate the code needed to store a register into a stack value +lvalue. + +@item gfunc_start() +@itemx gfunc_param() +@itemx gfunc_call() +should generate a function call + +@item gfunc_prolog() +@itemx gfunc_epilog() +should generate a function prolog/epilog. + +@item gen_opi(op) +must generate the binary integer operation @var{op} on the two top +entries of the stack which are guaranted to contain integer types. + +The result value should be put on the stack. + +@item gen_opf(op) +same as @code{gen_opi()} for floating point operations. The two top +entries of the stack are guaranted to contain floating point values of +same types. + +@item gen_cvt_itof() +integer to floating point conversion. + +@item gen_cvt_ftoi() +floating point to integer conversion. + +@item gen_cvt_ftof() +floating point to floating point of different size conversion. + +@item gen_bounded_ptr_add() +@item gen_bounded_ptr_deref() +are only used for bounds checking. + +@end table + +@section Optimizations done +@cindex optimizations +@cindex constant propagation +@cindex strength reduction +@cindex comparison operators +@cindex caching processor flags +@cindex flags, caching +@cindex jump optimization +Constant propagation is done for all operations. Multiplications and +divisions are optimized to shifts when appropriate. Comparison +operators are optimized by maintaining a special cache for the +processor flags. &&, || and ! are optimized by maintaining a special +'jump target' value. No other jump optimization is currently performed +because it would require to store the code in a more abstract fashion. + +@unnumbered Concept Index +@printindex cp + +@bye + +@c Local variables: +@c fill-column: 78 +@c texinfo-column-for-description: 32 +@c End: diff --git a/05/tcc-0.9.25/tcc.c b/05/tcc-0.9.25/tcc.c new file mode 100644 index 0000000..28c59a3 --- /dev/null +++ b/05/tcc-0.9.25/tcc.c @@ -0,0 +1,556 @@ +/* + * TCC - Tiny C Compiler + * + * Copyright (c) 2001-2004 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "libtcc.c" + +void help(void) +{ + printf("tcc version " TCC_VERSION " - Tiny C Compiler - Copyright (C) 2001-2006 Fabrice Bellard\n" + "usage: tcc [-v] [-c] [-o outfile] [-Bdir] [-bench] [-Idir] [-Dsym[=val]] [-Usym]\n" + " [-Wwarn] [-g] [-b] [-bt N] [-Ldir] [-llib] [-shared] [-soname name]\n" + " [-static] [infile1 infile2...] [-run infile args...]\n" + "\n" + "General options:\n" + " -v display current version, increase verbosity\n" + " -c compile only - generate an object file\n" + " -o outfile set output filename\n" + " -Bdir set tcc internal library path\n" + " -bench output compilation statistics\n" + " -run run compiled source\n" + " -fflag set or reset (with 'no-' prefix) 'flag' (see man page)\n" + " -Wwarning set or reset (with 'no-' prefix) 'warning' (see man page)\n" + " -w disable all warnings\n" + "Preprocessor options:\n" + " -E preprocess only\n" + " -Idir add include path 'dir'\n" + " -Dsym[=val] define 'sym' with value 'val'\n" + " -Usym undefine 'sym'\n" + "Linker options:\n" + " -Ldir add library path 'dir'\n" + " -llib link with dynamic or static library 'lib'\n" + " -shared generate a shared library\n" + " -soname set name for shared library to be used at runtime\n" + " -static static linking\n" + " -rdynamic export all global symbols to dynamic linker\n" + " -r generate (relocatable) object file\n" + "Debugger options:\n" + " -g generate runtime debug info\n" +#ifdef CONFIG_TCC_BCHECK + " -b compile with built-in memory and bounds checker (implies -g)\n" +#endif +#ifdef CONFIG_TCC_BACKTRACE + " -bt N show N callers in stack traces\n" +#endif + ); +} + +static char **files; +static int nb_files, nb_libraries; +static int multiple_files; +static int print_search_dirs; +static int output_type; +static int reloc_output; +static const char *outfile; +static int do_bench = 0; + +#define TCC_OPTION_HAS_ARG 0x0001 +#define TCC_OPTION_NOSEP 0x0002 /* cannot have space before option and arg */ + +typedef struct TCCOption { + const char *name; + uint16_t index; + uint16_t flags; +} TCCOption; + +enum { + TCC_OPTION_HELP, + TCC_OPTION_I, + TCC_OPTION_D, + TCC_OPTION_U, + TCC_OPTION_L, + TCC_OPTION_B, + TCC_OPTION_l, + TCC_OPTION_bench, + TCC_OPTION_bt, + TCC_OPTION_b, + TCC_OPTION_g, + TCC_OPTION_c, + TCC_OPTION_static, + TCC_OPTION_shared, + TCC_OPTION_soname, + TCC_OPTION_o, + TCC_OPTION_r, + TCC_OPTION_Wl, + TCC_OPTION_W, + TCC_OPTION_O, + TCC_OPTION_m, + TCC_OPTION_f, + TCC_OPTION_nostdinc, + TCC_OPTION_nostdlib, + TCC_OPTION_print_search_dirs, + TCC_OPTION_rdynamic, + TCC_OPTION_run, + TCC_OPTION_v, + TCC_OPTION_w, + TCC_OPTION_pipe, + TCC_OPTION_E, +}; + +static const TCCOption tcc_options[] = { + { "h", TCC_OPTION_HELP, 0 }, + { "?", TCC_OPTION_HELP, 0 }, + { "I", TCC_OPTION_I, TCC_OPTION_HAS_ARG }, + { "D", TCC_OPTION_D, TCC_OPTION_HAS_ARG }, + { "U", TCC_OPTION_U, TCC_OPTION_HAS_ARG }, + { "L", TCC_OPTION_L, TCC_OPTION_HAS_ARG }, + { "B", TCC_OPTION_B, TCC_OPTION_HAS_ARG }, + { "l", TCC_OPTION_l, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "bench", TCC_OPTION_bench, 0 }, + { "bt", TCC_OPTION_bt, TCC_OPTION_HAS_ARG }, +#ifdef CONFIG_TCC_BCHECK + { "b", TCC_OPTION_b, 0 }, +#endif + { "g", TCC_OPTION_g, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "c", TCC_OPTION_c, 0 }, + { "static", TCC_OPTION_static, 0 }, + { "shared", TCC_OPTION_shared, 0 }, + { "soname", TCC_OPTION_soname, TCC_OPTION_HAS_ARG }, + { "o", TCC_OPTION_o, TCC_OPTION_HAS_ARG }, + { "run", TCC_OPTION_run, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "rdynamic", TCC_OPTION_rdynamic, 0 }, + { "r", TCC_OPTION_r, 0 }, + { "Wl,", TCC_OPTION_Wl, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "W", TCC_OPTION_W, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "O", TCC_OPTION_O, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "m", TCC_OPTION_m, TCC_OPTION_HAS_ARG }, + { "f", TCC_OPTION_f, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "nostdinc", TCC_OPTION_nostdinc, 0 }, + { "nostdlib", TCC_OPTION_nostdlib, 0 }, + { "print-search-dirs", TCC_OPTION_print_search_dirs, 0 }, + { "v", TCC_OPTION_v, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "w", TCC_OPTION_w, 0 }, + { "pipe", TCC_OPTION_pipe, 0}, + { "E", TCC_OPTION_E, 0}, + { NULL } +}; + +static int64_t getclock_us(void) +{ +#ifdef _WIN32 + struct _timeb tb; + _ftime(&tb); + return (tb.time * 1000LL + tb.millitm) * 1000LL; +#else + struct timeval tv; + gettimeofday(&tv, NULL); + return tv.tv_sec * 1000000LL + tv.tv_usec; +#endif +} + +static int strstart(const char *str, const char *val, const char **ptr) +{ + const char *p, *q; + p = str; + q = val; + while (*q != '\0') { + if (*p != *q) + return 0; + p++; + q++; + } + if (ptr) + *ptr = p; + return 1; +} + +/* convert 'str' into an array of space separated strings */ +static int expand_args(char ***pargv, const char *str) +{ + const char *s1; + char **argv, *arg; + int argc, len; + + argc = 0; + argv = NULL; + for(;;) { + while (is_space(*str)) + str++; + if (*str == '\0') + break; + s1 = str; + while (*str != '\0' && !is_space(*str)) + str++; + len = str - s1; + arg = tcc_malloc(len + 1); + memcpy(arg, s1, len); + arg[len] = '\0'; + dynarray_add((void ***)&argv, &argc, arg); + } + *pargv = argv; + return argc; +} + +int parse_args(TCCState *s, int argc, char **argv) +{ + int optind; + const TCCOption *popt; + const char *optarg, *p1, *r1; + char *r; + + optind = 0; + while (optind < argc) { + + r = argv[optind++]; + if (r[0] != '-' || r[1] == '\0') { + /* add a new file */ + dynarray_add((void ***)&files, &nb_files, r); + if (!multiple_files) { + optind--; + /* argv[0] will be this file */ + break; + } + } else { + /* find option in table (match only the first chars */ + popt = tcc_options; + for(;;) { + p1 = popt->name; + if (p1 == NULL) + error("invalid option -- '%s'", r); + r1 = r + 1; + for(;;) { + if (*p1 == '\0') + goto option_found; + if (*r1 != *p1) + break; + p1++; + r1++; + } + popt++; + } + option_found: + if (popt->flags & TCC_OPTION_HAS_ARG) { + if (*r1 != '\0' || (popt->flags & TCC_OPTION_NOSEP)) { + optarg = r1; + } else { + if (optind >= argc) + error("argument to '%s' is missing", r); + optarg = argv[optind++]; + } + } else { + if (*r1 != '\0') + return 0; + optarg = NULL; + } + + switch(popt->index) { + case TCC_OPTION_HELP: + return 0; + + case TCC_OPTION_I: + if (tcc_add_include_path(s, optarg) < 0) + error("too many include paths"); + break; + case TCC_OPTION_D: + { + char *sym, *value; + sym = (char *)optarg; + value = strchr(sym, '='); + if (value) { + *value = '\0'; + value++; + } + tcc_define_symbol(s, sym, value); + } + break; + case TCC_OPTION_U: + tcc_undefine_symbol(s, optarg); + break; + case TCC_OPTION_L: + tcc_add_library_path(s, optarg); + break; + case TCC_OPTION_B: + /* set tcc utilities path (mainly for tcc development) */ + tcc_set_lib_path(s, optarg); + break; + case TCC_OPTION_l: + dynarray_add((void ***)&files, &nb_files, r); + nb_libraries++; + break; + case TCC_OPTION_bench: + do_bench = 1; + break; +#ifdef CONFIG_TCC_BACKTRACE + case TCC_OPTION_bt: + num_callers = atoi(optarg); + break; +#endif +#ifdef CONFIG_TCC_BCHECK + case TCC_OPTION_b: + s->do_bounds_check = 1; + s->do_debug = 1; + break; +#endif + case TCC_OPTION_g: + s->do_debug = 1; + break; + case TCC_OPTION_c: + multiple_files = 1; + output_type = TCC_OUTPUT_OBJ; + break; + case TCC_OPTION_static: + s->static_link = 1; + break; + case TCC_OPTION_shared: + output_type = TCC_OUTPUT_DLL; + break; + case TCC_OPTION_soname: + s->soname = optarg; + break; + case TCC_OPTION_o: + multiple_files = 1; + outfile = optarg; + break; + case TCC_OPTION_r: + /* generate a .o merging several output files */ + reloc_output = 1; + output_type = TCC_OUTPUT_OBJ; + break; + case TCC_OPTION_nostdinc: + s->nostdinc = 1; + break; + case TCC_OPTION_nostdlib: + s->nostdlib = 1; + break; + case TCC_OPTION_print_search_dirs: + print_search_dirs = 1; + break; + case TCC_OPTION_run: + { + int argc1; + char **argv1; + argc1 = expand_args(&argv1, optarg); + if (argc1 > 0) { + parse_args(s, argc1, argv1); + } + multiple_files = 0; + output_type = TCC_OUTPUT_MEMORY; + } + break; + case TCC_OPTION_v: + do { + if (0 == s->verbose++) + printf("tcc version %s\n", TCC_VERSION); + } while (*optarg++ == 'v'); + break; + case TCC_OPTION_f: + if (tcc_set_flag(s, optarg, 1) < 0 && s->warn_unsupported) + goto unsupported_option; + break; + case TCC_OPTION_W: + if (tcc_set_warning(s, optarg, 1) < 0 && + s->warn_unsupported) + goto unsupported_option; + break; + case TCC_OPTION_w: + s->warn_none = 1; + break; + case TCC_OPTION_rdynamic: + s->rdynamic = 1; + break; + case TCC_OPTION_Wl: + { + const char *p; + if (strstart(optarg, "-Ttext,", &p)) { + s->text_addr = strtoul(p, NULL, 16); + s->has_text_addr = 1; + } else if (strstart(optarg, "--oformat,", &p)) { + if (strstart(p, "elf32-", NULL)) { + s->output_format = TCC_OUTPUT_FORMAT_ELF; + } else if (!strcmp(p, "binary")) { + s->output_format = TCC_OUTPUT_FORMAT_BINARY; + } else +#ifdef TCC_TARGET_COFF + if (!strcmp(p, "coff")) { + s->output_format = TCC_OUTPUT_FORMAT_COFF; + } else +#endif + { + error("target %s not found", p); + } + } else { + error("unsupported linker option '%s'", optarg); + } + } + break; + case TCC_OPTION_E: + output_type = TCC_OUTPUT_PREPROCESS; + break; + default: + if (s->warn_unsupported) { + unsupported_option: + warning("unsupported option '%s'", r); + } + break; + } + } + } + return optind + 1; +} + +int main(int argc, char **argv) +{ + int i; + TCCState *s; + int nb_objfiles, ret, optind; + char objfilename[1024]; + int64_t start_time = 0; + _init_tcc_syms(); + _init_warning_defs(); + _init_flag_defs(); + + s = tcc_new(); +#ifdef _WIN32 + tcc_set_lib_path_w32(s); +#endif + output_type = TCC_OUTPUT_EXE; + outfile = NULL; + multiple_files = 1; + files = NULL; + nb_files = 0; + nb_libraries = 0; + reloc_output = 0; + print_search_dirs = 0; + ret = 0; + + optind = parse_args(s, argc - 1, argv + 1); + if (print_search_dirs) { + /* enough for Linux kernel */ + printf("install: %s/\n", s->tcc_lib_path); + return 0; + } + if (optind == 0 || nb_files == 0) { + if (optind && s->verbose) + return 0; + help(); + return 1; + } + + nb_objfiles = nb_files - nb_libraries; + + /* if outfile provided without other options, we output an + executable */ + if (outfile && output_type == TCC_OUTPUT_MEMORY) + output_type = TCC_OUTPUT_EXE; + + /* check -c consistency : only single file handled. XXX: checks file type */ + if (output_type == TCC_OUTPUT_OBJ && !reloc_output) { + /* accepts only a single input file */ + if (nb_objfiles != 1) + error("cannot specify multiple files with -c"); + if (nb_libraries != 0) + error("cannot specify libraries with -c"); + } + + + if (output_type == TCC_OUTPUT_PREPROCESS) { + if (!outfile) { + s->outfile = stdout; + } else { + s->outfile = fopen(outfile, "w"); + if (!s->outfile) + error("could not open '%s", outfile); + } + } else if (output_type != TCC_OUTPUT_MEMORY) { + if (!outfile) { + /* compute default outfile name */ + char *ext; + const char *name = + strcmp(files[0], "-") == 0 ? "a" : tcc_basename(files[0]); + pstrcpy(objfilename, sizeof(objfilename), name); + ext = tcc_fileextension(objfilename); +#ifdef TCC_TARGET_PE + if (output_type == TCC_OUTPUT_DLL) + strcpy(ext, ".dll"); + else + if (output_type == TCC_OUTPUT_EXE) + strcpy(ext, ".exe"); + else +#endif + if (output_type == TCC_OUTPUT_OBJ && !reloc_output && *ext) + strcpy(ext, ".o"); + else + pstrcpy(objfilename, sizeof(objfilename), "a.out"); + outfile = objfilename; + } + } + + if (do_bench) { + start_time = getclock_us(); + } + + tcc_set_output_type(s, output_type); + + /* compile or add each files or library */ + for(i = 0; i < nb_files && ret == 0; i++) { + const char *filename; + + filename = files[i]; + if (filename[0] == '-' && filename[1]) { + if (tcc_add_library(s, filename + 2) < 0) { + error_noabort("cannot find %s", filename); + ret = 1; + } + } else { + if (1 == s->verbose) + printf("-> %s\n", filename); + if (tcc_add_file(s, filename) < 0) + ret = 1; + } + } + + /* free all files */ + tcc_free(files); + + if (ret) + goto the_end; + + if (do_bench) + tcc_print_stats(s, getclock_us() - start_time); + + if (s->output_type == TCC_OUTPUT_PREPROCESS) { + if (outfile) + fclose(s->outfile); + } else if (s->output_type == TCC_OUTPUT_MEMORY) { + ret = tcc_run(s, argc - optind, argv + optind); + } else + ret = tcc_output_file(s, outfile) ? 1 : 0; + the_end: + /* XXX: cannot do it with bound checking because of the malloc hooks */ + if (!s->do_bounds_check) + tcc_delete(s); + +#ifdef MEM_DEBUG + if (do_bench) { + printf("memory: %d bytes, max = %d bytes\n", mem_cur_size, mem_max_size); + } +#endif + return ret; +} + diff --git a/05/tcc-0.9.25/tcc.h b/05/tcc-0.9.25/tcc.h new file mode 100644 index 0000000..4070249 --- /dev/null +++ b/05/tcc-0.9.25/tcc.h @@ -0,0 +1,757 @@ +/* + * TCC - Tiny C Compiler + * + * Copyright (c) 2001-2004 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE +#include "config.h" + +#ifdef CONFIG_TCCBOOT + +#include "tccboot.h" +#define CONFIG_TCC_STATIC + +#else + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#include /* open, close etc. */ +#include /* getcwd */ +#define inline __inline +#define inp next_inp +#endif + +#endif /* !CONFIG_TCCBOOT */ + +#ifndef PAGESIZE +#define PAGESIZE 4096 +#endif + +#include "elf.h" +#include "stab.h" + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#include "libtcc.h" + +/* parser debug */ +//#define PARSE_DEBUG +/* preprocessor debug */ +//#define PP_DEBUG +/* include file debug */ +//#define INC_DEBUG + +//#define MEM_DEBUG + +/* assembler debug */ +//#define ASM_DEBUG + +/* target selection */ +//#define TCC_TARGET_I386 /* i386 code generator */ +//#define TCC_TARGET_ARM /* ARMv4 code generator */ +//#define TCC_TARGET_C67 /* TMS320C67xx code generator */ +//#define TCC_TARGET_X86_64 /* x86-64 code generator */ + +/* default target is I386 */ +#if !defined(TCC_TARGET_I386) && !defined(TCC_TARGET_ARM) && \ + !defined(TCC_TARGET_C67) && !defined(TCC_TARGET_X86_64) +#define TCC_TARGET_I386 +#endif + +#if !defined(_WIN32) && !defined(TCC_UCLIBC) && !defined(TCC_TARGET_ARM) && \ + !defined(TCC_TARGET_C67) && !defined(TCC_TARGET_X86_64) +#define CONFIG_TCC_BCHECK /* enable bound checking code */ +#endif + +#if defined(_WIN32) && !defined(TCC_TARGET_PE) +#define CONFIG_TCC_STATIC +#endif + +/* define it to include assembler support */ +#if !defined(TCC_TARGET_ARM) && !defined(TCC_TARGET_C67) && \ + !defined(TCC_TARGET_X86_64) +#define CONFIG_TCC_ASM +#endif + +/* object format selection */ +#if defined(TCC_TARGET_C67) +#define TCC_TARGET_COFF +#endif + +#if !defined(_WIN32) && !defined(CONFIG_TCCBOOT) +//#define CONFIG_TCC_BACKTRACE// this uses sigaction() which we don't have +#endif + +#define FALSE 0 +#define false 0 +#define TRUE 1 +#define true 1 +typedef int BOOL; + +/* path to find crt1.o, crti.o and crtn.o. Only needed when generating + executables or dlls */ +#ifndef CONFIG_TCC_CRT_PREFIX +#define CONFIG_TCC_CRT_PREFIX CONFIG_SYSROOT "/usr/lib" +#endif + +#define INCLUDE_STACK_SIZE 32 +#define IFDEF_STACK_SIZE 64 +#define VSTACK_SIZE 256 +#define STRING_MAX_SIZE 1024 +#define PACK_STACK_SIZE 8 + +#define TOK_HASH_SIZE 8192 /* must be a power of two */ +#define TOK_ALLOC_INCR 512 /* must be a power of two */ +#define TOK_MAX_SIZE 4 /* token max size in int unit when stored in string */ + +/* token symbol management */ +typedef struct TokenSym { + struct TokenSym *hash_next; + struct Sym *sym_define; /* direct pointer to define */ + struct Sym *sym_label; /* direct pointer to label */ + struct Sym *sym_struct; /* direct pointer to structure */ + struct Sym *sym_identifier; /* direct pointer to identifier */ + int tok; /* token number */ + int len; + char str[1]; +} TokenSym; + +#ifdef TCC_TARGET_PE +typedef unsigned short nwchar_t; +#else +typedef int nwchar_t; +#endif + +typedef struct CString { + int size; /* size in bytes */ + void *data; /* either 'char *' or 'nwchar_t *' */ + int size_allocated; + void *data_allocated; /* if non NULL, data has been malloced */ +} CString; + +/* type definition */ +typedef struct CType { + int t; + struct Sym *ref; +} CType; + +/* constant value */ +typedef union CValue { + long double ld; + double d; + float f; + int i; + unsigned int ui; + unsigned int ul; /* address (should be unsigned long on 64 bit cpu) */ + long long ll; + unsigned long long ull; + struct CString *cstr; + void *ptr; + int tab[1]; +} CValue; + +/* value on stack */ +typedef struct SValue { + CType type; /* type */ + unsigned short r; /* register + flags */ + unsigned short r2; /* second register, used for 'long long' + type. If not used, set to VT_CONST */ + CValue c; /* constant, if VT_CONST */ + struct Sym *sym; /* symbol, if (VT_SYM | VT_CONST) */ +} SValue; + +/* symbol management */ +typedef struct Sym { + int v; /* symbol token */ + long r; /* associated register */ + long c; /* associated number */ + CType type; /* associated type */ + struct Sym *next; /* next related symbol */ + struct Sym *prev; /* prev symbol in stack */ + struct Sym *prev_tok; /* previous symbol for this token */ +} Sym; + +/* section definition */ +/* XXX: use directly ELF structure for parameters ? */ +/* special flag to indicate that the section should not be linked to + the other ones */ +#define SHF_PRIVATE 0x80000000 + +/* special flag, too */ +#define SECTION_ABS ((void *)1) + +typedef struct Section { + unsigned long data_offset; /* current data offset */ + unsigned char *data; /* section data */ + unsigned long data_allocated; /* used for realloc() handling */ + int sh_name; /* elf section name (only used during output) */ + int sh_num; /* elf section number */ + int sh_type; /* elf section type */ + int sh_flags; /* elf section flags */ + int sh_info; /* elf section info */ + int sh_addralign; /* elf section alignment */ + int sh_entsize; /* elf entry size */ + unsigned long sh_size; /* section size (only used during output) */ + unsigned long sh_addr; /* address at which the section is relocated */ + unsigned long sh_offset; /* file offset */ + int nb_hashed_syms; /* used to resize the hash table */ + struct Section *link; /* link to another section */ + struct Section *reloc; /* corresponding section for relocation, if any */ + struct Section *hash; /* hash table for symbols */ + struct Section *next; + char name[1]; /* section name */ +} Section; + +typedef struct DLLReference { + int level; + void *handle; + char name[1]; +} DLLReference; + +/* GNUC attribute definition */ +typedef struct AttributeDef { + int aligned; + int packed; + Section *section; + int func_attr; /* calling convention, exports, ... */ +} AttributeDef; + +/* -------------------------------------------------- */ +/* gr: wrappers for casting sym->r for other purposes */ +typedef struct { + unsigned char func_call, func_args, func_export; // should be equivalent to the bitfields that were here before +} func_attr_t; + +#define FUNC_CALL(r) (((func_attr_t*)&(r))->func_call) +#define FUNC_EXPORT(r) (((func_attr_t*)&(r))->func_export) +#define FUNC_ARGS(r) (((func_attr_t*)&(r))->func_args) +#define INLINE_DEF(r) (*(int **)&(r)) +/* -------------------------------------------------- */ + +#define SYM_STRUCT 0x40000000 /* struct/union/enum symbol space */ +#define SYM_FIELD 0x20000000 /* struct/union field symbol space */ +#define SYM_FIRST_ANOM 0x10000000 /* first anonymous sym */ + +/* stored in 'Sym.c' field */ +#define FUNC_NEW 1 /* ansi function prototype */ +#define FUNC_OLD 2 /* old function prototype */ +#define FUNC_ELLIPSIS 3 /* ansi function prototype with ... */ + +/* stored in 'Sym.r' field */ +#define FUNC_CDECL 0 /* standard c call */ +#define FUNC_STDCALL 1 /* pascal c call */ +#define FUNC_FASTCALL1 2 /* first param in %eax */ +#define FUNC_FASTCALL2 3 /* first parameters in %eax, %edx */ +#define FUNC_FASTCALL3 4 /* first parameter in %eax, %edx, %ecx */ +#define FUNC_FASTCALLW 5 /* first parameter in %ecx, %edx */ + +/* field 'Sym.t' for macros */ +#define MACRO_OBJ 0 /* object like macro */ +#define MACRO_FUNC 1 /* function like macro */ + +/* field 'Sym.r' for C labels */ +#define LABEL_DEFINED 0 /* label is defined */ +#define LABEL_FORWARD 1 /* label is forward defined */ +#define LABEL_DECLARED 2 /* label is declared but never used */ + +/* type_decl() types */ +#define TYPE_ABSTRACT 1 /* type without variable */ +#define TYPE_DIRECT 2 /* type with variable */ + +#define IO_BUF_SIZE 8192 + +typedef struct BufferedFile { + uint8_t *buf_ptr; + uint8_t *buf_end; + int fd; + int line_num; /* current line number - here to simplify code */ + int ifndef_macro; /* #ifndef macro / #endif search */ + int ifndef_macro_saved; /* saved ifndef_macro */ + int *ifdef_stack_ptr; /* ifdef_stack value at the start of the file */ + char inc_type; /* type of include */ + char inc_filename[512]; /* filename specified by the user */ + char filename[1024]; /* current filename - here to simplify code */ + unsigned char buffer[IO_BUF_SIZE + 1]; /* extra size for CH_EOB char */ +} BufferedFile; + +#define CH_EOB '\\' /* end of buffer or '\0' char in file */ +#define CH_EOF (-1) /* end of file */ + +/* parsing state (used to save parser state to reparse part of the + source several times) */ +typedef struct ParseState { + int *macro_ptr; + int line_num; + int tok; + CValue tokc; +} ParseState; + +/* used to record tokens */ +typedef struct TokenString { + int *str; + int len; + int allocated_len; + int last_line_num; +} TokenString; + +/* include file cache, used to find files faster and also to eliminate + inclusion if the include file is protected by #ifndef ... #endif */ +typedef struct CachedInclude { + int ifndef_macro; + int hash_next; /* -1 if none */ + char type; /* '"' or '>' to give include type */ + char filename[1]; /* path specified in #include */ +} CachedInclude; + +#define CACHED_INCLUDES_HASH_SIZE 512 + +#ifdef CONFIG_TCC_ASM +typedef struct ExprValue { + uint32_t v; + Sym *sym; +} ExprValue; + +#define MAX_ASM_OPERANDS 30 +typedef struct ASMOperand { + int id; /* GCC 3 optionnal identifier (0 if number only supported */ + char *constraint; + char asm_str[16]; /* computed asm string for operand */ + SValue *vt; /* C value of the expression */ + int ref_index; /* if >= 0, gives reference to a output constraint */ + int input_index; /* if >= 0, gives reference to an input constraint */ + int priority; /* priority, used to assign registers */ + int reg; /* if >= 0, register number used for this operand */ + int is_llong; /* true if double register value */ + int is_memory; /* true if memory operand */ + int is_rw; /* for '+' modifier */ +} ASMOperand; + +#endif + +struct TCCState { + int output_type; + + BufferedFile **include_stack_ptr; + int *ifdef_stack_ptr; + + /* include file handling */ + char **include_paths; + int nb_include_paths; + char **sysinclude_paths; + int nb_sysinclude_paths; + CachedInclude **cached_includes; + int nb_cached_includes; + + char **library_paths; + int nb_library_paths; + + /* array of all loaded dlls (including those referenced by loaded + dlls) */ + DLLReference **loaded_dlls; + int nb_loaded_dlls; + + /* sections */ + Section **sections; + int nb_sections; /* number of sections, including first dummy section */ + + Section **priv_sections; + int nb_priv_sections; /* number of private sections */ + + /* got handling */ + Section *got; + Section *plt; + unsigned long *got_offsets; + int nb_got_offsets; + /* give the correspondance from symtab indexes to dynsym indexes */ + int *symtab_to_dynsym; + + /* temporary dynamic symbol sections (for dll loading) */ + Section *dynsymtab_section; + /* exported dynamic symbol section */ + Section *dynsym; + + int nostdinc; /* if true, no standard headers are added */ + int nostdlib; /* if true, no standard libraries are added */ + int nocommon; /* if true, do not use common symbols for .bss data */ + + /* if true, static linking is performed */ + int static_link; + + /* soname as specified on the command line (-soname) */ + const char *soname; + + /* if true, all symbols are exported */ + int rdynamic; + + /* if true, only link in referenced objects from archive */ + int alacarte_link; + + /* address of text section */ + unsigned long text_addr; + int has_text_addr; + + /* output format, see TCC_OUTPUT_FORMAT_xxx */ + int output_format; + + /* C language options */ + int char_is_unsigned; + int leading_underscore; + + /* warning switches */ + int warn_write_strings; + int warn_unsupported; + int warn_error; + int warn_none; + int warn_implicit_function_declaration; + + /* display some information during compilation */ + int verbose; + /* compile with debug symbol (and use them if error during execution) */ + int do_debug; + /* compile with built-in memory and bounds checker */ + int do_bounds_check; + /* give the path of the tcc libraries */ + const char *tcc_lib_path; + + /* error handling */ + void *error_opaque; + void (*error_func)(void *opaque, const char *msg); + int error_set_jmp_enabled; + jmp_buf error_jmp_buf; + int nb_errors; + + /* tiny assembler state */ + Sym *asm_labels; + + /* see include_stack_ptr */ + BufferedFile *include_stack[INCLUDE_STACK_SIZE]; + + /* see ifdef_stack_ptr */ + int ifdef_stack[IFDEF_STACK_SIZE]; + + /* see cached_includes */ + int cached_includes_hash[CACHED_INCLUDES_HASH_SIZE]; + + /* pack stack */ + int pack_stack[PACK_STACK_SIZE]; + int *pack_stack_ptr; + + /* output file for preprocessing */ + FILE *outfile; + + /* for tcc_relocate */ + int runtime_added; + +#ifdef TCC_TARGET_X86_64 + /* write PLT and GOT here */ + char *runtime_plt_and_got; + unsigned int runtime_plt_and_got_offset; +#endif +}; + +/* The current value can be: */ +#define VT_VALMASK 0x00ff +#define VT_CONST 0x00f0 /* constant in vc + (must be first non register value) */ +#define VT_LLOCAL 0x00f1 /* lvalue, offset on stack */ +#define VT_LOCAL 0x00f2 /* offset on stack */ +#define VT_CMP 0x00f3 /* the value is stored in processor flags (in vc) */ +#define VT_JMP 0x00f4 /* value is the consequence of jmp true (even) */ +#define VT_JMPI 0x00f5 /* value is the consequence of jmp false (odd) */ +#define VT_LVAL 0x0100 /* var is an lvalue */ +#define VT_SYM 0x0200 /* a symbol value is added */ +#define VT_MUSTCAST 0x0400 /* value must be casted to be correct (used for + char/short stored in integer registers) */ +#define VT_MUSTBOUND 0x0800 /* bound checking must be done before + dereferencing value */ +#define VT_BOUNDED 0x8000 /* value is bounded. The address of the + bounding function call point is in vc */ +#define VT_LVAL_BYTE 0x1000 /* lvalue is a byte */ +#define VT_LVAL_SHORT 0x2000 /* lvalue is a short */ +#define VT_LVAL_UNSIGNED 0x4000 /* lvalue is unsigned */ +#define VT_LVAL_TYPE (VT_LVAL_BYTE | VT_LVAL_SHORT | VT_LVAL_UNSIGNED) + +/* types */ +#define VT_INT 0 /* integer type */ +#define VT_BYTE 1 /* signed byte type */ +#define VT_SHORT 2 /* short type */ +#define VT_VOID 3 /* void type */ +#define VT_PTR 4 /* pointer */ +#define VT_ENUM 5 /* enum definition */ +#define VT_FUNC 6 /* function type */ +#define VT_STRUCT 7 /* struct/union definition */ +#define VT_FLOAT 8 /* IEEE float */ +#define VT_DOUBLE 9 /* IEEE double */ +#define VT_LDOUBLE 10 /* IEEE long double */ +#define VT_BOOL 11 /* ISOC99 boolean type */ +#define VT_LLONG 12 /* 64 bit integer */ +#define VT_LONG 13 /* long integer (NEVER USED as type, only + during parsing) */ +#define VT_BTYPE 0x000f /* mask for basic type */ +#define VT_UNSIGNED 0x0010 /* unsigned type */ +#define VT_ARRAY 0x0020 /* array type (also has VT_PTR) */ +#define VT_BITFIELD 0x0040 /* bitfield modifier */ +#define VT_CONSTANT 0x0800 /* const modifier */ +#define VT_VOLATILE 0x1000 /* volatile modifier */ +#define VT_SIGNED 0x2000 /* signed type */ + +/* storage */ +#define VT_EXTERN 0x00000080 /* extern definition */ +#define VT_STATIC 0x00000100 /* static variable */ +#define VT_TYPEDEF 0x00000200 /* typedef definition */ +#define VT_INLINE 0x00000400 /* inline definition */ + +#define VT_STRUCT_SHIFT 16 /* shift for bitfield shift values */ + +/* type mask (except storage) */ +#define VT_STORAGE (VT_EXTERN | VT_STATIC | VT_TYPEDEF | VT_INLINE) +#define VT_TYPE (~(VT_STORAGE)) + +/* token values */ + +/* warning: the following compare tokens depend on i386 asm code */ +#define TOK_ULT 0x92 +#define TOK_UGE 0x93 +#define TOK_EQ 0x94 +#define TOK_NE 0x95 +#define TOK_ULE 0x96 +#define TOK_UGT 0x97 +#define TOK_Nset 0x98 +#define TOK_Nclear 0x99 +#define TOK_LT 0x9c +#define TOK_GE 0x9d +#define TOK_LE 0x9e +#define TOK_GT 0x9f + +#define TOK_LAND 0xa0 +#define TOK_LOR 0xa1 + +#define TOK_DEC 0xa2 +#define TOK_MID 0xa3 /* inc/dec, to void constant */ +#define TOK_INC 0xa4 +#define TOK_UDIV 0xb0 /* unsigned division */ +#define TOK_UMOD 0xb1 /* unsigned modulo */ +#define TOK_PDIV 0xb2 /* fast division with undefined rounding for pointers */ +#define TOK_CINT 0xb3 /* number in tokc */ +#define TOK_CCHAR 0xb4 /* char constant in tokc */ +#define TOK_STR 0xb5 /* pointer to string in tokc */ +#define TOK_TWOSHARPS 0xb6 /* ## preprocessing token */ +#define TOK_LCHAR 0xb7 +#define TOK_LSTR 0xb8 +#define TOK_CFLOAT 0xb9 /* float constant */ +#define TOK_LINENUM 0xba /* line number info */ +#define TOK_CDOUBLE 0xc0 /* double constant */ +#define TOK_CLDOUBLE 0xc1 /* long double constant */ +#define TOK_UMULL 0xc2 /* unsigned 32x32 -> 64 mul */ +#define TOK_ADDC1 0xc3 /* add with carry generation */ +#define TOK_ADDC2 0xc4 /* add with carry use */ +#define TOK_SUBC1 0xc5 /* add with carry generation */ +#define TOK_SUBC2 0xc6 /* add with carry use */ +#define TOK_CUINT 0xc8 /* unsigned int constant */ +#define TOK_CLLONG 0xc9 /* long long constant */ +#define TOK_CULLONG 0xca /* unsigned long long constant */ +#define TOK_ARROW 0xcb +#define TOK_DOTS 0xcc /* three dots */ +#define TOK_SHR 0xcd /* unsigned shift right */ +#define TOK_PPNUM 0xce /* preprocessor number */ + +#define TOK_SHL 0x01 /* shift left */ +#define TOK_SAR 0x02 /* signed shift right */ + +/* assignement operators : normal operator or 0x80 */ +#define TOK_A_MOD 0xa5 +#define TOK_A_AND 0xa6 +#define TOK_A_MUL 0xaa +#define TOK_A_ADD 0xab +#define TOK_A_SUB 0xad +#define TOK_A_DIV 0xaf +#define TOK_A_XOR 0xde +#define TOK_A_OR 0xfc +#define TOK_A_SHL 0x81 +#define TOK_A_SAR 0x82 + +#ifndef offsetof +#define offsetof(type, field) ((size_t) &((type *)0)->field) +#endif + +#ifndef countof +#define countof(tab) (sizeof(tab) / sizeof((tab)[0])) +#endif + +#define TOK_EOF (-1) /* end of file */ +#define TOK_LINEFEED 10 /* line feed */ + +/* all identificators and strings have token above that */ +#define TOK_IDENT 256 + +/* only used for i386 asm opcodes definitions */ +#define DEF_ASM(x) DEF(TOK_ASM_ ## x, #x) + +#define DEF_BWL(x) \ + DEF(TOK_ASM_ ## x ## b, #x "b") \ + DEF(TOK_ASM_ ## x ## w, #x "w") \ + DEF(TOK_ASM_ ## x ## l, #x "l") \ + DEF(TOK_ASM_ ## x, #x) + +#define DEF_WL(x) \ + DEF(TOK_ASM_ ## x ## w, #x "w") \ + DEF(TOK_ASM_ ## x ## l, #x "l") \ + DEF(TOK_ASM_ ## x, #x) + +#define DEF_FP1(x) \ + DEF(TOK_ASM_ ## f ## x ## s, "f" #x "s") \ + DEF(TOK_ASM_ ## fi ## x ## l, "fi" #x "l") \ + DEF(TOK_ASM_ ## f ## x ## l, "f" #x "l") \ + DEF(TOK_ASM_ ## fi ## x ## s, "fi" #x "s") + +#define DEF_FP(x) \ + DEF(TOK_ASM_ ## f ## x, "f" #x ) \ + DEF(TOK_ASM_ ## f ## x ## p, "f" #x "p") \ + DEF_FP1(x) + +#define DEF_ASMTEST(x) \ + DEF_ASM(x ## o) \ + DEF_ASM(x ## no) \ + DEF_ASM(x ## b) \ + DEF_ASM(x ## c) \ + DEF_ASM(x ## nae) \ + DEF_ASM(x ## nb) \ + DEF_ASM(x ## nc) \ + DEF_ASM(x ## ae) \ + DEF_ASM(x ## e) \ + DEF_ASM(x ## z) \ + DEF_ASM(x ## ne) \ + DEF_ASM(x ## nz) \ + DEF_ASM(x ## be) \ + DEF_ASM(x ## na) \ + DEF_ASM(x ## nbe) \ + DEF_ASM(x ## a) \ + DEF_ASM(x ## s) \ + DEF_ASM(x ## ns) \ + DEF_ASM(x ## p) \ + DEF_ASM(x ## pe) \ + DEF_ASM(x ## np) \ + DEF_ASM(x ## po) \ + DEF_ASM(x ## l) \ + DEF_ASM(x ## nge) \ + DEF_ASM(x ## nl) \ + DEF_ASM(x ## ge) \ + DEF_ASM(x ## le) \ + DEF_ASM(x ## ng) \ + DEF_ASM(x ## nle) \ + DEF_ASM(x ## g) + +#define TOK_ASM_int TOK_INT + +enum tcc_token { + TOK_LAST = TOK_IDENT - 1, +#define DEF(id, str) id, +#include "tcctok.h" +#undef DEF +}; + +#define TOK_UIDENT TOK_DEFINE + +#ifdef _WIN32 +#define snprintf _snprintf +#define vsnprintf _vsnprintf +#ifndef __GNUC__ + #define strtold (long double)strtod + #define strtof (float)strtod + #define strtoll (long long)strtol +#endif +#elif defined(TCC_UCLIBC) || defined(__FreeBSD__) || defined(__DragonFly__) \ + || defined(__OpenBSD__) +/* currently incorrect */ +long double strtold(const char *nptr, char **endptr) +{ + return (long double)strtod(nptr, endptr); +} +float strtof(const char *nptr, char **endptr) +{ + return (float)strtod(nptr, endptr); +} +#else +/* XXX: need to define this to use them in non ISOC99 context */ +extern float strtof (const char *__nptr, char **__endptr); +extern long double strtold (const char *__nptr, char **__endptr); +#endif + +#ifdef _WIN32 +#define IS_PATHSEP(c) (c == '/' || c == '\\') +#define IS_ABSPATH(p) (IS_PATHSEP(p[0]) || (p[0] && p[1] == ':' && IS_PATHSEP(p[2]))) +#define PATHCMP stricmp +#else +#define IS_PATHSEP(c) (c == '/') +#define IS_ABSPATH(p) IS_PATHSEP(p[0]) +#define PATHCMP strcmp +#endif + +void error(const char *fmt, ...); +void error_noabort(const char *fmt, ...); +void warning(const char *fmt, ...); + +void tcc_set_lib_path_w32(TCCState *s); +int tcc_set_flag(TCCState *s, const char *flag_name, int value); +void tcc_print_stats(TCCState *s, int64_t total_time); + +void tcc_free(void *ptr); +void *tcc_malloc(unsigned long size); +void *tcc_mallocz(unsigned long size); +void *tcc_realloc(void *ptr, unsigned long size); +char *tcc_strdup(const char *str); + +char *tcc_basename(const char *name); +char *tcc_fileextension (const char *name); +char *pstrcpy(char *buf, int buf_size, const char *s); +char *pstrcat(char *buf, int buf_size, const char *s); +void dynarray_add(void ***ptab, int *nb_ptr, void *data); +void dynarray_reset(void *pp, int *n); + +#ifdef CONFIG_TCC_BACKTRACE +extern int num_callers; +extern const char **rt_bound_error_msg; +#endif + +/* true if float/double/long double type */ +static inline int is_float(int t) +{ + int bt; + bt = t & VT_BTYPE; + return bt == VT_LDOUBLE || bt == VT_DOUBLE || bt == VT_FLOAT; +} + +/* space exlcuding newline */ +static inline int is_space(int ch) +{ + return ch == ' ' || ch == '\t' || ch == '\v' || ch == '\f' || ch == '\r'; +} + diff --git a/05/tcc-0.9.25/tccasm.c b/05/tcc-0.9.25/tccasm.c new file mode 100644 index 0000000..8834b53 --- /dev/null +++ b/05/tcc-0.9.25/tccasm.c @@ -0,0 +1,1021 @@ +/* + * GAS like assembler for TCC + * + * Copyright (c) 2001-2004 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +static int asm_get_local_label_name(TCCState *s1, unsigned int n) +{ + char buf[64]; + TokenSym *ts; + + snprintf(buf, sizeof(buf), "L..%u", n); + ts = tok_alloc(buf, strlen(buf)); + return ts->tok; +} + +static void asm_expr(TCCState *s1, ExprValue *pe); + +/* We do not use the C expression parser to handle symbols. Maybe the + C expression parser could be tweaked to do so. */ + +static void asm_expr_unary(TCCState *s1, ExprValue *pe) +{ + Sym *sym; + int op, n, label; + const char *p; + + switch(tok) { + case TOK_PPNUM: + p = tokc.cstr->data; + n = strtoul(p, (char **)&p, 0); + if (*p == 'b' || *p == 'f') { + /* backward or forward label */ + label = asm_get_local_label_name(s1, n); + sym = label_find(label); + if (*p == 'b') { + /* backward : find the last corresponding defined label */ + if (sym && sym->r == 0) + sym = sym->prev_tok; + if (!sym) + error("local label '%d' not found backward", n); + } else { + /* forward */ + if (!sym || sym->r) { + /* if the last label is defined, then define a new one */ + sym = label_push(&s1->asm_labels, label, 0); + sym->type.t = VT_STATIC | VT_VOID; + } + } + pe->v = 0; + pe->sym = sym; + } else if (*p == '\0') { + pe->v = n; + pe->sym = NULL; + } else { + error("invalid number syntax"); + } + next(); + break; + case '+': + next(); + asm_expr_unary(s1, pe); + break; + case '-': + case '~': + op = tok; + next(); + asm_expr_unary(s1, pe); + if (pe->sym) + error("invalid operation with label"); + if (op == '-') + pe->v = -pe->v; + else + pe->v = ~pe->v; + break; + case TOK_CCHAR: + case TOK_LCHAR: + pe->v = tokc.i; + pe->sym = NULL; + next(); + break; + case '(': + next(); + asm_expr(s1, pe); + skip(')'); + break; + default: + if (tok >= TOK_IDENT) { + /* label case : if the label was not found, add one */ + sym = label_find(tok); + if (!sym) { + sym = label_push(&s1->asm_labels, tok, 0); + /* NOTE: by default, the symbol is global */ + sym->type.t = VT_VOID; + } + if (sym->r == SHN_ABS) { + /* if absolute symbol, no need to put a symbol value */ + pe->v = (long)sym->next; + pe->sym = NULL; + } else { + pe->v = 0; + pe->sym = sym; + } + next(); + } else { + error("bad expression syntax [%s]", get_tok_str(tok, &tokc)); + } + break; + } +} + +static void asm_expr_prod(TCCState *s1, ExprValue *pe) +{ + int op; + ExprValue e2; + + asm_expr_unary(s1, pe); + for(;;) { + op = tok; + if (op != '*' && op != '/' && op != '%' && + op != TOK_SHL && op != TOK_SAR) + break; + next(); + asm_expr_unary(s1, &e2); + if (pe->sym || e2.sym) + error("invalid operation with label"); + switch(op) { + case '*': + pe->v *= e2.v; + break; + case '/': + if (e2.v == 0) { + div_error: + error("division by zero"); + } + pe->v /= e2.v; + break; + case '%': + if (e2.v == 0) + goto div_error; + pe->v %= e2.v; + break; + case TOK_SHL: + pe->v <<= e2.v; + break; + default: + case TOK_SAR: + pe->v >>= e2.v; + break; + } + } +} + +static void asm_expr_logic(TCCState *s1, ExprValue *pe) +{ + int op; + ExprValue e2; + + asm_expr_prod(s1, pe); + for(;;) { + op = tok; + if (op != '&' && op != '|' && op != '^') + break; + next(); + asm_expr_prod(s1, &e2); + if (pe->sym || e2.sym) + error("invalid operation with label"); + switch(op) { + case '&': + pe->v &= e2.v; + break; + case '|': + pe->v |= e2.v; + break; + default: + case '^': + pe->v ^= e2.v; + break; + } + } +} + +static inline void asm_expr_sum(TCCState *s1, ExprValue *pe) +{ + int op; + ExprValue e2; + + asm_expr_logic(s1, pe); + for(;;) { + op = tok; + if (op != '+' && op != '-') + break; + next(); + asm_expr_logic(s1, &e2); + if (op == '+') { + if (pe->sym != NULL && e2.sym != NULL) + goto cannot_relocate; + pe->v += e2.v; + if (pe->sym == NULL && e2.sym != NULL) + pe->sym = e2.sym; + } else { + pe->v -= e2.v; + /* NOTE: we are less powerful than gas in that case + because we store only one symbol in the expression */ + if (!pe->sym && !e2.sym) { + /* OK */ + } else if (pe->sym && !e2.sym) { + /* OK */ + } else if (pe->sym && e2.sym) { + if (pe->sym == e2.sym) { + /* OK */ + } else if (pe->sym->r == e2.sym->r && pe->sym->r != 0) { + /* we also accept defined symbols in the same section */ + pe->v += (long)pe->sym->next - (long)e2.sym->next; + } else { + goto cannot_relocate; + } + pe->sym = NULL; /* same symbols can be substracted to NULL */ + } else { + cannot_relocate: + error("invalid operation with label"); + } + } + } +} + +static void asm_expr(TCCState *s1, ExprValue *pe) +{ + asm_expr_sum(s1, pe); +} + +static int asm_int_expr(TCCState *s1) +{ + ExprValue e; + asm_expr(s1, &e); + if (e.sym) + expect("constant"); + return e.v; +} + +/* NOTE: the same name space as C labels is used to avoid using too + much memory when storing labels in TokenStrings */ +static void asm_new_label1(TCCState *s1, int label, int is_local, + int sh_num, int value) +{ + Sym *sym; + + sym = label_find(label); + if (sym) { + if (sym->r) { + /* the label is already defined */ + if (!is_local) { + error("assembler label '%s' already defined", + get_tok_str(label, NULL)); + } else { + /* redefinition of local labels is possible */ + goto new_label; + } + } + } else { + new_label: + sym = label_push(&s1->asm_labels, label, 0); + sym->type.t = VT_STATIC | VT_VOID; + } + sym->r = sh_num; + sym->next = (void *)value; +} + +static void asm_new_label(TCCState *s1, int label, int is_local) +{ + asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind); +} + +static void asm_free_labels(TCCState *st) +{ + Sym *s, *s1; + Section *sec; + + for(s = st->asm_labels; s != NULL; s = s1) { + s1 = s->prev; + /* define symbol value in object file */ + if (s->r) { + if (s->r == SHN_ABS) + sec = SECTION_ABS; + else + sec = st->sections[s->r]; + put_extern_sym2(s, sec, (long)s->next, 0, 0); + } + /* remove label */ + table_ident[s->v - TOK_IDENT]->sym_label = NULL; + sym_free(s); + } + st->asm_labels = NULL; +} + +static void use_section1(TCCState *s1, Section *sec) +{ + cur_text_section->data_offset = ind; + cur_text_section = sec; + ind = cur_text_section->data_offset; +} + +static void use_section(TCCState *s1, const char *name) +{ + Section *sec; + sec = find_section(s1, name); + use_section1(s1, sec); +} + +static void asm_parse_directive(TCCState *s1) +{ + int n, offset, v, size, tok1; + Section *sec; + uint8_t *ptr; + + /* assembler directive */ + next(); + sec = cur_text_section; + switch(tok) { + case TOK_ASM_align: + case TOK_ASM_skip: + case TOK_ASM_space: + tok1 = tok; + next(); + n = asm_int_expr(s1); + if (tok1 == TOK_ASM_align) { + if (n < 0 || (n & (n-1)) != 0) + error("alignment must be a positive power of two"); + offset = (ind + n - 1) & -n; + size = offset - ind; + /* the section must have a compatible alignment */ + if (sec->sh_addralign < n) + sec->sh_addralign = n; + } else { + size = n; + } + v = 0; + if (tok == ',') { + next(); + v = asm_int_expr(s1); + } + zero_pad: + if (sec->sh_type != SHT_NOBITS) { + sec->data_offset = ind; + ptr = section_ptr_add(sec, size); + memset(ptr, v, size); + } + ind += size; + break; + case TOK_ASM_quad: + next(); + for(;;) { + uint64_t vl; + const char *p; + + p = tokc.cstr->data; + if (tok != TOK_PPNUM) { + error_constant: + error("64 bit constant"); + } + vl = strtoll(p, (char **)&p, 0); + if (*p != '\0') + goto error_constant; + next(); + if (sec->sh_type != SHT_NOBITS) { + /* XXX: endianness */ + gen_le32(vl); + gen_le32(vl >> 32); + } else { + ind += 8; + } + if (tok != ',') + break; + next(); + } + break; + case TOK_ASM_byte: + size = 1; + goto asm_data; + case TOK_ASM_word: + case TOK_SHORT: + size = 2; + goto asm_data; + case TOK_LONG: + case TOK_INT: + size = 4; + asm_data: + next(); + for(;;) { + ExprValue e; + asm_expr(s1, &e); + if (sec->sh_type != SHT_NOBITS) { + if (size == 4) { + gen_expr32(&e); + } else { + if (e.sym) + expect("constant"); + if (size == 1) + g(e.v); + else + gen_le16(e.v); + } + } else { + ind += size; + } + if (tok != ',') + break; + next(); + } + break; + case TOK_ASM_fill: + { + int repeat, size, val, i, j; + uint8_t repeat_buf[8]; + next(); + repeat = asm_int_expr(s1); + if (repeat < 0) { + error("repeat < 0; .fill ignored"); + break; + } + size = 1; + val = 0; + if (tok == ',') { + next(); + size = asm_int_expr(s1); + if (size < 0) { + error("size < 0; .fill ignored"); + break; + } + if (size > 8) + size = 8; + if (tok == ',') { + next(); + val = asm_int_expr(s1); + } + } + /* XXX: endianness */ + repeat_buf[0] = val; + repeat_buf[1] = val >> 8; + repeat_buf[2] = val >> 16; + repeat_buf[3] = val >> 24; + repeat_buf[4] = 0; + repeat_buf[5] = 0; + repeat_buf[6] = 0; + repeat_buf[7] = 0; + for(i = 0; i < repeat; i++) { + for(j = 0; j < size; j++) { + g(repeat_buf[j]); + } + } + } + break; + case TOK_ASM_org: + { + unsigned long n; + next(); + /* XXX: handle section symbols too */ + n = asm_int_expr(s1); + if (n < ind) + error("attempt to .org backwards"); + v = 0; + size = n - ind; + goto zero_pad; + } + break; + case TOK_ASM_globl: + case TOK_ASM_global: + { + Sym *sym; + + next(); + sym = label_find(tok); + if (!sym) { + sym = label_push(&s1->asm_labels, tok, 0); + sym->type.t = VT_VOID; + } + sym->type.t &= ~VT_STATIC; + next(); + } + break; + case TOK_ASM_string: + case TOK_ASM_ascii: + case TOK_ASM_asciz: + { + const uint8_t *p; + int i, size, t; + + t = tok; + next(); + for(;;) { + if (tok != TOK_STR) + expect("string constant"); + p = tokc.cstr->data; + size = tokc.cstr->size; + if (t == TOK_ASM_ascii && size > 0) + size--; + for(i = 0; i < size; i++) + g(p[i]); + next(); + if (tok == ',') { + next(); + } else if (tok != TOK_STR) { + break; + } + } + } + break; + case TOK_ASM_text: + case TOK_ASM_data: + case TOK_ASM_bss: + { + char sname[64]; + tok1 = tok; + n = 0; + next(); + if (tok != ';' && tok != TOK_LINEFEED) { + n = asm_int_expr(s1); + next(); + } + sprintf(sname, (n?".%s%d":".%s"), get_tok_str(tok1, NULL), n); + use_section(s1, sname); + } + break; + case TOK_SECTION1: + { + char sname[256]; + + /* XXX: support more options */ + next(); + sname[0] = '\0'; + while (tok != ';' && tok != TOK_LINEFEED && tok != ',') { + if (tok == TOK_STR) + pstrcat(sname, sizeof(sname), tokc.cstr->data); + else + pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL)); + next(); + } + if (tok == ',') { + /* skip section options */ + next(); + if (tok != TOK_STR) + expect("string constant"); + next(); + } + last_text_section = cur_text_section; + use_section(s1, sname); + } + break; + case TOK_ASM_previous: + { + Section *sec; + next(); + if (!last_text_section) + error("no previous section referenced"); + sec = cur_text_section; + use_section1(s1, last_text_section); + last_text_section = sec; + } + break; + default: + error("unknown assembler directive '.%s'", get_tok_str(tok, NULL)); + break; + } +} + + +/* assemble a file */ +static int tcc_assemble_internal(TCCState *s1, int do_preprocess) +{ + int opcode; + +#if 0 + /* print stats about opcodes */ + { + const ASMInstr *pa; + int freq[4]; + int op_vals[500]; + int nb_op_vals, i, j; + + nb_op_vals = 0; + memset(freq, 0, sizeof(freq)); + for(pa = asm_instrs; pa->sym != 0; pa++) { + freq[pa->nb_ops]++; + for(i=0;inb_ops;i++) { + for(j=0;jop_type[i] == op_vals[j]) + goto found; + } + op_vals[nb_op_vals++] = pa->op_type[i]; + found: ; + } + } + for(i=0;ibuf_ptr[0]; + tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF; + parse_flags = PARSE_FLAG_ASM_COMMENTS; + if (do_preprocess) + parse_flags |= PARSE_FLAG_PREPROCESS; + next(); + for(;;) { + if (tok == TOK_EOF) + break; + parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */ + redo: + if (tok == '#') { + /* horrible gas comment */ + while (tok != TOK_LINEFEED) + next(); + } else if (tok == '.') { + asm_parse_directive(s1); + } else if (tok == TOK_PPNUM) { + const char *p; + int n; + p = tokc.cstr->data; + n = strtoul(p, (char **)&p, 10); + if (*p != '\0') + expect("':'"); + /* new local label */ + asm_new_label(s1, asm_get_local_label_name(s1, n), 1); + next(); + skip(':'); + goto redo; + } else if (tok >= TOK_IDENT) { + /* instruction or label */ + opcode = tok; + next(); + if (tok == ':') { + /* new label */ + asm_new_label(s1, opcode, 0); + next(); + goto redo; + } else if (tok == '=') { + int n; + next(); + n = asm_int_expr(s1); + asm_new_label1(s1, opcode, 0, SHN_ABS, n); + goto redo; + } else { + asm_opcode(s1, opcode); + } + } + /* end of line */ + if (tok != ';' && tok != TOK_LINEFEED){ + expect("end of line"); + } + parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */ + next(); + } + + asm_free_labels(s1); + + return 0; +} + +/* Assemble the current file */ +static int tcc_assemble(TCCState *s1, int do_preprocess) +{ + Sym *define_start; + int ret; + + preprocess_init(s1); + + /* default section is text */ + cur_text_section = text_section; + ind = cur_text_section->data_offset; + + define_start = define_stack; + + ret = tcc_assemble_internal(s1, do_preprocess); + + cur_text_section->data_offset = ind; + + free_defines(define_start); + + return ret; +} + +/********************************************************************/ +/* GCC inline asm support */ + +/* assemble the string 'str' in the current C compilation unit without + C preprocessing. NOTE: str is modified by modifying the '\0' at the + end */ +static void tcc_assemble_inline(TCCState *s1, char *str, int len) +{ + BufferedFile *bf, *saved_file; + int saved_parse_flags, *saved_macro_ptr; + + bf = tcc_malloc(sizeof(BufferedFile)); + memset(bf, 0, sizeof(BufferedFile)); + bf->fd = -1; + bf->buf_ptr = str; + bf->buf_end = str + len; + str[len] = CH_EOB; + /* same name as current file so that errors are correctly + reported */ + pstrcpy(bf->filename, sizeof(bf->filename), file->filename); + bf->line_num = file->line_num; + saved_file = file; + file = bf; + saved_parse_flags = parse_flags; + saved_macro_ptr = macro_ptr; + macro_ptr = NULL; + + tcc_assemble_internal(s1, 0); + + parse_flags = saved_parse_flags; + macro_ptr = saved_macro_ptr; + file = saved_file; + tcc_free(bf); +} + +/* find a constraint by its number or id (gcc 3 extended + syntax). return -1 if not found. Return in *pp in char after the + constraint */ +static int find_constraint(ASMOperand *operands, int nb_operands, + const char *name, const char **pp) +{ + int index; + TokenSym *ts; + const char *p; + + if (isnum(*name)) { + index = 0; + while (isnum(*name)) { + index = (index * 10) + (*name) - '0'; + name++; + } + if ((unsigned)index >= nb_operands) + index = -1; + } else if (*name == '[') { + name++; + p = strchr(name, ']'); + if (p) { + ts = tok_alloc(name, p - name); + for(index = 0; index < nb_operands; index++) { + if (operands[index].id == ts->tok) + goto found; + } + index = -1; + found: + name = p + 1; + } else { + index = -1; + } + } else { + index = -1; + } + if (pp) + *pp = name; + return index; +} + +static void subst_asm_operands(ASMOperand *operands, int nb_operands, + int nb_outputs, + CString *out_str, CString *in_str) +{ + int c, index, modifier; + const char *str; + ASMOperand *op; + SValue sv; + + cstr_new(out_str); + str = in_str->data; + for(;;) { + c = *str++; + if (c == '%') { + if (*str == '%') { + str++; + goto add_char; + } + modifier = 0; + if (*str == 'c' || *str == 'n' || + *str == 'b' || *str == 'w' || *str == 'h') + modifier = *str++; + index = find_constraint(operands, nb_operands, str, &str); + if (index < 0) + error("invalid operand reference after %%"); + op = &operands[index]; + sv = *op->vt; + if (op->reg >= 0) { + sv.r = op->reg; + if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory) + sv.r |= VT_LVAL; + } + subst_asm_operand(out_str, &sv, modifier); + } else { + add_char: + cstr_ccat(out_str, c); + if (c == '\0') + break; + } + } +} + + +static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr, + int is_output) +{ + ASMOperand *op; + int nb_operands; + + if (tok != ':') { + nb_operands = *nb_operands_ptr; + for(;;) { + if (nb_operands >= MAX_ASM_OPERANDS) + error("too many asm operands"); + op = &operands[nb_operands++]; + op->id = 0; + if (tok == '[') { + next(); + if (tok < TOK_IDENT) + expect("identifier"); + op->id = tok; + next(); + skip(']'); + } + if (tok != TOK_STR) + expect("string constant"); + op->constraint = tcc_malloc(tokc.cstr->size); + strcpy(op->constraint, tokc.cstr->data); + next(); + skip('('); + gexpr(); + if (is_output) { + test_lvalue(); + } else { + /* we want to avoid LLOCAL case, except when the 'm' + constraint is used. Note that it may come from + register storage, so we need to convert (reg) + case */ + if ((vtop->r & VT_LVAL) && + ((vtop->r & VT_VALMASK) == VT_LLOCAL || + (vtop->r & VT_VALMASK) < VT_CONST) && + !strchr(op->constraint, 'm')) { + gv(RC_INT); + } + } + op->vt = vtop; + skip(')'); + if (tok == ',') { + next(); + } else { + break; + } + } + *nb_operands_ptr = nb_operands; + } +} + +static void parse_asm_str(CString *astr) +{ + skip('('); + /* read the string */ + if (tok != TOK_STR) + expect("string constant"); + cstr_new(astr); + while (tok == TOK_STR) { + /* XXX: add \0 handling too ? */ + cstr_cat(astr, tokc.cstr->data); + next(); + } + cstr_ccat(astr, '\0'); +} + +/* parse the GCC asm() instruction */ +static void asm_instr(void) +{ + CString astr, astr1; + ASMOperand operands[MAX_ASM_OPERANDS]; + int nb_inputs, nb_outputs, nb_operands, i, must_subst, out_reg; + uint8_t clobber_regs[NB_ASM_REGS]; + + next(); + /* since we always generate the asm() instruction, we can ignore + volatile */ + if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) { + next(); + } + parse_asm_str(&astr); + nb_operands = 0; + nb_outputs = 0; + must_subst = 0; + memset(clobber_regs, 0, sizeof(clobber_regs)); + if (tok == ':') { + next(); + must_subst = 1; + /* output args */ + parse_asm_operands(operands, &nb_operands, 1); + nb_outputs = nb_operands; + if (tok == ':') { + next(); + if (tok != ')') { + /* input args */ + parse_asm_operands(operands, &nb_operands, 0); + if (tok == ':') { + /* clobber list */ + /* XXX: handle registers */ + next(); + for(;;) { + if (tok != TOK_STR) + expect("string constant"); + asm_clobber(clobber_regs, tokc.cstr->data); + next(); + if (tok == ',') { + next(); + } else { + break; + } + } + } + } + } + } + skip(')'); + /* NOTE: we do not eat the ';' so that we can restore the current + token after the assembler parsing */ + if (tok != ';') + expect("';'"); + nb_inputs = nb_operands - nb_outputs; + + /* save all values in the memory */ + save_regs(0); + + /* compute constraints */ + asm_compute_constraints(operands, nb_operands, nb_outputs, + clobber_regs, &out_reg); + + /* substitute the operands in the asm string. No substitution is + done if no operands (GCC behaviour) */ +#ifdef ASM_DEBUG + printf("asm: \"%s\"\n", (char *)astr.data); +#endif + if (must_subst) { + subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr); + cstr_free(&astr); + } else { + astr1 = astr; + } +#ifdef ASM_DEBUG + printf("subst_asm: \"%s\"\n", (char *)astr1.data); +#endif + + /* generate loads */ + asm_gen_code(operands, nb_operands, nb_outputs, 0, + clobber_regs, out_reg); + + /* assemble the string with tcc internal assembler */ + tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1); + + /* restore the current C token */ + next(); + + /* store the output values if needed */ + asm_gen_code(operands, nb_operands, nb_outputs, 1, + clobber_regs, out_reg); + + /* free everything */ + for(i=0;iconstraint); + vpop(); + } + cstr_free(&astr1); +} + +static void asm_global_instr(void) +{ + CString astr; + + next(); + parse_asm_str(&astr); + skip(')'); + /* NOTE: we do not eat the ';' so that we can restore the current + token after the assembler parsing */ + if (tok != ';') + expect("';'"); + +#ifdef ASM_DEBUG + printf("asm_global: \"%s\"\n", (char *)astr.data); +#endif + cur_text_section = text_section; + ind = cur_text_section->data_offset; + + /* assemble the string with tcc internal assembler */ + tcc_assemble_inline(tcc_state, astr.data, astr.size - 1); + + cur_text_section->data_offset = ind; + + /* restore the current C token */ + next(); + + cstr_free(&astr); +} diff --git a/05/tcc-0.9.25/tcccoff.c b/05/tcc-0.9.25/tcccoff.c new file mode 100644 index 0000000..0dcbe50 --- /dev/null +++ b/05/tcc-0.9.25/tcccoff.c @@ -0,0 +1,957 @@ +/* + * COFF file handling for TCC + * + * Copyright (c) 2003, 2004 TK + * Copyright (c) 2004 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "coff.h" + +#define MAXNSCNS 255 /* MAXIMUM NUMBER OF SECTIONS */ +#define MAX_STR_TABLE 1000000 +AOUTHDR o_filehdr; /* OPTIONAL (A.OUT) FILE HEADER */ + +SCNHDR section_header[MAXNSCNS]; + +#define MAX_FUNCS 1000 +#define MAX_FUNC_NAME_LENGTH 128 + +int nFuncs; +char Func[MAX_FUNCS][MAX_FUNC_NAME_LENGTH]; +char AssociatedFile[MAX_FUNCS][MAX_FUNC_NAME_LENGTH]; +int LineNoFilePtr[MAX_FUNCS]; +int EndAddress[MAX_FUNCS]; +int LastLineNo[MAX_FUNCS]; +int FuncEntries[MAX_FUNCS]; + +BOOL OutputTheSection(Section * sect); +short int GetCoffFlags(const char *s); +void SortSymbolTable(void); +Section *FindSection(TCCState * s1, const char *sname); + +int C67_main_entry_point; + +int FindCoffSymbolIndex(const char *func_name); +int nb_syms; + +typedef struct { + long tag; + long size; + long fileptr; + long nextsym; + short int dummy; +} AUXFUNC; + +typedef struct { + long regmask; + unsigned short lineno; + unsigned short nentries; + int localframe; + int nextentry; + short int dummy; +} AUXBF; + +typedef struct { + long dummy; + unsigned short lineno; + unsigned short dummy1; + int dummy2; + int dummy3; + unsigned short dummy4; +} AUXEF; + +int tcc_output_coff(TCCState *s1, FILE *f) +{ + Section *tcc_sect; + SCNHDR *coff_sec; + int file_pointer; + char *Coff_str_table, *pCoff_str_table; + int CoffTextSectionNo, coff_nb_syms; + FILHDR file_hdr; /* FILE HEADER STRUCTURE */ + Section *stext, *sdata, *sbss; + int i, NSectionsToOutput = 0; + + Coff_str_table = pCoff_str_table = NULL; + + stext = FindSection(s1, ".text"); + sdata = FindSection(s1, ".data"); + sbss = FindSection(s1, ".bss"); + + nb_syms = symtab_section->data_offset / sizeof(Elf32_Sym); + coff_nb_syms = FindCoffSymbolIndex("XXXXXXXXXX1"); + + file_hdr.f_magic = COFF_C67_MAGIC; /* magic number */ + file_hdr.f_timdat = 0; /* time & date stamp */ + file_hdr.f_opthdr = sizeof(AOUTHDR); /* sizeof(optional hdr) */ + file_hdr.f_flags = 0x1143; /* flags (copied from what code composer does) */ + file_hdr.f_TargetID = 0x99; /* for C6x = 0x0099 */ + + o_filehdr.magic = 0x0108; /* see magic.h */ + o_filehdr.vstamp = 0x0190; /* version stamp */ + o_filehdr.tsize = stext->data_offset; /* text size in bytes, padded to FW bdry */ + o_filehdr.dsize = sdata->data_offset; /* initialized data " " */ + o_filehdr.bsize = sbss->data_offset; /* uninitialized data " " */ + o_filehdr.entrypt = C67_main_entry_point; /* entry pt. */ + o_filehdr.text_start = stext->sh_addr; /* base of text used for this file */ + o_filehdr.data_start = sdata->sh_addr; /* base of data used for this file */ + + + // create all the section headers + + file_pointer = FILHSZ + sizeof(AOUTHDR); + + CoffTextSectionNo = -1; + + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + if (OutputTheSection(tcc_sect)) { + NSectionsToOutput++; + + if (CoffTextSectionNo == -1 && tcc_sect == stext) + CoffTextSectionNo = NSectionsToOutput; // rem which coff sect number the .text sect is + + strcpy(coff_sec->s_name, tcc_sect->name); /* section name */ + + coff_sec->s_paddr = tcc_sect->sh_addr; /* physical address */ + coff_sec->s_vaddr = tcc_sect->sh_addr; /* virtual address */ + coff_sec->s_size = tcc_sect->data_offset; /* section size */ + coff_sec->s_scnptr = 0; /* file ptr to raw data for section */ + coff_sec->s_relptr = 0; /* file ptr to relocation */ + coff_sec->s_lnnoptr = 0; /* file ptr to line numbers */ + coff_sec->s_nreloc = 0; /* number of relocation entries */ + coff_sec->s_flags = GetCoffFlags(coff_sec->s_name); /* flags */ + coff_sec->s_reserved = 0; /* reserved byte */ + coff_sec->s_page = 0; /* memory page id */ + + file_pointer += sizeof(SCNHDR); + } + } + + file_hdr.f_nscns = NSectionsToOutput; /* number of sections */ + + // now loop through and determine file pointer locations + // for the raw data + + + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + if (OutputTheSection(tcc_sect)) { + // put raw data + coff_sec->s_scnptr = file_pointer; /* file ptr to raw data for section */ + file_pointer += coff_sec->s_size; + } + } + + // now loop through and determine file pointer locations + // for the relocation data + + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + if (OutputTheSection(tcc_sect)) { + // put relocations data + if (coff_sec->s_nreloc > 0) { + coff_sec->s_relptr = file_pointer; /* file ptr to relocation */ + file_pointer += coff_sec->s_nreloc * sizeof(struct reloc); + } + } + } + + // now loop through and determine file pointer locations + // for the line number data + + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + coff_sec->s_nlnno = 0; + coff_sec->s_lnnoptr = 0; + + if (s1->do_debug && tcc_sect == stext) { + // count how many line nos data + + // also find association between source file name and function + // so we can sort the symbol table + + + Stab_Sym *sym, *sym_end; + char func_name[MAX_FUNC_NAME_LENGTH], + last_func_name[MAX_FUNC_NAME_LENGTH]; + unsigned long func_addr, last_pc, pc; + const char *incl_files[INCLUDE_STACK_SIZE]; + int incl_index, len, last_line_num; + const char *str, *p; + + coff_sec->s_lnnoptr = file_pointer; /* file ptr to linno */ + + + func_name[0] = '\0'; + func_addr = 0; + incl_index = 0; + last_func_name[0] = '\0'; + last_pc = 0xffffffff; + last_line_num = 1; + sym = (Stab_Sym *) stab_section->data + 1; + sym_end = + (Stab_Sym *) (stab_section->data + + stab_section->data_offset); + + nFuncs = 0; + while (sym < sym_end) { + switch (sym->n_type) { + /* function start or end */ + case N_FUN: + if (sym->n_strx == 0) { + // end of function + + coff_sec->s_nlnno++; + file_pointer += LINESZ; + + pc = sym->n_value + func_addr; + func_name[0] = '\0'; + func_addr = 0; + EndAddress[nFuncs] = pc; + FuncEntries[nFuncs] = + (file_pointer - + LineNoFilePtr[nFuncs]) / LINESZ - 1; + LastLineNo[nFuncs++] = last_line_num + 1; + } else { + // beginning of function + + LineNoFilePtr[nFuncs] = file_pointer; + coff_sec->s_nlnno++; + file_pointer += LINESZ; + + str = + (const char *) stabstr_section->data + + sym->n_strx; + + p = strchr(str, ':'); + if (!p) { + pstrcpy(func_name, sizeof(func_name), str); + pstrcpy(Func[nFuncs], sizeof(func_name), str); + } else { + len = p - str; + if (len > sizeof(func_name) - 1) + len = sizeof(func_name) - 1; + memcpy(func_name, str, len); + memcpy(Func[nFuncs], str, len); + func_name[len] = '\0'; + } + + // save the file that it came in so we can sort later + pstrcpy(AssociatedFile[nFuncs], sizeof(func_name), + incl_files[incl_index - 1]); + + func_addr = sym->n_value; + } + break; + + /* line number info */ + case N_SLINE: + pc = sym->n_value + func_addr; + + last_pc = pc; + last_line_num = sym->n_desc; + + /* XXX: slow! */ + strcpy(last_func_name, func_name); + + coff_sec->s_nlnno++; + file_pointer += LINESZ; + break; + /* include files */ + case N_BINCL: + str = + (const char *) stabstr_section->data + sym->n_strx; + add_incl: + if (incl_index < INCLUDE_STACK_SIZE) { + incl_files[incl_index++] = str; + } + break; + case N_EINCL: + if (incl_index > 1) + incl_index--; + break; + case N_SO: + if (sym->n_strx == 0) { + incl_index = 0; /* end of translation unit */ + } else { + str = + (const char *) stabstr_section->data + + sym->n_strx; + /* do not add path */ + len = strlen(str); + if (len > 0 && str[len - 1] != '/') + goto add_incl; + } + break; + } + sym++; + } + } + + } + + file_hdr.f_symptr = file_pointer; /* file pointer to symtab */ + + if (s1->do_debug) + file_hdr.f_nsyms = coff_nb_syms; /* number of symtab entries */ + else + file_hdr.f_nsyms = 0; + + file_pointer += file_hdr.f_nsyms * SYMNMLEN; + + // OK now we are all set to write the file + + + fwrite(&file_hdr, FILHSZ, 1, f); + fwrite(&o_filehdr, sizeof(o_filehdr), 1, f); + + // write section headers + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + if (OutputTheSection(tcc_sect)) { + fwrite(coff_sec, sizeof(SCNHDR), 1, f); + } + } + + // write raw data + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + if (OutputTheSection(tcc_sect)) { + fwrite(tcc_sect->data, tcc_sect->data_offset, 1, f); + } + } + + // write relocation data + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + if (OutputTheSection(tcc_sect)) { + // put relocations data + if (coff_sec->s_nreloc > 0) { + fwrite(tcc_sect->reloc, + coff_sec->s_nreloc * sizeof(struct reloc), 1, f); + } + } + } + + + // group the symbols in order of filename, func1, func2, etc + // finally global symbols + + if (s1->do_debug) + SortSymbolTable(); + + // write line no data + + for (i = 1; i < s1->nb_sections; i++) { + coff_sec = §ion_header[i]; + tcc_sect = s1->sections[i]; + + if (s1->do_debug && tcc_sect == stext) { + // count how many line nos data + + + Stab_Sym *sym, *sym_end; + char func_name[128], last_func_name[128]; + unsigned long func_addr, last_pc, pc; + const char *incl_files[INCLUDE_STACK_SIZE]; + int incl_index, len, last_line_num; + const char *str, *p; + + LINENO CoffLineNo; + + func_name[0] = '\0'; + func_addr = 0; + incl_index = 0; + last_func_name[0] = '\0'; + last_pc = 0; + last_line_num = 1; + sym = (Stab_Sym *) stab_section->data + 1; + sym_end = + (Stab_Sym *) (stab_section->data + + stab_section->data_offset); + + while (sym < sym_end) { + switch (sym->n_type) { + /* function start or end */ + case N_FUN: + if (sym->n_strx == 0) { + // end of function + + CoffLineNo.l_addr.l_paddr = last_pc; + CoffLineNo.l_lnno = last_line_num + 1; + fwrite(&CoffLineNo, 6, 1, f); + + pc = sym->n_value + func_addr; + func_name[0] = '\0'; + func_addr = 0; + } else { + // beginning of function + + str = + (const char *) stabstr_section->data + + sym->n_strx; + + + p = strchr(str, ':'); + if (!p) { + pstrcpy(func_name, sizeof(func_name), str); + } else { + len = p - str; + if (len > sizeof(func_name) - 1) + len = sizeof(func_name) - 1; + memcpy(func_name, str, len); + func_name[len] = '\0'; + } + func_addr = sym->n_value; + last_pc = func_addr; + last_line_num = -1; + + // output a function begin + + CoffLineNo.l_addr.l_symndx = + FindCoffSymbolIndex(func_name); + CoffLineNo.l_lnno = 0; + + fwrite(&CoffLineNo, 6, 1, f); + } + break; + + /* line number info */ + case N_SLINE: + pc = sym->n_value + func_addr; + + + /* XXX: slow! */ + strcpy(last_func_name, func_name); + + // output a line reference + + CoffLineNo.l_addr.l_paddr = last_pc; + + if (last_line_num == -1) { + CoffLineNo.l_lnno = sym->n_desc; + } else { + CoffLineNo.l_lnno = last_line_num + 1; + } + + fwrite(&CoffLineNo, 6, 1, f); + + last_pc = pc; + last_line_num = sym->n_desc; + + break; + + /* include files */ + case N_BINCL: + str = + (const char *) stabstr_section->data + sym->n_strx; + add_incl2: + if (incl_index < INCLUDE_STACK_SIZE) { + incl_files[incl_index++] = str; + } + break; + case N_EINCL: + if (incl_index > 1) + incl_index--; + break; + case N_SO: + if (sym->n_strx == 0) { + incl_index = 0; /* end of translation unit */ + } else { + str = + (const char *) stabstr_section->data + + sym->n_strx; + /* do not add path */ + len = strlen(str); + if (len > 0 && str[len - 1] != '/') + goto add_incl2; + } + break; + } + sym++; + } + } + } + + // write symbol table + if (s1->do_debug) { + int k; + struct syment csym; + AUXFUNC auxfunc; + AUXBF auxbf; + AUXEF auxef; + int i; + Elf32_Sym *p; + const char *name; + int nstr; + int n = 0; + + Coff_str_table = (char *) tcc_malloc(MAX_STR_TABLE); + pCoff_str_table = Coff_str_table; + nstr = 0; + + p = (Elf32_Sym *) symtab_section->data; + + + for (i = 0; i < nb_syms; i++) { + + name = symtab_section->link->data + p->st_name; + + for (k = 0; k < 8; k++) + csym._n._n_name[k] = 0; + + if (strlen(name) <= 8) { + strcpy(csym._n._n_name, name); + } else { + if (pCoff_str_table - Coff_str_table + strlen(name) > + MAX_STR_TABLE - 1) + error("String table too large"); + + csym._n._n_n._n_zeroes = 0; + csym._n._n_n._n_offset = + pCoff_str_table - Coff_str_table + 4; + + strcpy(pCoff_str_table, name); + pCoff_str_table += strlen(name) + 1; // skip over null + nstr++; + } + + if (p->st_info == 4) { + // put a filename symbol + csym.n_value = 33; // ????? + csym.n_scnum = N_DEBUG; + csym.n_type = 0; + csym.n_sclass = C_FILE; + csym.n_numaux = 0; + fwrite(&csym, 18, 1, f); + n++; + + } else if (p->st_info == 0x12) { + // find the function data + + for (k = 0; k < nFuncs; k++) { + if (strcmp(name, Func[k]) == 0) + break; + } + + if (k >= nFuncs) { + char s[256]; + + sprintf(s, "debug info can't find function: %s", name); + + error(s); + } + // put a Function Name + + csym.n_value = p->st_value; // physical address + csym.n_scnum = CoffTextSectionNo; + csym.n_type = MKTYPE(T_INT, DT_FCN, 0, 0, 0, 0, 0); + csym.n_sclass = C_EXT; + csym.n_numaux = 1; + fwrite(&csym, 18, 1, f); + + // now put aux info + + auxfunc.tag = 0; + auxfunc.size = EndAddress[k] - p->st_value; + auxfunc.fileptr = LineNoFilePtr[k]; + auxfunc.nextsym = n + 6; // tktk + auxfunc.dummy = 0; + fwrite(&auxfunc, 18, 1, f); + + // put a .bf + + strcpy(csym._n._n_name, ".bf"); + csym.n_value = p->st_value; // physical address + csym.n_scnum = CoffTextSectionNo; + csym.n_type = 0; + csym.n_sclass = C_FCN; + csym.n_numaux = 1; + fwrite(&csym, 18, 1, f); + + // now put aux info + + auxbf.regmask = 0; + auxbf.lineno = 0; + auxbf.nentries = FuncEntries[k]; + auxbf.localframe = 0; + auxbf.nextentry = n + 6; + auxbf.dummy = 0; + fwrite(&auxbf, 18, 1, f); + + // put a .ef + + strcpy(csym._n._n_name, ".ef"); + csym.n_value = EndAddress[k]; // physical address + csym.n_scnum = CoffTextSectionNo; + csym.n_type = 0; + csym.n_sclass = C_FCN; + csym.n_numaux = 1; + fwrite(&csym, 18, 1, f); + + // now put aux info + + auxef.dummy = 0; + auxef.lineno = LastLineNo[k]; + auxef.dummy1 = 0; + auxef.dummy2 = 0; + auxef.dummy3 = 0; + auxef.dummy4 = 0; + fwrite(&auxef, 18, 1, f); + + n += 6; + + } else { + // try an put some type info + + if ((p->st_other & VT_BTYPE) == VT_DOUBLE) { + csym.n_type = T_DOUBLE; // int + csym.n_sclass = C_EXT; + } else if ((p->st_other & VT_BTYPE) == VT_FLOAT) { + csym.n_type = T_FLOAT; + csym.n_sclass = C_EXT; + } else if ((p->st_other & VT_BTYPE) == VT_INT) { + csym.n_type = T_INT; // int + csym.n_sclass = C_EXT; + } else if ((p->st_other & VT_BTYPE) == VT_SHORT) { + csym.n_type = T_SHORT; + csym.n_sclass = C_EXT; + } else if ((p->st_other & VT_BTYPE) == VT_BYTE) { + csym.n_type = T_CHAR; + csym.n_sclass = C_EXT; + } else { + csym.n_type = T_INT; // just mark as a label + csym.n_sclass = C_LABEL; + } + + + csym.n_value = p->st_value; + csym.n_scnum = 2; + csym.n_numaux = 1; + fwrite(&csym, 18, 1, f); + + auxfunc.tag = 0; + auxfunc.size = 0x20; + auxfunc.fileptr = 0; + auxfunc.nextsym = 0; + auxfunc.dummy = 0; + fwrite(&auxfunc, 18, 1, f); + n++; + n++; + + } + + p++; + } + } + + if (s1->do_debug) { + // write string table + + // first write the size + i = pCoff_str_table - Coff_str_table; + fwrite(&i, 4, 1, f); + + // then write the strings + fwrite(Coff_str_table, i, 1, f); + + tcc_free(Coff_str_table); + } + + return 0; +} + + + +// group the symbols in order of filename, func1, func2, etc +// finally global symbols + +void SortSymbolTable(void) +{ + int i, j, k, n = 0; + Elf32_Sym *p, *p2, *NewTable; + char *name, *name2; + + NewTable = (Elf32_Sym *) tcc_malloc(nb_syms * sizeof(Elf32_Sym)); + + p = (Elf32_Sym *) symtab_section->data; + + + // find a file symbol, copy it over + // then scan the whole symbol list and copy any function + // symbols that match the file association + + for (i = 0; i < nb_syms; i++) { + if (p->st_info == 4) { + name = (char *) symtab_section->link->data + p->st_name; + + // this is a file symbol, copy it over + + NewTable[n++] = *p; + + p2 = (Elf32_Sym *) symtab_section->data; + + for (j = 0; j < nb_syms; j++) { + if (p2->st_info == 0x12) { + // this is a func symbol + + name2 = + (char *) symtab_section->link->data + p2->st_name; + + // find the function data index + + for (k = 0; k < nFuncs; k++) { + if (strcmp(name2, Func[k]) == 0) + break; + } + + if (k >= nFuncs) { + char s[256]; + + sprintf(s, + "debug (sort) info can't find function: %s", + name2); + + error(s); + } + + if (strcmp(AssociatedFile[k], name) == 0) { + // yes they match copy it over + + NewTable[n++] = *p2; + } + } + p2++; + } + } + p++; + } + + // now all the filename and func symbols should have been copied over + // copy all the rest over (all except file and funcs) + + p = (Elf32_Sym *) symtab_section->data; + for (i = 0; i < nb_syms; i++) { + if (p->st_info != 4 && p->st_info != 0x12) { + NewTable[n++] = *p; + } + p++; + } + + if (n != nb_syms) + error("Internal Compiler error, debug info"); + + // copy it all back + + p = (Elf32_Sym *) symtab_section->data; + for (i = 0; i < nb_syms; i++) { + *p++ = NewTable[i]; + } + + tcc_free(NewTable); +} + + +int FindCoffSymbolIndex(const char *func_name) +{ + int i, n = 0; + Elf32_Sym *p; + char *name; + + p = (Elf32_Sym *) symtab_section->data; + + for (i = 0; i < nb_syms; i++) { + + name = (char *) symtab_section->link->data + p->st_name; + + if (p->st_info == 4) { + // put a filename symbol + n++; + } else if (p->st_info == 0x12) { + + if (strcmp(func_name, name) == 0) + return n; + + n += 6; + + // put a Function Name + + // now put aux info + + // put a .bf + + // now put aux info + + // put a .ef + + // now put aux info + + } else { + n += 2; + } + + p++; + } + + return n; // total number of symbols +} + +BOOL OutputTheSection(Section * sect) +{ + const char *s = sect->name; + + if (!strcmp(s, ".text")) + return true; + else if (!strcmp(s, ".data")) + return true; + else + return 0; +} + +short int GetCoffFlags(const char *s) +{ + if (!strcmp(s, ".text")) + return STYP_TEXT | STYP_DATA | STYP_ALIGN | 0x400; + else if (!strcmp(s, ".data")) + return STYP_DATA; + else if (!strcmp(s, ".bss")) + return STYP_BSS; + else if (!strcmp(s, ".stack")) + return STYP_BSS | STYP_ALIGN | 0x200; + else if (!strcmp(s, ".cinit")) + return STYP_COPY | STYP_DATA | STYP_ALIGN | 0x200; + else + return 0; +} + +Section *FindSection(TCCState * s1, const char *sname) +{ + Section *s; + int i; + + for (i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; + + if (!strcmp(sname, s->name)) + return s; + } + + error("could not find section %s", sname); + return 0; +} + +int tcc_load_coff(TCCState * s1, int fd) +{ +// tktk TokenSym *ts; + + FILE *f; + unsigned int str_size; + char *Coff_str_table, *name; + int i, k; + struct syment csym; + char name2[9]; + FILHDR file_hdr; /* FILE HEADER STRUCTURE */ + + f = fdopen(fd, "rb"); + if (!f) { + error("Unable to open .out file for input"); + } + + if (fread(&file_hdr, FILHSZ, 1, f) != 1) + error("error reading .out file for input"); + + if (fread(&o_filehdr, sizeof(o_filehdr), 1, f) != 1) + error("error reading .out file for input"); + + // first read the string table + + if (fseek(f, file_hdr.f_symptr + file_hdr.f_nsyms * SYMESZ, SEEK_SET)) + error("error reading .out file for input"); + + if (fread(&str_size, sizeof(int), 1, f) != 1) + error("error reading .out file for input"); + + + Coff_str_table = (char *) tcc_malloc(str_size); + + if (fread(Coff_str_table, str_size - 4, 1, f) != 1) + error("error reading .out file for input"); + + // read/process all the symbols + + // seek back to symbols + + if (fseek(f, file_hdr.f_symptr, SEEK_SET)) + error("error reading .out file for input"); + + for (i = 0; i < file_hdr.f_nsyms; i++) { + if (fread(&csym, SYMESZ, 1, f) != 1) + error("error reading .out file for input"); + + if (csym._n._n_n._n_zeroes == 0) { + name = Coff_str_table + csym._n._n_n._n_offset - 4; + } else { + name = csym._n._n_name; + + if (name[7] != 0) { + for (k = 0; k < 8; k++) + name2[k] = name[k]; + + name2[8] = 0; + + name = name2; + } + } +// if (strcmp("_DAC_Buffer",name)==0) // tktk +// name[0]=0; + + if (((csym.n_type & 0x30) == 0x20 && csym.n_sclass == 0x2) || ((csym.n_type & 0x30) == 0x30 && csym.n_sclass == 0x2) || (csym.n_type == 0x4 && csym.n_sclass == 0x2) || (csym.n_type == 0x8 && csym.n_sclass == 0x2) || // structures + (csym.n_type == 0x18 && csym.n_sclass == 0x2) || // pointer to structure + (csym.n_type == 0x7 && csym.n_sclass == 0x2) || // doubles + (csym.n_type == 0x6 && csym.n_sclass == 0x2)) // floats + { + // strip off any leading underscore (except for other main routine) + + if (name[0] == '_' && strcmp(name, "_main") != 0) + name++; + + tcc_add_symbol(s1, name, (void*)csym.n_value); + } + // skip any aux records + + if (csym.n_numaux == 1) { + if (fread(&csym, SYMESZ, 1, f) != 1) + error("error reading .out file for input"); + i++; + } + } + + return 0; +} diff --git a/05/tcc-0.9.25/tccelf.c b/05/tcc-0.9.25/tccelf.c new file mode 100644 index 0000000..5da02e1 --- /dev/null +++ b/05/tcc-0.9.25/tccelf.c @@ -0,0 +1,2732 @@ +/* + * ELF file handling for TCC + * + * Copyright (c) 2001-2004 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef TCC_TARGET_X86_64 +#define ElfW_Rel ElfW(Rela) +#define SHT_RELX SHT_RELA +#define REL_SECTION_FMT ".rela%s" +/* x86-64 requires PLT for DLLs */ +#define TCC_OUTPUT_DLL_WITH_PLT +#else +#define ElfW_Rel ElfW(Rel) +#define SHT_RELX SHT_REL +#define REL_SECTION_FMT ".rel%s" +#endif + +/* XXX: DLL with PLT would only work with x86-64 for now */ +//#define TCC_OUTPUT_DLL_WITH_PLT + +static int put_elf_str(Section *s, const char *sym) +{ + int offset, len; + char *ptr; + + len = strlen(sym) + 1; + offset = s->data_offset; + ptr = section_ptr_add(s, len); + memcpy(ptr, sym, len); + return offset; +} + +/* elf symbol hashing function */ +static unsigned long elf_hash(const unsigned char *name) +{ + unsigned long h = 0, g; + + while (*name) { + h = (h << 4) + *name++; + g = h & 0xf0000000; + if (g) + h ^= g >> 24; + h &= ~g; + } + return h; +} + +/* rebuild hash table of section s */ +/* NOTE: we do factorize the hash table code to go faster */ +static void rebuild_hash(Section *s, unsigned int nb_buckets) +{ + ElfW(Sym) *sym; + int *ptr, *hash, nb_syms, sym_index, h; + char *strtab; + + strtab = s->link->data; + nb_syms = s->data_offset / sizeof(ElfW(Sym)); + + s->hash->data_offset = 0; + ptr = section_ptr_add(s->hash, (2 + nb_buckets + nb_syms) * sizeof(int)); + ptr[0] = nb_buckets; + ptr[1] = nb_syms; + ptr += 2; + hash = ptr; + memset(hash, 0, (nb_buckets + 1) * sizeof(int)); + ptr += nb_buckets + 1; + + sym = (ElfW(Sym) *)s->data + 1; + for(sym_index = 1; sym_index < nb_syms; sym_index++) { + if (ELF64_ST_BIND(sym->st_info) != STB_LOCAL) { + h = elf_hash(strtab + sym->st_name) % nb_buckets; + *ptr = hash[h]; + hash[h] = sym_index; + } else { + *ptr = 0; + } + ptr++; + sym++; + } +} + +/* return the symbol number */ +static int put_elf_sym(Section *s, + unsigned long value, unsigned long size, + int info, int other, int shndx, const char *name) +{ + int name_offset, sym_index; + int nbuckets, h; + ElfW(Sym) *sym; + Section *hs; + + sym = section_ptr_add(s, sizeof(ElfW(Sym))); + if (name) + name_offset = put_elf_str(s->link, name); + else + name_offset = 0; + /* XXX: endianness */ + sym->st_name = name_offset; + sym->st_value = value; + sym->st_size = size; + sym->st_info = info; + sym->st_other = other; + sym->st_shndx = shndx; + sym_index = sym - (ElfW(Sym) *)s->data; + hs = s->hash; + if (hs) { + int *ptr, *base; + ptr = section_ptr_add(hs, sizeof(int)); + base = (int *)hs->data; + /* only add global or weak symbols */ + if (ELF64_ST_BIND(info) != STB_LOCAL) { + /* add another hashing entry */ + nbuckets = base[0]; + h = elf_hash(name) % nbuckets; + *ptr = base[2 + h]; + base[2 + h] = sym_index; + base[1]++; + /* we resize the hash table */ + hs->nb_hashed_syms++; + if (hs->nb_hashed_syms > 2 * nbuckets) { + rebuild_hash(s, 2 * nbuckets); + } + } else { + *ptr = 0; + base[1]++; + } + } + return sym_index; +} + +/* find global ELF symbol 'name' and return its index. Return 0 if not + found. */ +static int find_elf_sym(Section *s, const char *name) +{ + ElfW(Sym) *sym; + Section *hs; + int nbuckets, sym_index, h; + const char *name1; + + hs = s->hash; + if (!hs) + return 0; + nbuckets = ((int *)hs->data)[0]; + h = elf_hash(name) % nbuckets; + sym_index = ((int *)hs->data)[2 + h]; + while (sym_index != 0) { + sym = &((ElfW(Sym) *)s->data)[sym_index]; + name1 = s->link->data + sym->st_name; + if (!strcmp(name, name1)) + return sym_index; + sym_index = ((int *)hs->data)[2 + nbuckets + sym_index]; + } + return 0; +} + +/* return elf symbol value or error */ +void *tcc_get_symbol(TCCState *s, const char *name) +{ + int sym_index; + ElfW(Sym) *sym; + sym_index = find_elf_sym(symtab_section, name); + if (!sym_index) + return NULL; + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + return (void*)(long)sym->st_value; +} + +void *tcc_get_symbol_err(TCCState *s, const char *name) +{ + void *sym; + sym = tcc_get_symbol(s, name); + if (!sym) + error("%s not defined", name); + return sym; +} + +/* add an elf symbol : check if it is already defined and patch + it. Return symbol index. NOTE that sh_num can be SHN_UNDEF. */ +static int add_elf_sym(Section *s, unsigned long value, unsigned long size, + int info, int other, int sh_num, const char *name) +{ + ElfW(Sym) *esym; + int sym_bind, sym_index, sym_type, esym_bind; + unsigned char sym_vis, esym_vis, new_vis; + + sym_bind = ELF64_ST_BIND(info); + sym_type = ELF64_ST_TYPE(info); + sym_vis = ELF64_ST_VISIBILITY(other); + + if (sym_bind != STB_LOCAL) { + /* we search global or weak symbols */ + sym_index = find_elf_sym(s, name); + if (!sym_index) + goto do_def; + esym = &((ElfW(Sym) *)s->data)[sym_index]; + if (esym->st_shndx != SHN_UNDEF) { + esym_bind = ELF64_ST_BIND(esym->st_info); + /* propagate the most constraining visibility */ + /* STV_DEFAULT(0)st_other); + if (esym_vis == STV_DEFAULT) { + new_vis = sym_vis; + } else if (sym_vis == STV_DEFAULT) { + new_vis = esym_vis; + } else { + new_vis = (esym_vis < sym_vis) ? esym_vis : sym_vis; + } + esym->st_other = (esym->st_other & ~ELF64_ST_VISIBILITY(-1)) + | new_vis; + other = esym->st_other; /* in case we have to patch esym */ + if (sh_num == SHN_UNDEF) { + /* ignore adding of undefined symbol if the + corresponding symbol is already defined */ + } else if (sym_bind == STB_GLOBAL && esym_bind == STB_WEAK) { + /* global overrides weak, so patch */ + goto do_patch; + } else if (sym_bind == STB_WEAK && esym_bind == STB_GLOBAL) { + /* weak is ignored if already global */ + } else if (sym_vis == STV_HIDDEN || sym_vis == STV_INTERNAL) { + /* ignore hidden symbols after */ + } else if (esym->st_shndx == SHN_COMMON && sh_num < SHN_LORESERVE) { + /* gr: Happens with 'tcc ... -static tcctest.c' on e.g. Ubuntu 6.01 + No idea if this is the correct solution ... */ + goto do_patch; + } else if (s == tcc_state->dynsymtab_section) { + /* we accept that two DLL define the same symbol */ + } else { +#if 1 + printf("new_bind=%x new_shndx=%x new_vis=%x old_bind=%x old_shndx=%x old_vis=%x\n", + sym_bind, sh_num, new_vis, esym_bind, esym->st_shndx, esym_vis); +#endif + error_noabort("'%s' defined twice", name); + } + } else { + do_patch: + esym->st_info = ELF64_ST_INFO(sym_bind, sym_type); + esym->st_shndx = sh_num; + esym->st_value = value; + esym->st_size = size; + esym->st_other = other; + } + } else { + do_def: + sym_index = put_elf_sym(s, value, size, + ELF64_ST_INFO(sym_bind, sym_type), other, + sh_num, name); + } + return sym_index; +} + +/* put relocation */ +static void put_elf_reloc(Section *symtab, Section *s, unsigned long offset, + int type, int symbol) +{ + char buf[256]; + Section *sr; + ElfW_Rel *rel; + + sr = s->reloc; + if (!sr) { + /* if no relocation section, create it */ + snprintf(buf, sizeof(buf), REL_SECTION_FMT, s->name); + /* if the symtab is allocated, then we consider the relocation + are also */ + sr = new_section(tcc_state, buf, SHT_RELX, symtab->sh_flags); + sr->sh_entsize = sizeof(ElfW_Rel); + sr->link = symtab; + sr->sh_info = s->sh_num; + s->reloc = sr; + } + rel = section_ptr_add(sr, sizeof(ElfW_Rel)); + rel->r_offset = offset; + rel->r_info = ELF64_R_INFO(symbol, type); +#ifdef TCC_TARGET_X86_64 + rel->r_addend = 0; +#endif +} + +/* put stab debug information */ + +typedef struct { + unsigned int n_strx; /* index into string table of name */ + unsigned char n_type; /* type of symbol */ + unsigned char n_other; /* misc info (usually empty) */ + unsigned short n_desc; /* description field */ + unsigned int n_value; /* value of symbol */ +} Stab_Sym; + +static void put_stabs(const char *str, int type, int other, int desc, + unsigned long value) +{ + Stab_Sym *sym; + + sym = section_ptr_add(stab_section, sizeof(Stab_Sym)); + if (str) { + sym->n_strx = put_elf_str(stabstr_section, str); + } else { + sym->n_strx = 0; + } + sym->n_type = type; + sym->n_other = other; + sym->n_desc = desc; + sym->n_value = value; +} + +static void put_stabs_r(const char *str, int type, int other, int desc, + unsigned long value, Section *sec, int sym_index) +{ + put_stabs(str, type, other, desc, value); + put_elf_reloc(symtab_section, stab_section, + stab_section->data_offset - sizeof(unsigned int), + R_DATA_32, sym_index); +} + +static void put_stabn(int type, int other, int desc, int value) +{ + put_stabs(NULL, type, other, desc, value); +} + +static void put_stabd(int type, int other, int desc) +{ + put_stabs(NULL, type, other, desc, 0); +} + +/* In an ELF file symbol table, the local symbols must appear below + the global and weak ones. Since TCC cannot sort it while generating + the code, we must do it after. All the relocation tables are also + modified to take into account the symbol table sorting */ +static void sort_syms(TCCState *s1, Section *s) +{ + int *old_to_new_syms; + ElfW(Sym) *new_syms; + int nb_syms, i; + ElfW(Sym) *p, *q; + ElfW_Rel *rel, *rel_end; + Section *sr; + int type, sym_index; + + nb_syms = s->data_offset / sizeof(ElfW(Sym)); + new_syms = tcc_malloc(nb_syms * sizeof(ElfW(Sym))); + old_to_new_syms = tcc_malloc(nb_syms * sizeof(int)); + + /* first pass for local symbols */ + p = (ElfW(Sym) *)s->data; + q = new_syms; + for(i = 0; i < nb_syms; i++) { + if (ELF64_ST_BIND(p->st_info) == STB_LOCAL) { + old_to_new_syms[i] = q - new_syms; + *q++ = *p; + } + p++; + } + /* save the number of local symbols in section header */ + s->sh_info = q - new_syms; + + /* then second pass for non local symbols */ + p = (ElfW(Sym) *)s->data; + for(i = 0; i < nb_syms; i++) { + if (ELF64_ST_BIND(p->st_info) != STB_LOCAL) { + old_to_new_syms[i] = q - new_syms; + *q++ = *p; + } + p++; + } + + /* we copy the new symbols to the old */ + memcpy(s->data, new_syms, nb_syms * sizeof(ElfW(Sym))); + tcc_free(new_syms); + + /* now we modify all the relocations */ + for(i = 1; i < s1->nb_sections; i++) { + sr = s1->sections[i]; + if (sr->sh_type == SHT_RELX && sr->link == s) { + rel_end = (ElfW_Rel *)(sr->data + sr->data_offset); + for(rel = (ElfW_Rel *)sr->data; + rel < rel_end; + rel++) { + sym_index = ELF64_R_SYM(rel->r_info); + type = ELF64_R_TYPE(rel->r_info); + sym_index = old_to_new_syms[sym_index]; + rel->r_info = ELF64_R_INFO(sym_index, type); + } + } + } + + tcc_free(old_to_new_syms); +} + +/* relocate common symbols in the .bss section */ +static void relocate_common_syms(void) +{ + ElfW(Sym) *sym, *sym_end; + unsigned long offset, align; + + sym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset); + for(sym = (ElfW(Sym) *)symtab_section->data + 1; + sym < sym_end; + sym++) { + if (sym->st_shndx == SHN_COMMON) { + /* align symbol */ + align = sym->st_value; + offset = bss_section->data_offset; + offset = (offset + align - 1) & -align; + sym->st_value = offset; + sym->st_shndx = bss_section->sh_num; + offset += sym->st_size; + bss_section->data_offset = offset; + } + } +} + +/* relocate symbol table, resolve undefined symbols if do_resolve is + true and output error if undefined symbol. */ +static void relocate_syms(TCCState *s1, int do_resolve) +{ + ElfW(Sym) *sym, *esym, *sym_end; + int sym_bind, sh_num, sym_index; + const char *name; + unsigned long addr; + + sym_end = (ElfW(Sym) *)(symtab_section->data + symtab_section->data_offset); + for(sym = (ElfW(Sym) *)symtab_section->data + 1; + sym < sym_end; + sym++) { + sh_num = sym->st_shndx; + if (sh_num == SHN_UNDEF) { + name = strtab_section->data + sym->st_name; + if (do_resolve) { + name = symtab_section->link->data + sym->st_name; + addr = (unsigned long)resolve_sym(s1, name, ELF64_ST_TYPE(sym->st_info)); + if (addr) { + sym->st_value = addr; + goto found; + } + } else if (s1->dynsym) { + /* if dynamic symbol exist, then use it */ + sym_index = find_elf_sym(s1->dynsym, name); + if (sym_index) { + esym = &((ElfW(Sym) *)s1->dynsym->data)[sym_index]; + sym->st_value = esym->st_value; + goto found; + } + } + /* XXX: _fp_hw seems to be part of the ABI, so we ignore + it */ + if (!strcmp(name, "_fp_hw")) + goto found; + /* only weak symbols are accepted to be undefined. Their + value is zero */ + sym_bind = ELF64_ST_BIND(sym->st_info); + if (sym_bind == STB_WEAK) { + sym->st_value = 0; + } else { + error_noabort("undefined symbol '%s'", name); + } + } else if (sh_num < SHN_LORESERVE) { + /* add section base */ + sym->st_value += s1->sections[sym->st_shndx]->sh_addr; + } + found: ; + } +} + +#ifdef TCC_TARGET_X86_64 +#define JMP_TABLE_ENTRY_SIZE 14 +static unsigned long add_jmp_table(TCCState *s1, unsigned long val) +{ + char *p = s1->runtime_plt_and_got + s1->runtime_plt_and_got_offset; + s1->runtime_plt_and_got_offset += JMP_TABLE_ENTRY_SIZE; + /* jmp *0x0(%rip) */ + p[0] = 0xff; + p[1] = 0x25; + *(int *)(p + 2) = 0; + *(unsigned long *)(p + 6) = val; + return (unsigned long)p; +} + +static unsigned long add_got_table(TCCState *s1, unsigned long val) +{ + unsigned long *p =(unsigned long *)(s1->runtime_plt_and_got + + s1->runtime_plt_and_got_offset); + s1->runtime_plt_and_got_offset += sizeof(void *); + *p = val; + return (unsigned long)p; +} +#endif + +/* relocate a given section (CPU dependent) */ +static void relocate_section(TCCState *s1, Section *s) +{ + Section *sr; + ElfW_Rel *rel, *rel_end, *qrel; + ElfW(Sym) *sym; + int type, sym_index; + unsigned char *ptr; + unsigned long val, addr; +#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 + int esym_index; +#endif + + sr = s->reloc; + rel_end = (ElfW_Rel *)(sr->data + sr->data_offset); + qrel = (ElfW_Rel *)sr->data; + for(rel = qrel; + rel < rel_end; + rel++) { + ptr = s->data + rel->r_offset; + + sym_index = ELF64_R_SYM(rel->r_info); + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + val = sym->st_value; +#ifdef TCC_TARGET_X86_64 + /* XXX: not tested */ + val += rel->r_addend; +#endif + type = ELF64_R_TYPE(rel->r_info); + addr = s->sh_addr + rel->r_offset; + + /* CPU specific */ + switch(type) { +#if defined(TCC_TARGET_I386) + case R_386_32: + if (s1->output_type == TCC_OUTPUT_DLL) { + esym_index = s1->symtab_to_dynsym[sym_index]; + qrel->r_offset = rel->r_offset; + if (esym_index) { + qrel->r_info = ELF64_R_INFO(esym_index, R_386_32); + qrel++; + break; + } else { + qrel->r_info = ELF64_R_INFO(0, R_386_RELATIVE); + qrel++; + } + } + *(int *)ptr += val; + break; + case R_386_PC32: + if (s1->output_type == TCC_OUTPUT_DLL) { + /* DLL relocation */ + esym_index = s1->symtab_to_dynsym[sym_index]; + if (esym_index) { + qrel->r_offset = rel->r_offset; + qrel->r_info = ELF64_R_INFO(esym_index, R_386_PC32); + qrel++; + break; + } + } + *(int *)ptr += val - addr; + break; + case R_386_PLT32: + *(int *)ptr += val - addr; + break; + case R_386_GLOB_DAT: + case R_386_JMP_SLOT: + *(int *)ptr = val; + break; + case R_386_GOTPC: + *(int *)ptr += s1->got->sh_addr - addr; + break; + case R_386_GOTOFF: + *(int *)ptr += val - s1->got->sh_addr; + break; + case R_386_GOT32: + /* we load the got offset */ + *(int *)ptr += s1->got_offsets[sym_index]; + break; +#elif defined(TCC_TARGET_ARM) + case R_ARM_PC24: + case R_ARM_CALL: + case R_ARM_JUMP24: + case R_ARM_PLT32: + { + int x; + x = (*(int *)ptr)&0xffffff; + (*(int *)ptr) &= 0xff000000; + if (x & 0x800000) + x -= 0x1000000; + x *= 4; + x += val - addr; + if((x & 3) != 0 || x >= 0x4000000 || x < -0x4000000) + error("can't relocate value at %x",addr); + x >>= 2; + x &= 0xffffff; + (*(int *)ptr) |= x; + } + break; + case R_ARM_PREL31: + { + int x; + x = (*(int *)ptr) & 0x7fffffff; + (*(int *)ptr) &= 0x80000000; + x = (x * 2) / 2; + x += val - addr; + if((x^(x>>1))&0x40000000) + error("can't relocate value at %x",addr); + (*(int *)ptr) |= x & 0x7fffffff; + } + case R_ARM_ABS32: + *(int *)ptr += val; + break; + case R_ARM_BASE_PREL: + *(int *)ptr += s1->got->sh_addr - addr; + break; + case R_ARM_GOTOFF32: + *(int *)ptr += val - s1->got->sh_addr; + break; + case R_ARM_GOT_BREL: + /* we load the got offset */ + *(int *)ptr += s1->got_offsets[sym_index]; + break; + case R_ARM_COPY: + break; + default: + fprintf(stderr,"FIXME: handle reloc type %x at %lx [%.8x] to %lx\n", + type,addr,(unsigned int )ptr,val); + break; +#elif defined(TCC_TARGET_C67) + case R_C60_32: + *(int *)ptr += val; + break; + case R_C60LO16: + { + uint32_t orig; + + /* put the low 16 bits of the absolute address */ + // add to what is already there + + orig = ((*(int *)(ptr )) >> 7) & 0xffff; + orig |= (((*(int *)(ptr+4)) >> 7) & 0xffff) << 16; + + //patch both at once - assumes always in pairs Low - High + + *(int *) ptr = (*(int *) ptr & (~(0xffff << 7)) ) | (((val+orig) & 0xffff) << 7); + *(int *)(ptr+4) = (*(int *)(ptr+4) & (~(0xffff << 7)) ) | ((((val+orig)>>16) & 0xffff) << 7); + } + break; + case R_C60HI16: + break; + default: + fprintf(stderr,"FIXME: handle reloc type %x at %lx [%.8x] to %lx\n", + type,addr,(unsigned int )ptr,val); + break; +#elif defined(TCC_TARGET_X86_64) + case R_X86_64_64: + if (s1->output_type == TCC_OUTPUT_DLL) { + qrel->r_info = ELF64_R_INFO(0, R_X86_64_RELATIVE); + qrel->r_addend = *(long long *)ptr + val; + qrel++; + } + *(long long *)ptr += val; + break; + case R_X86_64_32: + case R_X86_64_32S: + if (s1->output_type == TCC_OUTPUT_DLL) { + /* XXX: this logic may depend on TCC's codegen + now TCC uses R_X86_64_32 even for a 64bit pointer */ + qrel->r_info = ELF64_R_INFO(0, R_X86_64_RELATIVE); + qrel->r_addend = *(int *)ptr + val; + qrel++; + } + *(int *)ptr += val; + break; + case R_X86_64_PC32: { + if (s1->output_type == TCC_OUTPUT_DLL) { + /* DLL relocation */ + esym_index = s1->symtab_to_dynsym[sym_index]; + if (esym_index) { + qrel->r_offset = rel->r_offset; + qrel->r_info = ELF64_R_INFO(esym_index, R_X86_64_PC32); + qrel->r_addend = *(int *)ptr; + qrel++; + break; + } + } + long diff = val - addr; + if (diff <= -2147483647 || diff > 2147483647) { + /* XXX: naive support for over 32bit jump */ + if (s1->output_type == TCC_OUTPUT_MEMORY) { + val = add_jmp_table(s1, val); + diff = val - addr; + } + if (diff <= -2147483647 || diff > 2147483647) { + error("internal error: relocation failed"); + } + } + *(int *)ptr += diff; + } + break; + case R_X86_64_PLT32: + *(int *)ptr += val - addr; + break; + case R_X86_64_GLOB_DAT: + case R_X86_64_JUMP_SLOT: + *(int *)ptr = val; + break; + case R_X86_64_GOTPCREL: + if (s1->output_type == TCC_OUTPUT_MEMORY) { + val = add_got_table(s1, val - rel->r_addend) + rel->r_addend; + *(int *)ptr += val - addr; + break; + } + *(int *)ptr += (s1->got->sh_addr - addr + + s1->got_offsets[sym_index] - 4); + break; + case R_X86_64_GOTTPOFF: + *(int *)ptr += val - s1->got->sh_addr; + break; + case R_X86_64_GOT32: + /* we load the got offset */ + *(int *)ptr += s1->got_offsets[sym_index]; + break; +#else +#error unsupported processor +#endif + } + } + /* if the relocation is allocated, we change its symbol table */ + if (sr->sh_flags & SHF_ALLOC) + sr->link = s1->dynsym; +} + +/* relocate relocation table in 'sr' */ +static void relocate_rel(TCCState *s1, Section *sr) +{ + Section *s; + ElfW_Rel *rel, *rel_end; + + s = s1->sections[sr->sh_info]; + rel_end = (ElfW_Rel *)(sr->data + sr->data_offset); + for(rel = (ElfW_Rel *)sr->data; + rel < rel_end; + rel++) { + rel->r_offset += s->sh_addr; + } +} + +/* count the number of dynamic relocations so that we can reserve + their space */ +static int prepare_dynamic_rel(TCCState *s1, Section *sr) +{ + ElfW_Rel *rel, *rel_end; + int sym_index, esym_index, type, count; + + count = 0; + rel_end = (ElfW_Rel *)(sr->data + sr->data_offset); + for(rel = (ElfW_Rel *)sr->data; rel < rel_end; rel++) { + sym_index = ELF64_R_SYM(rel->r_info); + type = ELF64_R_TYPE(rel->r_info); + switch(type) { +#if defined(TCC_TARGET_I386) + case R_386_32: +#elif defined(TCC_TARGET_X86_64) + case R_X86_64_32: + case R_X86_64_32S: + case R_X86_64_64: +#endif + count++; + break; +#if defined(TCC_TARGET_I386) + case R_386_PC32: +#elif defined(TCC_TARGET_X86_64) + case R_X86_64_PC32: +#endif + esym_index = s1->symtab_to_dynsym[sym_index]; + if (esym_index) + count++; + break; + default: + break; + } + } + if (count) { + /* allocate the section */ + sr->sh_flags |= SHF_ALLOC; + sr->sh_size = count * sizeof(ElfW_Rel); + } + return count; +} + +static void put_got_offset(TCCState *s1, int index, unsigned long val) +{ + int n; + unsigned long *tab; + + if (index >= s1->nb_got_offsets) { + /* find immediately bigger power of 2 and reallocate array */ + n = 1; + while (index >= n) + n *= 2; + tab = tcc_realloc(s1->got_offsets, n * sizeof(unsigned long)); + if (!tab) + error("memory full"); + s1->got_offsets = tab; + memset(s1->got_offsets + s1->nb_got_offsets, 0, + (n - s1->nb_got_offsets) * sizeof(unsigned long)); + s1->nb_got_offsets = n; + } + s1->got_offsets[index] = val; +} + +/* XXX: suppress that */ +static void put32(unsigned char *p, uint32_t val) +{ + p[0] = val; + p[1] = val >> 8; + p[2] = val >> 16; + p[3] = val >> 24; +} + +#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_ARM) || \ + defined(TCC_TARGET_X86_64) +static uint32_t get32(unsigned char *p) +{ + return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); +} +#endif + +static void build_got(TCCState *s1) +{ + unsigned char *ptr; + + /* if no got, then create it */ + s1->got = new_section(s1, ".got", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); + s1->got->sh_entsize = 4; + add_elf_sym(symtab_section, 0, 4, ELF64_ST_INFO(STB_GLOBAL, STT_OBJECT), + 0, s1->got->sh_num, "_GLOBAL_OFFSET_TABLE_"); + ptr = section_ptr_add(s1->got, 3 * PTR_SIZE); +#if PTR_SIZE == 4 + /* keep space for _DYNAMIC pointer, if present */ + put32(ptr, 0); + /* two dummy got entries */ + put32(ptr + 4, 0); + put32(ptr + 8, 0); +#else + /* keep space for _DYNAMIC pointer, if present */ + put32(ptr, 0); + put32(ptr + 4, 0); + /* two dummy got entries */ + put32(ptr + 8, 0); + put32(ptr + 12, 0); + put32(ptr + 16, 0); + put32(ptr + 20, 0); +#endif +} + +/* put a got entry corresponding to a symbol in symtab_section. 'size' + and 'info' can be modifed if more precise info comes from the DLL */ +static void put_got_entry(TCCState *s1, + int reloc_type, unsigned long size, int info, + int sym_index) +{ + int index; + const char *name; + ElfW(Sym) *sym; + unsigned long offset; + int *ptr; + + if (!s1->got) + build_got(s1); + + /* if a got entry already exists for that symbol, no need to add one */ + if (sym_index < s1->nb_got_offsets && + s1->got_offsets[sym_index] != 0) + return; + + put_got_offset(s1, sym_index, s1->got->data_offset); + + if (s1->dynsym) { + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + name = symtab_section->link->data + sym->st_name; + offset = sym->st_value; +#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) + if (reloc_type == +#ifdef TCC_TARGET_X86_64 + R_X86_64_JUMP_SLOT +#else + R_386_JMP_SLOT +#endif + ) { + Section *plt; + uint8_t *p; + int modrm; + +#if defined(TCC_OUTPUT_DLL_WITH_PLT) + modrm = 0x25; +#else + /* if we build a DLL, we add a %ebx offset */ + if (s1->output_type == TCC_OUTPUT_DLL) + modrm = 0xa3; + else + modrm = 0x25; +#endif + + /* add a PLT entry */ + plt = s1->plt; + if (plt->data_offset == 0) { + /* first plt entry */ + p = section_ptr_add(plt, 16); + p[0] = 0xff; /* pushl got + PTR_SIZE */ + p[1] = modrm + 0x10; + put32(p + 2, PTR_SIZE); + p[6] = 0xff; /* jmp *(got + PTR_SIZE * 2) */ + p[7] = modrm; + put32(p + 8, PTR_SIZE * 2); + } + + p = section_ptr_add(plt, 16); + p[0] = 0xff; /* jmp *(got + x) */ + p[1] = modrm; + put32(p + 2, s1->got->data_offset); + p[6] = 0x68; /* push $xxx */ + put32(p + 7, (plt->data_offset - 32) >> 1); + p[11] = 0xe9; /* jmp plt_start */ + put32(p + 12, -(plt->data_offset)); + + /* the symbol is modified so that it will be relocated to + the PLT */ +#if !defined(TCC_OUTPUT_DLL_WITH_PLT) + if (s1->output_type == TCC_OUTPUT_EXE) +#endif + offset = plt->data_offset - 16; + } +#elif defined(TCC_TARGET_ARM) + if (reloc_type == R_ARM_JUMP_SLOT) { + Section *plt; + uint8_t *p; + + /* if we build a DLL, we add a %ebx offset */ + if (s1->output_type == TCC_OUTPUT_DLL) + error("DLLs unimplemented!"); + + /* add a PLT entry */ + plt = s1->plt; + if (plt->data_offset == 0) { + /* first plt entry */ + p = section_ptr_add(plt, 16); + put32(p , 0xe52de004); + put32(p + 4, 0xe59fe010); + put32(p + 8, 0xe08fe00e); + put32(p + 12, 0xe5bef008); + } + + p = section_ptr_add(plt, 16); + put32(p , 0xe59fc004); + put32(p+4, 0xe08fc00c); + put32(p+8, 0xe59cf000); + put32(p+12, s1->got->data_offset); + + /* the symbol is modified so that it will be relocated to + the PLT */ + if (s1->output_type == TCC_OUTPUT_EXE) + offset = plt->data_offset - 16; + } +#elif defined(TCC_TARGET_C67) + error("C67 got not implemented"); +#else +#error unsupported CPU +#endif + index = put_elf_sym(s1->dynsym, offset, + size, info, 0, sym->st_shndx, name); + /* put a got entry */ + put_elf_reloc(s1->dynsym, s1->got, + s1->got->data_offset, + reloc_type, index); + } + ptr = section_ptr_add(s1->got, PTR_SIZE); + *ptr = 0; +} + +/* build GOT and PLT entries */ +static void build_got_entries(TCCState *s1) +{ + Section *s, *symtab; + ElfW_Rel *rel, *rel_end; + ElfW(Sym) *sym; + int i, type, reloc_type, sym_index; + + for(i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; + if (s->sh_type != SHT_RELX) + continue; + /* no need to handle got relocations */ + if (s->link != symtab_section) + continue; + symtab = s->link; + rel_end = (ElfW_Rel *)(s->data + s->data_offset); + for(rel = (ElfW_Rel *)s->data; + rel < rel_end; + rel++) { + type = ELF64_R_TYPE(rel->r_info); + switch(type) { +#if defined(TCC_TARGET_I386) + case R_386_GOT32: + case R_386_GOTOFF: + case R_386_GOTPC: + case R_386_PLT32: + if (!s1->got) + build_got(s1); + if (type == R_386_GOT32 || type == R_386_PLT32) { + sym_index = ELF64_R_SYM(rel->r_info); + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + /* look at the symbol got offset. If none, then add one */ + if (type == R_386_GOT32) + reloc_type = R_386_GLOB_DAT; + else + reloc_type = R_386_JMP_SLOT; + put_got_entry(s1, reloc_type, sym->st_size, sym->st_info, + sym_index); + } + break; +#elif defined(TCC_TARGET_ARM) + case R_ARM_GOT_BREL: + case R_ARM_GOTOFF32: + case R_ARM_BASE_PREL: + case R_ARM_PLT32: + if (!s1->got) + build_got(s1); + if (type == R_ARM_GOT_BREL || type == R_ARM_PLT32) { + sym_index = ELF64_R_SYM(rel->r_info); + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + /* look at the symbol got offset. If none, then add one */ + if (type == R_ARM_GOT_BREL) + reloc_type = R_ARM_GLOB_DAT; + else + reloc_type = R_ARM_JUMP_SLOT; + put_got_entry(s1, reloc_type, sym->st_size, sym->st_info, + sym_index); + } + break; +#elif defined(TCC_TARGET_C67) + case R_C60_GOT32: + case R_C60_GOTOFF: + case R_C60_GOTPC: + case R_C60_PLT32: + if (!s1->got) + build_got(s1); + if (type == R_C60_GOT32 || type == R_C60_PLT32) { + sym_index = ELF64_R_SYM(rel->r_info); + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + /* look at the symbol got offset. If none, then add one */ + if (type == R_C60_GOT32) + reloc_type = R_C60_GLOB_DAT; + else + reloc_type = R_C60_JMP_SLOT; + put_got_entry(s1, reloc_type, sym->st_size, sym->st_info, + sym_index); + } + break; +#elif defined(TCC_TARGET_X86_64) + case R_X86_64_GOT32: + case R_X86_64_GOTTPOFF: + case R_X86_64_GOTPCREL: + case R_X86_64_PLT32: + if (!s1->got) + build_got(s1); + if (type == R_X86_64_GOT32 || type == R_X86_64_GOTPCREL || + type == R_X86_64_PLT32) { + sym_index = ELF64_R_SYM(rel->r_info); + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + /* look at the symbol got offset. If none, then add one */ + if (type == R_X86_64_GOT32 || type == R_X86_64_GOTPCREL) + reloc_type = R_X86_64_GLOB_DAT; + else + reloc_type = R_X86_64_JUMP_SLOT; + put_got_entry(s1, reloc_type, sym->st_size, sym->st_info, + sym_index); + } + break; +#else +#error unsupported CPU +#endif + default: + break; + } + } + } +} + +static Section *new_symtab(TCCState *s1, + const char *symtab_name, int sh_type, int sh_flags, + const char *strtab_name, + const char *hash_name, int hash_sh_flags) +{ + Section *symtab, *strtab, *hash; + int *ptr, nb_buckets; + + symtab = new_section(s1, symtab_name, sh_type, sh_flags); + symtab->sh_entsize = sizeof(ElfW(Sym)); + strtab = new_section(s1, strtab_name, SHT_STRTAB, sh_flags); + put_elf_str(strtab, ""); + symtab->link = strtab; + put_elf_sym(symtab, 0, 0, 0, 0, 0, NULL); + + nb_buckets = 1; + + hash = new_section(s1, hash_name, SHT_HASH, hash_sh_flags); + hash->sh_entsize = sizeof(int); + symtab->hash = hash; + hash->link = symtab; + + ptr = section_ptr_add(hash, (2 + nb_buckets + 1) * sizeof(int)); + ptr[0] = nb_buckets; + ptr[1] = 1; + memset(ptr + 2, 0, (nb_buckets + 1) * sizeof(int)); + return symtab; +} + +/* put dynamic tag */ +static void put_dt(Section *dynamic, int dt, unsigned long val) +{ + ElfW(Dyn) *dyn; + dyn = section_ptr_add(dynamic, sizeof(ElfW(Dyn))); + dyn->d_tag = dt; + dyn->d_un.d_val = val; +} + +static void add_init_array_defines(TCCState *s1, const char *section_name) +{ + Section *s; + long end_offset; + char sym_start[1024]; + char sym_end[1024]; + + snprintf(sym_start, sizeof(sym_start), "__%s_start", section_name + 1); + snprintf(sym_end, sizeof(sym_end), "__%s_end", section_name + 1); + + s = find_section(s1, section_name); + if (!s) { + end_offset = 0; + s = data_section; + } else { + end_offset = s->data_offset; + } + + add_elf_sym(symtab_section, + 0, 0, + ELF64_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, + s->sh_num, sym_start); + add_elf_sym(symtab_section, + end_offset, 0, + ELF64_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, + s->sh_num, sym_end); +} + +/* add tcc runtime libraries */ +static void tcc_add_runtime(TCCState *s1) +{ +#if defined(CONFIG_TCC_BCHECK) || !defined(CONFIG_USE_LIBGCC) + char buf[1024]; +#endif + +#ifdef CONFIG_TCC_BCHECK + if (s1->do_bounds_check) { + unsigned long *ptr; + Section *init_section; + unsigned char *pinit; + int sym_index; + + /* XXX: add an object file to do that */ + ptr = section_ptr_add(bounds_section, sizeof(unsigned long)); + *ptr = 0; + add_elf_sym(symtab_section, 0, 0, + ELF64_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, + bounds_section->sh_num, "__bounds_start"); + /* add bound check code */ + snprintf(buf, sizeof(buf), "%s/%s", s1->tcc_lib_path, "bcheck.o"); + tcc_add_file(s1, buf); +#ifdef TCC_TARGET_I386 + if (s1->output_type != TCC_OUTPUT_MEMORY) { + /* add 'call __bound_init()' in .init section */ + init_section = find_section(s1, ".init"); + pinit = section_ptr_add(init_section, 5); + pinit[0] = 0xe8; + put32(pinit + 1, -4); + sym_index = find_elf_sym(symtab_section, "__bound_init"); + put_elf_reloc(symtab_section, init_section, + init_section->data_offset - 4, R_386_PC32, sym_index); + } +#endif + } +#endif + /* add libc */ + if (!s1->nostdlib) { + tcc_add_library(s1, "c"); + +#ifdef CONFIG_USE_LIBGCC + tcc_add_file(s1, CONFIG_SYSROOT "/lib/libgcc_s.so.1"); +#else + snprintf(buf, sizeof(buf), "%s/%s", s1->tcc_lib_path, "libtcc1.a"); + tcc_add_file(s1, buf); +#endif + } + /* add crt end if not memory output */ + if (s1->output_type != TCC_OUTPUT_MEMORY && !s1->nostdlib) { + tcc_add_file(s1, CONFIG_TCC_CRT_PREFIX "/crtn.o"); + } +} + +/* add various standard linker symbols (must be done after the + sections are filled (for example after allocating common + symbols)) */ +static void tcc_add_linker_symbols(TCCState *s1) +{ + char buf[1024]; + int i; + Section *s; + + add_elf_sym(symtab_section, + text_section->data_offset, 0, + ELF64_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, + text_section->sh_num, "_etext"); + add_elf_sym(symtab_section, + data_section->data_offset, 0, + ELF64_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, + data_section->sh_num, "_edata"); + add_elf_sym(symtab_section, + bss_section->data_offset, 0, + ELF64_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, + bss_section->sh_num, "_end"); + /* horrible new standard ldscript defines */ + add_init_array_defines(s1, ".preinit_array"); + add_init_array_defines(s1, ".init_array"); + add_init_array_defines(s1, ".fini_array"); + + /* add start and stop symbols for sections whose name can be + expressed in C */ + for(i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; + if (s->sh_type == SHT_PROGBITS && + (s->sh_flags & SHF_ALLOC)) { + const char *p; + int ch; + + /* check if section name can be expressed in C */ + p = s->name; + for(;;) { + ch = *p; + if (!ch) + break; + if (!isid(ch) && !isnum(ch)) + goto next_sec; + p++; + } + snprintf(buf, sizeof(buf), "__start_%s", s->name); + add_elf_sym(symtab_section, + 0, 0, + ELF64_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, + s->sh_num, buf); + snprintf(buf, sizeof(buf), "__stop_%s", s->name); + add_elf_sym(symtab_section, + s->data_offset, 0, + ELF64_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, + s->sh_num, buf); + } + next_sec: ; + } +} + +/* name of ELF interpreter */ +#if defined __FreeBSD__ +static char elf_interp[] = "/usr/libexec/ld-elf.so.1"; +#elif defined TCC_ARM_EABI +static char elf_interp[] = "/lib/ld-linux.so.3"; +#elif defined(TCC_TARGET_X86_64) +static char elf_interp[] = "/lib/ld-linux-x86-64.so.2"; +#elif defined(TCC_UCLIBC) +static char elf_interp[] = "/lib/ld-uClibc.so.0"; +#else +static char elf_interp[] = "/lib/ld-linux.so.2"; +#endif + +static void tcc_output_binary(TCCState *s1, FILE *f, + const int *section_order) +{ + Section *s; + int i, offset, size; + + offset = 0; + for(i=1;inb_sections;i++) { + s = s1->sections[section_order[i]]; + if (s->sh_type != SHT_NOBITS && + (s->sh_flags & SHF_ALLOC)) { + while (offset < s->sh_offset) { + fputc(0, f); + offset++; + } + size = s->sh_size; + fwrite(s->data, 1, size, f); + offset += size; + } + } +} + +/* output an ELF file */ +/* XXX: suppress unneeded sections */ +int elf_output_file(TCCState *s1, const char *filename) +{ + ElfW(Ehdr) ehdr; + FILE *f; + int fd, mode, ret; + int *section_order; + int shnum, i, phnum, file_offset, offset, size, j, tmp, sh_order_index, k; + unsigned long addr; + Section *strsec, *s; + ElfW(Shdr) shdr, *sh; + ElfW(Phdr) *phdr, *ph; + Section *interp, *dynamic, *dynstr; + unsigned long saved_dynamic_data_offset; + ElfW(Sym) *sym; + int type, file_type; + unsigned long rel_addr, rel_size; + + file_type = s1->output_type; + s1->nb_errors = 0; + + if (file_type != TCC_OUTPUT_OBJ) { + tcc_add_runtime(s1); + } + + phdr = NULL; + section_order = NULL; + interp = NULL; + dynamic = NULL; + dynstr = NULL; /* avoid warning */ + saved_dynamic_data_offset = 0; /* avoid warning */ + + if (file_type != TCC_OUTPUT_OBJ) { + relocate_common_syms(); + + tcc_add_linker_symbols(s1); + + if (!s1->static_link) { + const char *name; + int sym_index, index; + ElfW(Sym) *esym, *sym_end; + + if (file_type == TCC_OUTPUT_EXE) { + char *ptr; + /* add interpreter section only if executable */ + interp = new_section(s1, ".interp", SHT_PROGBITS, SHF_ALLOC); + interp->sh_addralign = 1; + ptr = section_ptr_add(interp, sizeof(elf_interp)); + strcpy(ptr, elf_interp); + } + + /* add dynamic symbol table */ + s1->dynsym = new_symtab(s1, ".dynsym", SHT_DYNSYM, SHF_ALLOC, + ".dynstr", + ".hash", SHF_ALLOC); + dynstr = s1->dynsym->link; + + /* add dynamic section */ + dynamic = new_section(s1, ".dynamic", SHT_DYNAMIC, + SHF_ALLOC | SHF_WRITE); + dynamic->link = dynstr; + dynamic->sh_entsize = sizeof(ElfW(Dyn)); + + /* add PLT */ + s1->plt = new_section(s1, ".plt", SHT_PROGBITS, + SHF_ALLOC | SHF_EXECINSTR); + s1->plt->sh_entsize = 4; + + build_got(s1); + + /* scan for undefined symbols and see if they are in the + dynamic symbols. If a symbol STT_FUNC is found, then we + add it in the PLT. If a symbol STT_OBJECT is found, we + add it in the .bss section with a suitable relocation */ + sym_end = (ElfW(Sym) *)(symtab_section->data + + symtab_section->data_offset); + if (file_type == TCC_OUTPUT_EXE) { + for(sym = (ElfW(Sym) *)symtab_section->data + 1; + sym < sym_end; + sym++) { + if (sym->st_shndx == SHN_UNDEF) { + name = symtab_section->link->data + sym->st_name; + sym_index = find_elf_sym(s1->dynsymtab_section, name); + if (sym_index) { + esym = &((ElfW(Sym) *)s1->dynsymtab_section->data)[sym_index]; + type = ELF64_ST_TYPE(esym->st_info); + if (type == STT_FUNC) { + put_got_entry(s1, R_JMP_SLOT, esym->st_size, + esym->st_info, + sym - (ElfW(Sym) *)symtab_section->data); + } else if (type == STT_OBJECT) { + unsigned long offset; + offset = bss_section->data_offset; + /* XXX: which alignment ? */ + offset = (offset + 16 - 1) & -16; + index = put_elf_sym(s1->dynsym, offset, esym->st_size, + esym->st_info, 0, + bss_section->sh_num, name); + put_elf_reloc(s1->dynsym, bss_section, + offset, R_COPY, index); + offset += esym->st_size; + bss_section->data_offset = offset; + } + } else { + /* STB_WEAK undefined symbols are accepted */ + /* XXX: _fp_hw seems to be part of the ABI, so we ignore + it */ + if (ELF64_ST_BIND(sym->st_info) == STB_WEAK || + !strcmp(name, "_fp_hw")) { + } else { + error_noabort("undefined symbol '%s'", name); + } + } + } else if (s1->rdynamic && + ELF64_ST_BIND(sym->st_info) != STB_LOCAL) { + /* if -rdynamic option, then export all non + local symbols */ + name = symtab_section->link->data + sym->st_name; + put_elf_sym(s1->dynsym, sym->st_value, sym->st_size, + sym->st_info, 0, + sym->st_shndx, name); + } + } + + if (s1->nb_errors) + goto fail; + + /* now look at unresolved dynamic symbols and export + corresponding symbol */ + sym_end = (ElfW(Sym) *)(s1->dynsymtab_section->data + + s1->dynsymtab_section->data_offset); + for(esym = (ElfW(Sym) *)s1->dynsymtab_section->data + 1; + esym < sym_end; + esym++) { + if (esym->st_shndx == SHN_UNDEF) { + name = s1->dynsymtab_section->link->data + esym->st_name; + sym_index = find_elf_sym(symtab_section, name); + if (sym_index) { + /* XXX: avoid adding a symbol if already + present because of -rdynamic ? */ + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + put_elf_sym(s1->dynsym, sym->st_value, sym->st_size, + sym->st_info, 0, + sym->st_shndx, name); + } else { + if (ELF64_ST_BIND(esym->st_info) == STB_WEAK) { + /* weak symbols can stay undefined */ + } else { + warning("undefined dynamic symbol '%s'", name); + } + } + } + } + } else { + int nb_syms; + /* shared library case : we simply export all the global symbols */ + nb_syms = symtab_section->data_offset / sizeof(ElfW(Sym)); + s1->symtab_to_dynsym = tcc_mallocz(sizeof(int) * nb_syms); + for(sym = (ElfW(Sym) *)symtab_section->data + 1; + sym < sym_end; + sym++) { + if (ELF64_ST_BIND(sym->st_info) != STB_LOCAL) { +#if defined(TCC_OUTPUT_DLL_WITH_PLT) + if (ELF64_ST_TYPE(sym->st_info) == STT_FUNC && + sym->st_shndx == SHN_UNDEF) { + put_got_entry(s1, R_JMP_SLOT, sym->st_size, + sym->st_info, + sym - (ElfW(Sym) *)symtab_section->data); + } + else if (ELF64_ST_TYPE(sym->st_info) == STT_OBJECT) { + put_got_entry(s1, R_X86_64_GLOB_DAT, sym->st_size, + sym->st_info, + sym - (ElfW(Sym) *)symtab_section->data); + } + else +#endif + { + name = symtab_section->link->data + sym->st_name; + index = put_elf_sym(s1->dynsym, sym->st_value, sym->st_size, + sym->st_info, 0, + sym->st_shndx, name); + s1->symtab_to_dynsym[sym - + (ElfW(Sym) *)symtab_section->data] = + index; + } + } + } + } + + build_got_entries(s1); + + /* add a list of needed dlls */ + for(i = 0; i < s1->nb_loaded_dlls; i++) { + DLLReference *dllref = s1->loaded_dlls[i]; + if (dllref->level == 0) + put_dt(dynamic, DT_NEEDED, put_elf_str(dynstr, dllref->name)); + } + /* XXX: currently, since we do not handle PIC code, we + must relocate the readonly segments */ + if (file_type == TCC_OUTPUT_DLL) { + if (s1->soname) + put_dt(dynamic, DT_SONAME, put_elf_str(dynstr, s1->soname)); + put_dt(dynamic, DT_TEXTREL, 0); + } + + /* add necessary space for other entries */ + saved_dynamic_data_offset = dynamic->data_offset; + dynamic->data_offset += sizeof(ElfW(Dyn)) * 9; + } else { + /* still need to build got entries in case of static link */ + build_got_entries(s1); + } + } + + memset(&ehdr, 0, sizeof(ehdr)); + + /* we add a section for symbols */ + strsec = new_section(s1, ".shstrtab", SHT_STRTAB, 0); + put_elf_str(strsec, ""); + + /* compute number of sections */ + shnum = s1->nb_sections; + + /* this array is used to reorder sections in the output file */ + section_order = tcc_malloc(sizeof(int) * shnum); + section_order[0] = 0; + sh_order_index = 1; + + /* compute number of program headers */ + switch(file_type) { + case TCC_OUTPUT_EXE: + if (!s1->static_link) + phnum = 4; + else + phnum = 2; + break; + case TCC_OUTPUT_DLL: + phnum = 3; + break; + case TCC_OUTPUT_OBJ: + default: + phnum = 0; + break; + } + + /* allocate strings for section names and decide if an unallocated + section should be output */ + /* NOTE: the strsec section comes last, so its size is also + correct ! */ + for(i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; + s->sh_name = put_elf_str(strsec, s->name); +#if 0 //gr + printf("section: f=%08x t=%08x i=%08x %s %s\n", + s->sh_flags, + s->sh_type, + s->sh_info, + s->name, + s->reloc ? s->reloc->name : "n" + ); +#endif + /* when generating a DLL, we include relocations but we may + patch them */ + if (file_type == TCC_OUTPUT_DLL && + s->sh_type == SHT_RELX && + !(s->sh_flags & SHF_ALLOC)) { + /* //gr: avoid bogus relocs for empty (debug) sections */ + if (s1->sections[s->sh_info]->sh_flags & SHF_ALLOC) + prepare_dynamic_rel(s1, s); + else if (s1->do_debug) + s->sh_size = s->data_offset; + } else if (s1->do_debug || + file_type == TCC_OUTPUT_OBJ || + (s->sh_flags & SHF_ALLOC) || + i == (s1->nb_sections - 1)) { + /* we output all sections if debug or object file */ + s->sh_size = s->data_offset; + } + } + + /* allocate program segment headers */ + phdr = tcc_mallocz(phnum * sizeof(ElfW(Phdr))); + + if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) { + file_offset = sizeof(ElfW(Ehdr)) + phnum * sizeof(ElfW(Phdr)); + } else { + file_offset = 0; + } + if (phnum > 0) { + /* compute section to program header mapping */ + if (s1->has_text_addr) { + int a_offset, p_offset; + addr = s1->text_addr; + /* we ensure that (addr % ELF_PAGE_SIZE) == file_offset % + ELF_PAGE_SIZE */ + a_offset = addr & (ELF_PAGE_SIZE - 1); + p_offset = file_offset & (ELF_PAGE_SIZE - 1); + if (a_offset < p_offset) + a_offset += ELF_PAGE_SIZE; + file_offset += (a_offset - p_offset); + } else { + if (file_type == TCC_OUTPUT_DLL) + addr = 0; + else + addr = ELF_START_ADDR; + /* compute address after headers */ + addr += (file_offset & (ELF_PAGE_SIZE - 1)); + } + + /* dynamic relocation table information, for .dynamic section */ + rel_size = 0; + rel_addr = 0; + + /* leave one program header for the program interpreter */ + ph = &phdr[0]; + if (interp) + ph++; + + for(j = 0; j < 2; j++) { + ph->p_type = PT_LOAD; + if (j == 0) + ph->p_flags = PF_R | PF_X; + else + ph->p_flags = PF_R | PF_W; + ph->p_align = ELF_PAGE_SIZE; + + /* we do the following ordering: interp, symbol tables, + relocations, progbits, nobits */ + /* XXX: do faster and simpler sorting */ + for(k = 0; k < 5; k++) { + for(i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; + /* compute if section should be included */ + if (j == 0) { + if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE)) != + SHF_ALLOC) + continue; + } else { + if ((s->sh_flags & (SHF_ALLOC | SHF_WRITE)) != + (SHF_ALLOC | SHF_WRITE)) + continue; + } + if (s == interp) { + if (k != 0) + continue; + } else if (s->sh_type == SHT_DYNSYM || + s->sh_type == SHT_STRTAB || + s->sh_type == SHT_HASH) { + if (k != 1) + continue; + } else if (s->sh_type == SHT_RELX) { + if (k != 2) + continue; + } else if (s->sh_type == SHT_NOBITS) { + if (k != 4) + continue; + } else { + if (k != 3) + continue; + } + section_order[sh_order_index++] = i; + + /* section matches: we align it and add its size */ + tmp = addr; + addr = (addr + s->sh_addralign - 1) & + ~(s->sh_addralign - 1); + file_offset += addr - tmp; + s->sh_offset = file_offset; + s->sh_addr = addr; + + /* update program header infos */ + if (ph->p_offset == 0) { + ph->p_offset = file_offset; + ph->p_vaddr = addr; + ph->p_paddr = ph->p_vaddr; + } + /* update dynamic relocation infos */ + if (s->sh_type == SHT_RELX) { + if (rel_size == 0) + rel_addr = addr; + rel_size += s->sh_size; + } + addr += s->sh_size; + if (s->sh_type != SHT_NOBITS) + file_offset += s->sh_size; + } + } + ph->p_filesz = file_offset - ph->p_offset; + ph->p_memsz = addr - ph->p_vaddr; + ph++; + if (j == 0) { + if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) { + /* if in the middle of a page, we duplicate the page in + memory so that one copy is RX and the other is RW */ + if ((addr & (ELF_PAGE_SIZE - 1)) != 0) + addr += ELF_PAGE_SIZE; + } else { + addr = (addr + ELF_PAGE_SIZE - 1) & ~(ELF_PAGE_SIZE - 1); + file_offset = (file_offset + ELF_PAGE_SIZE - 1) & + ~(ELF_PAGE_SIZE - 1); + } + } + } + + /* if interpreter, then add corresponing program header */ + if (interp) { + ph = &phdr[0]; + + ph->p_type = PT_INTERP; + ph->p_offset = interp->sh_offset; + ph->p_vaddr = interp->sh_addr; + ph->p_paddr = ph->p_vaddr; + ph->p_filesz = interp->sh_size; + ph->p_memsz = interp->sh_size; + ph->p_flags = PF_R; + ph->p_align = interp->sh_addralign; + } + + /* if dynamic section, then add corresponing program header */ + if (dynamic) { + ElfW(Sym) *sym_end; + + ph = &phdr[phnum - 1]; + + ph->p_type = PT_DYNAMIC; + ph->p_offset = dynamic->sh_offset; + ph->p_vaddr = dynamic->sh_addr; + ph->p_paddr = ph->p_vaddr; + ph->p_filesz = dynamic->sh_size; + ph->p_memsz = dynamic->sh_size; + ph->p_flags = PF_R | PF_W; + ph->p_align = dynamic->sh_addralign; + + /* put GOT dynamic section address */ + put32(s1->got->data, dynamic->sh_addr); + + /* relocate the PLT */ + if (file_type == TCC_OUTPUT_EXE +#if defined(TCC_OUTPUT_DLL_WITH_PLT) + || file_type == TCC_OUTPUT_DLL +#endif + ) { + uint8_t *p, *p_end; + + p = s1->plt->data; + p_end = p + s1->plt->data_offset; + if (p < p_end) { +#if defined(TCC_TARGET_I386) + put32(p + 2, get32(p + 2) + s1->got->sh_addr); + put32(p + 8, get32(p + 8) + s1->got->sh_addr); + p += 16; + while (p < p_end) { + put32(p + 2, get32(p + 2) + s1->got->sh_addr); + p += 16; + } +#elif defined(TCC_TARGET_X86_64) + int x = s1->got->sh_addr - s1->plt->sh_addr - 6; + put32(p + 2, get32(p + 2) + x); + put32(p + 8, get32(p + 8) + x - 6); + p += 16; + while (p < p_end) { + put32(p + 2, get32(p + 2) + x + s1->plt->data - p); + p += 16; + } +#elif defined(TCC_TARGET_ARM) + int x; + x=s1->got->sh_addr - s1->plt->sh_addr - 12; + p +=16; + while (p < p_end) { + put32(p + 12, x + get32(p + 12) + s1->plt->data - p); + p += 16; + } +#elif defined(TCC_TARGET_C67) + /* XXX: TODO */ +#else +#error unsupported CPU +#endif + } + } + + /* relocate symbols in .dynsym */ + sym_end = (ElfW(Sym) *)(s1->dynsym->data + s1->dynsym->data_offset); + for(sym = (ElfW(Sym) *)s1->dynsym->data + 1; + sym < sym_end; + sym++) { + if (sym->st_shndx == SHN_UNDEF) { + /* relocate to the PLT if the symbol corresponds + to a PLT entry */ + if (sym->st_value) + sym->st_value += s1->plt->sh_addr; + } else if (sym->st_shndx < SHN_LORESERVE) { + /* do symbol relocation */ + sym->st_value += s1->sections[sym->st_shndx]->sh_addr; + } + } + + /* put dynamic section entries */ + dynamic->data_offset = saved_dynamic_data_offset; + put_dt(dynamic, DT_HASH, s1->dynsym->hash->sh_addr); + put_dt(dynamic, DT_STRTAB, dynstr->sh_addr); + put_dt(dynamic, DT_SYMTAB, s1->dynsym->sh_addr); + put_dt(dynamic, DT_STRSZ, dynstr->data_offset); + put_dt(dynamic, DT_SYMENT, sizeof(ElfW(Sym))); +#ifdef TCC_TARGET_X86_64 + put_dt(dynamic, DT_RELA, rel_addr); + put_dt(dynamic, DT_RELASZ, rel_size); + put_dt(dynamic, DT_RELAENT, sizeof(ElfW_Rel)); +#else + put_dt(dynamic, DT_REL, rel_addr); + put_dt(dynamic, DT_RELSZ, rel_size); + put_dt(dynamic, DT_RELENT, sizeof(ElfW_Rel)); +#endif + if (s1->do_debug) + put_dt(dynamic, DT_DEBUG, 0); + put_dt(dynamic, DT_NULL, 0); + } + + ehdr.e_phentsize = sizeof(ElfW(Phdr)); + ehdr.e_phnum = phnum; + ehdr.e_phoff = sizeof(ElfW(Ehdr)); + } + + /* all other sections come after */ + for(i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; + if (phnum > 0 && (s->sh_flags & SHF_ALLOC)) + continue; + section_order[sh_order_index++] = i; + + file_offset = (file_offset + s->sh_addralign - 1) & + ~(s->sh_addralign - 1); + s->sh_offset = file_offset; + if (s->sh_type != SHT_NOBITS) + file_offset += s->sh_size; + } + + /* if building executable or DLL, then relocate each section + except the GOT which is already relocated */ + if (file_type != TCC_OUTPUT_OBJ) { + relocate_syms(s1, 0); + + if (s1->nb_errors != 0) { + fail: + ret = -1; + goto the_end; + } + + /* relocate sections */ + /* XXX: ignore sections with allocated relocations ? */ + for(i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; + if (s->reloc && s != s1->got && (s->sh_flags & SHF_ALLOC)) //gr + relocate_section(s1, s); + } + + /* relocate relocation entries if the relocation tables are + allocated in the executable */ + for(i = 1; i < s1->nb_sections; i++) { + s = s1->sections[i]; + if ((s->sh_flags & SHF_ALLOC) && + s->sh_type == SHT_RELX) { + relocate_rel(s1, s); + } + } + + /* get entry point address */ + if (file_type == TCC_OUTPUT_EXE) + ehdr.e_entry = (unsigned long)tcc_get_symbol_err(s1, "_start"); + else + ehdr.e_entry = text_section->sh_addr; /* XXX: is it correct ? */ + } + + /* write elf file */ + if (file_type == TCC_OUTPUT_OBJ) + mode = 0666; + else + mode = 0777; + fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode); + if (fd < 0) { + error_noabort("could not write '%s'", filename); + goto fail; + } + f = fdopen(fd, "wb"); + if (s1->verbose) + printf("<- %s\n", filename); + +#ifdef TCC_TARGET_COFF + if (s1->output_format == TCC_OUTPUT_FORMAT_COFF) { + tcc_output_coff(s1, f); + } else +#endif + if (s1->output_format == TCC_OUTPUT_FORMAT_ELF) { + sort_syms(s1, symtab_section); + + /* align to 4 */ + file_offset = (file_offset + 3) & -4; + + /* fill header */ + ehdr.e_ident[0] = ELFMAG0; + ehdr.e_ident[1] = ELFMAG1; + ehdr.e_ident[2] = ELFMAG2; + ehdr.e_ident[3] = ELFMAG3; + ehdr.e_ident[4] = TCC_ELFCLASS; + ehdr.e_ident[5] = ELFDATA2LSB; + ehdr.e_ident[6] = EV_CURRENT; +#ifdef __FreeBSD__ + ehdr.e_ident[EI_OSABI] = ELFOSABI_FREEBSD; +#endif +#ifdef TCC_TARGET_ARM +#ifdef TCC_ARM_EABI + ehdr.e_ident[EI_OSABI] = 0; + ehdr.e_flags = 4 << 24; +#else + ehdr.e_ident[EI_OSABI] = ELFOSABI_ARM; +#endif +#endif + switch(file_type) { + case TCC_OUTPUT_DLL: + ehdr.e_type = ET_DYN; + break; + case TCC_OUTPUT_OBJ: + ehdr.e_type = ET_REL; + break; + case TCC_OUTPUT_EXE: + default: + ehdr.e_type = ET_EXEC; + break; + } + ehdr.e_machine = EM_TCC_TARGET; + ehdr.e_version = EV_CURRENT; + ehdr.e_shoff = file_offset; + ehdr.e_ehsize = sizeof(ElfW(Ehdr)); + ehdr.e_shentsize = sizeof(ElfW(Shdr)); + ehdr.e_shnum = shnum; + ehdr.e_shstrndx = shnum - 1; + + fwrite(&ehdr, 1, sizeof(ElfW(Ehdr)), f); + fwrite(phdr, 1, phnum * sizeof(ElfW(Phdr)), f); + offset = sizeof(ElfW(Ehdr)) + phnum * sizeof(ElfW(Phdr)); + + for(i=1;inb_sections;i++) { + s = s1->sections[section_order[i]]; + if (s->sh_type != SHT_NOBITS) { + while (offset < s->sh_offset) { + fputc(0, f); + offset++; + } + size = s->sh_size; + fwrite(s->data, 1, size, f); + offset += size; + } + } + + /* output section headers */ + while (offset < ehdr.e_shoff) { + fputc(0, f); + offset++; + } + + for(i=0;inb_sections;i++) { + sh = &shdr; + memset(sh, 0, sizeof(ElfW(Shdr))); + s = s1->sections[i]; + if (s) { + sh->sh_name = s->sh_name; + sh->sh_type = s->sh_type; + sh->sh_flags = s->sh_flags; + sh->sh_entsize = s->sh_entsize; + sh->sh_info = s->sh_info; + if (s->link) + sh->sh_link = s->link->sh_num; + sh->sh_addralign = s->sh_addralign; + sh->sh_addr = s->sh_addr; + sh->sh_offset = s->sh_offset; + sh->sh_size = s->sh_size; + } + fwrite(sh, 1, sizeof(ElfW(Shdr)), f); + } + } else { + tcc_output_binary(s1, f, section_order); + } + fclose(f); + + ret = 0; + the_end: + tcc_free(s1->symtab_to_dynsym); + tcc_free(section_order); + tcc_free(phdr); + tcc_free(s1->got_offsets); + return ret; +} + +int tcc_output_file(TCCState *s, const char *filename) +{ + int ret; +#ifdef TCC_TARGET_PE + if (s->output_type != TCC_OUTPUT_OBJ) { + ret = pe_output_file(s, filename); + } else +#endif + { + ret = elf_output_file(s, filename); + } + return ret; +} + +static void *load_data(int fd, unsigned long file_offset, unsigned long size) +{ + void *data; + + data = tcc_malloc(size); + lseek(fd, file_offset, SEEK_SET); + read(fd, data, size); + return data; +} + +typedef struct SectionMergeInfo { + Section *s; /* corresponding existing section */ + unsigned long offset; /* offset of the new section in the existing section */ + uint8_t new_section; /* true if section 's' was added */ + uint8_t link_once; /* true if link once section */ +} SectionMergeInfo; + +/* load an object file and merge it with current files */ +/* XXX: handle correctly stab (debug) info */ +static int tcc_load_object_file(TCCState *s1, + int fd, unsigned long file_offset) +{ + ElfW(Ehdr) ehdr; + ElfW(Shdr) *shdr, *sh; + int size, i, j, offset, offseti, nb_syms, sym_index, ret; + unsigned char *strsec, *strtab; + int *old_to_new_syms; + char *sh_name, *name; + SectionMergeInfo *sm_table, *sm; + ElfW(Sym) *sym, *symtab; + ElfW_Rel *rel, *rel_end; + Section *s; + + int stab_index; + int stabstr_index; + + stab_index = stabstr_index = 0; + + if (read(fd, &ehdr, sizeof(ehdr)) != sizeof(ehdr)) + goto fail1; + if (ehdr.e_ident[0] != ELFMAG0 || + ehdr.e_ident[1] != ELFMAG1 || + ehdr.e_ident[2] != ELFMAG2 || + ehdr.e_ident[3] != ELFMAG3) + goto fail1; + /* test if object file */ + if (ehdr.e_type != ET_REL) + goto fail1; + /* test CPU specific stuff */ + if (ehdr.e_ident[5] != ELFDATA2LSB || + ehdr.e_machine != EM_TCC_TARGET) { + fail1: + error_noabort("invalid object file"); + return -1; + } + /* read sections */ + shdr = load_data(fd, file_offset + ehdr.e_shoff, + sizeof(ElfW(Shdr)) * ehdr.e_shnum); + sm_table = tcc_mallocz(sizeof(SectionMergeInfo) * ehdr.e_shnum); + + /* load section names */ + sh = &shdr[ehdr.e_shstrndx]; + strsec = load_data(fd, file_offset + sh->sh_offset, sh->sh_size); + + /* load symtab and strtab */ + old_to_new_syms = NULL; + symtab = NULL; + strtab = NULL; + nb_syms = 0; + for(i = 1; i < ehdr.e_shnum; i++) { + sh = &shdr[i]; + if (sh->sh_type == SHT_SYMTAB) { + if (symtab) { + error_noabort("object must contain only one symtab"); + fail: + ret = -1; + goto the_end; + } + nb_syms = sh->sh_size / sizeof(ElfW(Sym)); + symtab = load_data(fd, file_offset + sh->sh_offset, sh->sh_size); + sm_table[i].s = symtab_section; + + /* now load strtab */ + sh = &shdr[sh->sh_link]; + strtab = load_data(fd, file_offset + sh->sh_offset, sh->sh_size); + } + } + + /* now examine each section and try to merge its content with the + ones in memory */ + for(i = 1; i < ehdr.e_shnum; i++) { + /* no need to examine section name strtab */ + if (i == ehdr.e_shstrndx) + continue; + sh = &shdr[i]; + sh_name = strsec + sh->sh_name; + /* ignore sections types we do not handle */ + if (sh->sh_type != SHT_PROGBITS && + sh->sh_type != SHT_RELX && +#ifdef TCC_ARM_EABI + sh->sh_type != SHT_ARM_EXIDX && +#endif + sh->sh_type != SHT_NOBITS && + strcmp(sh_name, ".stabstr") + ) + continue; + if (sh->sh_addralign < 1) + sh->sh_addralign = 1; + /* find corresponding section, if any */ + for(j = 1; j < s1->nb_sections;j++) { + s = s1->sections[j]; + if (!strcmp(s->name, sh_name)) { + if (!strncmp(sh_name, ".gnu.linkonce", + sizeof(".gnu.linkonce") - 1)) { + /* if a 'linkonce' section is already present, we + do not add it again. It is a little tricky as + symbols can still be defined in + it. */ + sm_table[i].link_once = 1; + goto next; + } else { + goto found; + } + } + } + /* not found: create new section */ + s = new_section(s1, sh_name, sh->sh_type, sh->sh_flags); + /* take as much info as possible from the section. sh_link and + sh_info will be updated later */ + s->sh_addralign = sh->sh_addralign; + s->sh_entsize = sh->sh_entsize; + sm_table[i].new_section = 1; + found: + if (sh->sh_type != s->sh_type) { + error_noabort("invalid section type"); + goto fail; + } + + /* align start of section */ + offset = s->data_offset; + + if (0 == strcmp(sh_name, ".stab")) { + stab_index = i; + goto no_align; + } + if (0 == strcmp(sh_name, ".stabstr")) { + stabstr_index = i; + goto no_align; + } + + size = sh->sh_addralign - 1; + offset = (offset + size) & ~size; + if (sh->sh_addralign > s->sh_addralign) + s->sh_addralign = sh->sh_addralign; + s->data_offset = offset; + no_align: + sm_table[i].offset = offset; + sm_table[i].s = s; + /* concatenate sections */ + size = sh->sh_size; + if (sh->sh_type != SHT_NOBITS) { + unsigned char *ptr; + lseek(fd, file_offset + sh->sh_offset, SEEK_SET); + ptr = section_ptr_add(s, size); + read(fd, ptr, size); + } else { + s->data_offset += size; + } + next: ; + } + + /* //gr relocate stab strings */ + if (stab_index && stabstr_index) { + Stab_Sym *a, *b; + unsigned o; + s = sm_table[stab_index].s; + a = (Stab_Sym *)(s->data + sm_table[stab_index].offset); + b = (Stab_Sym *)(s->data + s->data_offset); + o = sm_table[stabstr_index].offset; + while (a < b) + a->n_strx += o, a++; + } + + /* second short pass to update sh_link and sh_info fields of new + sections */ + for(i = 1; i < ehdr.e_shnum; i++) { + s = sm_table[i].s; + if (!s || !sm_table[i].new_section) + continue; + sh = &shdr[i]; + if (sh->sh_link > 0) + s->link = sm_table[sh->sh_link].s; + if (sh->sh_type == SHT_RELX) { + s->sh_info = sm_table[sh->sh_info].s->sh_num; + /* update backward link */ + s1->sections[s->sh_info]->reloc = s; + } + } + sm = sm_table; + + /* resolve symbols */ + old_to_new_syms = tcc_mallocz(nb_syms * sizeof(int)); + + sym = symtab + 1; + for(i = 1; i < nb_syms; i++, sym++) { + if (sym->st_shndx != SHN_UNDEF && + sym->st_shndx < SHN_LORESERVE) { + sm = &sm_table[sym->st_shndx]; + if (sm->link_once) { + /* if a symbol is in a link once section, we use the + already defined symbol. It is very important to get + correct relocations */ + if (ELF64_ST_BIND(sym->st_info) != STB_LOCAL) { + name = strtab + sym->st_name; + sym_index = find_elf_sym(symtab_section, name); + if (sym_index) + old_to_new_syms[i] = sym_index; + } + continue; + } + /* if no corresponding section added, no need to add symbol */ + if (!sm->s) + continue; + /* convert section number */ + sym->st_shndx = sm->s->sh_num; + /* offset value */ + sym->st_value += sm->offset; + } + /* add symbol */ + name = strtab + sym->st_name; + sym_index = add_elf_sym(symtab_section, sym->st_value, sym->st_size, + sym->st_info, sym->st_other, + sym->st_shndx, name); + old_to_new_syms[i] = sym_index; + } + + /* third pass to patch relocation entries */ + for(i = 1; i < ehdr.e_shnum; i++) { + s = sm_table[i].s; + if (!s) + continue; + sh = &shdr[i]; + offset = sm_table[i].offset; + switch(s->sh_type) { + case SHT_RELX: + /* take relocation offset information */ + offseti = sm_table[sh->sh_info].offset; + rel_end = (ElfW_Rel *)(s->data + s->data_offset); + for(rel = (ElfW_Rel *)(s->data + offset); + rel < rel_end; + rel++) { + int type; + unsigned sym_index; + /* convert symbol index */ + type = ELF64_R_TYPE(rel->r_info); + sym_index = ELF64_R_SYM(rel->r_info); + /* NOTE: only one symtab assumed */ + if (sym_index >= nb_syms) + goto invalid_reloc; + sym_index = old_to_new_syms[sym_index]; + /* ignore link_once in rel section. */ + if (!sym_index && !sm->link_once) { + invalid_reloc: + error_noabort("Invalid relocation entry [%2d] '%s' @ %.8x", + i, strsec + sh->sh_name, rel->r_offset); + goto fail; + } + rel->r_info = ELF64_R_INFO(sym_index, type); + /* offset the relocation offset */ + rel->r_offset += offseti; + } + break; + default: + break; + } + } + + ret = 0; + the_end: + tcc_free(symtab); + tcc_free(strtab); + tcc_free(old_to_new_syms); + tcc_free(sm_table); + tcc_free(strsec); + tcc_free(shdr); + return ret; +} + +#define ARMAG "!\012" /* For COFF and a.out archives */ + +typedef struct ArchiveHeader { + char ar_name[16]; /* name of this member */ + char ar_date[12]; /* file mtime */ + char ar_uid[6]; /* owner uid; printed as decimal */ + char ar_gid[6]; /* owner gid; printed as decimal */ + char ar_mode[8]; /* file mode, printed as octal */ + char ar_size[10]; /* file size, printed as decimal */ + char ar_fmag[2]; /* should contain ARFMAG */ +} ArchiveHeader; + +static int get_be32(const uint8_t *b) +{ + return b[3] | (b[2] << 8) | (b[1] << 16) | (b[0] << 24); +} + +/* load only the objects which resolve undefined symbols */ +static int tcc_load_alacarte(TCCState *s1, int fd, int size) +{ + int i, bound, nsyms, sym_index, off, ret; + uint8_t *data; + const char *ar_names, *p; + const uint8_t *ar_index; + ElfW(Sym) *sym; + + data = tcc_malloc(size); + if (read(fd, data, size) != size) + goto fail; + nsyms = get_be32(data); + ar_index = data + 4; + ar_names = ar_index + nsyms * 4; + + do { + bound = 0; + for(p = ar_names, i = 0; i < nsyms; i++, p += strlen(p)+1) { + sym_index = find_elf_sym(symtab_section, p); + if(sym_index) { + sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + if(sym->st_shndx == SHN_UNDEF) { + off = get_be32(ar_index + i * 4) + sizeof(ArchiveHeader); +#if 0 + printf("%5d\t%s\t%08x\n", i, p, sym->st_shndx); +#endif + ++bound; + lseek(fd, off, SEEK_SET); + if(tcc_load_object_file(s1, fd, off) < 0) { + fail: + ret = -1; + goto the_end; + } + } + } + } + } while(bound); + ret = 0; + the_end: + tcc_free(data); + return ret; +} + +/* load a '.a' file */ +static int tcc_load_archive(TCCState *s1, int fd) +{ + ArchiveHeader hdr; + char ar_size[11]; + char ar_name[17]; + char magic[8]; + int size, len, i; + unsigned long file_offset; + + /* skip magic which was already checked */ + read(fd, magic, sizeof(magic)); + + for(;;) { + len = read(fd, &hdr, sizeof(hdr)); + if (len == 0) + break; + if (len != sizeof(hdr)) { + error_noabort("invalid archive"); + return -1; + } + memcpy(ar_size, hdr.ar_size, sizeof(hdr.ar_size)); + ar_size[sizeof(hdr.ar_size)] = '\0'; + size = strtol(ar_size, NULL, 0); + memcpy(ar_name, hdr.ar_name, sizeof(hdr.ar_name)); + for(i = sizeof(hdr.ar_name) - 1; i >= 0; i--) { + if (ar_name[i] != ' ') + break; + } + ar_name[i + 1] = '\0'; + // printf("name='%s' size=%d %s\n", ar_name, size, ar_size); + file_offset = lseek(fd, 0, SEEK_CUR); + /* align to even */ + size = (size + 1) & ~1; + if (!strcmp(ar_name, "/")) { + /* coff symbol table : we handle it */ + if(s1->alacarte_link) + return tcc_load_alacarte(s1, fd, size); + } else if (!strcmp(ar_name, "//") || + !strcmp(ar_name, "__.SYMDEF") || + !strcmp(ar_name, "__.SYMDEF/") || + !strcmp(ar_name, "ARFILENAMES/")) { + /* skip symbol table or archive names */ + } else { + if (tcc_load_object_file(s1, fd, file_offset) < 0) + return -1; + } + lseek(fd, file_offset + size, SEEK_SET); + } + return 0; +} + +/* load a DLL and all referenced DLLs. 'level = 0' means that the DLL + is referenced by the user (so it should be added as DT_NEEDED in + the generated ELF file) */ +static int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level) +{ + ElfW(Ehdr) ehdr; + ElfW(Shdr) *shdr, *sh, *sh1; + int i, j, nb_syms, nb_dts, sym_bind, ret; + ElfW(Sym) *sym, *dynsym; + ElfW(Dyn) *dt, *dynamic; + unsigned char *dynstr; + const char *name, *soname; + DLLReference *dllref; + + read(fd, &ehdr, sizeof(ehdr)); + + /* test CPU specific stuff */ + if (ehdr.e_ident[5] != ELFDATA2LSB || + ehdr.e_machine != EM_TCC_TARGET) { + error_noabort("bad architecture"); + return -1; + } + + /* read sections */ + shdr = load_data(fd, ehdr.e_shoff, sizeof(ElfW(Shdr)) * ehdr.e_shnum); + + /* load dynamic section and dynamic symbols */ + nb_syms = 0; + nb_dts = 0; + dynamic = NULL; + dynsym = NULL; /* avoid warning */ + dynstr = NULL; /* avoid warning */ + for(i = 0, sh = shdr; i < ehdr.e_shnum; i++, sh++) { + switch(sh->sh_type) { + case SHT_DYNAMIC: + nb_dts = sh->sh_size / sizeof(ElfW(Dyn)); + dynamic = load_data(fd, sh->sh_offset, sh->sh_size); + break; + case SHT_DYNSYM: + nb_syms = sh->sh_size / sizeof(ElfW(Sym)); + dynsym = load_data(fd, sh->sh_offset, sh->sh_size); + sh1 = &shdr[sh->sh_link]; + dynstr = load_data(fd, sh1->sh_offset, sh1->sh_size); + break; + default: + break; + } + } + + /* compute the real library name */ + soname = tcc_basename(filename); + + for(i = 0, dt = dynamic; i < nb_dts; i++, dt++) { + if (dt->d_tag == DT_SONAME) { + soname = dynstr + dt->d_un.d_val; + } + } + + /* if the dll is already loaded, do not load it */ + for(i = 0; i < s1->nb_loaded_dlls; i++) { + dllref = s1->loaded_dlls[i]; + if (!strcmp(soname, dllref->name)) { + /* but update level if needed */ + if (level < dllref->level) + dllref->level = level; + ret = 0; + goto the_end; + } + } + + // printf("loading dll '%s'\n", soname); + + /* add the dll and its level */ + dllref = tcc_mallocz(sizeof(DLLReference) + strlen(soname)); + dllref->level = level; + strcpy(dllref->name, soname); + dynarray_add((void ***)&s1->loaded_dlls, &s1->nb_loaded_dlls, dllref); + + /* add dynamic symbols in dynsym_section */ + for(i = 1, sym = dynsym + 1; i < nb_syms; i++, sym++) { + sym_bind = ELF64_ST_BIND(sym->st_info); + if (sym_bind == STB_LOCAL) + continue; + name = dynstr + sym->st_name; + add_elf_sym(s1->dynsymtab_section, sym->st_value, sym->st_size, + sym->st_info, sym->st_other, sym->st_shndx, name); + } + + /* load all referenced DLLs */ + for(i = 0, dt = dynamic; i < nb_dts; i++, dt++) { + switch(dt->d_tag) { + case DT_NEEDED: + name = dynstr + dt->d_un.d_val; + for(j = 0; j < s1->nb_loaded_dlls; j++) { + dllref = s1->loaded_dlls[j]; + if (!strcmp(name, dllref->name)) + goto already_loaded; + } + if (tcc_add_dll(s1, name, AFF_REFERENCED_DLL) < 0) { + error_noabort("referenced dll '%s' not found", name); + ret = -1; + goto the_end; + } + already_loaded: + break; + } + } + ret = 0; + the_end: + tcc_free(dynstr); + tcc_free(dynsym); + tcc_free(dynamic); + tcc_free(shdr); + return ret; +} + +#define LD_TOK_NAME 256 +#define LD_TOK_EOF (-1) + +/* return next ld script token */ +static int ld_next(TCCState *s1, char *name, int name_size) +{ + int c; + char *q; + + redo: + switch(ch) { + case ' ': + case '\t': + case '\f': + case '\v': + case '\r': + case '\n': + inp(); + goto redo; + case '/': + minp(); + if (ch == '*') { + file->buf_ptr = parse_comment(file->buf_ptr); + ch = file->buf_ptr[0]; + goto redo; + } else { + q = name; + *q++ = '/'; + goto parse_name; + } + break; + /* case 'a' ... 'z': */ + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + /* case 'A' ... 'z': */ + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case '\\': + case '.': + case '$': + case '~': + q = name; + parse_name: + for(;;) { + if (!((ch >= 'a' && ch <= 'z') || + (ch >= 'A' && ch <= 'Z') || + (ch >= '0' && ch <= '9') || + strchr("/.-_+=$:\\,~", ch))) + break; + if ((q - name) < name_size - 1) { + *q++ = ch; + } + minp(); + } + *q = '\0'; + c = LD_TOK_NAME; + break; + case CH_EOF: + c = LD_TOK_EOF; + break; + default: + c = ch; + inp(); + break; + } +#if 0 + printf("tok=%c %d\n", c, c); + if (c == LD_TOK_NAME) + printf(" name=%s\n", name); +#endif + return c; +} + +static int ld_add_file_list(TCCState *s1, int as_needed) +{ + char filename[1024]; + int t, ret; + + t = ld_next(s1, filename, sizeof(filename)); + if (t != '(') + expect("("); + t = ld_next(s1, filename, sizeof(filename)); + for(;;) { + if (t == LD_TOK_EOF) { + error_noabort("unexpected end of file"); + return -1; + } else if (t == ')') { + break; + } else if (t != LD_TOK_NAME) { + error_noabort("filename expected"); + return -1; + } + if (!strcmp(filename, "AS_NEEDED")) { + ret = ld_add_file_list(s1, 1); + if (ret) + return ret; + } else { + /* TODO: Implement AS_NEEDED support. Ignore it for now */ + if (!as_needed) + tcc_add_file(s1, filename); + } + t = ld_next(s1, filename, sizeof(filename)); + if (t == ',') { + t = ld_next(s1, filename, sizeof(filename)); + } + } + return 0; +} + +/* interpret a subset of GNU ldscripts to handle the dummy libc.so + files */ +static int tcc_load_ldscript(TCCState *s1) +{ + char cmd[64]; + char filename[1024]; + int t, ret; + + ch = file->buf_ptr[0]; + ch = handle_eob(); + for(;;) { + t = ld_next(s1, cmd, sizeof(cmd)); + if (t == LD_TOK_EOF) + return 0; + else if (t != LD_TOK_NAME) + return -1; + if (!strcmp(cmd, "INPUT") || + !strcmp(cmd, "GROUP")) { + ret = ld_add_file_list(s1, 0); + if (ret) + return ret; + } else if (!strcmp(cmd, "OUTPUT_FORMAT") || + !strcmp(cmd, "TARGET")) { + /* ignore some commands */ + t = ld_next(s1, cmd, sizeof(cmd)); + if (t != '(') + expect("("); + for(;;) { + t = ld_next(s1, filename, sizeof(filename)); + if (t == LD_TOK_EOF) { + error_noabort("unexpected end of file"); + return -1; + } else if (t == ')') { + break; + } + } + } else { + return -1; + } + } + return 0; +} diff --git a/05/tcc-0.9.25/tccgen.c b/05/tcc-0.9.25/tccgen.c new file mode 100644 index 0000000..860e580 --- /dev/null +++ b/05/tcc-0.9.25/tccgen.c @@ -0,0 +1,5123 @@ +/* + * TCC - Tiny C Compiler + * + * Copyright (c) 2001-2004 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void swap(int *p, int *q) +{ + int t; + t = *p; + *p = *q; + *q = t; +} + +void vsetc(CType *type, int r, CValue *vc) +{ + int v; + + if (vtop >= vstack + (VSTACK_SIZE - 1)) + error("memory full"); + /* cannot let cpu flags if other instruction are generated. Also + avoid leaving VT_JMP anywhere except on the top of the stack + because it would complicate the code generator. */ + if (vtop >= vstack) { + v = vtop->r & VT_VALMASK; + if (v == VT_CMP || (v & ~1) == VT_JMP) + gv(RC_INT); + } + vtop++; + vtop->type = *type; + vtop->r = r; + vtop->r2 = VT_CONST; + vtop->c = *vc; +} + +/* push integer constant */ +void vpushi(int v) +{ + CValue cval; + cval.i = v; + vsetc(&int_type, VT_CONST, &cval); +} + +/* push long long constant */ +void vpushll(long long v) +{ + CValue cval; + CType ctype; + ctype.t = VT_LLONG; + cval.ull = v; + vsetc(&ctype, VT_CONST, &cval); +} + +/* Return a static symbol pointing to a section */ +static Sym *get_sym_ref(CType *type, Section *sec, + unsigned long offset, unsigned long size) +{ + int v; + Sym *sym; + + v = anon_sym++; + sym = global_identifier_push(v, type->t | VT_STATIC, 0); + sym->type.ref = type->ref; + sym->r = VT_CONST | VT_SYM; + put_extern_sym(sym, sec, offset, size); + return sym; +} + +/* push a reference to a section offset by adding a dummy symbol */ +static void vpush_ref(CType *type, Section *sec, unsigned long offset, unsigned long size) +{ + CValue cval; + + cval.ul = 0; + vsetc(type, VT_CONST | VT_SYM, &cval); + vtop->sym = get_sym_ref(type, sec, offset, size); +} + +/* define a new external reference to a symbol 'v' of type 'u' */ +static Sym *external_global_sym(int v, CType *type, int r) +{ + Sym *s; + + s = sym_find(v); + if (!s) { + /* push forward reference */ + s = global_identifier_push(v, type->t | VT_EXTERN, 0); + s->type.ref = type->ref; + s->r = r | VT_CONST | VT_SYM; + } + return s; +} + +/* define a new external reference to a symbol 'v' of type 'u' */ +static Sym *external_sym(int v, CType *type, int r) +{ + Sym *s; + + s = sym_find(v); + if (!s) { + /* push forward reference */ + s = sym_push(v, type, r | VT_CONST | VT_SYM, 0); + s->type.t |= VT_EXTERN; + } else { + if (!is_compatible_types(&s->type, type)) + error("incompatible types for redefinition of '%s'", + get_tok_str(v, NULL)); + } + return s; +} + +/* push a reference to global symbol v */ +static void vpush_global_sym(CType *type, int v) +{ + Sym *sym; + CValue cval; + + sym = external_global_sym(v, type, 0); + cval.ul = 0; + vsetc(type, VT_CONST | VT_SYM, &cval); + vtop->sym = sym; +} + +void vset(CType *type, int r, int v) +{ + CValue cval; + + cval.i = v; + vsetc(type, r, &cval); +} + +void vseti(int r, int v) +{ + CType type; + type.t = VT_INT; + vset(&type, r, v); +} + +void vswap(void) +{ + SValue tmp; + + tmp = vtop[0]; + vtop[0] = vtop[-1]; + vtop[-1] = tmp; +} + +void vpushv(SValue *v) +{ + if (vtop >= vstack + (VSTACK_SIZE - 1)) + error("memory full"); + vtop++; + *vtop = *v; +} + +void vdup(void) +{ + vpushv(vtop); +} + +/* save r to the memory stack, and mark it as being free */ +void save_reg(int r) +{ + int l, saved, size, align; + SValue *p, sv; + CType *type; + + /* modify all stack values */ + saved = 0; + l = 0; + for(p=vstack;p<=vtop;p++) { + if ((p->r & VT_VALMASK) == r || + ((p->type.t & VT_BTYPE) == VT_LLONG && (p->r2 & VT_VALMASK) == r)) { + /* must save value on stack if not already done */ + if (!saved) { + /* NOTE: must reload 'r' because r might be equal to r2 */ + r = p->r & VT_VALMASK; + /* store register in the stack */ + type = &p->type; + if ((p->r & VT_LVAL) || + (!is_float(type->t) && (type->t & VT_BTYPE) != VT_LLONG)) +#ifdef TCC_TARGET_X86_64 + type = &char_pointer_type; +#else + type = &int_type; +#endif + size = type_size(type, &align); + loc = (loc - size) & -align; + sv.type.t = type->t; + sv.r = VT_LOCAL | VT_LVAL; + sv.c.ul = loc; + store(r, &sv); +#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) + /* x86 specific: need to pop fp register ST0 if saved */ + if (r == TREG_ST0) { + o(0xd9dd); /* fstp %st(1) */ + } +#endif +#ifndef TCC_TARGET_X86_64 + /* special long long case */ + if ((type->t & VT_BTYPE) == VT_LLONG) { + sv.c.ul += 4; + store(p->r2, &sv); + } +#endif + l = loc; + saved = 1; + } + /* mark that stack entry as being saved on the stack */ + if (p->r & VT_LVAL) { + /* also clear the bounded flag because the + relocation address of the function was stored in + p->c.ul */ + p->r = (p->r & ~(VT_VALMASK | VT_BOUNDED)) | VT_LLOCAL; + } else { + p->r = lvalue_type(p->type.t) | VT_LOCAL; + } + p->r2 = VT_CONST; + p->c.ul = l; + } + } +} + +/* find a register of class 'rc2' with at most one reference on stack. + * If none, call get_reg(rc) */ +int get_reg_ex(int rc, int rc2) +{ + int r; + SValue *p; + + for(r=0;rr & VT_VALMASK) == r || + (p->r2 & VT_VALMASK) == r) + n++; + } + if (n <= 1) + return r; + } + } + return get_reg(rc); +} + +/* find a free register of class 'rc'. If none, save one register */ +int get_reg(int rc) +{ + int r; + SValue *p; + + /* find a free register */ + for(r=0;rr & VT_VALMASK) == r || + (p->r2 & VT_VALMASK) == r) + goto notfound; + } + return r; + } + notfound: ; + } + + /* no register left : free the first one on the stack (VERY + IMPORTANT to start from the bottom to ensure that we don't + spill registers used in gen_opi()) */ + for(p=vstack;p<=vtop;p++) { + r = p->r & VT_VALMASK; + if (r < VT_CONST && (reg_classes[r] & rc)) + goto save_found; + /* also look at second register (if long long) */ + r = p->r2 & VT_VALMASK; + if (r < VT_CONST && (reg_classes[r] & rc)) { + save_found: + save_reg(r); + return r; + } + } + /* Should never comes here */ + return -1; +} + +/* save registers up to (vtop - n) stack entry */ +void save_regs(int n) +{ + int r; + SValue *p, *p1; + p1 = vtop - n; + for(p = vstack;p <= p1; p++) { + r = p->r & VT_VALMASK; + if (r < VT_CONST) { + save_reg(r); + } + } +} + +/* move register 's' to 'r', and flush previous value of r to memory + if needed */ +void move_reg(int r, int s) +{ + SValue sv; + + if (r != s) { + save_reg(r); + sv.type.t = VT_INT; + sv.r = s; + sv.c.ul = 0; + load(r, &sv); + } +} + +/* get address of vtop (vtop MUST BE an lvalue) */ +void gaddrof(void) +{ + vtop->r &= ~VT_LVAL; + /* tricky: if saved lvalue, then we can go back to lvalue */ + if ((vtop->r & VT_VALMASK) == VT_LLOCAL) + vtop->r = (vtop->r & ~(VT_VALMASK | VT_LVAL_TYPE)) | VT_LOCAL | VT_LVAL; +} + +#ifdef CONFIG_TCC_BCHECK +/* generate lvalue bound code */ +void gbound(void) +{ + int lval_type; + CType type1; + + vtop->r &= ~VT_MUSTBOUND; + /* if lvalue, then use checking code before dereferencing */ + if (vtop->r & VT_LVAL) { + /* if not VT_BOUNDED value, then make one */ + if (!(vtop->r & VT_BOUNDED)) { + lval_type = vtop->r & (VT_LVAL_TYPE | VT_LVAL); + /* must save type because we must set it to int to get pointer */ + type1 = vtop->type; + vtop->type.t = VT_INT; + gaddrof(); + vpushi(0); + gen_bounded_ptr_add(); + vtop->r |= lval_type; + vtop->type = type1; + } + /* then check for dereferencing */ + gen_bounded_ptr_deref(); + } +} +#endif + +/* store vtop a register belonging to class 'rc'. lvalues are + converted to values. Cannot be used if cannot be converted to + register value (such as structures). */ +int gv(int rc) +{ + int r, rc2, bit_pos, bit_size, size, align, i; + + /* NOTE: get_reg can modify vstack[] */ + if (vtop->type.t & VT_BITFIELD) { + CType type; + int bits = 32; + bit_pos = (vtop->type.t >> VT_STRUCT_SHIFT) & 0x3f; + bit_size = (vtop->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f; + /* remove bit field info to avoid loops */ + vtop->type.t &= ~(VT_BITFIELD | (-1 << VT_STRUCT_SHIFT)); + /* cast to int to propagate signedness in following ops */ + if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { + type.t = VT_LLONG; + bits = 64; + } else + type.t = VT_INT; + if((vtop->type.t & VT_UNSIGNED) || + (vtop->type.t & VT_BTYPE) == VT_BOOL) + type.t |= VT_UNSIGNED; + gen_cast(&type); + /* generate shifts */ + vpushi(bits - (bit_pos + bit_size)); + gen_op(TOK_SHL); + vpushi(bits - bit_size); + /* NOTE: transformed to SHR if unsigned */ + gen_op(TOK_SAR); + r = gv(rc); + } else { + if (is_float(vtop->type.t) && + (vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { + Sym *sym; + int *ptr; + unsigned long offset; +#if defined(TCC_TARGET_ARM) && !defined(TCC_ARM_VFP) + CValue check; +#endif + + /* XXX: unify with initializers handling ? */ + /* CPUs usually cannot use float constants, so we store them + generically in data segment */ + size = type_size(&vtop->type, &align); + offset = (data_section->data_offset + align - 1) & -align; + data_section->data_offset = offset; + /* XXX: not portable yet */ +#if defined(__i386__) || defined(__x86_64__) + /* Zero pad x87 tenbyte long doubles */ + if (size == LDOUBLE_SIZE) + vtop->c.tab[2] &= 0xffff; +#endif + ptr = section_ptr_add(data_section, size); + size = size >> 2; +#if defined(TCC_TARGET_ARM) && !defined(TCC_ARM_VFP) + check.d = 1; + if(check.tab[0]) + for(i=0;ic.tab[size-1-i]; + else +#endif + for(i=0;ic.tab[i]; + sym = get_sym_ref(&vtop->type, data_section, offset, size << 2); + vtop->r |= VT_LVAL | VT_SYM; + vtop->sym = sym; + vtop->c.ul = 0; + } +#ifdef CONFIG_TCC_BCHECK + if (vtop->r & VT_MUSTBOUND) + gbound(); +#endif + + r = vtop->r & VT_VALMASK; + rc2 = RC_INT; + if (rc == RC_IRET) + rc2 = RC_LRET; + /* need to reload if: + - constant + - lvalue (need to dereference pointer) + - already a register, but not in the right class */ + if (r >= VT_CONST || + (vtop->r & VT_LVAL) || + !(reg_classes[r] & rc) || + ((vtop->type.t & VT_BTYPE) == VT_LLONG && + !(reg_classes[vtop->r2] & rc2))) { + r = get_reg(rc); +#ifndef TCC_TARGET_X86_64 + if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { + int r2; + unsigned long long ll; + /* two register type load : expand to two words + temporarily */ + if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { + /* load constant */ + ll = vtop->c.ull; + vtop->c.ui = ll; /* first word */ + load(r, vtop); + vtop->r = r; /* save register value */ + vpushi(ll >> 32); /* second word */ + } else if (r >= VT_CONST || /* XXX: test to VT_CONST incorrect ? */ + (vtop->r & VT_LVAL)) { + /* We do not want to modifier the long long + pointer here, so the safest (and less + efficient) is to save all the other registers + in the stack. XXX: totally inefficient. */ + save_regs(1); + /* load from memory */ + load(r, vtop); + vdup(); + vtop[-1].r = r; /* save register value */ + /* increment pointer to get second word */ + vtop->type.t = VT_INT; + gaddrof(); + vpushi(4); + gen_op('+'); + vtop->r |= VT_LVAL; + } else { + /* move registers */ + load(r, vtop); + vdup(); + vtop[-1].r = r; /* save register value */ + vtop->r = vtop[-1].r2; + } + /* allocate second register */ + r2 = get_reg(rc2); + load(r2, vtop); + vpop(); + /* write second register */ + vtop->r2 = r2; + } else +#endif + if ((vtop->r & VT_LVAL) && !is_float(vtop->type.t)) { + int t1, t; + /* lvalue of scalar type : need to use lvalue type + because of possible cast */ + t = vtop->type.t; + t1 = t; + /* compute memory access type */ + if (vtop->r & VT_LVAL_BYTE) + t = VT_BYTE; + else if (vtop->r & VT_LVAL_SHORT) + t = VT_SHORT; + if (vtop->r & VT_LVAL_UNSIGNED) + t |= VT_UNSIGNED; + vtop->type.t = t; + load(r, vtop); + /* restore wanted type */ + vtop->type.t = t1; + } else { + /* one register type load */ + load(r, vtop); + } + } + vtop->r = r; +#ifdef TCC_TARGET_C67 + /* uses register pairs for doubles */ + if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) + vtop->r2 = r+1; +#endif + } + return r; +} + +/* generate vtop[-1] and vtop[0] in resp. classes rc1 and rc2 */ +void gv2(int rc1, int rc2) +{ + int v; + + /* generate more generic register first. But VT_JMP or VT_CMP + values must be generated first in all cases to avoid possible + reload errors */ + v = vtop[0].r & VT_VALMASK; + if (v != VT_CMP && (v & ~1) != VT_JMP && rc1 <= rc2) { + vswap(); + gv(rc1); + vswap(); + gv(rc2); + /* test if reload is needed for first register */ + if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) { + vswap(); + gv(rc1); + vswap(); + } + } else { + gv(rc2); + vswap(); + gv(rc1); + vswap(); + /* test if reload is needed for first register */ + if ((vtop[0].r & VT_VALMASK) >= VT_CONST) { + gv(rc2); + } + } +} + +/* wrapper around RC_FRET to return a register by type */ +int rc_fret(int t) +{ +#ifdef TCC_TARGET_X86_64 + if (t == VT_LDOUBLE) { + return RC_ST0; + } +#endif + return RC_FRET; +} + +/* wrapper around REG_FRET to return a register by type */ +int reg_fret(int t) +{ +#ifdef TCC_TARGET_X86_64 + if (t == VT_LDOUBLE) { + return TREG_ST0; + } +#endif + return REG_FRET; +} + +/* expand long long on stack in two int registers */ +void lexpand(void) +{ + int u; + + u = vtop->type.t & VT_UNSIGNED; + gv(RC_INT); + vdup(); + vtop[0].r = vtop[-1].r2; + vtop[0].r2 = VT_CONST; + vtop[-1].r2 = VT_CONST; + vtop[0].type.t = VT_INT | u; + vtop[-1].type.t = VT_INT | u; +} + +#ifdef TCC_TARGET_ARM +/* expand long long on stack */ +void lexpand_nr(void) +{ + int u,v; + + u = vtop->type.t & VT_UNSIGNED; + vdup(); + vtop->r2 = VT_CONST; + vtop->type.t = VT_INT | u; + v=vtop[-1].r & (VT_VALMASK | VT_LVAL); + if (v == VT_CONST) { + vtop[-1].c.ui = vtop->c.ull; + vtop->c.ui = vtop->c.ull >> 32; + vtop->r = VT_CONST; + } else if (v == (VT_LVAL|VT_CONST) || v == (VT_LVAL|VT_LOCAL)) { + vtop->c.ui += 4; + vtop->r = vtop[-1].r; + } else if (v > VT_CONST) { + vtop--; + lexpand(); + } else + vtop->r = vtop[-1].r2; + vtop[-1].r2 = VT_CONST; + vtop[-1].type.t = VT_INT | u; +} +#endif + +/* build a long long from two ints */ +void lbuild(int t) +{ + gv2(RC_INT, RC_INT); + vtop[-1].r2 = vtop[0].r; + vtop[-1].type.t = t; + vpop(); +} + +/* rotate n first stack elements to the bottom + I1 ... In -> I2 ... In I1 [top is right] +*/ +void vrotb(int n) +{ + int i; + SValue tmp; + + tmp = vtop[-n + 1]; + for(i=-n+1;i!=0;i++) + vtop[i] = vtop[i+1]; + vtop[0] = tmp; +} + +/* rotate n first stack elements to the top + I1 ... In -> In I1 ... I(n-1) [top is right] + */ +void vrott(int n) +{ + int i; + SValue tmp; + + tmp = vtop[0]; + for(i = 0;i < n - 1; i++) + vtop[-i] = vtop[-i - 1]; + vtop[-n + 1] = tmp; +} + +#ifdef TCC_TARGET_ARM +/* like vrott but in other direction + In ... I1 -> I(n-1) ... I1 In [top is right] + */ +void vnrott(int n) +{ + int i; + SValue tmp; + + tmp = vtop[-n + 1]; + for(i = n - 1; i > 0; i--) + vtop[-i] = vtop[-i + 1]; + vtop[0] = tmp; +} +#endif + +/* pop stack value */ +void vpop(void) +{ + int v; + v = vtop->r & VT_VALMASK; +#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) + /* for x86, we need to pop the FP stack */ + if (v == TREG_ST0 && !nocode_wanted) { + o(0xd9dd); /* fstp %st(1) */ + } else +#endif + if (v == VT_JMP || v == VT_JMPI) { + /* need to put correct jump if && or || without test */ + gsym(vtop->c.ul); + } + vtop--; +} + +/* convert stack entry to register and duplicate its value in another + register */ +void gv_dup(void) +{ + int rc, t, r, r1; + SValue sv; + + t = vtop->type.t; + if ((t & VT_BTYPE) == VT_LLONG) { + lexpand(); + gv_dup(); + vswap(); + vrotb(3); + gv_dup(); + vrotb(4); + /* stack: H L L1 H1 */ + lbuild(t); + vrotb(3); + vrotb(3); + vswap(); + lbuild(t); + vswap(); + } else { + /* duplicate value */ + rc = RC_INT; + sv.type.t = VT_INT; + if (is_float(t)) { + rc = RC_FLOAT; +#ifdef TCC_TARGET_X86_64 + if ((t & VT_BTYPE) == VT_LDOUBLE) { + rc = RC_ST0; + } +#endif + sv.type.t = t; + } + r = gv(rc); + r1 = get_reg(rc); + sv.r = r; + sv.c.ul = 0; + load(r1, &sv); /* move r to r1 */ + vdup(); + /* duplicates value */ + vtop->r = r1; + } +} + +#ifndef TCC_TARGET_X86_64 +/* generate CPU independent (unsigned) long long operations */ +void gen_opl(int op) +{ + int t, a, b, op1, c, i; + int func; + unsigned short reg_iret = REG_IRET; + unsigned short reg_lret = REG_LRET; + SValue tmp; + + switch(op) { + case '/': + case TOK_PDIV: + func = TOK___divdi3; + goto gen_func; + case TOK_UDIV: + func = TOK___udivdi3; + goto gen_func; + case '%': + func = TOK___moddi3; + goto gen_mod_func; + case TOK_UMOD: + func = TOK___umoddi3; + gen_mod_func: +#ifdef TCC_ARM_EABI + reg_iret = TREG_R2; + reg_lret = TREG_R3; +#endif + gen_func: + /* call generic long long function */ + vpush_global_sym(&func_old_type, func); + vrott(3); + gfunc_call(2); + vpushi(0); + vtop->r = reg_iret; + vtop->r2 = reg_lret; + break; + case '^': + case '&': + case '|': + case '*': + case '+': + case '-': + t = vtop->type.t; + vswap(); + lexpand(); + vrotb(3); + lexpand(); + /* stack: L1 H1 L2 H2 */ + tmp = vtop[0]; + vtop[0] = vtop[-3]; + vtop[-3] = tmp; + tmp = vtop[-2]; + vtop[-2] = vtop[-3]; + vtop[-3] = tmp; + vswap(); + /* stack: H1 H2 L1 L2 */ + if (op == '*') { + vpushv(vtop - 1); + vpushv(vtop - 1); + gen_op(TOK_UMULL); + lexpand(); + /* stack: H1 H2 L1 L2 ML MH */ + for(i=0;i<4;i++) + vrotb(6); + /* stack: ML MH H1 H2 L1 L2 */ + tmp = vtop[0]; + vtop[0] = vtop[-2]; + vtop[-2] = tmp; + /* stack: ML MH H1 L2 H2 L1 */ + gen_op('*'); + vrotb(3); + vrotb(3); + gen_op('*'); + /* stack: ML MH M1 M2 */ + gen_op('+'); + gen_op('+'); + } else if (op == '+' || op == '-') { + /* XXX: add non carry method too (for MIPS or alpha) */ + if (op == '+') + op1 = TOK_ADDC1; + else + op1 = TOK_SUBC1; + gen_op(op1); + /* stack: H1 H2 (L1 op L2) */ + vrotb(3); + vrotb(3); + gen_op(op1 + 1); /* TOK_xxxC2 */ + } else { + gen_op(op); + /* stack: H1 H2 (L1 op L2) */ + vrotb(3); + vrotb(3); + /* stack: (L1 op L2) H1 H2 */ + gen_op(op); + /* stack: (L1 op L2) (H1 op H2) */ + } + /* stack: L H */ + lbuild(t); + break; + case TOK_SAR: + case TOK_SHR: + case TOK_SHL: + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + t = vtop[-1].type.t; + vswap(); + lexpand(); + vrotb(3); + /* stack: L H shift */ + c = (int)vtop->c.i; + /* constant: simpler */ + /* NOTE: all comments are for SHL. the other cases are + done by swaping words */ + vpop(); + if (op != TOK_SHL) + vswap(); + if (c >= 32) { + /* stack: L H */ + vpop(); + if (c > 32) { + vpushi(c - 32); + gen_op(op); + } + if (op != TOK_SAR) { + vpushi(0); + } else { + gv_dup(); + vpushi(31); + gen_op(TOK_SAR); + } + vswap(); + } else { + vswap(); + gv_dup(); + /* stack: H L L */ + vpushi(c); + gen_op(op); + vswap(); + vpushi(32 - c); + if (op == TOK_SHL) + gen_op(TOK_SHR); + else + gen_op(TOK_SHL); + vrotb(3); + /* stack: L L H */ + vpushi(c); + if (op == TOK_SHL) + gen_op(TOK_SHL); + else + gen_op(TOK_SHR); + gen_op('|'); + } + if (op != TOK_SHL) + vswap(); + lbuild(t); + } else { + /* XXX: should provide a faster fallback on x86 ? */ + switch(op) { + case TOK_SAR: + func = TOK___ashrdi3; + goto gen_func; + case TOK_SHR: + func = TOK___lshrdi3; + goto gen_func; + case TOK_SHL: + func = TOK___ashldi3; + goto gen_func; + } + } + break; + default: + /* compare operations */ + t = vtop->type.t; + vswap(); + lexpand(); + vrotb(3); + lexpand(); + /* stack: L1 H1 L2 H2 */ + tmp = vtop[-1]; + vtop[-1] = vtop[-2]; + vtop[-2] = tmp; + /* stack: L1 L2 H1 H2 */ + /* compare high */ + op1 = op; + /* when values are equal, we need to compare low words. since + the jump is inverted, we invert the test too. */ + if (op1 == TOK_LT) + op1 = TOK_LE; + else if (op1 == TOK_GT) + op1 = TOK_GE; + else if (op1 == TOK_ULT) + op1 = TOK_ULE; + else if (op1 == TOK_UGT) + op1 = TOK_UGE; + a = 0; + b = 0; + gen_op(op1); + if (op1 != TOK_NE) { + a = gtst(1, 0); + } + if (op != TOK_EQ) { + /* generate non equal test */ + /* XXX: NOT PORTABLE yet */ + if (a == 0) { + b = gtst(0, 0); + } else { +#if defined(TCC_TARGET_I386) + b = psym(0x850f, 0); +#elif defined(TCC_TARGET_ARM) + b = ind; + o(0x1A000000 | encbranch(ind, 0, 1)); +#elif defined(TCC_TARGET_C67) + error("not implemented"); +#else +#error not supported +#endif + } + } + /* compare low. Always unsigned */ + op1 = op; + if (op1 == TOK_LT) + op1 = TOK_ULT; + else if (op1 == TOK_LE) + op1 = TOK_ULE; + else if (op1 == TOK_GT) + op1 = TOK_UGT; + else if (op1 == TOK_GE) + op1 = TOK_UGE; + gen_op(op1); + a = gtst(1, a); + gsym(b); + vseti(VT_JMPI, a); + break; + } +} +#endif + +typedef unsigned long long _U; + +/* handle integer constant optimizations and various machine + independent opt */ +void gen_opic(int op) +{ + int c1, c2, t1, t2, n; + SValue *v1, *v2; + long long l1, l2; + + v1 = vtop - 1; + v2 = vtop; + t1 = v1->type.t & VT_BTYPE; + t2 = v2->type.t & VT_BTYPE; + + if (t1 == VT_LLONG) + l1 = v1->c.ll; + else if (v1->type.t & VT_UNSIGNED) + l1 = v1->c.ui; + else + l1 = v1->c.i; + + if (t2 == VT_LLONG) + l2 = v2->c.ll; + else if (v2->type.t & VT_UNSIGNED) + l2 = v2->c.ui; + else + l2 = v2->c.i; + + /* currently, we cannot do computations with forward symbols */ + c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; + c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; + if (c1 && c2) { + switch(op) { + case '+': l1 += l2; break; + case '-': l1 -= l2; break; + case '&': l1 &= l2; break; + case '^': l1 ^= l2; break; + case '|': l1 |= l2; break; + case '*': l1 *= l2; break; + + case TOK_PDIV: + case '/': + case '%': + case TOK_UDIV: + case TOK_UMOD: + /* if division by zero, generate explicit division */ + if (l2 == 0) { + if (const_wanted) + error("division by zero in constant"); + goto general_case; + } + switch(op) { + case '%': l1 %= l2; break; + case TOK_UDIV: l1 = (_U)l1 / l2; break; + case TOK_UMOD: l1 = (_U)l1 % l2; break; + default: l1 /= l2; break; + } + break; + case TOK_SHL: l1 <<= l2; break; + case TOK_SHR: l1 = (_U)l1 >> l2; break; + case TOK_SAR: l1 >>= l2; break; + /* tests */ + case TOK_ULT: l1 = (_U)l1 < (_U)l2; break; + case TOK_UGE: l1 = (_U)l1 >= (_U)l2; break; + case TOK_EQ: l1 = l1 == l2; break; + case TOK_NE: l1 = l1 != l2; break; + case TOK_ULE: l1 = (_U)l1 <= (_U)l2; break; + case TOK_UGT: l1 = (_U)l1 > (_U)l2; break; + case TOK_LT: l1 = l1 < l2; break; + case TOK_GE: l1 = l1 >= l2; break; + case TOK_LE: l1 = l1 <= l2; break; + case TOK_GT: l1 = l1 > l2; break; + /* logical */ + case TOK_LAND: l1 = l1 && l2; break; + case TOK_LOR: l1 = l1 || l2; break; + default: + goto general_case; + } + v1->c.ll = l1; + vtop--; + } else { + /* if commutative ops, put c2 as constant */ + if (c1 && (op == '+' || op == '&' || op == '^' || + op == '|' || op == '*')) { + vswap(); + c2 = c1; //c = c1, c1 = c2, c2 = c; + l2 = l1; //l = l1, l1 = l2, l2 = l; + } + /* Filter out NOP operations like x*1, x-0, x&-1... */ + if (c2 && (((op == '*' || op == '/' || op == TOK_UDIV || + op == TOK_PDIV) && + l2 == 1) || + ((op == '+' || op == '-' || op == '|' || op == '^' || + op == TOK_SHL || op == TOK_SHR || op == TOK_SAR) && + l2 == 0) || + (op == '&' && + l2 == -1))) { + /* nothing to do */ + vtop--; + } else if (c2 && (op == '*' || op == TOK_PDIV || op == TOK_UDIV)) { + /* try to use shifts instead of muls or divs */ + if (l2 > 0 && (l2 & (l2 - 1)) == 0) { + n = -1; + while (l2) { + l2 >>= 1; + n++; + } + vtop->c.ll = n; + if (op == '*') + op = TOK_SHL; + else if (op == TOK_PDIV) + op = TOK_SAR; + else + op = TOK_SHR; + } + goto general_case; + } else if (c2 && (op == '+' || op == '-') && + ((vtop[-1].r & (VT_VALMASK | VT_LVAL | VT_SYM)) == + (VT_CONST | VT_SYM) || + (vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_LOCAL)) { + /* symbol + constant case */ + if (op == '-') + l2 = -l2; + vtop--; + vtop->c.ll += l2; + } else { + general_case: + if (!nocode_wanted) { + /* call low level op generator */ + if (t1 == VT_LLONG || t2 == VT_LLONG) + gen_opl(op); + else + gen_opi(op); + } else { + vtop--; + } + } + } +} + +/* generate a floating point operation with constant propagation */ +void gen_opif(int op) +{ + int c1, c2; + SValue *v1, *v2; + long double f1, f2; + + v1 = vtop - 1; + v2 = vtop; + /* currently, we cannot do computations with forward symbols */ + c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; + c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; + if (c1 && c2) { + if (v1->type.t == VT_FLOAT) { + f1 = v1->c.f; + f2 = v2->c.f; + } else if (v1->type.t == VT_DOUBLE) { + f1 = v1->c.d; + f2 = v2->c.d; + } else { + f1 = v1->c.ld; + f2 = v2->c.ld; + } + + /* NOTE: we only do constant propagation if finite number (not + NaN or infinity) (ANSI spec) */ + if (!ieee_finite(f1) || !ieee_finite(f2)) + goto general_case; + + switch(op) { + case '+': f1 += f2; break; + case '-': f1 -= f2; break; + case '*': f1 *= f2; break; + case '/': + if (f2 == 0.0) { + if (const_wanted) + error("division by zero in constant"); + goto general_case; + } + f1 /= f2; + break; + /* XXX: also handles tests ? */ + default: + goto general_case; + } + /* XXX: overflow test ? */ + if (v1->type.t == VT_FLOAT) { + v1->c.f = f1; + } else if (v1->type.t == VT_DOUBLE) { + v1->c.d = f1; + } else { + v1->c.ld = f1; + } + vtop--; + } else { + general_case: + if (!nocode_wanted) { + gen_opf(op); + } else { + vtop--; + } + } +} + +static int pointed_size(CType *type) +{ + int align; + return type_size(pointed_type(type), &align); +} + +static inline int is_null_pointer(SValue *p) +{ + if ((p->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) + return 0; + return ((p->type.t & VT_BTYPE) == VT_INT && p->c.i == 0) || + ((p->type.t & VT_BTYPE) == VT_LLONG && p->c.ll == 0); +} + +static inline int is_integer_btype(int bt) +{ + return (bt == VT_BYTE || bt == VT_SHORT || + bt == VT_INT || bt == VT_LLONG); +} + +/* check types for comparison or substraction of pointers */ +static void check_comparison_pointer_types(SValue *p1, SValue *p2, int op) +{ + CType *type1, *type2, tmp_type1, tmp_type2; + int bt1, bt2; + + /* null pointers are accepted for all comparisons as gcc */ + if (is_null_pointer(p1) || is_null_pointer(p2)) + return; + type1 = &p1->type; + type2 = &p2->type; + bt1 = type1->t & VT_BTYPE; + bt2 = type2->t & VT_BTYPE; + /* accept comparison between pointer and integer with a warning */ + if ((is_integer_btype(bt1) || is_integer_btype(bt2)) && op != '-') { + if (op != TOK_LOR && op != TOK_LAND ) + warning("comparison between pointer and integer"); + return; + } + + /* both must be pointers or implicit function pointers */ + if (bt1 == VT_PTR) { + type1 = pointed_type(type1); + } else if (bt1 != VT_FUNC) + goto invalid_operands; + + if (bt2 == VT_PTR) { + type2 = pointed_type(type2); + } else if (bt2 != VT_FUNC) { + invalid_operands: + error("invalid operands to binary %s", get_tok_str(op, NULL)); + } + if ((type1->t & VT_BTYPE) == VT_VOID || + (type2->t & VT_BTYPE) == VT_VOID) + return; + tmp_type1 = *type1; + tmp_type2 = *type2; + tmp_type1.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE); + tmp_type2.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE); + if (!is_compatible_types(&tmp_type1, &tmp_type2)) { + /* gcc-like error if '-' is used */ + if (op == '-') + goto invalid_operands; + else + warning("comparison of distinct pointer types lacks a cast"); + } +} + +/* generic gen_op: handles types problems */ +void gen_op(int op) +{ + int u, t1, t2, bt1, bt2, t; + CType type1; + + t1 = vtop[-1].type.t; + t2 = vtop[0].type.t; + bt1 = t1 & VT_BTYPE; + bt2 = t2 & VT_BTYPE; + + if (bt1 == VT_PTR || bt2 == VT_PTR) { + /* at least one operand is a pointer */ + /* relationnal op: must be both pointers */ + if (op >= TOK_ULT && op <= TOK_LOR) { + check_comparison_pointer_types(vtop - 1, vtop, op); + /* pointers are handled are unsigned */ +#ifdef TCC_TARGET_X86_64 + t = VT_LLONG | VT_UNSIGNED; +#else + t = VT_INT | VT_UNSIGNED; +#endif + goto std_op; + } + /* if both pointers, then it must be the '-' op */ + if (bt1 == VT_PTR && bt2 == VT_PTR) { + if (op != '-') + error("cannot use pointers here"); + check_comparison_pointer_types(vtop - 1, vtop, op); + /* XXX: check that types are compatible */ + u = pointed_size(&vtop[-1].type); + gen_opic(op); + /* set to integer type */ +#ifdef TCC_TARGET_X86_64 + vtop->type.t = VT_LLONG; +#else + vtop->type.t = VT_INT; +#endif + vpushi(u); + gen_op(TOK_PDIV); + } else { + /* exactly one pointer : must be '+' or '-'. */ + if (op != '-' && op != '+') + error("cannot use pointers here"); + /* Put pointer as first operand */ + if (bt2 == VT_PTR) { + vswap(); + swap(&t1, &t2); + } + type1 = vtop[-1].type; +#ifdef TCC_TARGET_X86_64 + vpushll(pointed_size(&vtop[-1].type)); +#else + /* XXX: cast to int ? (long long case) */ + vpushi(pointed_size(&vtop[-1].type)); +#endif + gen_op('*'); +#ifdef CONFIG_TCC_BCHECK + /* if evaluating constant expression, no code should be + generated, so no bound check */ + if (tcc_state->do_bounds_check && !const_wanted) { + /* if bounded pointers, we generate a special code to + test bounds */ + if (op == '-') { + vpushi(0); + vswap(); + gen_op('-'); + } + gen_bounded_ptr_add(); + } else +#endif + { + gen_opic(op); + } + /* put again type if gen_opic() swaped operands */ + vtop->type = type1; + } + } else if (is_float(bt1) || is_float(bt2)) { + /* compute bigger type and do implicit casts */ + if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) { + t = VT_LDOUBLE; + } else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) { + t = VT_DOUBLE; + } else { + t = VT_FLOAT; + } + /* floats can only be used for a few operations */ + if (op != '+' && op != '-' && op != '*' && op != '/' && + (op < TOK_ULT || op > TOK_GT)) + error("invalid operands for binary operation"); + goto std_op; + } else if (bt1 == VT_LLONG || bt2 == VT_LLONG) { + /* cast to biggest op */ + t = VT_LLONG; + /* convert to unsigned if it does not fit in a long long */ + if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED) || + (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED)) + t |= VT_UNSIGNED; + goto std_op; + } else { + /* integer operations */ + t = VT_INT; + /* convert to unsigned if it does not fit in an integer */ + if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED) || + (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED)) + t |= VT_UNSIGNED; + std_op: + /* XXX: currently, some unsigned operations are explicit, so + we modify them here */ + if (t & VT_UNSIGNED) { + if (op == TOK_SAR) + op = TOK_SHR; + else if (op == '/') + op = TOK_UDIV; + else if (op == '%') + op = TOK_UMOD; + else if (op == TOK_LT) + op = TOK_ULT; + else if (op == TOK_GT) + op = TOK_UGT; + else if (op == TOK_LE) + op = TOK_ULE; + else if (op == TOK_GE) + op = TOK_UGE; + } + vswap(); + type1.t = t; + gen_cast(&type1); + vswap(); + /* special case for shifts and long long: we keep the shift as + an integer */ + if (op == TOK_SHR || op == TOK_SAR || op == TOK_SHL) + type1.t = VT_INT; + gen_cast(&type1); + if (is_float(t)) + gen_opif(op); + else + gen_opic(op); + if (op >= TOK_ULT && op <= TOK_GT) { + /* relationnal op: the result is an int */ + vtop->type.t = VT_INT; + } else { + vtop->type.t = t; + } + } +} + +#ifndef TCC_TARGET_ARM +/* generic itof for unsigned long long case */ +void gen_cvt_itof1(int t) +{ + if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == + (VT_LLONG | VT_UNSIGNED)) { + + if (t == VT_FLOAT) + vpush_global_sym(&func_old_type, TOK___floatundisf); +#if LDOUBLE_SIZE != 8 + else if (t == VT_LDOUBLE) + vpush_global_sym(&func_old_type, TOK___floatundixf); +#endif + else + vpush_global_sym(&func_old_type, TOK___floatundidf); + vrott(2); + gfunc_call(1); + vpushi(0); + vtop->r = reg_fret(t); + } else { + gen_cvt_itof(t); + } +} +#endif + +/* generic ftoi for unsigned long long case */ +void gen_cvt_ftoi1(int t) +{ + int st; + + if (t == (VT_LLONG | VT_UNSIGNED)) { + /* not handled natively */ + st = vtop->type.t & VT_BTYPE; + if (st == VT_FLOAT) + vpush_global_sym(&func_old_type, TOK___fixunssfdi); +#if LDOUBLE_SIZE != 8 + else if (st == VT_LDOUBLE) + vpush_global_sym(&func_old_type, TOK___fixunsxfdi); +#endif + else + vpush_global_sym(&func_old_type, TOK___fixunsdfdi); + vrott(2); + gfunc_call(1); + vpushi(0); + vtop->r = REG_IRET; + vtop->r2 = REG_LRET; + } else { + gen_cvt_ftoi(t); + } +} + +/* force char or short cast */ +void force_charshort_cast(int t) +{ + int bits, dbt; + dbt = t & VT_BTYPE; + /* XXX: add optimization if lvalue : just change type and offset */ + if (dbt == VT_BYTE) + bits = 8; + else + bits = 16; + if (t & VT_UNSIGNED) { + vpushi((1 << bits) - 1); + gen_op('&'); + } else { + bits = 32 - bits; + vpushi(bits); + gen_op(TOK_SHL); + /* result must be signed or the SAR is converted to an SHL + This was not the case when "t" was a signed short + and the last value on the stack was an unsigned int */ + vtop->type.t &= ~VT_UNSIGNED; + vpushi(bits); + gen_op(TOK_SAR); + } +} + +/* cast 'vtop' to 'type'. Casting to bitfields is forbidden. */ +static void gen_cast(CType *type) +{ + int sbt, dbt, sf, df, c, p; + + /* special delayed cast for char/short */ + /* XXX: in some cases (multiple cascaded casts), it may still + be incorrect */ + if (vtop->r & VT_MUSTCAST) { + vtop->r &= ~VT_MUSTCAST; + force_charshort_cast(vtop->type.t); + } + + /* bitfields first get cast to ints */ + if (vtop->type.t & VT_BITFIELD) { + gv(RC_INT); + } + + dbt = type->t & (VT_BTYPE | VT_UNSIGNED); + sbt = vtop->type.t & (VT_BTYPE | VT_UNSIGNED); + + if (sbt != dbt) { + sf = is_float(sbt); + df = is_float(dbt); + c = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; + p = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == (VT_CONST | VT_SYM); + if (c) { + /* constant case: we can do it now */ + /* XXX: in ISOC, cannot do it if error in convert */ + if (sbt == VT_FLOAT) + vtop->c.ld = vtop->c.f; + else if (sbt == VT_DOUBLE) + vtop->c.ld = vtop->c.d; + + if (df) { + if ((sbt & VT_BTYPE) == VT_LLONG) { + if (sbt & VT_UNSIGNED) + vtop->c.ld = vtop->c.ull; + else + vtop->c.ld = vtop->c.ll; + } else if(!sf) { + if (sbt & VT_UNSIGNED) + vtop->c.ld = vtop->c.ui; + else + vtop->c.ld = vtop->c.i; + } + + if (dbt == VT_FLOAT) + vtop->c.f = (float)vtop->c.ld; + else if (dbt == VT_DOUBLE) + vtop->c.d = (double)vtop->c.ld; + } else if (sf && dbt == (VT_LLONG|VT_UNSIGNED)) { + vtop->c.ull = (unsigned long long)vtop->c.ld; + } else if (sf && dbt == VT_BOOL) { + vtop->c.i = (vtop->c.ld != 0); + } else { + if(sf) + vtop->c.ll = (long long)vtop->c.ld; + else if (sbt == (VT_LLONG|VT_UNSIGNED)) + vtop->c.ll = vtop->c.ull; + else if (sbt & VT_UNSIGNED) + vtop->c.ll = vtop->c.ui; + else if (sbt != VT_LLONG) + vtop->c.ll = vtop->c.i; + + if (dbt == (VT_LLONG|VT_UNSIGNED)) + vtop->c.ull = vtop->c.ll; + else if (dbt == VT_BOOL) + vtop->c.i = (vtop->c.ll != 0); + else if (dbt != VT_LLONG) { + int s = 0; + if ((dbt & VT_BTYPE) == VT_BYTE) + s = 24; + else if ((dbt & VT_BTYPE) == VT_SHORT) + s = 16; + + if(dbt & VT_UNSIGNED) + vtop->c.ui = ((unsigned int)vtop->c.ll << s) >> s; + else + vtop->c.i = ((int)vtop->c.ll << s) >> s; + } + } + } else if (p && dbt == VT_BOOL) { + vtop->r = VT_CONST; + vtop->c.i = 1; + } else if (!nocode_wanted) { + /* non constant case: generate code */ + if (sf && df) { + /* convert from fp to fp */ + gen_cvt_ftof(dbt); + } else if (df) { + /* convert int to fp */ + gen_cvt_itof1(dbt); + } else if (sf) { + /* convert fp to int */ + if (dbt == VT_BOOL) { + vpushi(0); + gen_op(TOK_NE); + } else { + /* we handle char/short/etc... with generic code */ + if (dbt != (VT_INT | VT_UNSIGNED) && + dbt != (VT_LLONG | VT_UNSIGNED) && + dbt != VT_LLONG) + dbt = VT_INT; + gen_cvt_ftoi1(dbt); + if (dbt == VT_INT && (type->t & (VT_BTYPE | VT_UNSIGNED)) != dbt) { + /* additional cast for char/short... */ + vtop->type.t = dbt; + gen_cast(type); + } + } +#ifndef TCC_TARGET_X86_64 + } else if ((dbt & VT_BTYPE) == VT_LLONG) { + if ((sbt & VT_BTYPE) != VT_LLONG) { + /* scalar to long long */ + /* machine independent conversion */ + gv(RC_INT); + /* generate high word */ + if (sbt == (VT_INT | VT_UNSIGNED)) { + vpushi(0); + gv(RC_INT); + } else { + if (sbt == VT_PTR) { + /* cast from pointer to int before we apply + shift operation, which pointers don't support*/ + gen_cast(&int_type); + } + gv_dup(); + vpushi(31); + gen_op(TOK_SAR); + } + /* patch second register */ + vtop[-1].r2 = vtop->r; + vpop(); + } +#else + } else if ((dbt & VT_BTYPE) == VT_LLONG || + (dbt & VT_BTYPE) == VT_PTR) { + /* XXX: not sure if this is perfect... need more tests */ + if ((sbt & VT_BTYPE) != VT_LLONG) { + int r = gv(RC_INT); + if (sbt != (VT_INT | VT_UNSIGNED) && + sbt != VT_PTR && sbt != VT_FUNC) { + /* x86_64 specific: movslq */ + o(0x6348); + o(0xc0 + (REG_VALUE(r) << 3) + REG_VALUE(r)); + } + } +#endif + } else if (dbt == VT_BOOL) { + /* scalar to bool */ + vpushi(0); + gen_op(TOK_NE); + } else if ((dbt & VT_BTYPE) == VT_BYTE || + (dbt & VT_BTYPE) == VT_SHORT) { + if (sbt == VT_PTR) { + vtop->type.t = VT_INT; + warning("nonportable conversion from pointer to char/short"); + } + force_charshort_cast(dbt); + } else if ((dbt & VT_BTYPE) == VT_INT) { + /* scalar to int */ + if (sbt == VT_LLONG) { + /* from long long: just take low order word */ + lexpand(); + vpop(); + } + /* if lvalue and single word type, nothing to do because + the lvalue already contains the real type size (see + VT_LVAL_xxx constants) */ + } + } + } else if ((dbt & VT_BTYPE) == VT_PTR && !(vtop->r & VT_LVAL)) { + /* if we are casting between pointer types, + we must update the VT_LVAL_xxx size */ + vtop->r = (vtop->r & ~VT_LVAL_TYPE) + | (lvalue_type(type->ref->type.t) & VT_LVAL_TYPE); + } + vtop->type = *type; +} + +/* return type size. Put alignment at 'a' */ +static int type_size(CType *type, int *a) +{ + Sym *s; + int bt; + + bt = type->t & VT_BTYPE; + if (bt == VT_STRUCT) { + /* struct/union */ + s = type->ref; + *a = s->r; + return s->c; + } else if (bt == VT_PTR) { + if (type->t & VT_ARRAY) { + int ts; + + s = type->ref; + ts = type_size(&s->type, a); + + if (ts < 0 && s->c < 0) + ts = -ts; + + return ts * s->c; + } else { + *a = PTR_SIZE; + return PTR_SIZE; + } + } else if (bt == VT_LDOUBLE) { + *a = LDOUBLE_ALIGN; + return LDOUBLE_SIZE; + } else if (bt == VT_DOUBLE || bt == VT_LLONG) { +#ifdef TCC_TARGET_I386 +#ifdef TCC_TARGET_PE + *a = 8; +#else + *a = 4; +#endif +#elif defined(TCC_TARGET_ARM) +#ifdef TCC_ARM_EABI + *a = 8; +#else + *a = 4; +#endif +#else + *a = 8; +#endif + return 8; + } else if (bt == VT_INT || bt == VT_ENUM || bt == VT_FLOAT) { + *a = 4; + return 4; + } else if (bt == VT_SHORT) { + *a = 2; + return 2; + } else { + /* char, void, function, _Bool */ + *a = 1; + return 1; + } +} + +/* return the pointed type of t */ +static inline CType *pointed_type(CType *type) +{ + return &type->ref->type; +} + +/* modify type so that its it is a pointer to type. */ +static void mk_pointer(CType *type) +{ + Sym *s; + s = sym_push(SYM_FIELD, type, 0, -1); + type->t = VT_PTR | (type->t & ~VT_TYPE); + type->ref = s; +} + +/* compare function types. OLD functions match any new functions */ +static int is_compatible_func(CType *type1, CType *type2) +{ + Sym *s1, *s2; + + s1 = type1->ref; + s2 = type2->ref; + if (!is_compatible_types(&s1->type, &s2->type)) + return 0; + /* check func_call */ + if (FUNC_CALL(s1->r) != FUNC_CALL(s2->r)) + return 0; + /* XXX: not complete */ + if (s1->c == FUNC_OLD || s2->c == FUNC_OLD) + return 1; + if (s1->c != s2->c) + return 0; + while (s1 != NULL) { + if (s2 == NULL) + return 0; + if (!is_compatible_parameter_types(&s1->type, &s2->type)) + return 0; + s1 = s1->next; + s2 = s2->next; + } + if (s2) + return 0; + return 1; +} + +/* return true if type1 and type2 are the same. If unqualified is + true, qualifiers on the types are ignored. + + - enums are not checked as gcc __builtin_types_compatible_p () + */ +static int compare_types(CType *type1, CType *type2, int unqualified) +{ + int bt1, t1, t2; + + t1 = type1->t & VT_TYPE; + t2 = type2->t & VT_TYPE; + if (unqualified) { + /* strip qualifiers before comparing */ + t1 &= ~(VT_CONSTANT | VT_VOLATILE); + t2 &= ~(VT_CONSTANT | VT_VOLATILE); + } + /* XXX: bitfields ? */ + if (t1 != t2) + return 0; + /* test more complicated cases */ + bt1 = t1 & VT_BTYPE; + if (bt1 == VT_PTR) { + type1 = pointed_type(type1); + type2 = pointed_type(type2); + return is_compatible_types(type1, type2); + } else if (bt1 == VT_STRUCT) { + return (type1->ref == type2->ref); + } else if (bt1 == VT_FUNC) { + return is_compatible_func(type1, type2); + } else { + return 1; + } +} + +/* return true if type1 and type2 are exactly the same (including + qualifiers). +*/ +static int is_compatible_types(CType *type1, CType *type2) +{ + return compare_types(type1,type2,0); +} + +/* return true if type1 and type2 are the same (ignoring qualifiers). +*/ +static int is_compatible_parameter_types(CType *type1, CType *type2) +{ + return compare_types(type1,type2,1); +} + +/* print a type. If 'varstr' is not NULL, then the variable is also + printed in the type */ +/* XXX: union */ +/* XXX: add array and function pointers */ +void type_to_str(char *buf, int buf_size, + CType *type, const char *varstr) +{ + int bt, v, t; + Sym *s, *sa; + char buf1[256]; + const char *tstr; + + t = type->t & VT_TYPE; + bt = t & VT_BTYPE; + buf[0] = '\0'; + if (t & VT_CONSTANT) + pstrcat(buf, buf_size, "const "); + if (t & VT_VOLATILE) + pstrcat(buf, buf_size, "volatile "); + if (t & VT_UNSIGNED) + pstrcat(buf, buf_size, "unsigned "); + switch(bt) { + case VT_VOID: + tstr = "void"; + goto add_tstr; + case VT_BOOL: + tstr = "_Bool"; + goto add_tstr; + case VT_BYTE: + tstr = "char"; + goto add_tstr; + case VT_SHORT: + tstr = "short"; + goto add_tstr; + case VT_INT: + tstr = "int"; + goto add_tstr; + case VT_LONG: + tstr = "long"; + goto add_tstr; + case VT_LLONG: + tstr = "long long"; + goto add_tstr; + case VT_FLOAT: + tstr = "float"; + goto add_tstr; + case VT_DOUBLE: + tstr = "double"; + goto add_tstr; + case VT_LDOUBLE: + tstr = "long double"; + add_tstr: + pstrcat(buf, buf_size, tstr); + break; + case VT_ENUM: + case VT_STRUCT: + if (bt == VT_STRUCT) + tstr = "struct "; + else + tstr = "enum "; + pstrcat(buf, buf_size, tstr); + v = type->ref->v & ~SYM_STRUCT; + if (v >= SYM_FIRST_ANOM) + pstrcat(buf, buf_size, ""); + else + pstrcat(buf, buf_size, get_tok_str(v, NULL)); + break; + case VT_FUNC: + s = type->ref; + type_to_str(buf, buf_size, &s->type, varstr); + pstrcat(buf, buf_size, "("); + sa = s->next; + while (sa != NULL) { + type_to_str(buf1, sizeof(buf1), &sa->type, NULL); + pstrcat(buf, buf_size, buf1); + sa = sa->next; + if (sa) + pstrcat(buf, buf_size, ", "); + } + pstrcat(buf, buf_size, ")"); + goto no_var; + case VT_PTR: + s = type->ref; + pstrcpy(buf1, sizeof(buf1), "*"); + if (varstr) + pstrcat(buf1, sizeof(buf1), varstr); + type_to_str(buf, buf_size, &s->type, buf1); + goto no_var; + } + if (varstr) { + pstrcat(buf, buf_size, " "); + pstrcat(buf, buf_size, varstr); + } + no_var: ; +} + +/* verify type compatibility to store vtop in 'dt' type, and generate + casts if needed. */ +static void gen_assign_cast(CType *dt) +{ + CType *st, *type1, *type2, tmp_type1, tmp_type2; + char buf1[256], buf2[256]; + int dbt, sbt; + + st = &vtop->type; /* source type */ + dbt = dt->t & VT_BTYPE; + sbt = st->t & VT_BTYPE; + if (dt->t & VT_CONSTANT) + warning("assignment of read-only location"); + switch(dbt) { + case VT_PTR: + /* special cases for pointers */ + /* '0' can also be a pointer */ + if (is_null_pointer(vtop)) + goto type_ok; + /* accept implicit pointer to integer cast with warning */ + if (is_integer_btype(sbt)) { + warning("assignment makes pointer from integer without a cast"); + goto type_ok; + } + type1 = pointed_type(dt); + /* a function is implicitely a function pointer */ + if (sbt == VT_FUNC) { + if ((type1->t & VT_BTYPE) != VT_VOID && + !is_compatible_types(pointed_type(dt), st)) + goto error; + else + goto type_ok; + } + if (sbt != VT_PTR) + goto error; + type2 = pointed_type(st); + if ((type1->t & VT_BTYPE) == VT_VOID || + (type2->t & VT_BTYPE) == VT_VOID) { + /* void * can match anything */ + } else { + /* exact type match, except for unsigned */ + tmp_type1 = *type1; + tmp_type2 = *type2; + tmp_type1.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE); + tmp_type2.t &= ~(VT_UNSIGNED | VT_CONSTANT | VT_VOLATILE); + if (!is_compatible_types(&tmp_type1, &tmp_type2)) + warning("assignment from incompatible pointer type"); + } + /* check const and volatile */ + if ((!(type1->t & VT_CONSTANT) && (type2->t & VT_CONSTANT)) || + (!(type1->t & VT_VOLATILE) && (type2->t & VT_VOLATILE))) + warning("assignment discards qualifiers from pointer target type"); + break; + case VT_BYTE: + case VT_SHORT: + case VT_INT: + case VT_LLONG: + if (sbt == VT_PTR || sbt == VT_FUNC) { + warning("assignment makes integer from pointer without a cast"); + } + /* XXX: more tests */ + break; + case VT_STRUCT: + tmp_type1 = *dt; + tmp_type2 = *st; + tmp_type1.t &= ~(VT_CONSTANT | VT_VOLATILE); + tmp_type2.t &= ~(VT_CONSTANT | VT_VOLATILE); + if (!is_compatible_types(&tmp_type1, &tmp_type2)) { + error: + type_to_str(buf1, sizeof(buf1), st, NULL); + type_to_str(buf2, sizeof(buf2), dt, NULL); + error("cannot cast '%s' to '%s'", buf1, buf2); + } + break; + } + type_ok: + gen_cast(dt); +} + +/* store vtop in lvalue pushed on stack */ +void vstore(void) +{ + int sbt, dbt, ft, r, t, size, align, bit_size, bit_pos, rc, delayed_cast; + + ft = vtop[-1].type.t; + sbt = vtop->type.t & VT_BTYPE; + dbt = ft & VT_BTYPE; + if (((sbt == VT_INT || sbt == VT_SHORT) && dbt == VT_BYTE) || + (sbt == VT_INT && dbt == VT_SHORT)) { + /* optimize char/short casts */ + delayed_cast = VT_MUSTCAST; + vtop->type.t = ft & (VT_TYPE & ~(VT_BITFIELD | (-1 << VT_STRUCT_SHIFT))); + /* XXX: factorize */ + if (ft & VT_CONSTANT) + warning("assignment of read-only location"); + } else { + delayed_cast = 0; + if (!(ft & VT_BITFIELD)) + gen_assign_cast(&vtop[-1].type); + } + + if (sbt == VT_STRUCT) { + /* if structure, only generate pointer */ + /* structure assignment : generate memcpy */ + /* XXX: optimize if small size */ + if (!nocode_wanted) { + size = type_size(&vtop->type, &align); + +#ifdef TCC_ARM_EABI + if(!(align & 7)) + vpush_global_sym(&func_old_type, TOK_memcpy8); + else if(!(align & 3)) + vpush_global_sym(&func_old_type, TOK_memcpy4); + else +#endif + vpush_global_sym(&func_old_type, TOK_memcpy); + + /* destination */ + vpushv(vtop - 2); + vtop->type.t = VT_PTR; + gaddrof(); + /* source */ + vpushv(vtop - 2); + vtop->type.t = VT_PTR; + gaddrof(); + /* type size */ + vpushi(size); + gfunc_call(3); + + vswap(); + vpop(); + } else { + vswap(); + vpop(); + } + /* leave source on stack */ + } else if (ft & VT_BITFIELD) { + /* bitfield store handling */ + bit_pos = (ft >> VT_STRUCT_SHIFT) & 0x3f; + bit_size = (ft >> (VT_STRUCT_SHIFT + 6)) & 0x3f; + /* remove bit field info to avoid loops */ + vtop[-1].type.t = ft & ~(VT_BITFIELD | (-1 << VT_STRUCT_SHIFT)); + + /* duplicate source into other register */ + gv_dup(); + vswap(); + vrott(3); + + if((ft & VT_BTYPE) == VT_BOOL) { + gen_cast(&vtop[-1].type); + vtop[-1].type.t = (vtop[-1].type.t & ~VT_BTYPE) | (VT_BYTE | VT_UNSIGNED); + } + + /* duplicate destination */ + vdup(); + vtop[-1] = vtop[-2]; + + /* mask and shift source */ + if((ft & VT_BTYPE) != VT_BOOL) { + if((ft & VT_BTYPE) == VT_LLONG) { + vpushll((1ULL << bit_size) - 1ULL); + } else { + vpushi((1 << bit_size) - 1); + } + gen_op('&'); + } + vpushi(bit_pos); + gen_op(TOK_SHL); + /* load destination, mask and or with source */ + vswap(); + if((ft & VT_BTYPE) == VT_LLONG) { + vpushll(~(((1ULL << bit_size) - 1ULL) << bit_pos)); + } else { + vpushi(~(((1 << bit_size) - 1) << bit_pos)); + } + gen_op('&'); + gen_op('|'); + /* store result */ + vstore(); + + /* pop off shifted source from "duplicate source..." above */ + vpop(); + + } else { +#ifdef CONFIG_TCC_BCHECK + /* bound check case */ + if (vtop[-1].r & VT_MUSTBOUND) { + vswap(); + gbound(); + vswap(); + } +#endif + if (!nocode_wanted) { + rc = RC_INT; + if (is_float(ft)) { + rc = RC_FLOAT; +#ifdef TCC_TARGET_X86_64 + if ((ft & VT_BTYPE) == VT_LDOUBLE) { + rc = RC_ST0; + } +#endif + } + r = gv(rc); /* generate value */ + /* if lvalue was saved on stack, must read it */ + if ((vtop[-1].r & VT_VALMASK) == VT_LLOCAL) { + SValue sv; + t = get_reg(RC_INT); +#ifdef TCC_TARGET_X86_64 + sv.type.t = VT_PTR; +#else + sv.type.t = VT_INT; +#endif + sv.r = VT_LOCAL | VT_LVAL; + sv.c.ul = vtop[-1].c.ul; + load(t, &sv); + vtop[-1].r = t | VT_LVAL; + } + store(r, vtop - 1); +#ifndef TCC_TARGET_X86_64 + /* two word case handling : store second register at word + 4 */ + if ((ft & VT_BTYPE) == VT_LLONG) { + vswap(); + /* convert to int to increment easily */ + vtop->type.t = VT_INT; + gaddrof(); + vpushi(4); + gen_op('+'); + vtop->r |= VT_LVAL; + vswap(); + /* XXX: it works because r2 is spilled last ! */ + store(vtop->r2, vtop - 1); + } +#endif + } + vswap(); + vtop--; /* NOT vpop() because on x86 it would flush the fp stack */ + vtop->r |= delayed_cast; + } +} + +/* post defines POST/PRE add. c is the token ++ or -- */ +void inc(int post, int c) +{ + test_lvalue(); + vdup(); /* save lvalue */ + if (post) { + gv_dup(); /* duplicate value */ + vrotb(3); + vrotb(3); + } + /* add constant */ + vpushi(c - TOK_MID); + gen_op('+'); + vstore(); /* store value */ + if (post) + vpop(); /* if post op, return saved value */ +} + +/* Parse GNUC __attribute__ extension. Currently, the following + extensions are recognized: + - aligned(n) : set data/function alignment. + - packed : force data alignment to 1 + - section(x) : generate data/code in this section. + - unused : currently ignored, but may be used someday. + - regparm(n) : pass function parameters in registers (i386 only) + */ +static void parse_attribute(AttributeDef *ad) +{ + int t, n; + + while (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) { + next(); + skip('('); + skip('('); + while (tok != ')') { + if (tok < TOK_IDENT) + expect("attribute name"); + t = tok; + next(); + switch(t) { + case TOK_SECTION1: + case TOK_SECTION2: + skip('('); + if (tok != TOK_STR) + expect("section name"); + ad->section = find_section(tcc_state, (char *)tokc.cstr->data); + next(); + skip(')'); + break; + case TOK_ALIGNED1: + case TOK_ALIGNED2: + if (tok == '(') { + next(); + n = expr_const(); + if (n <= 0 || (n & (n - 1)) != 0) + error("alignment must be a positive power of two"); + skip(')'); + } else { + n = MAX_ALIGN; + } + ad->aligned = n; + break; + case TOK_PACKED1: + case TOK_PACKED2: + ad->packed = 1; + break; + case TOK_UNUSED1: + case TOK_UNUSED2: + /* currently, no need to handle it because tcc does not + track unused objects */ + break; + case TOK_NORETURN1: + case TOK_NORETURN2: + /* currently, no need to handle it because tcc does not + track unused objects */ + break; + case TOK_CDECL1: + case TOK_CDECL2: + case TOK_CDECL3: + FUNC_CALL(ad->func_attr) = FUNC_CDECL; + break; + case TOK_STDCALL1: + case TOK_STDCALL2: + case TOK_STDCALL3: + FUNC_CALL(ad->func_attr) = FUNC_STDCALL; + break; +#ifdef TCC_TARGET_I386 + case TOK_REGPARM1: + case TOK_REGPARM2: + skip('('); + n = expr_const(); + if (n > 3) + n = 3; + else if (n < 0) + n = 0; + if (n > 0) + FUNC_CALL(ad->func_attr) = FUNC_FASTCALL1 + n - 1; + skip(')'); + break; + case TOK_FASTCALL1: + case TOK_FASTCALL2: + case TOK_FASTCALL3: + FUNC_CALL(ad->func_attr) = FUNC_FASTCALLW; + break; +#endif + case TOK_DLLEXPORT: + FUNC_EXPORT(ad->func_attr) = 1; + break; + default: + if (tcc_state->warn_unsupported) + warning("'%s' attribute ignored", get_tok_str(t, NULL)); + /* skip parameters */ + if (tok == '(') { + int parenthesis = 0; + do { + if (tok == '(') + parenthesis++; + else if (tok == ')') + parenthesis--; + next(); + } while (parenthesis && tok != -1); + } + break; + } + if (tok != ',') + break; + next(); + } + skip(')'); + skip(')'); + } +} + +/* enum/struct/union declaration. u is either VT_ENUM or VT_STRUCT */ +static void struct_decl(CType *type, int u) +{ + int a, v, size, align, maxalign, c, offset; + int bit_size, bit_pos, bsize, bt, lbit_pos, prevbt; + Sym *s, *ss, *ass, **ps; + AttributeDef ad; + CType type1, btype; + + a = tok; /* save decl type */ + next(); + if (tok != '{') { + v = tok; + next(); + /* struct already defined ? return it */ + if (v < TOK_IDENT) + expect("struct/union/enum name"); + s = struct_find(v); + if (s) { + if (s->type.t != a) + error("invalid type"); + goto do_decl; + } + } else { + v = anon_sym++; + } + type1.t = a; + /* we put an undefined size for struct/union */ + s = sym_push(v | SYM_STRUCT, &type1, 0, -1); + s->r = 0; /* default alignment is zero as gcc */ + /* put struct/union/enum name in type */ + do_decl: + type->t = u; + type->ref = s; + + if (tok == '{') { + next(); + if (s->c != -1) + error("struct/union/enum already defined"); + /* cannot be empty */ + c = 0; + /* non empty enums are not allowed */ + if (a == TOK_ENUM) { + for(;;) { + v = tok; + if (v < TOK_UIDENT) + expect("identifier"); + next(); + if (tok == '=') { + next(); + c = expr_const(); + } + /* enum symbols have static storage */ + ss = sym_push(v, &int_type, VT_CONST, c); + ss->type.t |= VT_STATIC; + if (tok != ',') + break; + next(); + c++; + /* NOTE: we accept a trailing comma */ + if (tok == '}') + break; + } + skip('}'); + } else { + maxalign = 1; + ps = &s->next; + prevbt = VT_INT; + bit_pos = 0; + offset = 0; + while (tok != '}') { + parse_btype(&btype, &ad); + while (1) { + bit_size = -1; + v = 0; + type1 = btype; + if (tok != ':') { + type_decl(&type1, &ad, &v, TYPE_DIRECT | TYPE_ABSTRACT); + if (v == 0 && (type1.t & VT_BTYPE) != VT_STRUCT) + expect("identifier"); + if ((type1.t & VT_BTYPE) == VT_FUNC || + (type1.t & (VT_TYPEDEF | VT_STATIC | VT_EXTERN | VT_INLINE))) + error("invalid type for '%s'", + get_tok_str(v, NULL)); + } + if (tok == ':') { + next(); + bit_size = expr_const(); + /* XXX: handle v = 0 case for messages */ + if (bit_size < 0) + error("negative width in bit-field '%s'", + get_tok_str(v, NULL)); + if (v && bit_size == 0) + error("zero width for bit-field '%s'", + get_tok_str(v, NULL)); + } + size = type_size(&type1, &align); + if (ad.aligned) { + if (align < ad.aligned) + align = ad.aligned; + } else if (ad.packed) { + align = 1; + } else if (*tcc_state->pack_stack_ptr) { + if (align > *tcc_state->pack_stack_ptr) + align = *tcc_state->pack_stack_ptr; + } + lbit_pos = 0; + if (bit_size >= 0) { + bt = type1.t & VT_BTYPE; + if (bt != VT_INT && + bt != VT_BYTE && + bt != VT_SHORT && + bt != VT_BOOL && + bt != VT_ENUM && + bt != VT_LLONG) + error("bitfields must have scalar type"); + bsize = size * 8; + if (bit_size > bsize) { + error("width of '%s' exceeds its type", + get_tok_str(v, NULL)); + } else if (bit_size == bsize) { + /* no need for bit fields */ + bit_pos = 0; + } else if (bit_size == 0) { + /* XXX: what to do if only padding in a + structure ? */ + /* zero size: means to pad */ + bit_pos = 0; + } else { + /* we do not have enough room ? + did the type change? + is it a union? */ + if ((bit_pos + bit_size) > bsize || + bt != prevbt || a == TOK_UNION) + bit_pos = 0; + lbit_pos = bit_pos; + /* XXX: handle LSB first */ + type1.t |= VT_BITFIELD | + (bit_pos << VT_STRUCT_SHIFT) | + (bit_size << (VT_STRUCT_SHIFT + 6)); + bit_pos += bit_size; + } + prevbt = bt; + } else { + bit_pos = 0; + } + if (v != 0 || (type1.t & VT_BTYPE) == VT_STRUCT) { + /* add new memory data only if starting + bit field */ + if (lbit_pos == 0) { + if (a == TOK_STRUCT) { + c = (c + align - 1) & -align; + offset = c; + if (size > 0) + c += size; + } else { + offset = 0; + if (size > c) + c = size; + } + if (align > maxalign) + maxalign = align; + } +#if 0 + printf("add field %s offset=%d", + get_tok_str(v, NULL), offset); + if (type1.t & VT_BITFIELD) { + printf(" pos=%d size=%d", + (type1.t >> VT_STRUCT_SHIFT) & 0x3f, + (type1.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f); + } + printf("\n"); +#endif + } + if (v == 0 && (type1.t & VT_BTYPE) == VT_STRUCT) { + ass = type1.ref; + while ((ass = ass->next) != NULL) { + ss = sym_push(ass->v, &ass->type, 0, offset + ass->c); + *ps = ss; + ps = &ss->next; + } + } else if (v) { + ss = sym_push(v | SYM_FIELD, &type1, 0, offset); + *ps = ss; + ps = &ss->next; + } + if (tok == ';' || tok == TOK_EOF) + break; + skip(','); + } + skip(';'); + } + skip('}'); + /* store size and alignment */ + s->c = (c + maxalign - 1) & -maxalign; + s->r = maxalign; + } + } +} + +/* return 0 if no type declaration. otherwise, return the basic type + and skip it. + */ +static int parse_btype(CType *type, AttributeDef *ad) +{ + int t, u, type_found, typespec_found, typedef_found; + Sym *s; + CType type1; + + memset(ad, 0, sizeof(AttributeDef)); + type_found = 0; + typespec_found = 0; + typedef_found = 0; + t = 0; + while(1) { + switch(tok) { + case TOK_EXTENSION: + /* currently, we really ignore extension */ + next(); + continue; + + /* basic types */ + case TOK_CHAR: + u = VT_BYTE; + basic_type: + next(); + basic_type1: + if ((t & VT_BTYPE) != 0) + error("too many basic types"); + t |= u; + typespec_found = 1; + break; + case TOK_VOID: + u = VT_VOID; + goto basic_type; + case TOK_SHORT: + u = VT_SHORT; + goto basic_type; + case TOK_INT: + next(); + typespec_found = 1; + break; + case TOK_LONG: + next(); + if ((t & VT_BTYPE) == VT_DOUBLE) { + t = (t & ~VT_BTYPE) | VT_LDOUBLE; + } else if ((t & VT_BTYPE) == VT_LONG) { + t = (t & ~VT_BTYPE) | VT_LLONG; + } else { + u = VT_LONG; + goto basic_type1; + } + break; + case TOK_BOOL: + u = VT_BOOL; + goto basic_type; + case TOK_FLOAT: + u = VT_FLOAT; + goto basic_type; + case TOK_DOUBLE: + next(); + if ((t & VT_BTYPE) == VT_LONG) { + t = (t & ~VT_BTYPE) | VT_LDOUBLE; + } else { + u = VT_DOUBLE; + goto basic_type1; + } + break; + case TOK_ENUM: + struct_decl(&type1, VT_ENUM); + basic_type2: + u = type1.t; + type->ref = type1.ref; + goto basic_type1; + case TOK_STRUCT: + case TOK_UNION: + struct_decl(&type1, VT_STRUCT); + goto basic_type2; + + /* type modifiers */ + case TOK_CONST1: + case TOK_CONST2: + case TOK_CONST3: + t |= VT_CONSTANT; + next(); + break; + case TOK_VOLATILE1: + case TOK_VOLATILE2: + case TOK_VOLATILE3: + t |= VT_VOLATILE; + next(); + break; + case TOK_SIGNED1: + case TOK_SIGNED2: + case TOK_SIGNED3: + typespec_found = 1; + t |= VT_SIGNED; + next(); + break; + case TOK_REGISTER: + case TOK_AUTO: + case TOK_RESTRICT1: + case TOK_RESTRICT2: + case TOK_RESTRICT3: + next(); + break; + case TOK_UNSIGNED: + t |= VT_UNSIGNED; + next(); + typespec_found = 1; + break; + + /* storage */ + case TOK_EXTERN: + t |= VT_EXTERN; + next(); + break; + case TOK_STATIC: + t |= VT_STATIC; + next(); + break; + case TOK_TYPEDEF: + t |= VT_TYPEDEF; + next(); + break; + case TOK_INLINE1: + case TOK_INLINE2: + case TOK_INLINE3: + t |= VT_INLINE; + next(); + break; + + /* GNUC attribute */ + case TOK_ATTRIBUTE1: + case TOK_ATTRIBUTE2: + parse_attribute(ad); + break; + /* GNUC typeof */ + case TOK_TYPEOF1: + case TOK_TYPEOF2: + case TOK_TYPEOF3: + next(); + parse_expr_type(&type1); + goto basic_type2; + default: + if (typespec_found || typedef_found) + goto the_end; + s = sym_find(tok); + if (!s || !(s->type.t & VT_TYPEDEF)) + goto the_end; + typedef_found = 1; + t |= (s->type.t & ~VT_TYPEDEF); + type->ref = s->type.ref; + next(); + typespec_found = 1; + break; + } + type_found = 1; + } +the_end: + if ((t & (VT_SIGNED|VT_UNSIGNED)) == (VT_SIGNED|VT_UNSIGNED)) + error("signed and unsigned modifier"); + if (tcc_state->char_is_unsigned) { + if ((t & (VT_SIGNED|VT_UNSIGNED|VT_BTYPE)) == VT_BYTE) + t |= VT_UNSIGNED; + } + t &= ~VT_SIGNED; + + /* long is never used as type */ + if ((t & VT_BTYPE) == VT_LONG) +#ifndef TCC_TARGET_X86_64 + t = (t & ~VT_BTYPE) | VT_INT; +#else + t = (t & ~VT_BTYPE) | VT_LLONG; +#endif + type->t = t; + return type_found; +} + +/* convert a function parameter type (array to pointer and function to + function pointer) */ +static inline void convert_parameter_type(CType *pt) +{ + /* remove const and volatile qualifiers (XXX: const could be used + to indicate a const function parameter */ + pt->t &= ~(VT_CONSTANT | VT_VOLATILE); + /* array must be transformed to pointer according to ANSI C */ + pt->t &= ~VT_ARRAY; + if ((pt->t & VT_BTYPE) == VT_FUNC) { + mk_pointer(pt); + } +} + +static void post_type(CType *type, AttributeDef *ad) +{ + int n, l, t1, arg_size, align; + Sym **plast, *s, *first; + AttributeDef ad1; + CType pt; + + if (tok == '(') { + /* function declaration */ + next(); + l = 0; + first = NULL; + plast = &first; + arg_size = 0; + if (tok != ')') { + for(;;) { + /* read param name and compute offset */ + if (l != FUNC_OLD) { + if (!parse_btype(&pt, &ad1)) { + if (l) { + error("invalid type"); + } else { + l = FUNC_OLD; + goto old_proto; + } + } + l = FUNC_NEW; + if ((pt.t & VT_BTYPE) == VT_VOID && tok == ')') + break; + type_decl(&pt, &ad1, &n, TYPE_DIRECT | TYPE_ABSTRACT); + if ((pt.t & VT_BTYPE) == VT_VOID) + error("parameter declared as void"); + arg_size += (type_size(&pt, &align) + 3) & ~3; + } else { + old_proto: + n = tok; + if (n < TOK_UIDENT) + expect("identifier"); + pt.t = VT_INT; + next(); + } + convert_parameter_type(&pt); + s = sym_push(n | SYM_FIELD, &pt, 0, 0); + *plast = s; + plast = &s->next; + if (tok == ')') + break; + skip(','); + if (l == FUNC_NEW && tok == TOK_DOTS) { + l = FUNC_ELLIPSIS; + next(); + break; + } + } + } + /* if no parameters, then old type prototype */ + if (l == 0) + l = FUNC_OLD; + skip(')'); + t1 = type->t & VT_STORAGE; + /* NOTE: const is ignored in returned type as it has a special + meaning in gcc / C++ */ + type->t &= ~(VT_STORAGE | VT_CONSTANT); + post_type(type, ad); + /* we push a anonymous symbol which will contain the function prototype */ + FUNC_ARGS(ad->func_attr) = arg_size; + s = sym_push(SYM_FIELD, type, ad->func_attr, l); + s->next = first; + type->t = t1 | VT_FUNC; + type->ref = s; + } else if (tok == '[') { + /* array definition */ + next(); + if (tok == TOK_RESTRICT1) + next(); + n = -1; + if (tok != ']') { + n = expr_const(); + if (n < 0) + error("invalid array size"); + } + skip(']'); + /* parse next post type */ + t1 = type->t & VT_STORAGE; + type->t &= ~VT_STORAGE; + post_type(type, ad); + + /* we push a anonymous symbol which will contain the array + element type */ + s = sym_push(SYM_FIELD, type, 0, n); + type->t = t1 | VT_ARRAY | VT_PTR; + type->ref = s; + } +} + +/* Parse a type declaration (except basic type), and return the type + in 'type'. 'td' is a bitmask indicating which kind of type decl is + expected. 'type' should contain the basic type. 'ad' is the + attribute definition of the basic type. It can be modified by + type_decl(). + */ +static void type_decl(CType *type, AttributeDef *ad, int *v, int td) +{ + Sym *s; + CType type1, *type2; + int qualifiers; + + while (tok == '*') { + qualifiers = 0; + redo: + next(); + switch(tok) { + case TOK_CONST1: + case TOK_CONST2: + case TOK_CONST3: + qualifiers |= VT_CONSTANT; + goto redo; + case TOK_VOLATILE1: + case TOK_VOLATILE2: + case TOK_VOLATILE3: + qualifiers |= VT_VOLATILE; + goto redo; + case TOK_RESTRICT1: + case TOK_RESTRICT2: + case TOK_RESTRICT3: + goto redo; + } + mk_pointer(type); + type->t |= qualifiers; + } + + /* XXX: clarify attribute handling */ + if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) + parse_attribute(ad); + + /* recursive type */ + /* XXX: incorrect if abstract type for functions (e.g. 'int ()') */ + type1.t = 0; /* XXX: same as int */ + if (tok == '(') { + next(); + /* XXX: this is not correct to modify 'ad' at this point, but + the syntax is not clear */ + if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) + parse_attribute(ad); + type_decl(&type1, ad, v, td); + skip(')'); + } else { + /* type identifier */ + if (tok >= TOK_IDENT && (td & TYPE_DIRECT)) { + *v = tok; + next(); + } else { + if (!(td & TYPE_ABSTRACT)) + expect("identifier"); + *v = 0; + } + } + post_type(type, ad); + if (tok == TOK_ATTRIBUTE1 || tok == TOK_ATTRIBUTE2) + parse_attribute(ad); + if (!type1.t) + return; + /* append type at the end of type1 */ + type2 = &type1; + for(;;) { + s = type2->ref; + type2 = &s->type; + if (!type2->t) { + *type2 = *type; + break; + } + } + *type = type1; +} + +/* compute the lvalue VT_LVAL_xxx needed to match type t. */ +static int lvalue_type(int t) +{ + int bt, r; + r = VT_LVAL; + bt = t & VT_BTYPE; + if (bt == VT_BYTE || bt == VT_BOOL) + r |= VT_LVAL_BYTE; + else if (bt == VT_SHORT) + r |= VT_LVAL_SHORT; + else + return r; + if (t & VT_UNSIGNED) + r |= VT_LVAL_UNSIGNED; + return r; +} + +/* indirection with full error checking and bound check */ +static void indir(void) +{ + if ((vtop->type.t & VT_BTYPE) != VT_PTR) { + if ((vtop->type.t & VT_BTYPE) == VT_FUNC) + return; + expect("pointer"); + } + if ((vtop->r & VT_LVAL) && !nocode_wanted) + gv(RC_INT); + vtop->type = *pointed_type(&vtop->type); + /* Arrays and functions are never lvalues */ + if (!(vtop->type.t & VT_ARRAY) + && (vtop->type.t & VT_BTYPE) != VT_FUNC) { + vtop->r |= lvalue_type(vtop->type.t); + /* if bound checking, the referenced pointer must be checked */ + if (tcc_state->do_bounds_check) + vtop->r |= VT_MUSTBOUND; + } +} + +/* pass a parameter to a function and do type checking and casting */ +static void gfunc_param_typed(Sym *func, Sym *arg) +{ + int func_type; + CType type; + + func_type = func->c; + if (func_type == FUNC_OLD || + (func_type == FUNC_ELLIPSIS && arg == NULL)) { + /* default casting : only need to convert float to double */ + if ((vtop->type.t & VT_BTYPE) == VT_FLOAT) { + type.t = VT_DOUBLE; + gen_cast(&type); + } + } else if (arg == NULL) { + error("too many arguments to function"); + } else { + type = arg->type; + type.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */ + gen_assign_cast(&type); + } +} + +/* parse an expression of the form '(type)' or '(expr)' and return its + type */ +static void parse_expr_type(CType *type) +{ + int n; + AttributeDef ad; + + skip('('); + if (parse_btype(type, &ad)) { + type_decl(type, &ad, &n, TYPE_ABSTRACT); + } else { + expr_type(type); + } + skip(')'); +} + +static void parse_type(CType *type) +{ + AttributeDef ad; + int n; + + if (!parse_btype(type, &ad)) { + expect("type"); + } + type_decl(type, &ad, &n, TYPE_ABSTRACT); +} + +static void vpush_tokc(int t) +{ + CType type; + type.t = t; + vsetc(&type, VT_CONST, &tokc); +} + +static void unary(void) +{ + int n, t, align, size, r; + CType type; + Sym *s; + AttributeDef ad; + + /* XXX: GCC 2.95.3 does not generate a table although it should be + better here */ + tok_next: + switch(tok) { + case TOK_EXTENSION: + next(); + goto tok_next; + case TOK_CINT: + case TOK_CCHAR: + case TOK_LCHAR: + vpushi(tokc.i); + next(); + break; + case TOK_CUINT: + vpush_tokc(VT_INT | VT_UNSIGNED); + next(); + break; + case TOK_CLLONG: + vpush_tokc(VT_LLONG); + next(); + break; + case TOK_CULLONG: + vpush_tokc(VT_LLONG | VT_UNSIGNED); + next(); + break; + case TOK_CFLOAT: + vpush_tokc(VT_FLOAT); + next(); + break; + case TOK_CDOUBLE: + vpush_tokc(VT_DOUBLE); + next(); + break; + case TOK_CLDOUBLE: + vpush_tokc(VT_LDOUBLE); + next(); + break; + case TOK___FUNCTION__: + if (!gnu_ext) + goto tok_identifier; + /* fall thru */ + case TOK___FUNC__: + { + void *ptr; + int len; + /* special function name identifier */ + len = strlen(funcname) + 1; + /* generate char[len] type */ + type.t = VT_BYTE; + mk_pointer(&type); + type.t |= VT_ARRAY; + type.ref->c = len; + vpush_ref(&type, data_section, data_section->data_offset, len); + ptr = section_ptr_add(data_section, len); + memcpy(ptr, funcname, len); + next(); + } + break; + case TOK_LSTR: +#ifdef TCC_TARGET_PE + t = VT_SHORT | VT_UNSIGNED; +#else + t = VT_INT; +#endif + goto str_init; + case TOK_STR: + /* string parsing */ + t = VT_BYTE; + str_init: + if (tcc_state->warn_write_strings) + t |= VT_CONSTANT; + type.t = t; + mk_pointer(&type); + type.t |= VT_ARRAY; + memset(&ad, 0, sizeof(AttributeDef)); + decl_initializer_alloc(&type, &ad, VT_CONST, 2, 0, 0); + break; + case '(': + next(); + /* cast ? */ + if (parse_btype(&type, &ad)) { + type_decl(&type, &ad, &n, TYPE_ABSTRACT); + skip(')'); + /* check ISOC99 compound literal */ + if (tok == '{') { + /* data is allocated locally by default */ + if (global_expr) + r = VT_CONST; + else + r = VT_LOCAL; + /* all except arrays are lvalues */ + if (!(type.t & VT_ARRAY)) + r |= lvalue_type(type.t); + memset(&ad, 0, sizeof(AttributeDef)); + decl_initializer_alloc(&type, &ad, r, 1, 0, 0); + } else { + unary(); + gen_cast(&type); + } + } else if (tok == '{') { + /* save all registers */ + save_regs(0); + /* statement expression : we do not accept break/continue + inside as GCC does */ + block(NULL, NULL, NULL, NULL, 0, 1); + skip(')'); + } else { + gexpr(); + skip(')'); + } + break; + case '*': + next(); + unary(); + indir(); + break; + case '&': + next(); + unary(); + /* functions names must be treated as function pointers, + except for unary '&' and sizeof. Since we consider that + functions are not lvalues, we only have to handle it + there and in function calls. */ + /* arrays can also be used although they are not lvalues */ + if ((vtop->type.t & VT_BTYPE) != VT_FUNC && + !(vtop->type.t & VT_ARRAY) && !(vtop->type.t & VT_LLOCAL)) + test_lvalue(); + mk_pointer(&vtop->type); + gaddrof(); + break; + case '!': + next(); + unary(); + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + CType boolean; + boolean.t = VT_BOOL; + gen_cast(&boolean); + vtop->c.i = !vtop->c.i; + } else if ((vtop->r & VT_VALMASK) == VT_CMP) + vtop->c.i = vtop->c.i ^ 1; + else { + save_regs(1); + vseti(VT_JMP, gtst(1, 0)); + } + break; + case '~': + next(); + unary(); + vpushi(-1); + gen_op('^'); + break; + case '+': + next(); + /* in order to force cast, we add zero */ + unary(); + if ((vtop->type.t & VT_BTYPE) == VT_PTR) + error("pointer not accepted for unary plus"); + vpushi(0); + gen_op('+'); + break; + case TOK_SIZEOF: + case TOK_ALIGNOF1: + case TOK_ALIGNOF2: + t = tok; + next(); + if (tok == '(') { + parse_expr_type(&type); + } else { + unary_type(&type); + } + size = type_size(&type, &align); + if (t == TOK_SIZEOF) { + if (size < 0) + error("sizeof applied to an incomplete type"); + vpushi(size); + } else { + vpushi(align); + } + vtop->type.t |= VT_UNSIGNED; + break; + + case TOK_builtin_types_compatible_p: + { + CType type1, type2; + next(); + skip('('); + parse_type(&type1); + skip(','); + parse_type(&type2); + skip(')'); + type1.t &= ~(VT_CONSTANT | VT_VOLATILE); + type2.t &= ~(VT_CONSTANT | VT_VOLATILE); + vpushi(is_compatible_types(&type1, &type2)); + } + break; + case TOK_builtin_constant_p: + { + int saved_nocode_wanted, res; + next(); + skip('('); + saved_nocode_wanted = nocode_wanted; + nocode_wanted = 1; + gexpr(); + res = (vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; + vpop(); + nocode_wanted = saved_nocode_wanted; + skip(')'); + vpushi(res); + } + break; + case TOK_builtin_frame_address: + { + CType type; + next(); + skip('('); + if (tok != TOK_CINT) { + error("__builtin_frame_address only takes integers"); + } + if (tokc.i != 0) { + error("TCC only supports __builtin_frame_address(0)"); + } + next(); + skip(')'); + type.t = VT_VOID; + mk_pointer(&type); + vset(&type, VT_LOCAL, 0); + } + break; +#ifdef TCC_TARGET_X86_64 + case TOK_builtin_malloc: + tok = TOK_malloc; + goto tok_identifier; + case TOK_builtin_free: + tok = TOK_free; + goto tok_identifier; +#endif + case TOK_INC: + case TOK_DEC: + t = tok; + next(); + unary(); + inc(0, t); + break; + case '-': + next(); + vpushi(0); + unary(); + gen_op('-'); + break; + case TOK_LAND: + if (!gnu_ext) + goto tok_identifier; + next(); + /* allow to take the address of a label */ + if (tok < TOK_UIDENT) + expect("label identifier"); + s = label_find(tok); + if (!s) { + s = label_push(&global_label_stack, tok, LABEL_FORWARD); + } else { + if (s->r == LABEL_DECLARED) + s->r = LABEL_FORWARD; + } + if (!s->type.t) { + s->type.t = VT_VOID; + mk_pointer(&s->type); + s->type.t |= VT_STATIC; + } + vset(&s->type, VT_CONST | VT_SYM, 0); + vtop->sym = s; + next(); + break; + default: + tok_identifier: + t = tok; + next(); + if (t < TOK_UIDENT) + expect("identifier"); + s = sym_find(t); + if (!s) { + if (tok != '(') + error("'%s' undeclared", get_tok_str(t, NULL)); + /* for simple function calls, we tolerate undeclared + external reference to int() function */ + if (tcc_state->warn_implicit_function_declaration) + warning("implicit declaration of function '%s'", + get_tok_str(t, NULL)); + s = external_global_sym(t, &func_old_type, 0); + } + if ((s->type.t & (VT_STATIC | VT_INLINE | VT_BTYPE)) == + (VT_STATIC | VT_INLINE | VT_FUNC)) { + /* if referencing an inline function, then we generate a + symbol to it if not already done. It will have the + effect to generate code for it at the end of the + compilation unit. Inline function as always + generated in the text section. */ + if (!s->c) + put_extern_sym(s, text_section, 0, 0); + r = VT_SYM | VT_CONST; + } else { + r = s->r; + } + vset(&s->type, r, s->c); + /* if forward reference, we must point to s */ + if (vtop->r & VT_SYM) { + vtop->sym = s; + vtop->c.ul = 0; + } + break; + } + + /* post operations */ + while (1) { + if (tok == TOK_INC || tok == TOK_DEC) { + inc(1, tok); + next(); + } else if (tok == '.' || tok == TOK_ARROW) { + /* field */ + if (tok == TOK_ARROW) + indir(); + test_lvalue(); + gaddrof(); + next(); + /* expect pointer on structure */ + if ((vtop->type.t & VT_BTYPE) != VT_STRUCT) + expect("struct or union"); + s = vtop->type.ref; + /* find field */ + tok |= SYM_FIELD; + while ((s = s->next) != NULL) { + if (s->v == tok) + break; + } + if (!s) + error("field not found: %s", get_tok_str(tok & ~SYM_FIELD, NULL)); + /* add field offset to pointer */ + vtop->type = char_pointer_type; /* change type to 'char *' */ + vpushi(s->c); + gen_op('+'); + /* change type to field type, and set to lvalue */ + vtop->type = s->type; + /* an array is never an lvalue */ + if (!(vtop->type.t & VT_ARRAY)) { + vtop->r |= lvalue_type(vtop->type.t); + /* if bound checking, the referenced pointer must be checked */ + if (tcc_state->do_bounds_check) + vtop->r |= VT_MUSTBOUND; + } + next(); + } else if (tok == '[') { + next(); + gexpr(); + gen_op('+'); + indir(); + skip(']'); + } else if (tok == '(') { + SValue ret; + Sym *sa; + int nb_args; + + /* function call */ + if ((vtop->type.t & VT_BTYPE) != VT_FUNC) { + /* pointer test (no array accepted) */ + if ((vtop->type.t & (VT_BTYPE | VT_ARRAY)) == VT_PTR) { + vtop->type = *pointed_type(&vtop->type); + if ((vtop->type.t & VT_BTYPE) != VT_FUNC) + goto error_func; + } else { + error_func: + expect("function pointer"); + } + } else { + vtop->r &= ~VT_LVAL; /* no lvalue */ + } + /* get return type */ + s = vtop->type.ref; + next(); + sa = s->next; /* first parameter */ + nb_args = 0; + ret.r2 = VT_CONST; + /* compute first implicit argument if a structure is returned */ + if ((s->type.t & VT_BTYPE) == VT_STRUCT) { + /* get some space for the returned structure */ + size = type_size(&s->type, &align); + loc = (loc - size) & -align; + ret.type = s->type; + ret.r = VT_LOCAL | VT_LVAL; + /* pass it as 'int' to avoid structure arg passing + problems */ + vseti(VT_LOCAL, loc); + ret.c = vtop->c; + nb_args++; + } else { + ret.type = s->type; + /* return in register */ + if (is_float(ret.type.t)) { + ret.r = reg_fret(ret.type.t); + } else { + if ((ret.type.t & VT_BTYPE) == VT_LLONG) + ret.r2 = REG_LRET; + ret.r = REG_IRET; + } + ret.c.i = 0; + } + if (tok != ')') { + for(;;) { + expr_eq(); + gfunc_param_typed(s, sa); + nb_args++; + if (sa) + sa = sa->next; + if (tok == ')') + break; + skip(','); + } + } + if (sa) + error("too few arguments to function"); + skip(')'); + if (!nocode_wanted) { + gfunc_call(nb_args); + } else { + vtop -= (nb_args + 1); + } + /* return value */ + vsetc(&ret.type, ret.r, &ret.c); + vtop->r2 = ret.r2; + } else { + break; + } + } +} + +static void uneq(void) +{ + int t; + + unary(); + if (tok == '=' || + (tok >= TOK_A_MOD && tok <= TOK_A_DIV) || + tok == TOK_A_XOR || tok == TOK_A_OR || + tok == TOK_A_SHL || tok == TOK_A_SAR) { + test_lvalue(); + t = tok; + next(); + if (t == '=') { + expr_eq(); + } else { + vdup(); + expr_eq(); + gen_op(t & 0x7f); + } + vstore(); + } +} + +static void expr_prod(void) +{ + int t; + + uneq(); + while (tok == '*' || tok == '/' || tok == '%') { + t = tok; + next(); + uneq(); + gen_op(t); + } +} + +static void expr_sum(void) +{ + int t; + + expr_prod(); + while (tok == '+' || tok == '-') { + t = tok; + next(); + expr_prod(); + gen_op(t); + } +} + +static void expr_shift(void) +{ + int t; + + expr_sum(); + while (tok == TOK_SHL || tok == TOK_SAR) { + t = tok; + next(); + expr_sum(); + gen_op(t); + } +} + +static void expr_cmp(void) +{ + int t; + + expr_shift(); + while ((tok >= TOK_ULE && tok <= TOK_GT) || + tok == TOK_ULT || tok == TOK_UGE) { + t = tok; + next(); + expr_shift(); + gen_op(t); + } +} + +static void expr_cmpeq(void) +{ + int t; + + expr_cmp(); + while (tok == TOK_EQ || tok == TOK_NE) { + t = tok; + next(); + expr_cmp(); + gen_op(t); + } +} + +static void expr_and(void) +{ + expr_cmpeq(); + while (tok == '&') { + next(); + expr_cmpeq(); + gen_op('&'); + } +} + +static void expr_xor(void) +{ + expr_and(); + while (tok == '^') { + next(); + expr_and(); + gen_op('^'); + } +} + +static void expr_or(void) +{ + expr_xor(); + while (tok == '|') { + next(); + expr_xor(); + gen_op('|'); + } +} + +/* XXX: fix this mess */ +static void expr_land_const(void) +{ + expr_or(); + while (tok == TOK_LAND) { + next(); + expr_or(); + gen_op(TOK_LAND); + } +} + +/* XXX: fix this mess */ +static void expr_lor_const(void) +{ + expr_land_const(); + while (tok == TOK_LOR) { + next(); + expr_land_const(); + gen_op(TOK_LOR); + } +} + +/* only used if non constant */ +static void expr_land(void) +{ + int t; + + expr_or(); + if (tok == TOK_LAND) { + t = 0; + save_regs(1); + for(;;) { + t = gtst(1, t); + if (tok != TOK_LAND) { + vseti(VT_JMPI, t); + break; + } + next(); + expr_or(); + } + } +} + +static void expr_lor(void) +{ + int t; + + expr_land(); + if (tok == TOK_LOR) { + t = 0; + save_regs(1); + for(;;) { + t = gtst(0, t); + if (tok != TOK_LOR) { + vseti(VT_JMP, t); + break; + } + next(); + expr_land(); + } + } +} + +/* XXX: better constant handling */ +static void expr_eq(void) +{ + int tt, u, r1, r2, rc, t1, t2, bt1, bt2; + SValue sv; + CType type, type1, type2; + + if (const_wanted) { + expr_lor_const(); + if (tok == '?') { + CType boolean; + int c; + boolean.t = VT_BOOL; + vdup(); + gen_cast(&boolean); + c = vtop->c.i; + vpop(); + next(); + if (tok != ':' || !gnu_ext) { + vpop(); + gexpr(); + } + if (!c) + vpop(); + skip(':'); + expr_eq(); + if (c) + vpop(); + } + } else { + expr_lor(); + if (tok == '?') { + next(); + if (vtop != vstack) { + /* needed to avoid having different registers saved in + each branch */ + if (is_float(vtop->type.t)) { + rc = RC_FLOAT; +#ifdef TCC_TARGET_X86_64 + if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { + rc = RC_ST0; + } +#endif + } + else + rc = RC_INT; + gv(rc); + save_regs(1); + } + if (tok == ':' && gnu_ext) { + gv_dup(); + tt = gtst(1, 0); + } else { + tt = gtst(1, 0); + gexpr(); + } + type1 = vtop->type; + sv = *vtop; /* save value to handle it later */ + vtop--; /* no vpop so that FP stack is not flushed */ + skip(':'); + u = gjmp(0); + gsym(tt); + expr_eq(); + type2 = vtop->type; + + t1 = type1.t; + bt1 = t1 & VT_BTYPE; + t2 = type2.t; + bt2 = t2 & VT_BTYPE; + /* cast operands to correct type according to ISOC rules */ + if (is_float(bt1) || is_float(bt2)) { + if (bt1 == VT_LDOUBLE || bt2 == VT_LDOUBLE) { + type.t = VT_LDOUBLE; + } else if (bt1 == VT_DOUBLE || bt2 == VT_DOUBLE) { + type.t = VT_DOUBLE; + } else { + type.t = VT_FLOAT; + } + } else if (bt1 == VT_LLONG || bt2 == VT_LLONG) { + /* cast to biggest op */ + type.t = VT_LLONG; + /* convert to unsigned if it does not fit in a long long */ + if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED) || + (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_LLONG | VT_UNSIGNED)) + type.t |= VT_UNSIGNED; + } else if (bt1 == VT_PTR || bt2 == VT_PTR) { + /* XXX: test pointer compatibility */ + type = type1; + } else if (bt1 == VT_FUNC || bt2 == VT_FUNC) { + /* XXX: test function pointer compatibility */ + type = type1; + } else if (bt1 == VT_STRUCT || bt2 == VT_STRUCT) { + /* XXX: test structure compatibility */ + type = type1; + } else if (bt1 == VT_VOID || bt2 == VT_VOID) { + /* NOTE: as an extension, we accept void on only one side */ + type.t = VT_VOID; + } else { + /* integer operations */ + type.t = VT_INT; + /* convert to unsigned if it does not fit in an integer */ + if ((t1 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED) || + (t2 & (VT_BTYPE | VT_UNSIGNED)) == (VT_INT | VT_UNSIGNED)) + type.t |= VT_UNSIGNED; + } + + /* now we convert second operand */ + gen_cast(&type); + if (VT_STRUCT == (vtop->type.t & VT_BTYPE)) + gaddrof(); + rc = RC_INT; + if (is_float(type.t)) { + rc = RC_FLOAT; +#ifdef TCC_TARGET_X86_64 + if ((type.t & VT_BTYPE) == VT_LDOUBLE) { + rc = RC_ST0; + } +#endif + } else if ((type.t & VT_BTYPE) == VT_LLONG) { + /* for long longs, we use fixed registers to avoid having + to handle a complicated move */ + rc = RC_IRET; + } + + r2 = gv(rc); + /* this is horrible, but we must also convert first + operand */ + tt = gjmp(0); + gsym(u); + /* put again first value and cast it */ + *vtop = sv; + gen_cast(&type); + if (VT_STRUCT == (vtop->type.t & VT_BTYPE)) + gaddrof(); + r1 = gv(rc); + move_reg(r2, r1); + vtop->r = r2; + gsym(tt); + } + } +} + +static void gexpr(void) +{ + while (1) { + expr_eq(); + if (tok != ',') + break; + vpop(); + next(); + } +} + +/* parse an expression and return its type without any side effect. */ +static void expr_type(CType *type) +{ + int saved_nocode_wanted; + + saved_nocode_wanted = nocode_wanted; + nocode_wanted = 1; + gexpr(); + *type = vtop->type; + vpop(); + nocode_wanted = saved_nocode_wanted; +} + +/* parse a unary expression and return its type without any side + effect. */ +static void unary_type(CType *type) +{ + int a; + + a = nocode_wanted; + nocode_wanted = 1; + unary(); + *type = vtop->type; + vpop(); + nocode_wanted = a; +} + +/* parse a constant expression and return value in vtop. */ +static void expr_const1(void) +{ + int a; + a = const_wanted; + const_wanted = 1; + expr_eq(); + const_wanted = a; +} + +/* parse an integer constant and return its value. */ +static int expr_const(void) +{ + int c; + expr_const1(); + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) != VT_CONST) + expect("constant expression"); + c = vtop->c.i; + vpop(); + return c; +} + +/* return the label token if current token is a label, otherwise + return zero */ +static int is_label(void) +{ + int last_tok; + + /* fast test first */ + if (tok < TOK_UIDENT) + return 0; + /* no need to save tokc because tok is an identifier */ + last_tok = tok; + next(); + if (tok == ':') { + next(); + return last_tok; + } else { + unget_tok(last_tok); + return 0; + } +} + +static void block(int *bsym, int *csym, int *case_sym, int *def_sym, + int case_reg, int is_expr) +{ + int a, b, c, d; + Sym *s; + + /* generate line number info */ + if (tcc_state->do_debug && + (last_line_num != file->line_num || last_ind != ind)) { + put_stabn(N_SLINE, 0, file->line_num, ind - func_ind); + last_ind = ind; + last_line_num = file->line_num; + } + + if (is_expr) { + /* default return value is (void) */ + vpushi(0); + vtop->type.t = VT_VOID; + } + + if (tok == TOK_IF) { + /* if test */ + next(); + skip('('); + gexpr(); + skip(')'); + a = gtst(1, 0); + block(bsym, csym, case_sym, def_sym, case_reg, 0); + c = tok; + if (c == TOK_ELSE) { + next(); + d = gjmp(0); + gsym(a); + block(bsym, csym, case_sym, def_sym, case_reg, 0); + gsym(d); /* patch else jmp */ + } else + gsym(a); + } else if (tok == TOK_WHILE) { + next(); + d = ind; + skip('('); + gexpr(); + skip(')'); + a = gtst(1, 0); + b = 0; + block(&a, &b, case_sym, def_sym, case_reg, 0); + gjmp_addr(d); + gsym(a); + gsym_addr(b, d); + } else if (tok == '{') { + Sym *llabel; + + next(); + /* record local declaration stack position */ + s = local_stack; + llabel = local_label_stack; + /* handle local labels declarations */ + if (tok == TOK_LABEL) { + next(); + for(;;) { + if (tok < TOK_UIDENT) + expect("label identifier"); + label_push(&local_label_stack, tok, LABEL_DECLARED); + next(); + if (tok == ',') { + next(); + } else { + skip(';'); + break; + } + } + } + while (tok != '}') { + decl(VT_LOCAL); + if (tok != '}') { + if (is_expr) + vpop(); + block(bsym, csym, case_sym, def_sym, case_reg, is_expr); + } + } + /* pop locally defined labels */ + label_pop(&local_label_stack, llabel); + /* pop locally defined symbols */ + if(is_expr) { + /* XXX: this solution makes only valgrind happy... + triggered by gcc.c-torture/execute/20000917-1.c */ + Sym *p; + switch(vtop->type.t & VT_BTYPE) { + case VT_PTR: + case VT_STRUCT: + case VT_ENUM: + case VT_FUNC: + for(p=vtop->type.ref;p;p=p->prev) + if(p->prev==s) + error("unsupported expression type"); + } + } + sym_pop(&local_stack, s); + next(); + } else if (tok == TOK_RETURN) { + next(); + if (tok != ';') { + gexpr(); + gen_assign_cast(&func_vt); + if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { + CType type; + /* if returning structure, must copy it to implicit + first pointer arg location */ +#ifdef TCC_ARM_EABI + int align, size; + size = type_size(&func_vt,&align); + if(size <= 4) + { + if((vtop->r != (VT_LOCAL | VT_LVAL) || (vtop->c.i & 3)) + && (align & 3)) + { + int addr; + loc = (loc - size) & -4; + addr = loc; + type = func_vt; + vset(&type, VT_LOCAL | VT_LVAL, addr); + vswap(); + vstore(); + vset(&int_type, VT_LOCAL | VT_LVAL, addr); + } + vtop->type = int_type; + gv(RC_IRET); + } else { +#endif + type = func_vt; + mk_pointer(&type); + vset(&type, VT_LOCAL | VT_LVAL, func_vc); + indir(); + vswap(); + /* copy structure value to pointer */ + vstore(); +#ifdef TCC_ARM_EABI + } +#endif + } else if (is_float(func_vt.t)) { + gv(rc_fret(func_vt.t)); + } else { + gv(RC_IRET); + } + vtop--; /* NOT vpop() because on x86 it would flush the fp stack */ + } + skip(';'); + rsym = gjmp(rsym); /* jmp */ + } else if (tok == TOK_BREAK) { + /* compute jump */ + if (!bsym) + error("cannot break"); + *bsym = gjmp(*bsym); + next(); + skip(';'); + } else if (tok == TOK_CONTINUE) { + /* compute jump */ + if (!csym) + error("cannot continue"); + *csym = gjmp(*csym); + next(); + skip(';'); + } else if (tok == TOK_FOR) { + int e; + next(); + skip('('); + if (tok != ';') { + gexpr(); + vpop(); + } + skip(';'); + d = ind; + c = ind; + a = 0; + b = 0; + if (tok != ';') { + gexpr(); + a = gtst(1, 0); + } + skip(';'); + if (tok != ')') { + e = gjmp(0); + c = ind; + gexpr(); + vpop(); + gjmp_addr(d); + gsym(e); + } + skip(')'); + block(&a, &b, case_sym, def_sym, case_reg, 0); + gjmp_addr(c); + gsym(a); + gsym_addr(b, c); + } else + if (tok == TOK_DO) { + next(); + a = 0; + b = 0; + d = ind; + block(&a, &b, case_sym, def_sym, case_reg, 0); + skip(TOK_WHILE); + skip('('); + gsym(b); + gexpr(); + c = gtst(0, 0); + gsym_addr(c, d); + skip(')'); + gsym(a); + skip(';'); + } else + if (tok == TOK_SWITCH) { + next(); + skip('('); + gexpr(); + /* XXX: other types than integer */ + case_reg = gv(RC_INT); + vpop(); + skip(')'); + a = 0; + b = gjmp(0); /* jump to first case */ + c = 0; + block(&a, csym, &b, &c, case_reg, 0); + /* if no default, jmp after switch */ + if (c == 0) + c = ind; + /* default label */ + gsym_addr(b, c); + /* break label */ + gsym(a); + } else + if (tok == TOK_CASE) { + int v1, v2; + if (!case_sym) + expect("switch"); + next(); + v1 = expr_const(); + v2 = v1; + if (gnu_ext && tok == TOK_DOTS) { + next(); + v2 = expr_const(); + if (v2 < v1) + warning("empty case range"); + } + /* since a case is like a label, we must skip it with a jmp */ + b = gjmp(0); + gsym(*case_sym); + vseti(case_reg, 0); + vpushi(v1); + if (v1 == v2) { + gen_op(TOK_EQ); + *case_sym = gtst(1, 0); + } else { + gen_op(TOK_GE); + *case_sym = gtst(1, 0); + vseti(case_reg, 0); + vpushi(v2); + gen_op(TOK_LE); + *case_sym = gtst(1, *case_sym); + } + gsym(b); + skip(':'); + is_expr = 0; + goto block_after_label; + } else + if (tok == TOK_DEFAULT) { + next(); + skip(':'); + if (!def_sym) + expect("switch"); + if (*def_sym) + error("too many 'default'"); + *def_sym = ind; + is_expr = 0; + goto block_after_label; + } else + if (tok == TOK_GOTO) { + next(); + if (tok == '*' && gnu_ext) { + /* computed goto */ + next(); + gexpr(); + if ((vtop->type.t & VT_BTYPE) != VT_PTR) + expect("pointer"); + ggoto(); + } else if (tok >= TOK_UIDENT) { + s = label_find(tok); + /* put forward definition if needed */ + if (!s) { + s = label_push(&global_label_stack, tok, LABEL_FORWARD); + } else { + if (s->r == LABEL_DECLARED) + s->r = LABEL_FORWARD; + } + /* label already defined */ + if (s->r & LABEL_FORWARD) + s->next = (void *)gjmp((long)s->next); + else + gjmp_addr((long)s->next); + next(); + } else { + expect("label identifier"); + } + skip(';'); + } else if (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3) { + asm_instr(); + } else { + b = is_label(); + if (b) { + /* label case */ + s = label_find(b); + if (s) { + if (s->r == LABEL_DEFINED) + error("duplicate label '%s'", get_tok_str(s->v, NULL)); + gsym((long)s->next); + s->r = LABEL_DEFINED; + } else { + s = label_push(&global_label_stack, b, LABEL_DEFINED); + } + s->next = (void *)ind; + /* we accept this, but it is a mistake */ + block_after_label: + if (tok == '}') { + warning("deprecated use of label at end of compound statement"); + } else { + if (is_expr) + vpop(); + block(bsym, csym, case_sym, def_sym, case_reg, is_expr); + } + } else { + /* expression case */ + if (tok != ';') { + if (is_expr) { + vpop(); + gexpr(); + } else { + gexpr(); + vpop(); + } + } + skip(';'); + } + } +} + +/* t is the array or struct type. c is the array or struct + address. cur_index/cur_field is the pointer to the current + value. 'size_only' is true if only size info is needed (only used + in arrays) */ +static void decl_designator(CType *type, Section *sec, unsigned long c, + int *cur_index, Sym **cur_field, + int size_only) +{ + Sym *s, *f; + int notfirst, index, index_last, align, l, nb_elems, elem_size; + CType type1; + + notfirst = 0; + elem_size = 0; + nb_elems = 1; + if (gnu_ext && (l = is_label()) != 0) + goto struct_field; + while (tok == '[' || tok == '.') { + if (tok == '[') { + if (!(type->t & VT_ARRAY)) + expect("array type"); + s = type->ref; + next(); + index = expr_const(); + if (index < 0 || (s->c >= 0 && index >= s->c)) + expect("invalid index"); + if (tok == TOK_DOTS && gnu_ext) { + next(); + index_last = expr_const(); + if (index_last < 0 || + (s->c >= 0 && index_last >= s->c) || + index_last < index) + expect("invalid index"); + } else { + index_last = index; + } + skip(']'); + if (!notfirst) + *cur_index = index_last; + type = pointed_type(type); + elem_size = type_size(type, &align); + c += index * elem_size; + /* NOTE: we only support ranges for last designator */ + nb_elems = index_last - index + 1; + if (nb_elems != 1) { + notfirst = 1; + break; + } + } else { + next(); + l = tok; + next(); + struct_field: + if ((type->t & VT_BTYPE) != VT_STRUCT) + expect("struct/union type"); + s = type->ref; + l |= SYM_FIELD; + f = s->next; + while (f) { + if (f->v == l) + break; + f = f->next; + } + if (!f) + expect("field"); + if (!notfirst) + *cur_field = f; + /* XXX: fix this mess by using explicit storage field */ + type1 = f->type; + type1.t |= (type->t & ~VT_TYPE); + type = &type1; + c += f->c; + } + notfirst = 1; + } + if (notfirst) { + if (tok == '=') { + next(); + } else { + if (!gnu_ext) + expect("="); + } + } else { + if (type->t & VT_ARRAY) { + index = *cur_index; + type = pointed_type(type); + c += index * type_size(type, &align); + } else { + f = *cur_field; + if (!f) + error("too many field init"); + /* XXX: fix this mess by using explicit storage field */ + type1 = f->type; + type1.t |= (type->t & ~VT_TYPE); + type = &type1; + c += f->c; + } + } + decl_initializer(type, sec, c, 0, size_only); + + /* XXX: make it more general */ + if (!size_only && nb_elems > 1) { + unsigned long c_end; + uint8_t *src, *dst; + int i; + + if (!sec) + error("range init not supported yet for dynamic storage"); + c_end = c + nb_elems * elem_size; + if (c_end > sec->data_allocated) + section_realloc(sec, c_end); + src = sec->data + c; + dst = src; + for(i = 1; i < nb_elems; i++) { + dst += elem_size; + memcpy(dst, src, elem_size); + } + } +} + +#define EXPR_VAL 0 +#define EXPR_CONST 1 +#define EXPR_ANY 2 + +/* store a value or an expression directly in global data or in local array */ +static void init_putv(CType *type, Section *sec, unsigned long c, + int v, int expr_type) +{ + int saved_global_expr, bt, bit_pos, bit_size; + void *ptr; + unsigned long long bit_mask; + CType dtype; + + switch(expr_type) { + case EXPR_VAL: + vpushi(v); + break; + case EXPR_CONST: + /* compound literals must be allocated globally in this case */ + saved_global_expr = global_expr; + global_expr = 1; + expr_const1(); + global_expr = saved_global_expr; + /* NOTE: symbols are accepted */ + if ((vtop->r & (VT_VALMASK | VT_LVAL)) != VT_CONST) + error("initializer element is not constant"); + break; + case EXPR_ANY: + expr_eq(); + break; + } + + dtype = *type; + dtype.t &= ~VT_CONSTANT; /* need to do that to avoid false warning */ + + if (sec) { + /* XXX: not portable */ + /* XXX: generate error if incorrect relocation */ + gen_assign_cast(&dtype); + bt = type->t & VT_BTYPE; + /* we'll write at most 12 bytes */ + if (c + 12 > sec->data_allocated) { + section_realloc(sec, c + 12); + } + ptr = sec->data + c; + /* XXX: make code faster ? */ + if (!(type->t & VT_BITFIELD)) { + bit_pos = 0; + bit_size = 32; + bit_mask = -1LL; + } else { + bit_pos = (vtop->type.t >> VT_STRUCT_SHIFT) & 0x3f; + bit_size = (vtop->type.t >> (VT_STRUCT_SHIFT + 6)) & 0x3f; + bit_mask = (1LL << bit_size) - 1; + } + if ((vtop->r & VT_SYM) && + (bt == VT_BYTE || + bt == VT_SHORT || + bt == VT_DOUBLE || + bt == VT_LDOUBLE || + bt == VT_LLONG || + (bt == VT_INT && bit_size != 32))) + error("initializer element is not computable at load time"); + switch(bt) { + case VT_BOOL: + vtop->c.i = (vtop->c.i != 0); + case VT_BYTE: + *(char *)ptr |= (vtop->c.i & bit_mask) << bit_pos; + break; + case VT_SHORT: + *(short *)ptr |= (vtop->c.i & bit_mask) << bit_pos; + break; + case VT_DOUBLE: + *(double *)ptr = vtop->c.d; + break; + case VT_LDOUBLE: + *(long double *)ptr = vtop->c.ld; + break; + case VT_LLONG: + *(long long *)ptr |= (vtop->c.ll & bit_mask) << bit_pos; + break; + default: + if (vtop->r & VT_SYM) { + greloc(sec, vtop->sym, c, R_DATA_32); + } + *(int *)ptr |= (vtop->c.i & bit_mask) << bit_pos; + break; + } + vtop--; + } else { + vset(&dtype, VT_LOCAL|VT_LVAL, c); + vswap(); + vstore(); + vpop(); + } +} + +/* put zeros for variable based init */ +static void init_putz(CType *t, Section *sec, unsigned long c, int size) +{ + if (sec) { + /* nothing to do because globals are already set to zero */ + } else { + vpush_global_sym(&func_old_type, TOK_memset); + vseti(VT_LOCAL, c); + vpushi(0); + vpushi(size); + gfunc_call(3); + } +} + +/* 't' contains the type and storage info. 'c' is the offset of the + object in section 'sec'. If 'sec' is NULL, it means stack based + allocation. 'first' is true if array '{' must be read (multi + dimension implicit array init handling). 'size_only' is true if + size only evaluation is wanted (only for arrays). */ +static void decl_initializer(CType *type, Section *sec, unsigned long c, + int first, int size_only) +{ + int index, array_length, n, no_oblock, nb, parlevel, i; + int size1, align1, expr_type; + Sym *s, *f; + CType *t1; + + if (type->t & VT_ARRAY) { + s = type->ref; + n = s->c; + array_length = 0; + t1 = pointed_type(type); + size1 = type_size(t1, &align1); + + no_oblock = 1; + if ((first && tok != TOK_LSTR && tok != TOK_STR) || + tok == '{') { + skip('{'); + no_oblock = 0; + } + + /* only parse strings here if correct type (otherwise: handle + them as ((w)char *) expressions */ + if ((tok == TOK_LSTR && +#ifdef TCC_TARGET_PE + (t1->t & VT_BTYPE) == VT_SHORT && (t1->t & VT_UNSIGNED) +#else + (t1->t & VT_BTYPE) == VT_INT +#endif + ) || (tok == TOK_STR && (t1->t & VT_BTYPE) == VT_BYTE)) { + while (tok == TOK_STR || tok == TOK_LSTR) { + int cstr_len, ch; + CString *cstr; + + cstr = tokc.cstr; + /* compute maximum number of chars wanted */ + if (tok == TOK_STR) + cstr_len = cstr->size; + else + cstr_len = cstr->size / sizeof(nwchar_t); + cstr_len--; + nb = cstr_len; + if (n >= 0 && nb > (n - array_length)) + nb = n - array_length; + if (!size_only) { + if (cstr_len > nb) + warning("initializer-string for array is too long"); + /* in order to go faster for common case (char + string in global variable, we handle it + specifically */ + if (sec && tok == TOK_STR && size1 == 1) { + memcpy(sec->data + c + array_length, cstr->data, nb); + } else { + for(i=0;idata)[i]; + else + ch = ((nwchar_t *)cstr->data)[i]; + init_putv(t1, sec, c + (array_length + i) * size1, + ch, EXPR_VAL); + } + } + } + array_length += nb; + next(); + } + /* only add trailing zero if enough storage (no + warning in this case since it is standard) */ + if (n < 0 || array_length < n) { + if (!size_only) { + init_putv(t1, sec, c + (array_length * size1), 0, EXPR_VAL); + } + array_length++; + } + } else { + index = 0; + while (tok != '}') { + decl_designator(type, sec, c, &index, NULL, size_only); + if (n >= 0 && index >= n) + error("index too large"); + /* must put zero in holes (note that doing it that way + ensures that it even works with designators) */ + if (!size_only && array_length < index) { + init_putz(t1, sec, c + array_length * size1, + (index - array_length) * size1); + } + index++; + if (index > array_length) + array_length = index; + /* special test for multi dimensional arrays (may not + be strictly correct if designators are used at the + same time) */ + if (index >= n && no_oblock) + break; + if (tok == '}') + break; + skip(','); + } + } + if (!no_oblock) + skip('}'); + /* put zeros at the end */ + if (!size_only && n >= 0 && array_length < n) { + init_putz(t1, sec, c + array_length * size1, + (n - array_length) * size1); + } + /* patch type size if needed */ + if (n < 0) + s->c = array_length; + } else if ((type->t & VT_BTYPE) == VT_STRUCT && + (sec || !first || tok == '{')) { + int par_count; + + /* NOTE: the previous test is a specific case for automatic + struct/union init */ + /* XXX: union needs only one init */ + + /* XXX: this test is incorrect for local initializers + beginning with ( without {. It would be much more difficult + to do it correctly (ideally, the expression parser should + be used in all cases) */ + par_count = 0; + if (tok == '(') { + AttributeDef ad1; + CType type1; + next(); + while (tok == '(') { + par_count++; + next(); + } + if (!parse_btype(&type1, &ad1)) + expect("cast"); + type_decl(&type1, &ad1, &n, TYPE_ABSTRACT); +#if 0 + if (!is_assignable_types(type, &type1)) + error("invalid type for cast"); +#endif + skip(')'); + } + no_oblock = 1; + if (first || tok == '{') { + skip('{'); + no_oblock = 0; + } + s = type->ref; + f = s->next; + array_length = 0; + index = 0; + n = s->c; + while (tok != '}') { + decl_designator(type, sec, c, NULL, &f, size_only); + index = f->c; + if (!size_only && array_length < index) { + init_putz(type, sec, c + array_length, + index - array_length); + } + index = index + type_size(&f->type, &align1); + if (index > array_length) + array_length = index; + f = f->next; + if (no_oblock && f == NULL) + break; + if (tok == '}') + break; + skip(','); + } + /* put zeros at the end */ + if (!size_only && array_length < n) { + init_putz(type, sec, c + array_length, + n - array_length); + } + if (!no_oblock) + skip('}'); + while (par_count) { + skip(')'); + par_count--; + } + } else if (tok == '{') { + next(); + decl_initializer(type, sec, c, first, size_only); + skip('}'); + } else if (size_only) { + /* just skip expression */ + parlevel = 0; + while ((parlevel > 0 || (tok != '}' && tok != ',')) && + tok != -1) { + if (tok == '(') + parlevel++; + else if (tok == ')') + parlevel--; + next(); + } + } else { + /* currently, we always use constant expression for globals + (may change for scripting case) */ + expr_type = EXPR_CONST; + if (!sec) + expr_type = EXPR_ANY; + init_putv(type, sec, c, 0, expr_type); + } +} + +/* parse an initializer for type 't' if 'has_init' is non zero, and + allocate space in local or global data space ('r' is either + VT_LOCAL or VT_CONST). If 'v' is non zero, then an associated + variable 'v' of scope 'scope' is declared before initializers are + parsed. If 'v' is zero, then a reference to the new object is put + in the value stack. If 'has_init' is 2, a special parsing is done + to handle string constants. */ +static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, + int has_init, int v, int scope) +{ + int size, align, addr, data_offset; + int level; + ParseState saved_parse_state = {0}; + TokenString init_str; + Section *sec; + + size = type_size(type, &align); + /* If unknown size, we must evaluate it before + evaluating initializers because + initializers can generate global data too + (e.g. string pointers or ISOC99 compound + literals). It also simplifies local + initializers handling */ + tok_str_new(&init_str); + if (size < 0) { + if (!has_init) + error("unknown type size"); + /* get all init string */ + if (has_init == 2) { + /* only get strings */ + while (tok == TOK_STR || tok == TOK_LSTR) { + tok_str_add_tok(&init_str); + next(); + } + } else { + level = 0; + while (level > 0 || (tok != ',' && tok != ';')) { + if (tok < 0) + error("unexpected end of file in initializer"); + tok_str_add_tok(&init_str); + if (tok == '{') + level++; + else if (tok == '}') { + level--; + if (level <= 0) { + next(); + break; + } + } + next(); + } + } + tok_str_add(&init_str, -1); + tok_str_add(&init_str, 0); + + /* compute size */ + save_parse_state(&saved_parse_state); + + macro_ptr = init_str.str; + next(); + decl_initializer(type, NULL, 0, 1, 1); + /* prepare second initializer parsing */ + macro_ptr = init_str.str; + next(); + + /* if still unknown size, error */ + size = type_size(type, &align); + if (size < 0) + error("unknown type size"); + } + /* take into account specified alignment if bigger */ + if (ad->aligned) { + if (ad->aligned > align) + align = ad->aligned; + } else if (ad->packed) { + align = 1; + } + if ((r & VT_VALMASK) == VT_LOCAL) { + sec = NULL; + if (tcc_state->do_bounds_check && (type->t & VT_ARRAY)) + loc--; + loc = (loc - size) & -align; + addr = loc; + /* handles bounds */ + /* XXX: currently, since we do only one pass, we cannot track + '&' operators, so we add only arrays */ + if (tcc_state->do_bounds_check && (type->t & VT_ARRAY)) { + unsigned long *bounds_ptr; + /* add padding between regions */ + loc--; + /* then add local bound info */ + bounds_ptr = section_ptr_add(lbounds_section, 2 * sizeof(unsigned long)); + bounds_ptr[0] = addr; + bounds_ptr[1] = size; + } + if (v) { + /* local variable */ + sym_push(v, type, r, addr); + } else { + /* push local reference */ + vset(type, r, addr); + } + } else { + Sym *sym; + + sym = NULL; + if (v && scope == VT_CONST) { + /* see if the symbol was already defined */ + sym = sym_find(v); + if (sym) { + if (!is_compatible_types(&sym->type, type)) + error("incompatible types for redefinition of '%s'", + get_tok_str(v, NULL)); + if (sym->type.t & VT_EXTERN) { + /* if the variable is extern, it was not allocated */ + sym->type.t &= ~VT_EXTERN; + /* set array size if it was ommited in extern + declaration */ + if ((sym->type.t & VT_ARRAY) && + sym->type.ref->c < 0 && + type->ref->c >= 0) + sym->type.ref->c = type->ref->c; + } else { + /* we accept several definitions of the same + global variable. this is tricky, because we + must play with the SHN_COMMON type of the symbol */ + /* XXX: should check if the variable was already + initialized. It is incorrect to initialized it + twice */ + /* no init data, we won't add more to the symbol */ + if (!has_init) + goto no_alloc; + } + } + } + + /* allocate symbol in corresponding section */ + sec = ad->section; + if (!sec) { + if (has_init) + sec = data_section; + else if (tcc_state->nocommon) + sec = bss_section; + } + if (sec) { + data_offset = sec->data_offset; + data_offset = (data_offset + align - 1) & -align; + addr = data_offset; + /* very important to increment global pointer at this time + because initializers themselves can create new initializers */ + data_offset += size; + /* add padding if bound check */ + if (tcc_state->do_bounds_check) + data_offset++; + sec->data_offset = data_offset; + /* allocate section space to put the data */ + if (sec->sh_type != SHT_NOBITS && + data_offset > sec->data_allocated) + section_realloc(sec, data_offset); + /* align section if needed */ + if (align > sec->sh_addralign) + sec->sh_addralign = align; + } else { + addr = 0; /* avoid warning */ + } + + if (v) { + if (scope != VT_CONST || !sym) { + sym = sym_push(v, type, r | VT_SYM, 0); + } + /* update symbol definition */ + if (sec) { + put_extern_sym(sym, sec, addr, size); + } else { + ElfW(Sym) *esym; + /* put a common area */ + put_extern_sym(sym, NULL, align, size); + /* XXX: find a nicer way */ + esym = &((ElfW(Sym) *)symtab_section->data)[sym->c]; + esym->st_shndx = SHN_COMMON; + } + } else { + CValue cval; + + /* push global reference */ + sym = get_sym_ref(type, sec, addr, size); + cval.ul = 0; + vsetc(type, VT_CONST | VT_SYM, &cval); + vtop->sym = sym; + } + + /* handles bounds now because the symbol must be defined + before for the relocation */ + if (tcc_state->do_bounds_check) { + unsigned long *bounds_ptr; + + greloc(bounds_section, sym, bounds_section->data_offset, R_DATA_32); + /* then add global bound info */ + bounds_ptr = section_ptr_add(bounds_section, 2 * sizeof(long)); + bounds_ptr[0] = 0; /* relocated */ + bounds_ptr[1] = size; + } + } + if (has_init) { + decl_initializer(type, sec, addr, 1, 0); + /* restore parse state if needed */ + if (init_str.str) { + tok_str_free(init_str.str); + restore_parse_state(&saved_parse_state); + } + } + no_alloc: ; +} + +void put_func_debug(Sym *sym) +{ + char buf[512]; + + /* stabs info */ + /* XXX: we put here a dummy type */ + snprintf(buf, sizeof(buf), "%s:%c1", + funcname, sym->type.t & VT_STATIC ? 'f' : 'F'); + put_stabs_r(buf, N_FUN, 0, file->line_num, 0, + cur_text_section, sym->c); + /* //gr gdb wants a line at the function */ + put_stabn(N_SLINE, 0, file->line_num, 0); + last_ind = 0; + last_line_num = 0; +} + +/* parse an old style function declaration list */ +/* XXX: check multiple parameter */ +static void func_decl_list(Sym *func_sym) +{ + AttributeDef ad; + int v; + Sym *s; + CType btype, type; + + /* parse each declaration */ + while (tok != '{' && tok != ';' && tok != ',' && tok != TOK_EOF) { + if (!parse_btype(&btype, &ad)) + expect("declaration list"); + if (((btype.t & VT_BTYPE) == VT_ENUM || + (btype.t & VT_BTYPE) == VT_STRUCT) && + tok == ';') { + /* we accept no variable after */ + } else { + for(;;) { + type = btype; + type_decl(&type, &ad, &v, TYPE_DIRECT); + /* find parameter in function parameter list */ + s = func_sym->next; + while (s != NULL) { + if ((s->v & ~SYM_FIELD) == v) + goto found; + s = s->next; + } + error("declaration for parameter '%s' but no such parameter", + get_tok_str(v, NULL)); + found: + /* check that no storage specifier except 'register' was given */ + if (type.t & VT_STORAGE) + error("storage class specified for '%s'", get_tok_str(v, NULL)); + convert_parameter_type(&type); + /* we can add the type (NOTE: it could be local to the function) */ + s->type = type; + /* accept other parameters */ + if (tok == ',') + next(); + else + break; + } + } + skip(';'); + } +} + +/* parse a function defined by symbol 'sym' and generate its code in + 'cur_text_section' */ +static void gen_function(Sym *sym) +{ + int saved_nocode_wanted = nocode_wanted; + nocode_wanted = 0; + ind = cur_text_section->data_offset; + /* NOTE: we patch the symbol size later */ + put_extern_sym(sym, cur_text_section, ind, 0); + funcname = get_tok_str(sym->v, NULL); + func_ind = ind; + /* put debug symbol */ + if (tcc_state->do_debug) + put_func_debug(sym); + /* push a dummy symbol to enable local sym storage */ + sym_push2(&local_stack, SYM_FIELD, 0, 0); + gfunc_prolog(&sym->type); + rsym = 0; + block(NULL, NULL, NULL, NULL, 0, 0); + gsym(rsym); + gfunc_epilog(); + cur_text_section->data_offset = ind; + label_pop(&global_label_stack, NULL); + sym_pop(&local_stack, NULL); /* reset local stack */ + /* end of function */ + /* patch symbol size */ + ((ElfW(Sym) *)symtab_section->data)[sym->c].st_size = + ind - func_ind; + if (tcc_state->do_debug) { + put_stabn(N_FUN, 0, 0, ind - func_ind); + } + /* It's better to crash than to generate wrong code */ + cur_text_section = NULL; + funcname = ""; /* for safety */ + func_vt.t = VT_VOID; /* for safety */ + ind = 0; /* for safety */ + nocode_wanted = saved_nocode_wanted; +} + +static void gen_inline_functions(void) +{ + Sym *sym; + CType *type; + int *str, inline_generated; + + /* iterate while inline function are referenced */ + for(;;) { + inline_generated = 0; + for(sym = global_stack; sym != NULL; sym = sym->prev) { + type = &sym->type; + if (((type->t & VT_BTYPE) == VT_FUNC) && + (type->t & (VT_STATIC | VT_INLINE)) == + (VT_STATIC | VT_INLINE) && + sym->c != 0) { + /* the function was used: generate its code and + convert it to a normal function */ + str = INLINE_DEF(sym->r); + sym->r = VT_SYM | VT_CONST; + sym->type.t &= ~VT_INLINE; + + macro_ptr = str; + next(); + cur_text_section = text_section; + gen_function(sym); + macro_ptr = NULL; /* fail safe */ + + tok_str_free(str); + inline_generated = 1; + } + } + if (!inline_generated) + break; + } + + /* free all remaining inline function tokens */ + for(sym = global_stack; sym != NULL; sym = sym->prev) { + type = &sym->type; + if (((type->t & VT_BTYPE) == VT_FUNC) && + (type->t & (VT_STATIC | VT_INLINE)) == + (VT_STATIC | VT_INLINE)) { + //gr printf("sym %d %s\n", sym->r, get_tok_str(sym->v, NULL)); + if (sym->r == (VT_SYM | VT_CONST)) //gr beware! + continue; + str = INLINE_DEF(sym->r); + tok_str_free(str); + sym->r = 0; /* fail safe */ + } + } +} + +/* 'l' is VT_LOCAL or VT_CONST to define default storage type */ +static void decl(int l) +{ + int v, has_init, r; + CType type, btype; + Sym *sym; + AttributeDef ad; + + while (1) { + if (!parse_btype(&btype, &ad)) { + /* skip redundant ';' */ + /* XXX: find more elegant solution */ + if (tok == ';') { + next(); + continue; + } + if (l == VT_CONST && + (tok == TOK_ASM1 || tok == TOK_ASM2 || tok == TOK_ASM3)) { + /* global asm block */ + asm_global_instr(); + continue; + } + /* special test for old K&R protos without explicit int + type. Only accepted when defining global data */ + if (l == VT_LOCAL || tok < TOK_DEFINE) + break; + btype.t = VT_INT; + } + if (((btype.t & VT_BTYPE) == VT_ENUM || + (btype.t & VT_BTYPE) == VT_STRUCT) && + tok == ';') { + /* we accept no variable after */ + next(); + continue; + } + while (1) { /* iterate thru each declaration */ + type = btype; + type_decl(&type, &ad, &v, TYPE_DIRECT); +#if 0 + { + char buf[500]; + type_to_str(buf, sizeof(buf), t, get_tok_str(v, NULL)); + printf("type = '%s'\n", buf); + } +#endif + if ((type.t & VT_BTYPE) == VT_FUNC) { + /* if old style function prototype, we accept a + declaration list */ + sym = type.ref; + if (sym->c == FUNC_OLD) + func_decl_list(sym); + } + + if (tok == '{') { + if (l == VT_LOCAL) + error("cannot use local functions"); + if ((type.t & VT_BTYPE) != VT_FUNC) + expect("function definition"); + + /* reject abstract declarators in function definition */ + sym = type.ref; + while ((sym = sym->next) != NULL) + if (!(sym->v & ~SYM_FIELD)) + expect("identifier"); + + /* XXX: cannot do better now: convert extern line to static inline */ + if ((type.t & (VT_EXTERN | VT_INLINE)) == (VT_EXTERN | VT_INLINE)) + type.t = (type.t & ~VT_EXTERN) | VT_STATIC; + + sym = sym_find(v); + if (sym) { + if ((sym->type.t & VT_BTYPE) != VT_FUNC) + goto func_error1; + /* specific case: if not func_call defined, we put + the one of the prototype */ + /* XXX: should have default value */ + r = sym->type.ref->r; + if (FUNC_CALL(r) != FUNC_CDECL + && FUNC_CALL(type.ref->r) == FUNC_CDECL) + FUNC_CALL(type.ref->r) = FUNC_CALL(r); + if (FUNC_EXPORT(r)) + FUNC_EXPORT(type.ref->r) = 1; + + if (!is_compatible_types(&sym->type, &type)) { + func_error1: + error("incompatible types for redefinition of '%s'", + get_tok_str(v, NULL)); + } + /* if symbol is already defined, then put complete type */ + sym->type = type; + } else { + /* put function symbol */ + sym = global_identifier_push(v, type.t, 0); + sym->type.ref = type.ref; + } + + /* static inline functions are just recorded as a kind + of macro. Their code will be emitted at the end of + the compilation unit only if they are used */ + if ((type.t & (VT_INLINE | VT_STATIC)) == + (VT_INLINE | VT_STATIC)) { + TokenString func_str; + int block_level; + + tok_str_new(&func_str); + + block_level = 0; + for(;;) { + int t; + if (tok == TOK_EOF) + error("unexpected end of file"); + tok_str_add_tok(&func_str); + t = tok; + next(); + if (t == '{') { + block_level++; + } else if (t == '}') { + block_level--; + if (block_level == 0) + break; + } + } + tok_str_add(&func_str, -1); + tok_str_add(&func_str, 0); + INLINE_DEF(sym->r) = func_str.str; + } else { + /* compute text section */ + cur_text_section = ad.section; + if (!cur_text_section) + cur_text_section = text_section; + sym->r = VT_SYM | VT_CONST; + gen_function(sym); + } + break; + } else { + if (btype.t & VT_TYPEDEF) { + /* save typedefed type */ + /* XXX: test storage specifiers ? */ + sym = sym_push(v, &type, 0, 0); + sym->type.t |= VT_TYPEDEF; + } else if ((type.t & VT_BTYPE) == VT_FUNC) { + /* external function definition */ + /* specific case for func_call attribute */ + if (ad.func_attr) + type.ref->r = ad.func_attr; + external_sym(v, &type, 0); + } else { + /* not lvalue if array */ + r = 0; + if (!(type.t & VT_ARRAY)) + r |= lvalue_type(type.t); + has_init = (tok == '='); + if ((btype.t & VT_EXTERN) || + ((type.t & VT_ARRAY) && (type.t & VT_STATIC) && + !has_init && l == VT_CONST && type.ref->c < 0)) { + /* external variable */ + /* NOTE: as GCC, uninitialized global static + arrays of null size are considered as + extern */ + external_sym(v, &type, r); + } else { + type.t |= (btype.t & VT_STATIC); /* Retain "static". */ + if (type.t & VT_STATIC) + r |= VT_CONST; + else + r |= l; + if (has_init) + next(); + decl_initializer_alloc(&type, &ad, r, + has_init, v, l); + } + } + if (tok != ',') { + skip(';'); + break; + } + next(); + } + } + } +} + diff --git a/05/tcc-0.9.25/tccpe.c b/05/tcc-0.9.25/tccpe.c new file mode 100644 index 0000000..1e3fdb3 --- /dev/null +++ b/05/tcc-0.9.25/tccpe.c @@ -0,0 +1,1559 @@ +/* + * TCCPE.C - PE file output for the Tiny C Compiler + * + * Copyright (c) 2005-2007 grischka + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef TCC_TARGET_PE + +#define ST_FN static +#define ST_DATA static +#define PUB_FN + +#ifndef _WIN32 +#define stricmp strcasecmp +#define strnicmp strncasecmp +#endif + +#ifndef MAX_PATH +#define MAX_PATH 260 +#endif + +#define PE_MERGE_DATA +// #define PE_PRINT_SECTIONS + +/* ----------------------------------------------------------- */ +#ifndef IMAGE_NT_SIGNATURE +/* ----------------------------------------------------------- */ +/* definitions below are from winnt.h */ + +typedef unsigned char BYTE; +typedef unsigned short WORD; +typedef unsigned long DWORD; +#pragma pack(push, 1) + +typedef struct _IMAGE_DOS_HEADER { /* DOS .EXE header */ + WORD e_magic; /* Magic number */ + WORD e_cblp; /* Bytes on last page of file */ + WORD e_cp; /* Pages in file */ + WORD e_crlc; /* Relocations */ + WORD e_cparhdr; /* Size of header in paragraphs */ + WORD e_minalloc; /* Minimum extra paragraphs needed */ + WORD e_maxalloc; /* Maximum extra paragraphs needed */ + WORD e_ss; /* Initial (relative) SS value */ + WORD e_sp; /* Initial SP value */ + WORD e_csum; /* Checksum */ + WORD e_ip; /* Initial IP value */ + WORD e_cs; /* Initial (relative) CS value */ + WORD e_lfarlc; /* File address of relocation table */ + WORD e_ovno; /* Overlay number */ + WORD e_res[4]; /* Reserved words */ + WORD e_oemid; /* OEM identifier (for e_oeminfo) */ + WORD e_oeminfo; /* OEM information; e_oemid specific */ + WORD e_res2[10]; /* Reserved words */ + DWORD e_lfanew; /* File address of new exe header */ +} IMAGE_DOS_HEADER, *PIMAGE_DOS_HEADER; + +#define IMAGE_NT_SIGNATURE 0x00004550 /* PE00 */ +#define SIZE_OF_NT_SIGNATURE 4 + +typedef struct _IMAGE_FILE_HEADER { + WORD Machine; + WORD NumberOfSections; + DWORD TimeDateStamp; + DWORD PointerToSymbolTable; + DWORD NumberOfSymbols; + WORD SizeOfOptionalHeader; + WORD Characteristics; +} IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER; + + +#define IMAGE_SIZEOF_FILE_HEADER 20 + +typedef struct _IMAGE_DATA_DIRECTORY { + DWORD VirtualAddress; + DWORD Size; +} IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY; + + +typedef struct _IMAGE_OPTIONAL_HEADER { + /* Standard fields. */ + WORD Magic; + BYTE MajorLinkerVersion; + BYTE MinorLinkerVersion; + DWORD SizeOfCode; + DWORD SizeOfInitializedData; + DWORD SizeOfUninitializedData; + DWORD AddressOfEntryPoint; + DWORD BaseOfCode; + DWORD BaseOfData; + + /* NT additional fields. */ + DWORD ImageBase; + DWORD SectionAlignment; + DWORD FileAlignment; + WORD MajorOperatingSystemVersion; + WORD MinorOperatingSystemVersion; + WORD MajorImageVersion; + WORD MinorImageVersion; + WORD MajorSubsystemVersion; + WORD MinorSubsystemVersion; + DWORD Win32VersionValue; + DWORD SizeOfImage; + DWORD SizeOfHeaders; + DWORD CheckSum; + WORD Subsystem; + WORD DllCharacteristics; + DWORD SizeOfStackReserve; + DWORD SizeOfStackCommit; + DWORD SizeOfHeapReserve; + DWORD SizeOfHeapCommit; + DWORD LoaderFlags; + DWORD NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[16]; + +} IMAGE_OPTIONAL_HEADER, *PIMAGE_OPTIONAL_HEADER; + + +#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 /* Export Directory */ +#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 /* Import Directory */ +#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 /* Resource Directory */ +#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 /* Exception Directory */ +#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 /* Security Directory */ +#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 /* Base Relocation Table */ +#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 /* Debug Directory */ +/* IMAGE_DIRECTORY_ENTRY_COPYRIGHT 7 (X86 usage) */ +#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 /* Architecture Specific Data */ +#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 /* RVA of GP */ +#define IMAGE_DIRECTORY_ENTRY_TLS 9 /* TLS Directory */ +#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 /* Load Configuration Directory */ +#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 /* Bound Import Directory in headers */ +#define IMAGE_DIRECTORY_ENTRY_IAT 12 /* Import Address Table */ +#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 /* Delay Load Import Descriptors */ +#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 /* COM Runtime descriptor */ + +/* Section header format. */ +#define IMAGE_SIZEOF_SHORT_NAME 8 + +typedef struct _IMAGE_SECTION_HEADER { + BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; + union { + DWORD PhysicalAddress; + DWORD VirtualSize; + } Misc; + DWORD VirtualAddress; + DWORD SizeOfRawData; + DWORD PointerToRawData; + DWORD PointerToRelocations; + DWORD PointerToLinenumbers; + WORD NumberOfRelocations; + WORD NumberOfLinenumbers; + DWORD Characteristics; +} IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER; + +#define IMAGE_SIZEOF_SECTION_HEADER 40 + +typedef struct _IMAGE_BASE_RELOCATION { + DWORD VirtualAddress; + DWORD SizeOfBlock; +// WORD TypeOffset[1]; +} IMAGE_BASE_RELOCATION; + +#define IMAGE_SIZEOF_BASE_RELOCATION 8 + +#define IMAGE_REL_BASED_ABSOLUTE 0 +#define IMAGE_REL_BASED_HIGH 1 +#define IMAGE_REL_BASED_LOW 2 +#define IMAGE_REL_BASED_HIGHLOW 3 +#define IMAGE_REL_BASED_HIGHADJ 4 +#define IMAGE_REL_BASED_MIPS_JMPADDR 5 +#define IMAGE_REL_BASED_SECTION 6 +#define IMAGE_REL_BASED_REL32 7 + +#pragma pack(pop) + +/* ----------------------------------------------------------- */ +#endif /* ndef IMAGE_NT_SIGNATURE */ +/* ----------------------------------------------------------- */ + +#pragma pack(push, 1) + +struct pe_header +{ + IMAGE_DOS_HEADER doshdr; + BYTE dosstub[0x40]; + DWORD nt_sig; + IMAGE_FILE_HEADER filehdr; + IMAGE_OPTIONAL_HEADER opthdr; +}; + +struct pe_import_header { + DWORD first_entry; + DWORD time_date; + DWORD forwarder; + DWORD lib_name_offset; + DWORD first_thunk; +}; + +struct pe_export_header { + DWORD Characteristics; + DWORD TimeDateStamp; + DWORD Version; + DWORD Name; + DWORD Base; + DWORD NumberOfFunctions; + DWORD NumberOfNames; + DWORD AddressOfFunctions; + DWORD AddressOfNames; + DWORD AddressOfNameOrdinals; +}; + +struct pe_reloc_header { + DWORD offset; + DWORD size; +}; + +struct pe_rsrc_header { + struct _IMAGE_FILE_HEADER filehdr; + struct _IMAGE_SECTION_HEADER sectionhdr; +}; + +struct pe_rsrc_reloc { + DWORD offset; + DWORD size; + WORD type; +}; + +#pragma pack(pop) + +/* ----------------------------------------------------------- */ +ST_DATA struct pe_header pe_header = { +{ + /* IMAGE_DOS_HEADER doshdr */ + 0x5A4D, /*WORD e_magic; Magic number */ + 0x0090, /*WORD e_cblp; Bytes on last page of file */ + 0x0003, /*WORD e_cp; Pages in file */ + 0x0000, /*WORD e_crlc; Relocations */ + + 0x0004, /*WORD e_cparhdr; Size of header in paragraphs */ + 0x0000, /*WORD e_minalloc; Minimum extra paragraphs needed */ + 0xFFFF, /*WORD e_maxalloc; Maximum extra paragraphs needed */ + 0x0000, /*WORD e_ss; Initial (relative) SS value */ + + 0x00B8, /*WORD e_sp; Initial SP value */ + 0x0000, /*WORD e_csum; Checksum */ + 0x0000, /*WORD e_ip; Initial IP value */ + 0x0000, /*WORD e_cs; Initial (relative) CS value */ + 0x0040, /*WORD e_lfarlc; File address of relocation table */ + 0x0000, /*WORD e_ovno; Overlay number */ + {0,0,0,0}, /*WORD e_res[4]; Reserved words */ + 0x0000, /*WORD e_oemid; OEM identifier (for e_oeminfo) */ + 0x0000, /*WORD e_oeminfo; OEM information; e_oemid specific */ + {0,0,0,0,0,0,0,0,0,0}, /*WORD e_res2[10]; Reserved words */ + 0x00000080 /*DWORD e_lfanew; File address of new exe header */ +},{ + /* BYTE dosstub[0x40] */ + /* 14 code bytes + "This program cannot be run in DOS mode.\r\r\n$" + 6 * 0x00 */ + 0x0e,0x1f,0xba,0x0e,0x00,0xb4,0x09,0xcd,0x21,0xb8,0x01,0x4c,0xcd,0x21,0x54,0x68, + 0x69,0x73,0x20,0x70,0x72,0x6f,0x67,0x72,0x61,0x6d,0x20,0x63,0x61,0x6e,0x6e,0x6f, + 0x74,0x20,0x62,0x65,0x20,0x72,0x75,0x6e,0x20,0x69,0x6e,0x20,0x44,0x4f,0x53,0x20, + 0x6d,0x6f,0x64,0x65,0x2e,0x0d,0x0d,0x0a,0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}, + 0x00004550, /* DWORD nt_sig = IMAGE_NT_SIGNATURE */ +{ + /* IMAGE_FILE_HEADER filehdr */ + 0x014C, /*WORD Machine; */ + 0x0003, /*WORD NumberOfSections; */ + 0x00000000, /*DWORD TimeDateStamp; */ + 0x00000000, /*DWORD PointerToSymbolTable; */ + 0x00000000, /*DWORD NumberOfSymbols; */ + 0x00E0, /*WORD SizeOfOptionalHeader; */ + 0x030F /*WORD Characteristics; */ +},{ + /* IMAGE_OPTIONAL_HEADER opthdr */ + /* Standard fields. */ + 0x010B, /*WORD Magic; */ + 0x06, /*BYTE MajorLinkerVersion; */ + 0x00, /*BYTE MinorLinkerVersion; */ + 0x00000000, /*DWORD SizeOfCode; */ + 0x00000000, /*DWORD SizeOfInitializedData; */ + 0x00000000, /*DWORD SizeOfUninitializedData; */ + 0x00000000, /*DWORD AddressOfEntryPoint; */ + 0x00000000, /*DWORD BaseOfCode; */ + 0x00000000, /*DWORD BaseOfData; */ + + /* NT additional fields. */ + 0x00400000, /*DWORD ImageBase; */ + 0x00001000, /*DWORD SectionAlignment; */ + 0x00000200, /*DWORD FileAlignment; */ + 0x0004, /*WORD MajorOperatingSystemVersion; */ + 0x0000, /*WORD MinorOperatingSystemVersion; */ + 0x0000, /*WORD MajorImageVersion; */ + 0x0000, /*WORD MinorImageVersion; */ + 0x0004, /*WORD MajorSubsystemVersion; */ + 0x0000, /*WORD MinorSubsystemVersion; */ + 0x00000000, /*DWORD Win32VersionValue; */ + 0x00000000, /*DWORD SizeOfImage; */ + 0x00000200, /*DWORD SizeOfHeaders; */ + 0x00000000, /*DWORD CheckSum; */ + 0x0002, /*WORD Subsystem; */ + 0x0000, /*WORD DllCharacteristics; */ + 0x00100000, /*DWORD SizeOfStackReserve; */ + 0x00001000, /*DWORD SizeOfStackCommit; */ + 0x00100000, /*DWORD SizeOfHeapReserve; */ + 0x00001000, /*DWORD SizeOfHeapCommit; */ + 0x00000000, /*DWORD LoaderFlags; */ + 0x00000010, /*DWORD NumberOfRvaAndSizes; */ + + /* IMAGE_DATA_DIRECTORY DataDirectory[16]; */ + {{0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, + {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}, {0,0}} +}}; + +/* ------------------------------------------------------------- */ +/* internal temporary structures */ + +/* +#define IMAGE_SCN_CNT_CODE 0x00000020 +#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 +#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 +#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 +#define IMAGE_SCN_MEM_SHARED 0x10000000 +#define IMAGE_SCN_MEM_EXECUTE 0x20000000 +#define IMAGE_SCN_MEM_READ 0x40000000 +#define IMAGE_SCN_MEM_WRITE 0x80000000 +*/ + +enum { + sec_text = 0, + sec_data , + sec_bss , + sec_idata , + sec_other , + sec_rsrc , + sec_stab , + sec_reloc , + sec_last +}; + +ST_DATA DWORD pe_sec_flags[] = { + 0x60000020, /* ".text" , */ + 0xC0000040, /* ".data" , */ + 0xC0000080, /* ".bss" , */ + 0x40000040, /* ".idata" , */ + 0xE0000060, /* < other > , */ + 0x40000040, /* ".rsrc" , */ + 0x42000802, /* ".stab" , */ + 0x42000040, /* ".reloc" , */ +}; + +struct section_info { + int cls, ord; + char name[32]; + DWORD sh_addr; + DWORD sh_size; + DWORD sh_flags; + unsigned char *data; + DWORD data_size; + IMAGE_SECTION_HEADER ish; +}; + +struct import_symbol { + int sym_index; + int iat_index; + int thk_offset; +}; + +struct pe_import_info { + int dll_index; + int sym_count; + struct import_symbol **symbols; +}; + +struct pe_info { + TCCState *s1; + Section *reloc; + Section *thunk; + const char *filename; + int type; + DWORD sizeofheaders; + DWORD imagebase; + DWORD start_addr; + DWORD imp_offs; + DWORD imp_size; + DWORD iat_offs; + DWORD iat_size; + DWORD exp_offs; + DWORD exp_size; + struct section_info *sec_info; + int sec_count; + struct pe_import_info **imp_info; + int imp_count; +}; + +/* ------------------------------------------------------------- */ + +#define PE_NUL 0 +#define PE_DLL 1 +#define PE_GUI 2 +#define PE_EXE 3 + +void error_noabort(const char *, ...); + +#ifdef _WIN32 +void dbg_printf (const char *fmt, ...) +{ + char buffer[4000]; + va_list arg; + int x; + va_start(arg, fmt); + x = vsprintf (buffer, fmt, arg); + strcpy(buffer+x, "\n"); + OutputDebugString(buffer); +} +#endif + +/* --------------------------------------------*/ + +ST_FN const char* get_alt_symbol(char *buffer, const char *symbol) +{ + const char *p; + p = strrchr(symbol, '@'); + if (p && isnum(p[1]) && symbol[0] == '_') { /* stdcall decor */ + strcpy(buffer, symbol+1)[p-symbol-1] = 0; + } else if (symbol[0] != '_') { /* try non-ansi function */ + buffer[0] = '_', strcpy(buffer + 1, symbol); + } else if (0 == memcmp(symbol, "__imp__", 7)) { /* mingw 2.0 */ + strcpy(buffer, symbol + 6); + } else if (0 == memcmp(symbol, "_imp___", 7)) { /* mingw 3.7 */ + strcpy(buffer, symbol + 6); + } else { + return symbol; + } + return buffer; +} + +ST_FN int pe_find_import(TCCState * s1, const char *symbol) +{ + char buffer[200]; + const char *s; + int sym_index, n = 0; + do { + s = n ? get_alt_symbol(buffer, symbol) : symbol; + sym_index = find_elf_sym(s1->dynsymtab_section, s); + // printf("find %d %s\n", sym_index, s); + } while (0 == sym_index && ++n < 2); + return sym_index; +} + +#if defined _WIN32 || defined __CYGWIN__ + +#ifdef __CYGWIN__ +# include +# define LoadLibrary(s) dlopen(s, RTLD_NOW) +# define GetProcAddress(h,s) dlsym(h, s) +#else +# define dlclose(h) FreeLibrary(h) +#endif + +/* for the -run option: dynamically load symbol from dll */ +void *resolve_sym(struct TCCState *s1, const char *symbol, int type) +{ + char buffer[100]; + int sym_index, dll_index; + void *addr, **m; + DLLReference *dllref; + + sym_index = pe_find_import(s1, symbol); + if (0 == sym_index) + return NULL; + dll_index = ((Elf32_Sym *)s1->dynsymtab_section->data + sym_index)->st_value; + dllref = s1->loaded_dlls[dll_index-1]; + if ( !dllref->handle ) + { + dllref->handle = LoadLibrary(dllref->name); + } + addr = GetProcAddress(dllref->handle, symbol); + if (NULL == addr) + addr = GetProcAddress(dllref->handle, get_alt_symbol(buffer, symbol)); + + if (addr && STT_OBJECT == type) { + /* need to return a pointer to the address for data objects */ + m = (void**)tcc_malloc(sizeof addr), *m = addr, addr = m; +#ifdef MEM_DEBUG + /* yep, we don't free it */ + mem_cur_size -= sizeof (void*); +#endif + } + return addr; +} +#endif + +/*----------------------------------------------------------------------------*/ + +ST_FN int dynarray_assoc(void **pp, int n, int key) +{ + int i; + for (i = 0; i < n; ++i, ++pp) + if (key == **(int **) pp) + return i; + return -1; +} + +#if 0 +ST_FN DWORD umin(DWORD a, DWORD b) +{ + return a < b ? a : b; +} +#endif + +ST_FN DWORD umax(DWORD a, DWORD b) +{ + return a < b ? b : a; +} + +ST_FN void pe_fpad(FILE *fp, DWORD new_pos) +{ + DWORD pos = ftell(fp); + while (++pos <= new_pos) + fputc(0, fp); +} + +ST_FN DWORD pe_file_align(DWORD n) +{ + return (n + (0x200 - 1)) & ~(0x200 - 1); +} + +ST_FN DWORD pe_virtual_align(DWORD n) +{ + return (n + (0x1000 - 1)) & ~(0x1000 - 1); +} + +ST_FN void pe_align_section(Section *s, int a) +{ + int i = s->data_offset & (a-1); + if (i) + section_ptr_add(s, a - i); +} + +ST_FN void pe_set_datadir(int dir, DWORD addr, DWORD size) +{ + pe_header.opthdr.DataDirectory[dir].VirtualAddress = addr; + pe_header.opthdr.DataDirectory[dir].Size = size; +} + +/*----------------------------------------------------------------------------*/ +ST_FN int pe_write(struct pe_info *pe) +{ + int i; + FILE *op; + DWORD file_offset, r; + + op = fopen(pe->filename, "wb"); + if (NULL == op) { + error_noabort("could not write '%s': %s", pe->filename, strerror(errno)); + return 1; + } + + pe->sizeofheaders = pe_file_align( + sizeof pe_header + + pe->sec_count * sizeof (IMAGE_SECTION_HEADER) + ); + + file_offset = pe->sizeofheaders; + pe_fpad(op, file_offset); + + if (2 == pe->s1->verbose) + printf("-------------------------------" + "\n virt file size section" "\n"); + + for (i = 0; i < pe->sec_count; ++i) { + struct section_info *si = pe->sec_info + i; + const char *sh_name = si->name; + unsigned long addr = si->sh_addr - pe->imagebase; + unsigned long size = si->sh_size; + IMAGE_SECTION_HEADER *psh = &si->ish; + + if (2 == pe->s1->verbose) + printf("%6lx %6lx %6lx %s\n", + addr, file_offset, size, sh_name); + + switch (si->cls) { + case sec_text: + pe_header.opthdr.BaseOfCode = addr; + pe_header.opthdr.AddressOfEntryPoint = addr + pe->start_addr; + break; + + case sec_data: + pe_header.opthdr.BaseOfData = addr; + break; + + case sec_bss: + break; + + case sec_reloc: + pe_set_datadir(IMAGE_DIRECTORY_ENTRY_BASERELOC, addr, size); + break; + + case sec_rsrc: + pe_set_datadir(IMAGE_DIRECTORY_ENTRY_RESOURCE, addr, size); + break; + + case sec_stab: + break; + } + + if (pe->thunk == pe->s1->sections[si->ord]) { + if (pe->imp_size) { + pe_set_datadir(IMAGE_DIRECTORY_ENTRY_IMPORT, + pe->imp_offs + addr, pe->imp_size); + pe_set_datadir(IMAGE_DIRECTORY_ENTRY_IAT, + pe->iat_offs + addr, pe->iat_size); + } + if (pe->exp_size) { + pe_set_datadir(IMAGE_DIRECTORY_ENTRY_EXPORT, + pe->exp_offs + addr, pe->exp_size); + } + } + + strcpy((char*)psh->Name, sh_name); + + psh->Characteristics = pe_sec_flags[si->cls]; + psh->VirtualAddress = addr; + psh->Misc.VirtualSize = size; + pe_header.opthdr.SizeOfImage = + umax(pe_virtual_align(size + addr), pe_header.opthdr.SizeOfImage); + + if (si->data_size) { + psh->PointerToRawData = r = file_offset; + fwrite(si->data, 1, si->data_size, op); + file_offset = pe_file_align(file_offset + si->data_size); + psh->SizeOfRawData = file_offset - r; + pe_fpad(op, file_offset); + } + } + + // pe_header.filehdr.TimeDateStamp = time(NULL); + pe_header.filehdr.NumberOfSections = pe->sec_count; + pe_header.opthdr.SizeOfHeaders = pe->sizeofheaders; + pe_header.opthdr.ImageBase = pe->imagebase; + if (PE_DLL == pe->type) + pe_header.filehdr.Characteristics = 0x230E; + else if (PE_GUI != pe->type) + pe_header.opthdr.Subsystem = 3; + + fseek(op, SEEK_SET, 0); + fwrite(&pe_header, 1, sizeof pe_header, op); + for (i = 0; i < pe->sec_count; ++i) + fwrite(&pe->sec_info[i].ish, 1, sizeof(IMAGE_SECTION_HEADER), op); + fclose (op); + + if (2 == pe->s1->verbose) + printf("-------------------------------\n"); + if (pe->s1->verbose) + printf("<- %s (%lu bytes)\n", pe->filename, file_offset); + + return 0; +} + +/*----------------------------------------------------------------------------*/ + +ST_FN struct import_symbol *pe_add_import(struct pe_info *pe, int sym_index) +{ + int i; + int dll_index; + struct pe_import_info *p; + struct import_symbol *s; + + dll_index = ((Elf32_Sym *)pe->s1->dynsymtab_section->data + sym_index)->st_value; + if (0 == dll_index) + return NULL; + + i = dynarray_assoc ((void**)pe->imp_info, pe->imp_count, dll_index); + if (-1 != i) { + p = pe->imp_info[i]; + goto found_dll; + } + p = tcc_mallocz(sizeof *p); + p->dll_index = dll_index; + dynarray_add((void***)&pe->imp_info, &pe->imp_count, p); + +found_dll: + i = dynarray_assoc ((void**)p->symbols, p->sym_count, sym_index); + if (-1 != i) + return p->symbols[i]; + + s = tcc_mallocz(sizeof *s); + dynarray_add((void***)&p->symbols, &p->sym_count, s); + s->sym_index = sym_index; + return s; +} + +/*----------------------------------------------------------------------------*/ +ST_FN void pe_build_imports(struct pe_info *pe) +{ + int thk_ptr, ent_ptr, dll_ptr, sym_cnt, i; + DWORD rva_base = pe->thunk->sh_addr - pe->imagebase; + int ndlls = pe->imp_count; + + for (sym_cnt = i = 0; i < ndlls; ++i) + sym_cnt += pe->imp_info[i]->sym_count; + + if (0 == sym_cnt) + return; + + pe_align_section(pe->thunk, 16); + + pe->imp_offs = dll_ptr = pe->thunk->data_offset; + pe->imp_size = (ndlls + 1) * sizeof(struct pe_import_header); + pe->iat_offs = dll_ptr + pe->imp_size; + pe->iat_size = (sym_cnt + ndlls) * sizeof(DWORD); + section_ptr_add(pe->thunk, pe->imp_size + 2*pe->iat_size); + + thk_ptr = pe->iat_offs; + ent_ptr = pe->iat_offs + pe->iat_size; + + for (i = 0; i < pe->imp_count; ++i) { + struct pe_import_header *hdr; + int k, n, v; + struct pe_import_info *p = pe->imp_info[i]; + const char *name = pe->s1->loaded_dlls[p->dll_index-1]->name; + + /* put the dll name into the import header */ + v = put_elf_str(pe->thunk, name); + + hdr = (struct pe_import_header*)(pe->thunk->data + dll_ptr); + hdr->first_thunk = thk_ptr + rva_base; + hdr->first_entry = ent_ptr + rva_base; + hdr->lib_name_offset = v + rva_base; + + for (k = 0, n = p->sym_count; k <= n; ++k) { + if (k < n) { + DWORD iat_index = p->symbols[k]->iat_index; + int sym_index = p->symbols[k]->sym_index; + Elf32_Sym *imp_sym = (Elf32_Sym *)pe->s1->dynsymtab_section->data + sym_index; + Elf32_Sym *org_sym = (Elf32_Sym *)symtab_section->data + iat_index; + const char *name = pe->s1->dynsymtab_section->link->data + imp_sym->st_name; + + org_sym->st_value = thk_ptr; + org_sym->st_shndx = pe->thunk->sh_num; + v = pe->thunk->data_offset + rva_base; + section_ptr_add(pe->thunk, sizeof(WORD)); /* hint, not used */ + put_elf_str(pe->thunk, name); + + } else { + v = 0; /* last entry is zero */ + } + *(DWORD*)(pe->thunk->data+thk_ptr) = + *(DWORD*)(pe->thunk->data+ent_ptr) = v; + thk_ptr += sizeof (DWORD); + ent_ptr += sizeof (DWORD); + } + dll_ptr += sizeof(struct pe_import_header); + dynarray_reset(&p->symbols, &p->sym_count); + } + dynarray_reset(&pe->imp_info, &pe->imp_count); +} + +/* ------------------------------------------------------------- */ +/* + For now only functions are exported. Export of data + would work, but import requires compiler support to + do an additional indirection. + + For instance: + __declspec(dllimport) extern int something; + + needs to be translated to: + + *(int*)something +*/ + +ST_FN int sym_cmp(const void *va, const void *vb) +{ + const char *ca = ((const char **)va)[1]; + const char *cb = ((const char **)vb)[1]; + return strcmp(ca, cb); +} + +ST_FN void pe_build_exports(struct pe_info *pe) +{ + Elf32_Sym *sym; + int sym_index, sym_end; + DWORD rva_base, func_o, name_o, ord_o, str_o; + struct pe_export_header *hdr; + int sym_count, n, ord, *sorted, *sp; + + FILE *op; + char buf[MAX_PATH]; + const char *dllname; + const char *name; + + rva_base = pe->thunk->sh_addr - pe->imagebase; + sym_count = 0, n = 1, sorted = NULL, op = NULL; + + sym_end = symtab_section->data_offset / sizeof(Elf32_Sym); + for (sym_index = 1; sym_index < sym_end; ++sym_index) { + sym = (Elf32_Sym*)symtab_section->data + sym_index; + name = symtab_section->link->data + sym->st_name; + if ((sym->st_other & 1) + /* export only symbols from actually written sections */ + && pe->s1->sections[sym->st_shndx]->sh_addr) { + dynarray_add((void***)&sorted, &sym_count, (void*)n); + dynarray_add((void***)&sorted, &sym_count, (void*)name); + } + ++n; +#if 0 + if (sym->st_other & 1) + printf("export: %s\n", name); + if (sym->st_other & 2) + printf("stdcall: %s\n", name); +#endif + } + + if (0 == sym_count) + return; + sym_count /= 2; + + qsort (sorted, sym_count, 2 * sizeof sorted[0], sym_cmp); + pe_align_section(pe->thunk, 16); + dllname = tcc_basename(pe->filename); + + pe->exp_offs = pe->thunk->data_offset; + func_o = pe->exp_offs + sizeof(struct pe_export_header); + name_o = func_o + sym_count * sizeof (DWORD); + ord_o = name_o + sym_count * sizeof (DWORD); + str_o = ord_o + sym_count * sizeof(WORD); + + hdr = section_ptr_add(pe->thunk, str_o - pe->exp_offs); + hdr->Characteristics = 0; + hdr->Base = 1; + hdr->NumberOfFunctions = sym_count; + hdr->NumberOfNames = sym_count; + hdr->AddressOfFunctions = func_o + rva_base; + hdr->AddressOfNames = name_o + rva_base; + hdr->AddressOfNameOrdinals = ord_o + rva_base; + hdr->Name = str_o + rva_base; + put_elf_str(pe->thunk, dllname); + +#if 1 + /* automatically write exports to .def */ + strcpy(buf, pe->filename); + strcpy(tcc_fileextension(buf), ".def"); + op = fopen(buf, "w"); + if (NULL == op) { + error_noabort("could not create '%s': %s", buf, strerror(errno)); + } else { + fprintf(op, "LIBRARY %s\n\nEXPORTS\n", dllname); + if (pe->s1->verbose) + printf("<- %s (%d symbols)\n", buf, sym_count); + } +#endif + + for (sp = sorted, ord = 0; ord < sym_count; ++ord, sp += 2) + { + sym_index = sp[0], name = (const char *)sp[1]; + /* insert actual address later in pe_relocate_rva */ + put_elf_reloc(symtab_section, pe->thunk, + func_o, R_386_RELATIVE, sym_index); + *(DWORD*)(pe->thunk->data + name_o) + = pe->thunk->data_offset + rva_base; + *(WORD*)(pe->thunk->data + ord_o) + = ord; + put_elf_str(pe->thunk, name); + func_o += sizeof (DWORD); + name_o += sizeof (DWORD); + ord_o += sizeof (WORD); + + if (op) + fprintf(op, "%s\n", name); + } + pe->exp_size = pe->thunk->data_offset - pe->exp_offs; + tcc_free(sorted); +} + +/* ------------------------------------------------------------- */ +ST_FN void pe_build_reloc (struct pe_info *pe) +{ + DWORD offset, block_ptr, addr; + int count, i; + Elf32_Rel *rel, *rel_end; + Section *s = NULL, *sr; + + offset = addr = block_ptr = count = i = 0; + rel = rel_end = NULL; + + for(;;) { + if (rel < rel_end) { + int type = ELF32_R_TYPE(rel->r_info); + addr = rel->r_offset + s->sh_addr; + ++ rel; + if (type != R_386_32) + continue; + if (count == 0) { /* new block */ + block_ptr = pe->reloc->data_offset; + section_ptr_add(pe->reloc, sizeof(struct pe_reloc_header)); + offset = addr & 0xFFFFFFFF<<12; + } + if ((addr -= offset) < (1<<12)) { /* one block spans 4k addresses */ + WORD *wp = section_ptr_add(pe->reloc, sizeof (WORD)); + *wp = addr | IMAGE_REL_BASED_HIGHLOW<<12; + ++count; + continue; + } + -- rel; + + } else if (i < pe->sec_count) { + sr = (s = pe->s1->sections[pe->sec_info[i++].ord])->reloc; + if (sr) { + rel = (Elf32_Rel *)sr->data; + rel_end = (Elf32_Rel *)(sr->data + sr->data_offset); + } + continue; + } + + if (count) { + /* store the last block and ready for a new one */ + struct pe_reloc_header *hdr; + if (count & 1) /* align for DWORDS */ + section_ptr_add(pe->reloc, sizeof(WORD)), ++count; + hdr = (struct pe_reloc_header *)(pe->reloc->data + block_ptr); + hdr -> offset = offset - pe->imagebase; + hdr -> size = count * sizeof(WORD) + sizeof(struct pe_reloc_header); + count = 0; + } + + if (rel >= rel_end) + break; + } +} + +/* ------------------------------------------------------------- */ +ST_FN int pe_section_class(Section *s) +{ + int type, flags; + const char *name; + + type = s->sh_type; + flags = s->sh_flags; + name = s->name; + if (flags & SHF_ALLOC) { + if (type == SHT_PROGBITS) { + if (flags & SHF_EXECINSTR) + return sec_text; + if (flags & SHF_WRITE) + return sec_data; + if (0 == strcmp(name, ".rsrc")) + return sec_rsrc; + if (0 == strcmp(name, ".iedat")) + return sec_idata; + return sec_other; + } else if (type == SHT_NOBITS) { + if (flags & SHF_WRITE) + return sec_bss; + } + } else { + if (0 == strcmp(name, ".reloc")) + return sec_reloc; + if (0 == strncmp(name, ".stab", 5)) /* .stab and .stabstr */ + return sec_stab; + } + return -1; +} + +ST_FN int pe_assign_addresses (struct pe_info *pe) +{ + int i, k, o, c; + DWORD addr; + int *section_order; + struct section_info *si; + Section *s; + + // pe->thunk = new_section(pe->s1, ".iedat", SHT_PROGBITS, SHF_ALLOC); + + section_order = tcc_malloc(pe->s1->nb_sections * sizeof (int)); + for (o = k = 0 ; k < sec_last; ++k) { + for (i = 1; i < pe->s1->nb_sections; ++i) { + s = pe->s1->sections[i]; + if (k == pe_section_class(s)) { + // printf("%s %d\n", s->name, k); + s->sh_addr = pe->imagebase; + section_order[o++] = i; + } + } + } + + pe->sec_info = tcc_mallocz(o * sizeof (struct section_info)); + addr = pe->imagebase + 1; + + for (i = 0; i < o; ++i) + { + k = section_order[i]; + s = pe->s1->sections[k]; + c = pe_section_class(s); + si = &pe->sec_info[pe->sec_count]; + +#ifdef PE_MERGE_DATA + if (c == sec_bss && pe->sec_count && si[-1].cls == sec_data) { + /* append .bss to .data */ + s->sh_addr = addr = ((addr-1) | 15) + 1; + addr += s->data_offset; + si[-1].sh_size = addr - si[-1].sh_addr; + continue; + } +#endif + strcpy(si->name, s->name); + si->cls = c; + si->ord = k; + si->sh_addr = s->sh_addr = addr = pe_virtual_align(addr); + si->sh_flags = s->sh_flags; + + if (c == sec_data && NULL == pe->thunk) + pe->thunk = s; + + if (s == pe->thunk) { + pe_build_imports(pe); + pe_build_exports(pe); + } + + if (c == sec_reloc) + pe_build_reloc (pe); + + if (s->data_offset) + { + if (s->sh_type != SHT_NOBITS) { + si->data = s->data; + si->data_size = s->data_offset; + } + + addr += s->data_offset; + si->sh_size = s->data_offset; + ++pe->sec_count; + } + // printf("%08x %05x %s\n", si->sh_addr, si->sh_size, si->name); + } + +#if 0 + for (i = 1; i < pe->s1->nb_sections; ++i) { + Section *s = pe->s1->sections[i]; + int type = s->sh_type; + int flags = s->sh_flags; + printf("section %-16s %-10s %5x %s,%s,%s\n", + s->name, + type == SHT_PROGBITS ? "progbits" : + type == SHT_NOBITS ? "nobits" : + type == SHT_SYMTAB ? "symtab" : + type == SHT_STRTAB ? "strtab" : + type == SHT_REL ? "rel" : "???", + s->data_offset, + flags & SHF_ALLOC ? "alloc" : "", + flags & SHF_WRITE ? "write" : "", + flags & SHF_EXECINSTR ? "exec" : "" + ); + } + pe->s1->verbose = 2; +#endif + + tcc_free(section_order); + return 0; +} + +/* ------------------------------------------------------------- */ +ST_FN void pe_relocate_rva (struct pe_info *pe, Section *s) +{ + Section *sr = s->reloc; + Elf32_Rel *rel, *rel_end; + rel_end = (Elf32_Rel *)(sr->data + sr->data_offset); + for(rel = (Elf32_Rel *)sr->data; rel < rel_end; rel++) + if (ELF32_R_TYPE(rel->r_info) == R_386_RELATIVE) { + int sym_index = ELF32_R_SYM(rel->r_info); + DWORD addr = s->sh_addr; + if (sym_index) { + Elf32_Sym *sym = (Elf32_Sym *)symtab_section->data + sym_index; + addr = sym->st_value; + } + *(DWORD*)(s->data + rel->r_offset) += addr - pe->imagebase; + } +} + +/*----------------------------------------------------------------------------*/ +ST_FN int pe_check_symbols(struct pe_info *pe) +{ + Elf32_Sym *sym; + int sym_index, sym_end; + int ret = 0; + + pe_align_section(text_section, 8); + + sym_end = symtab_section->data_offset / sizeof(Elf32_Sym); + for (sym_index = 1; sym_index < sym_end; ++sym_index) { + + sym = (Elf32_Sym*)symtab_section->data + sym_index; + if (sym->st_shndx == SHN_UNDEF) { + + const char *name = symtab_section->link->data + sym->st_name; + unsigned type = ELF32_ST_TYPE(sym->st_info); + int imp_sym = pe_find_import(pe->s1, name); + struct import_symbol *is; + + if (0 == imp_sym) + goto not_found; + is = pe_add_import(pe, imp_sym); + if (!is) + goto not_found; + + if (type == STT_FUNC) { + unsigned long offset = is->thk_offset; + if (offset) { + /* got aliased symbol, like stricmp and _stricmp */ + + } else { + char buffer[100]; + + offset = text_section->data_offset; + /* add the 'jmp IAT[x]' instruction */ + *(WORD*)section_ptr_add(text_section, 8) = 0x25FF; + + /* add a helper symbol, will be patched later in + pe_build_imports */ + sprintf(buffer, "IAT.%s", name); + is->iat_index = put_elf_sym( + symtab_section, 0, sizeof(DWORD), + ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT), + 0, SHN_UNDEF, buffer); + put_elf_reloc(symtab_section, text_section, + offset + 2, R_386_32, is->iat_index); + is->thk_offset = offset; + } + + /* tcc_realloc might have altered sym's address */ + sym = (Elf32_Sym*)symtab_section->data + sym_index; + + /* patch the original symbol */ + sym->st_value = offset; + sym->st_shndx = text_section->sh_num; + sym->st_other &= ~1; /* do not export */ + continue; + } + + if (type == STT_OBJECT) { /* data, ptr to that should be */ + if (0 == is->iat_index) { + /* original symbol will be patched later in pe_build_imports */ + is->iat_index = sym_index; + continue; + } + } + + not_found: + error_noabort("undefined symbol '%s'", name); + ret = 1; + + } else if (pe->s1->rdynamic + && ELF32_ST_BIND(sym->st_info) != STB_LOCAL) { + /* if -rdynamic option, then export all non local symbols */ + sym->st_other |= 1; + } + } + return ret; +} + +/*----------------------------------------------------------------------------*/ +#ifdef PE_PRINT_SECTIONS +ST_FN void pe_print_section(FILE * f, Section * s) +{ + /* just if you'r curious */ + BYTE *p, *e, b; + int i, n, l, m; + p = s->data; + e = s->data + s->data_offset; + l = e - p; + + fprintf(f, "section \"%s\"", s->name); + if (s->link) + fprintf(f, "\nlink \"%s\"", s->link->name); + if (s->reloc) + fprintf(f, "\nreloc \"%s\"", s->reloc->name); + fprintf(f, "\nv_addr %08X", s->sh_addr); + fprintf(f, "\ncontents %08X", l); + fprintf(f, "\n\n"); + + if (s->sh_type == SHT_NOBITS) + return; + + if (0 == l) + return; + + if (s->sh_type == SHT_SYMTAB) + m = sizeof(Elf32_Sym); + if (s->sh_type == SHT_REL) + m = sizeof(Elf32_Rel); + else + m = 16; + + fprintf(f, "%-8s", "offset"); + for (i = 0; i < m; ++i) + fprintf(f, " %02x", i); + n = 56; + + if (s->sh_type == SHT_SYMTAB || s->sh_type == SHT_REL) { + const char *fields1[] = { + "name", + "value", + "size", + "bind", + "type", + "other", + "shndx", + NULL + }; + + const char *fields2[] = { + "offs", + "type", + "symb", + NULL + }; + + const char **p; + + if (s->sh_type == SHT_SYMTAB) + p = fields1, n = 106; + else + p = fields2, n = 58; + + for (i = 0; p[i]; ++i) + fprintf(f, "%6s", p[i]); + fprintf(f, " symbol"); + } + + fprintf(f, "\n"); + for (i = 0; i < n; ++i) + fprintf(f, "-"); + fprintf(f, "\n"); + + for (i = 0; i < l;) + { + fprintf(f, "%08X", i); + for (n = 0; n < m; ++n) { + if (n + i < l) + fprintf(f, " %02X", p[i + n]); + else + fprintf(f, " "); + } + + if (s->sh_type == SHT_SYMTAB) { + Elf32_Sym *sym = (Elf32_Sym *) (p + i); + const char *name = s->link->data + sym->st_name; + fprintf(f, " %04X %04X %04X %02X %02X %02X %04X \"%s\"", + sym->st_name, + sym->st_value, + sym->st_size, + ELF32_ST_BIND(sym->st_info), + ELF32_ST_TYPE(sym->st_info), + sym->st_other, sym->st_shndx, name); + + } else if (s->sh_type == SHT_REL) { + Elf32_Rel *rel = (Elf32_Rel *) (p + i); + Elf32_Sym *sym = + (Elf32_Sym *) s->link->data + ELF32_R_SYM(rel->r_info); + const char *name = s->link->link->data + sym->st_name; + fprintf(f, " %04X %02X %04X \"%s\"", + rel->r_offset, + ELF32_R_TYPE(rel->r_info), + ELF32_R_SYM(rel->r_info), name); + } else { + fprintf(f, " "); + for (n = 0; n < m; ++n) { + if (n + i < l) { + b = p[i + n]; + if (b < 32 || b >= 127) + b = '.'; + fprintf(f, "%c", b); + } + } + } + i += m; + fprintf(f, "\n"); + } + fprintf(f, "\n\n"); +} + +ST_FN void pe_print_sections(TCCState *s1, const char *fname) +{ + Section *s; + FILE *f; + int i; + f = fopen(fname, "wt"); + for (i = 1; i < s1->nb_sections; ++i) { + s = s1->sections[i]; + pe_print_section(f, s); + } + pe_print_section(f, s1->dynsymtab_section); + fclose(f); +} +#endif + +/* ------------------------------------------------------------- + * This is for compiled windows resources in 'coff' format + * as generated by 'windres.exe -O coff ...'. + */ + +PUB_FN int pe_test_res_file(void *v, int size) +{ + struct pe_rsrc_header *p = (struct pe_rsrc_header *)v; + return + size >= IMAGE_SIZEOF_FILE_HEADER + IMAGE_SIZEOF_SHORT_NAME /* = 28 */ + && p->filehdr.Machine == 0x014C + && 1 == p->filehdr.NumberOfSections + && 0 == strcmp(p->sectionhdr.Name, ".rsrc") + ; +} + +ST_FN int read_n(int fd, void *ptr, unsigned size) +{ + return size == read(fd, ptr, size); +} + +PUB_FN int pe_load_res_file(TCCState *s1, int fd) +{ + struct pe_rsrc_header hdr; + Section *rsrc_section; + int i, ret = -1; + BYTE *ptr; + + lseek (fd, 0, SEEK_SET); + if (!read_n(fd, &hdr, sizeof hdr)) + goto quit; + if (!pe_test_res_file(&hdr, sizeof hdr)) + goto quit; + + rsrc_section = new_section(s1, ".rsrc", SHT_PROGBITS, SHF_ALLOC); + ptr = section_ptr_add(rsrc_section, hdr.sectionhdr.SizeOfRawData); + lseek (fd, hdr.sectionhdr.PointerToRawData, SEEK_SET); + if (!read_n(fd, ptr, hdr.sectionhdr.SizeOfRawData)) + goto quit; + + lseek (fd, hdr.sectionhdr.PointerToRelocations, SEEK_SET); + for (i = 0; i < hdr.sectionhdr.NumberOfRelocations; ++i) + { + struct pe_rsrc_reloc rel; + if (!read_n(fd, &rel, sizeof rel)) + goto quit; + // printf("rsrc_reloc: %x %x %x\n", rel.offset, rel.size, rel.type); + if (rel.type != 7) /* DIR32NB */ + goto quit; + put_elf_reloc(symtab_section, rsrc_section, + rel.offset, R_386_RELATIVE, 0); + } + ret = 0; +quit: + if (ret) + error_noabort("unrecognized resource file format"); + return ret; +} + +/* ------------------------------------------------------------- */ +ST_FN char *trimfront(char *p) +{ + while (*p && (unsigned char)*p <= ' ') + ++p; + return p; +} + +ST_FN char *trimback(char *a, char *e) +{ + while (e > a && (unsigned char)e[-1] <= ' ') + --e; + *e = 0;; + return a; +} + +ST_FN char *get_line(char *line, int size, FILE *fp) +{ + if (NULL == fgets(line, size, fp)) + return NULL; + trimback(line, strchr(line, 0)); + return trimfront(line); +} + +/* ------------------------------------------------------------- */ +PUB_FN int pe_load_def_file(TCCState *s1, int fd) +{ + DLLReference *dllref; + int state = 0, ret = -1; + char line[400], dllname[80], *p; + FILE *fp = fdopen(dup(fd), "rb"); + + if (NULL == fp) + goto quit; + + for (;;) { + p = get_line(line, sizeof line, fp); + if (NULL == p) + break; + if (0 == *p || ';' == *p) + continue; + switch (state) { + case 0: + if (0 != strnicmp(p, "LIBRARY", 7)) + goto quit; + strcpy(dllname, trimfront(p+7)); + ++state; + continue; + + case 1: + if (0 != stricmp(p, "EXPORTS")) + goto quit; + ++state; + continue; + + case 2: + dllref = tcc_mallocz(sizeof(DLLReference) + strlen(dllname)); + strcpy(dllref->name, dllname); + dynarray_add((void ***) &s1->loaded_dlls, &s1->nb_loaded_dlls, dllref); + ++state; + + default: + add_elf_sym(s1->dynsymtab_section, + s1->nb_loaded_dlls, 0, + ELF32_ST_INFO(STB_GLOBAL, STT_FUNC), 0, + text_section->sh_num, p); + continue; + } + } + ret = 0; +quit: + if (fp) + fclose(fp); + if (ret) + error_noabort("unrecognized export definition file format"); + return ret; +} + +/* ------------------------------------------------------------- */ + +ST_FN void pe_add_runtime_ex(TCCState *s1, struct pe_info *pe) +{ + const char *start_symbol; + unsigned long addr = 0; + int pe_type = 0; + + if (find_elf_sym(symtab_section, "_WinMain@16")) + pe_type = PE_GUI; + else + if (TCC_OUTPUT_DLL == s1->output_type) { + pe_type = PE_DLL; + /* need this for 'tccelf.c:relocate_section()' */ + s1->output_type = TCC_OUTPUT_EXE; + } + + start_symbol = + TCC_OUTPUT_MEMORY == s1->output_type + ? PE_GUI == pe_type ? "_runwinmain" : NULL + : PE_DLL == pe_type ? "__dllstart@12" + : PE_GUI == pe_type ? "_winstart" : "_start" + ; + + /* grab the startup code from libtcc1 */ + if (start_symbol) + add_elf_sym(symtab_section, + 0, 0, + ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, + SHN_UNDEF, start_symbol); + + if (0 == s1->nostdlib) { + tcc_add_library(s1, "tcc1"); +#ifdef __CYGWIN__ + tcc_add_library(s1, "cygwin1"); +#else + tcc_add_library(s1, "msvcrt"); +#endif + tcc_add_library(s1, "kernel32"); + if (PE_DLL == pe_type || PE_GUI == pe_type) { + tcc_add_library(s1, "user32"); + tcc_add_library(s1, "gdi32"); + } + } + + if (start_symbol) { + addr = (unsigned long)tcc_get_symbol_err(s1, start_symbol); + if (s1->output_type == TCC_OUTPUT_MEMORY && addr) + /* for -run GUI's, put '_runwinmain' instead of 'main' */ + add_elf_sym(symtab_section, + addr, 0, + ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, + text_section->sh_num, "main"); + } + + if (pe) { + pe->type = pe_type; + pe->start_addr = addr; + } +} + +PUB_FN void pe_add_runtime(TCCState *s1) +{ + pe_add_runtime_ex(s1, NULL); +} + +PUB_FN int pe_output_file(TCCState * s1, const char *filename) +{ + int ret; + struct pe_info pe; + int i; + + memset(&pe, 0, sizeof pe); + pe.filename = filename; + pe.s1 = s1; + + pe_add_runtime_ex(s1, &pe); + relocate_common_syms(); /* assign bss adresses */ + tcc_add_linker_symbols(s1); + + ret = pe_check_symbols(&pe); + if (0 == ret) { + if (PE_DLL == pe.type) { + pe.reloc = new_section(pe.s1, ".reloc", SHT_PROGBITS, 0); + pe.imagebase = 0x10000000; + } else { + pe.imagebase = 0x00400000; + } + pe_assign_addresses(&pe); + relocate_syms(s1, 0); + for (i = 1; i < s1->nb_sections; ++i) { + Section *s = s1->sections[i]; + if (s->reloc) { + relocate_section(s1, s); + pe_relocate_rva(&pe, s); + } + } + if (s1->nb_errors) + ret = 1; + else + ret = pe_write(&pe); + tcc_free(pe.sec_info); + } + +#ifdef PE_PRINT_SECTIONS + pe_print_sections(s1, "tcc.log"); +#endif + return ret; +} + +/* ------------------------------------------------------------- */ +#endif /* def TCC_TARGET_PE */ +/* ------------------------------------------------------------- */ diff --git a/05/tcc-0.9.25/tccpp.c b/05/tcc-0.9.25/tccpp.c new file mode 100644 index 0000000..ff17d8b --- /dev/null +++ b/05/tcc-0.9.25/tccpp.c @@ -0,0 +1,2935 @@ +/* + * TCC - Tiny C Compiler + * + * Copyright (c) 2001-2004 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +static const char tcc_keywords[] = +#define DEF(id, str) str "\0" +#include "tcctok.h" +#undef DEF +; + +/* WARNING: the content of this string encodes token numbers */ +static char tok_two_chars[] = "<=\236>=\235!=\225&&\240||\241++\244--\242==\224<<\1>>\2+=\253-=\255*=\252/=\257%=\245&=\246^=\336|=\374->\313..\250##\266"; + +/* true if isid(c) || isnum(c) */ +static unsigned char isidnum_table[256-CH_EOF]; + + +struct macro_level { + struct macro_level *prev; + int *p; +}; + +static void next_nomacro(void); +static void next_nomacro_spc(void); +static void macro_subst(TokenString *tok_str, Sym **nested_list, + const int *macro_str, struct macro_level **can_read_stream); + + +/* allocate a new token */ +static TokenSym *tok_alloc_new(TokenSym **pts, const char *str, int len) +{ + TokenSym *ts, **ptable; + int i; + + if (tok_ident >= SYM_FIRST_ANOM) + error("memory full"); + + /* expand token table if needed */ + i = tok_ident - TOK_IDENT; + if ((i % TOK_ALLOC_INCR) == 0) { + ptable = tcc_realloc(table_ident, (i + TOK_ALLOC_INCR) * sizeof(TokenSym *)); + if (!ptable) + error("memory full"); + table_ident = ptable; + } + + ts = tcc_malloc(sizeof(TokenSym) + len); + table_ident[i] = ts; + ts->tok = tok_ident++; + ts->sym_define = NULL; + ts->sym_label = NULL; + ts->sym_struct = NULL; + ts->sym_identifier = NULL; + ts->len = len; + ts->hash_next = NULL; + memcpy(ts->str, str, len); + ts->str[len] = '\0'; + *pts = ts; + return ts; +} + +#define TOK_HASH_INIT 1 +#define TOK_HASH_FUNC(h, c) ((h) * 263 + (c)) + +/* find a token and add it if not found */ +static TokenSym *tok_alloc(const char *str, int len) +{ + TokenSym *ts, **pts; + int i; + unsigned int h; + + h = TOK_HASH_INIT; + for(i=0;ilen == len && !memcmp(ts->str, str, len)) + return ts; + pts = &(ts->hash_next); + } + return tok_alloc_new(pts, str, len); +} + +/* XXX: buffer overflow */ +/* XXX: float tokens */ +char *get_tok_str(int v, CValue *cv) +{ + static char buf[STRING_MAX_SIZE + 1]; + static CString cstr_buf; + CString *cstr; + unsigned char *q; + char *p; + int i, len; + + /* NOTE: to go faster, we give a fixed buffer for small strings */ + cstr_reset(&cstr_buf); + cstr_buf.data = buf; + cstr_buf.size_allocated = sizeof(buf); + p = buf; + + switch(v) { + case TOK_CINT: + case TOK_CUINT: + /* XXX: not quite exact, but only useful for testing */ + sprintf(p, "%u", cv->ui); + break; + case TOK_CLLONG: + case TOK_CULLONG: + /* XXX: not quite exact, but only useful for testing */ + sprintf(p, "%Lu", cv->ull); + break; + case TOK_LCHAR: + cstr_ccat(&cstr_buf, 'L'); + case TOK_CCHAR: + cstr_ccat(&cstr_buf, '\''); + add_char(&cstr_buf, cv->i); + cstr_ccat(&cstr_buf, '\''); + cstr_ccat(&cstr_buf, '\0'); + break; + case TOK_PPNUM: + cstr = cv->cstr; + len = cstr->size - 1; + for(i=0;idata)[i]); + cstr_ccat(&cstr_buf, '\0'); + break; + case TOK_LSTR: + cstr_ccat(&cstr_buf, 'L'); + case TOK_STR: + cstr = cv->cstr; + cstr_ccat(&cstr_buf, '\"'); + if (v == TOK_STR) { + len = cstr->size - 1; + for(i=0;idata)[i]); + } else { + len = (cstr->size / sizeof(nwchar_t)) - 1; + for(i=0;idata)[i]); + } + cstr_ccat(&cstr_buf, '\"'); + cstr_ccat(&cstr_buf, '\0'); + break; + case TOK_LT: + v = '<'; + goto addv; + case TOK_GT: + v = '>'; + goto addv; + case TOK_DOTS: + return strcpy(p, "..."); + case TOK_A_SHL: + return strcpy(p, "<<="); + case TOK_A_SAR: + return strcpy(p, ">>="); + default: + if (v < TOK_IDENT) { + /* search in two bytes table */ + q = tok_two_chars; + while (*q) { + if (q[2] == v) { + *p++ = q[0]; + *p++ = q[1]; + *p = '\0'; + return buf; + } + q += 3; + } + addv: + *p++ = v; + *p = '\0'; + } else if (v < tok_ident) { + return table_ident[v - TOK_IDENT]->str; + } else if (v >= SYM_FIRST_ANOM) { + /* special name for anonymous symbol */ + sprintf(p, "L.%u", v - SYM_FIRST_ANOM); + } else { + /* should never happen */ + return NULL; + } + break; + } + return cstr_buf.data; +} + +/* fill input buffer and peek next char */ +static int tcc_peekc_slow(BufferedFile *bf) +{ + int len; + /* only tries to read if really end of buffer */ + if (bf->buf_ptr >= bf->buf_end) { + if (bf->fd != -1) { +#if defined(PARSE_DEBUG) + len = 8; +#else + len = IO_BUF_SIZE; +#endif + len = read(bf->fd, bf->buffer, len); + if (len < 0) + len = 0; + } else { + len = 0; + } + total_bytes += len; + bf->buf_ptr = bf->buffer; + bf->buf_end = bf->buffer + len; + *bf->buf_end = CH_EOB; + } + if (bf->buf_ptr < bf->buf_end) { + return bf->buf_ptr[0]; + } else { + bf->buf_ptr = bf->buf_end; + return CH_EOF; + } +} + +/* return the current character, handling end of block if necessary + (but not stray) */ +static int handle_eob(void) +{ + return tcc_peekc_slow(file); +} + +/* read next char from current input file and handle end of input buffer */ +static inline void inp(void) +{ + ch = *(++(file->buf_ptr)); + /* end of buffer/file handling */ + if (ch == CH_EOB) + ch = handle_eob(); +} + +/* handle '\[\r]\n' */ +static int handle_stray_noerror(void) +{ + while (ch == '\\') { + inp(); + if (ch == '\n') { + file->line_num++; + inp(); + } else if (ch == '\r') { + inp(); + if (ch != '\n') + goto fail; + file->line_num++; + inp(); + } else { + fail: + return 1; + } + } + return 0; +} + +static void handle_stray(void) +{ + if (handle_stray_noerror()) + error("stray '\\' in program"); +} + +/* skip the stray and handle the \\n case. Output an error if + incorrect char after the stray */ +static int handle_stray1(uint8_t *p) +{ + int c; + + if (p >= file->buf_end) { + file->buf_ptr = p; + c = handle_eob(); + p = file->buf_ptr; + if (c == '\\') + goto parse_stray; + } else { + parse_stray: + file->buf_ptr = p; + ch = *p; + handle_stray(); + p = file->buf_ptr; + c = *p; + } + return c; +} + +/* handle just the EOB case, but not stray */ +#define PEEKC_EOB(c, p)\ +{\ + p++;\ + c = *p;\ + if (c == '\\') {\ + file->buf_ptr = p;\ + c = handle_eob();\ + p = file->buf_ptr;\ + }\ +} + +/* handle the complicated stray case */ +#define PEEKC(c, p)\ +{\ + p++;\ + c = *p;\ + if (c == '\\') {\ + c = handle_stray1(p);\ + p = file->buf_ptr;\ + }\ +} + +/* input with '\[\r]\n' handling. Note that this function cannot + handle other characters after '\', so you cannot call it inside + strings or comments */ +static void minp(void) +{ + inp(); + if (ch == '\\') + handle_stray(); +} + + +/* single line C++ comments */ +static uint8_t *parse_line_comment(uint8_t *p) +{ + int c; + + p++; + for(;;) { + c = *p; + redo: + if (c == '\n' || c == CH_EOF) { + break; + } else if (c == '\\') { + file->buf_ptr = p; + c = handle_eob(); + p = file->buf_ptr; + if (c == '\\') { + PEEKC_EOB(c, p); + if (c == '\n') { + file->line_num++; + PEEKC_EOB(c, p); + } else if (c == '\r') { + PEEKC_EOB(c, p); + if (c == '\n') { + file->line_num++; + PEEKC_EOB(c, p); + } + } + } else { + goto redo; + } + } else { + p++; + } + } + return p; +} + +/* C comments */ +static uint8_t *parse_comment(uint8_t *p) +{ + int c; + + p++; + for(;;) { + /* fast skip loop */ + for(;;) { + c = *p; + if (c == '\n' || c == '*' || c == '\\') + break; + p++; + c = *p; + if (c == '\n' || c == '*' || c == '\\') + break; + p++; + } + /* now we can handle all the cases */ + if (c == '\n') { + file->line_num++; + p++; + } else if (c == '*') { + p++; + for(;;) { + c = *p; + if (c == '*') { + p++; + } else if (c == '/') { + goto end_of_comment; + } else if (c == '\\') { + file->buf_ptr = p; + c = handle_eob(); + p = file->buf_ptr; + if (c == '\\') { + /* skip '\[\r]\n', otherwise just skip the stray */ + while (c == '\\') { + PEEKC_EOB(c, p); + if (c == '\n') { + file->line_num++; + PEEKC_EOB(c, p); + } else if (c == '\r') { + PEEKC_EOB(c, p); + if (c == '\n') { + file->line_num++; + PEEKC_EOB(c, p); + } + } else { + goto after_star; + } + } + } + } else { + break; + } + } + after_star: ; + } else { + /* stray, eob or eof */ + file->buf_ptr = p; + c = handle_eob(); + p = file->buf_ptr; + if (c == CH_EOF) { + error("unexpected end of file in comment"); + } else if (c == '\\') { + p++; + } + } + } + end_of_comment: + p++; + return p; +} + +#define cinp minp + +static inline void skip_spaces(void) +{ + while (is_space(ch)) + cinp(); +} + +static inline int check_space(int t, int *spc) +{ + if (is_space(t)) { + if (*spc) + return 1; + *spc = 1; + } else + *spc = 0; + return 0; +} + +/* parse a string without interpreting escapes */ +static uint8_t *parse_pp_string(uint8_t *p, + int sep, CString *str) +{ + int c; + p++; + for(;;) { + c = *p; + if (c == sep) { + break; + } else if (c == '\\') { + file->buf_ptr = p; + c = handle_eob(); + p = file->buf_ptr; + if (c == CH_EOF) { + unterminated_string: + /* XXX: indicate line number of start of string */ + error("missing terminating %c character", sep); + } else if (c == '\\') { + /* escape : just skip \[\r]\n */ + PEEKC_EOB(c, p); + if (c == '\n') { + file->line_num++; + p++; + } else if (c == '\r') { + PEEKC_EOB(c, p); + if (c != '\n') + expect("'\n' after '\r'"); + file->line_num++; + p++; + } else if (c == CH_EOF) { + goto unterminated_string; + } else { + if (str) { + cstr_ccat(str, '\\'); + cstr_ccat(str, c); + } + p++; + } + } + } else if (c == '\n') { + file->line_num++; + goto add_char; + } else if (c == '\r') { + PEEKC_EOB(c, p); + if (c != '\n') { + if (str) + cstr_ccat(str, '\r'); + } else { + file->line_num++; + goto add_char; + } + } else { + add_char: + if (str) + cstr_ccat(str, c); + p++; + } + } + p++; + return p; +} + +/* skip block of text until #else, #elif or #endif. skip also pairs of + #if/#endif */ +void preprocess_skip(void) +{ + int a, start_of_line, c, in_warn_or_error; + uint8_t *p; + + p = file->buf_ptr; + a = 0; +redo_start: + start_of_line = 1; + in_warn_or_error = 0; + for(;;) { + redo_no_start: + c = *p; + switch(c) { + case ' ': + case '\t': + case '\f': + case '\v': + case '\r': + p++; + goto redo_no_start; + case '\n': + file->line_num++; + p++; + goto redo_start; + case '\\': + file->buf_ptr = p; + c = handle_eob(); + if (c == CH_EOF) { + expect("#endif"); + } else if (c == '\\') { + ch = file->buf_ptr[0]; + handle_stray_noerror(); + } + p = file->buf_ptr; + goto redo_no_start; + /* skip strings */ + case '\"': + case '\'': + if (in_warn_or_error) + goto _default; + p = parse_pp_string(p, c, NULL); + break; + /* skip comments */ + case '/': + if (in_warn_or_error) + goto _default; + file->buf_ptr = p; + ch = *p; + minp(); + p = file->buf_ptr; + if (ch == '*') { + p = parse_comment(p); + } else if (ch == '/') { + p = parse_line_comment(p); + } + break; + case '#': + p++; + if (start_of_line) { + file->buf_ptr = p; + next_nomacro(); + p = file->buf_ptr; + if (a == 0 && + (tok == TOK_ELSE || tok == TOK_ELIF || tok == TOK_ENDIF)) + goto the_end; + if (tok == TOK_IF || tok == TOK_IFDEF || tok == TOK_IFNDEF) + a++; + else if (tok == TOK_ENDIF) + a--; + else if( tok == TOK_ERROR || tok == TOK_WARNING) + in_warn_or_error = 1; + } + break; +_default: + default: + p++; + break; + } + start_of_line = 0; + } + the_end: ; + file->buf_ptr = p; +} + +/* ParseState handling */ + +/* XXX: currently, no include file info is stored. Thus, we cannot display + accurate messages if the function or data definition spans multiple + files */ + +/* save current parse state in 's' */ +void save_parse_state(ParseState *s) +{ + s->line_num = file->line_num; + s->macro_ptr = macro_ptr; + s->tok = tok; + s->tokc = tokc; +} + +/* restore parse state from 's' */ +void restore_parse_state(ParseState *s) +{ + file->line_num = s->line_num; + macro_ptr = s->macro_ptr; + tok = s->tok; + tokc = s->tokc; +} + +/* return the number of additional 'ints' necessary to store the + token */ +static inline int tok_ext_size(int t) +{ + switch(t) { + /* 4 bytes */ + case TOK_CINT: + case TOK_CUINT: + case TOK_CCHAR: + case TOK_LCHAR: + case TOK_CFLOAT: + case TOK_LINENUM: + return 1; + case TOK_STR: + case TOK_LSTR: + case TOK_PPNUM: + error("unsupported token"); + return 1; + case TOK_CDOUBLE: + case TOK_CLLONG: + case TOK_CULLONG: + return 2; + case TOK_CLDOUBLE: + return LDOUBLE_SIZE / 4; + default: + return 0; + } +} + +/* token string handling */ + +static inline void tok_str_new(TokenString *s) +{ + s->str = NULL; + s->len = 0; + s->allocated_len = 0; + s->last_line_num = -1; +} + +static void tok_str_free(int *str) +{ + tcc_free(str); +} + +static int *tok_str_realloc(TokenString *s) +{ + int *str, len; + + if (s->allocated_len == 0) { + len = 8; + } else { + len = s->allocated_len * 2; + } + str = tcc_realloc(s->str, len * sizeof(int)); + if (!str) + error("memory full"); + s->allocated_len = len; + s->str = str; + return str; +} + +static void tok_str_add(TokenString *s, int t) +{ + int len, *str; + + len = s->len; + str = s->str; + if (len >= s->allocated_len) + str = tok_str_realloc(s); + str[len++] = t; + s->len = len; +} + +static void tok_str_add2(TokenString *s, int t, CValue *cv) +{ + int len, *str; + + len = s->len; + str = s->str; + + /* allocate space for worst case */ + if (len + TOK_MAX_SIZE > s->allocated_len) + str = tok_str_realloc(s); + str[len++] = t; + switch(t) { + case TOK_CINT: + case TOK_CUINT: + case TOK_CCHAR: + case TOK_LCHAR: + case TOK_CFLOAT: + case TOK_LINENUM: + str[len++] = cv->tab[0]; + break; + case TOK_PPNUM: + case TOK_STR: + case TOK_LSTR: + { + int nb_words; + CString *cstr; + + nb_words = (sizeof(CString) + cv->cstr->size + 3) >> 2; + while ((len + nb_words) > s->allocated_len) + str = tok_str_realloc(s); + cstr = (CString *)(str + len); + cstr->data = NULL; + cstr->size = cv->cstr->size; + cstr->data_allocated = NULL; + cstr->size_allocated = cstr->size; + memcpy((char *)cstr + sizeof(CString), + cv->cstr->data, cstr->size); + len += nb_words; + } + break; + case TOK_CDOUBLE: + case TOK_CLLONG: + case TOK_CULLONG: +#if LDOUBLE_SIZE == 8 + case TOK_CLDOUBLE: +#endif + str[len++] = cv->tab[0]; + str[len++] = cv->tab[1]; + break; +#if LDOUBLE_SIZE == 12 + case TOK_CLDOUBLE: + str[len++] = cv->tab[0]; + str[len++] = cv->tab[1]; + str[len++] = cv->tab[2]; +#elif LDOUBLE_SIZE == 16 + case TOK_CLDOUBLE: + str[len++] = cv->tab[0]; + str[len++] = cv->tab[1]; + str[len++] = cv->tab[2]; + str[len++] = cv->tab[3]; +#elif LDOUBLE_SIZE != 8 +#error add long double size support +#endif + break; + default: + break; + } + s->len = len; +} + +/* add the current parse token in token string 's' */ +static void tok_str_add_tok(TokenString *s) +{ + CValue cval; + + /* save line number info */ + if (file->line_num != s->last_line_num) { + s->last_line_num = file->line_num; + cval.i = s->last_line_num; + tok_str_add2(s, TOK_LINENUM, &cval); + } + tok_str_add2(s, tok, &tokc); +} + +#if LDOUBLE_SIZE == 16 +#define LDOUBLE_GET(p, cv) \ + cv.tab[0] = p[0]; \ + cv.tab[1] = p[1]; \ + cv.tab[2] = p[2]; \ + cv.tab[3] = p[3]; +#elif LDOUBLE_SIZE == 12 +#define LDOUBLE_GET(p, cv) \ + cv.tab[0] = p[0]; \ + cv.tab[1] = p[1]; \ + cv.tab[2] = p[2]; +#elif LDOUBLE_SIZE == 8 +#define LDOUBLE_GET(p, cv) \ + cv.tab[0] = p[0]; \ + cv.tab[1] = p[1]; +#else +#error add long double size support +#endif + + +/* get a token from an integer array and increment pointer + accordingly. we code it as a macro to avoid pointer aliasing. */ +#define TOK_GET(t, p, cv) \ +{ \ + t = *p++; \ + switch(t) { \ + case TOK_CINT: \ + case TOK_CUINT: \ + case TOK_CCHAR: \ + case TOK_LCHAR: \ + case TOK_CFLOAT: \ + case TOK_LINENUM: \ + cv.tab[0] = *p++; \ + break; \ + case TOK_STR: \ + case TOK_LSTR: \ + case TOK_PPNUM: \ + cv.cstr = (CString *)p; \ + cv.cstr->data = (char *)p + sizeof(CString);\ + p += (sizeof(CString) + cv.cstr->size + 3) >> 2;\ + break; \ + case TOK_CDOUBLE: \ + case TOK_CLLONG: \ + case TOK_CULLONG: \ + cv.tab[0] = p[0]; \ + cv.tab[1] = p[1]; \ + p += 2; \ + break; \ + case TOK_CLDOUBLE: \ + LDOUBLE_GET(p, cv); \ + p += LDOUBLE_SIZE / 4; \ + break; \ + default: \ + break; \ + } \ +} + +/* defines handling */ +static inline void define_push(int v, int macro_type, int *str, Sym *first_arg) +{ + Sym *s; + + s = sym_push2(&define_stack, v, macro_type, (long)str); + s->next = first_arg; + table_ident[v - TOK_IDENT]->sym_define = s; +} + +/* undefined a define symbol. Its name is just set to zero */ +static void define_undef(Sym *s) +{ + int v; + v = s->v; + if (v >= TOK_IDENT && v < tok_ident) + table_ident[v - TOK_IDENT]->sym_define = NULL; + s->v = 0; +} + +static inline Sym *define_find(int v) +{ + v -= TOK_IDENT; + if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT)) + return NULL; + return table_ident[v]->sym_define; +} + +/* free define stack until top reaches 'b' */ +static void free_defines(Sym *b) +{ + Sym *top, *top1; + int v; + + top = define_stack; + while (top != b) { + top1 = top->prev; + /* do not free args or predefined defines */ + if (top->c) + tok_str_free((int *)top->c); + v = top->v; + if (v >= TOK_IDENT && v < tok_ident) + table_ident[v - TOK_IDENT]->sym_define = NULL; + sym_free(top); + top = top1; + } + define_stack = b; +} + +/* label lookup */ +static Sym *label_find(int v) +{ + v -= TOK_IDENT; + if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT)) + return NULL; + return table_ident[v]->sym_label; +} + +static Sym *label_push(Sym **ptop, int v, int flags) +{ + Sym *s, **ps; + s = sym_push2(ptop, v, 0, 0); + s->r = flags; + ps = &table_ident[v - TOK_IDENT]->sym_label; + if (ptop == &global_label_stack) { + /* modify the top most local identifier, so that + sym_identifier will point to 's' when popped */ + while (*ps != NULL) + ps = &(*ps)->prev_tok; + } + s->prev_tok = *ps; + *ps = s; + return s; +} + +/* pop labels until element last is reached. Look if any labels are + undefined. Define symbols if '&&label' was used. */ +static void label_pop(Sym **ptop, Sym *slast) +{ + Sym *s, *s1; + for(s = *ptop; s != slast; s = s1) { + s1 = s->prev; + if (s->r == LABEL_DECLARED) { + warning("label '%s' declared but not used", get_tok_str(s->v, NULL)); + } else if (s->r == LABEL_FORWARD) { + error("label '%s' used but not defined", + get_tok_str(s->v, NULL)); + } else { + if (s->c) { + /* define corresponding symbol. A size of + 1 is put. */ + put_extern_sym(s, cur_text_section, (long)s->next, 1); + } + } + /* remove label */ + table_ident[s->v - TOK_IDENT]->sym_label = s->prev_tok; + sym_free(s); + } + *ptop = slast; +} + +/* eval an expression for #if/#elif */ +static int expr_preprocess(void) +{ + int c, t; + TokenString str; + + tok_str_new(&str); + while (tok != TOK_LINEFEED && tok != TOK_EOF) { + next(); /* do macro subst */ + if (tok == TOK_DEFINED) { + next_nomacro(); + t = tok; + if (t == '(') + next_nomacro(); + c = define_find(tok) != 0; + if (t == '(') + next_nomacro(); + tok = TOK_CINT; + tokc.i = c; + } else if (tok >= TOK_IDENT) { + /* if undefined macro */ + tok = TOK_CINT; + tokc.i = 0; + } + tok_str_add_tok(&str); + } + tok_str_add(&str, -1); /* simulate end of file */ + tok_str_add(&str, 0); + /* now evaluate C constant expression */ + macro_ptr = str.str; + next(); + c = expr_const(); + macro_ptr = NULL; + tok_str_free(str.str); + return c != 0; +} + +#if defined(PARSE_DEBUG) || defined(PP_DEBUG) +static void tok_print(int *str) +{ + int t; + CValue cval; + + printf("<"); + while (1) { + TOK_GET(t, str, cval); + if (!t) + break; + printf("%s", get_tok_str(t, &cval)); + } + printf(">\n"); +} +#endif + +/* parse after #define */ +static void parse_define(void) +{ + Sym *s, *first, **ps; + int v, t, varg, is_vaargs, spc; + TokenString str; + + v = tok; + if (v < TOK_IDENT) + error("invalid macro name '%s'", get_tok_str(tok, &tokc)); + /* XXX: should check if same macro (ANSI) */ + first = NULL; + t = MACRO_OBJ; + /* '(' must be just after macro definition for MACRO_FUNC */ + next_nomacro_spc(); + if (tok == '(') { + next_nomacro(); + ps = &first; + while (tok != ')') { + varg = tok; + next_nomacro(); + is_vaargs = 0; + if (varg == TOK_DOTS) { + varg = TOK___VA_ARGS__; + is_vaargs = 1; + } else if (tok == TOK_DOTS && gnu_ext) { + is_vaargs = 1; + next_nomacro(); + } + if (varg < TOK_IDENT) + error("badly punctuated parameter list"); + s = sym_push2(&define_stack, varg | SYM_FIELD, is_vaargs, 0); + *ps = s; + ps = &s->next; + if (tok != ',') + break; + next_nomacro(); + } + if (tok == ')') + next_nomacro_spc(); + t = MACRO_FUNC; + } + tok_str_new(&str); + spc = 2; + /* EOF testing necessary for '-D' handling */ + while (tok != TOK_LINEFEED && tok != TOK_EOF) { + /* remove spaces around ## and after '#' */ + if (TOK_TWOSHARPS == tok) { + if (1 == spc) + --str.len; + spc = 2; + } else if ('#' == tok) { + spc = 2; + } else if (check_space(tok, &spc)) { + goto skip; + } + tok_str_add2(&str, tok, &tokc); + skip: + next_nomacro_spc(); + } + if (spc == 1) + --str.len; /* remove trailing space */ + tok_str_add(&str, 0); +#ifdef PP_DEBUG + printf("define %s %d: ", get_tok_str(v, NULL), t); + tok_print(str.str); +#endif + define_push(v, t, str.str, first); +} + +static inline int hash_cached_include(int type, const char *filename) +{ + const unsigned char *s; + unsigned int h; + + h = TOK_HASH_INIT; + h = TOK_HASH_FUNC(h, type); + s = filename; + while (*s) { + h = TOK_HASH_FUNC(h, *s); + s++; + } + h &= (CACHED_INCLUDES_HASH_SIZE - 1); + return h; +} + +/* XXX: use a token or a hash table to accelerate matching ? */ +static CachedInclude *search_cached_include(TCCState *s1, + int type, const char *filename) +{ + CachedInclude *e; + int i, h; + h = hash_cached_include(type, filename); + i = s1->cached_includes_hash[h]; + for(;;) { + if (i == 0) + break; + e = s1->cached_includes[i - 1]; + if (e->type == type && !PATHCMP(e->filename, filename)) + return e; + i = e->hash_next; + } + return NULL; +} + +static inline void add_cached_include(TCCState *s1, int type, + const char *filename, int ifndef_macro) +{ + CachedInclude *e; + int h; + + if (search_cached_include(s1, type, filename)) + return; +#ifdef INC_DEBUG + printf("adding cached '%s' %s\n", filename, get_tok_str(ifndef_macro, NULL)); +#endif + e = tcc_malloc(sizeof(CachedInclude) + strlen(filename)); + if (!e) + return; + e->type = type; + strcpy(e->filename, filename); + e->ifndef_macro = ifndef_macro; + dynarray_add((void ***)&s1->cached_includes, &s1->nb_cached_includes, e); + /* add in hash table */ + h = hash_cached_include(type, filename); + e->hash_next = s1->cached_includes_hash[h]; + s1->cached_includes_hash[h] = s1->nb_cached_includes; +} + +static void pragma_parse(TCCState *s1) +{ + int val; + + next(); + if (tok == TOK_pack) { + /* + This may be: + #pragma pack(1) // set + #pragma pack() // reset to default + #pragma pack(push,1) // push & set + #pragma pack(pop) // restore previous + */ + next(); + skip('('); + if (tok == TOK_ASM_pop) { + next(); + if (s1->pack_stack_ptr <= s1->pack_stack) { + stk_error: + error("out of pack stack"); + } + s1->pack_stack_ptr--; + } else { + val = 0; + if (tok != ')') { + if (tok == TOK_ASM_push) { + next(); + if (s1->pack_stack_ptr >= s1->pack_stack + PACK_STACK_SIZE - 1) + goto stk_error; + s1->pack_stack_ptr++; + skip(','); + } + if (tok != TOK_CINT) { + pack_error: + error("invalid pack pragma"); + } + val = tokc.i; + if (val < 1 || val > 16 || (val & (val - 1)) != 0) + goto pack_error; + next(); + } + *s1->pack_stack_ptr = val; + skip(')'); + } + } +} + +/* is_bof is true if first non space token at beginning of file */ +static void preprocess(int is_bof) +{ + TCCState *s1 = tcc_state; + int i, c, n, saved_parse_flags; + char buf[1024], *q; + Sym *s; + + saved_parse_flags = parse_flags; + parse_flags = PARSE_FLAG_PREPROCESS | PARSE_FLAG_TOK_NUM | + PARSE_FLAG_LINEFEED; + next_nomacro(); + redo: + switch(tok) { + case TOK_DEFINE: + next_nomacro(); + parse_define(); + break; + case TOK_UNDEF: + next_nomacro(); + s = define_find(tok); + /* undefine symbol by putting an invalid name */ + if (s) + define_undef(s); + break; + case TOK_INCLUDE: + case TOK_INCLUDE_NEXT: + ch = file->buf_ptr[0]; + /* XXX: incorrect if comments : use next_nomacro with a special mode */ + skip_spaces(); + if (ch == '<') { + c = '>'; + goto read_name; + } else if (ch == '\"') { + c = ch; + read_name: + inp(); + q = buf; + while (ch != c && ch != '\n' && ch != CH_EOF) { + if ((q - buf) < sizeof(buf) - 1) + *q++ = ch; + if (ch == '\\') { + if (handle_stray_noerror() == 0) + --q; + } else + inp(); + } + *q = '\0'; + minp(); +#if 0 + /* eat all spaces and comments after include */ + /* XXX: slightly incorrect */ + while (ch1 != '\n' && ch1 != CH_EOF) + inp(); +#endif + } else { + /* computed #include : either we have only strings or + we have anything enclosed in '<>' */ + next(); + buf[0] = '\0'; + if (tok == TOK_STR) { + while (tok != TOK_LINEFEED) { + if (tok != TOK_STR) { + include_syntax: + error("'#include' expects \"FILENAME\" or "); + } + pstrcat(buf, sizeof(buf), (char *)tokc.cstr->data); + next(); + } + c = '\"'; + } else { + int len; + while (tok != TOK_LINEFEED) { + pstrcat(buf, sizeof(buf), get_tok_str(tok, &tokc)); + next(); + } + len = strlen(buf); + /* check syntax and remove '<>' */ + if (len < 2 || buf[0] != '<' || buf[len - 1] != '>') + goto include_syntax; + memmove(buf, buf + 1, len - 2); + buf[len - 2] = '\0'; + c = '>'; + } + } + + if (s1->include_stack_ptr >= s1->include_stack + INCLUDE_STACK_SIZE) + error("#include recursion too deep"); + + n = s1->nb_include_paths + s1->nb_sysinclude_paths; + for (i = -2; i < n; ++i) { + char buf1[sizeof file->filename]; + BufferedFile *f; + CachedInclude *e; + const char *path; + int size; + + if (i == -2) { + /* check absolute include path */ + if (!IS_ABSPATH(buf)) + continue; + buf1[0] = 0; + + } else if (i == -1) { + /* search in current dir if "header.h" */ + if (c != '\"') + continue; + size = tcc_basename(file->filename) - file->filename; + memcpy(buf1, file->filename, size); + buf1[size] = '\0'; + + } else { + /* search in all the include paths */ + if (i < s1->nb_include_paths) + path = s1->include_paths[i]; + else + path = s1->sysinclude_paths[i - s1->nb_include_paths]; + pstrcpy(buf1, sizeof(buf1), path); + pstrcat(buf1, sizeof(buf1), "/"); + } + + pstrcat(buf1, sizeof(buf1), buf); + + e = search_cached_include(s1, c, buf1); + if (e && define_find(e->ifndef_macro)) { + /* no need to parse the include because the 'ifndef macro' + is defined */ +#ifdef INC_DEBUG + printf("%s: skipping %s\n", file->filename, buf); +#endif + f = NULL; + } else { + f = tcc_open(s1, buf1); + if (!f) + continue; + } + + if (tok == TOK_INCLUDE_NEXT) { + tok = TOK_INCLUDE; + if (f) + tcc_close(f); + continue; + } + + if (!f) + goto include_done; + +#ifdef INC_DEBUG + printf("%s: including %s\n", file->filename, buf1); +#endif + + /* XXX: fix current line init */ + /* push current file in stack */ + *s1->include_stack_ptr++ = file; + f->inc_type = c; + pstrcpy(f->inc_filename, sizeof(f->inc_filename), buf1); + file = f; + /* add include file debug info */ + if (tcc_state->do_debug) { + put_stabs(file->filename, N_BINCL, 0, 0, 0); + } + tok_flags |= TOK_FLAG_BOF | TOK_FLAG_BOL; + ch = file->buf_ptr[0]; + goto the_end; + } + error("include file '%s' not found", buf); +include_done: + break; + case TOK_IFNDEF: + c = 1; + goto do_ifdef; + case TOK_IF: + c = expr_preprocess(); + goto do_if; + case TOK_IFDEF: + c = 0; + do_ifdef: + next_nomacro(); + if (tok < TOK_IDENT) + error("invalid argument for '#if%sdef'", c ? "n" : ""); + if (is_bof) { + if (c) { +#ifdef INC_DEBUG + printf("#ifndef %s\n", get_tok_str(tok, NULL)); +#endif + file->ifndef_macro = tok; + } + } + c = (define_find(tok) != 0) ^ c; + do_if: + if (s1->ifdef_stack_ptr >= s1->ifdef_stack + IFDEF_STACK_SIZE) + error("memory full"); + *s1->ifdef_stack_ptr++ = c; + goto test_skip; + case TOK_ELSE: + if (s1->ifdef_stack_ptr == s1->ifdef_stack) + error("#else without matching #if"); + if (s1->ifdef_stack_ptr[-1] & 2) + error("#else after #else"); + c = (s1->ifdef_stack_ptr[-1] ^= 3); + goto test_skip; + case TOK_ELIF: + if (s1->ifdef_stack_ptr == s1->ifdef_stack) + error("#elif without matching #if"); + c = s1->ifdef_stack_ptr[-1]; + if (c > 1) + error("#elif after #else"); + /* last #if/#elif expression was true: we skip */ + if (c == 1) + goto skip; + c = expr_preprocess(); + s1->ifdef_stack_ptr[-1] = c; + test_skip: + if (!(c & 1)) { + skip: + preprocess_skip(); + is_bof = 0; + goto redo; + } + break; + case TOK_ENDIF: + if (s1->ifdef_stack_ptr <= file->ifdef_stack_ptr) + error("#endif without matching #if"); + s1->ifdef_stack_ptr--; + /* '#ifndef macro' was at the start of file. Now we check if + an '#endif' is exactly at the end of file */ + if (file->ifndef_macro && + s1->ifdef_stack_ptr == file->ifdef_stack_ptr) { + file->ifndef_macro_saved = file->ifndef_macro; + /* need to set to zero to avoid false matches if another + #ifndef at middle of file */ + file->ifndef_macro = 0; + while (tok != TOK_LINEFEED) + next_nomacro(); + tok_flags |= TOK_FLAG_ENDIF; + goto the_end; + } + break; + case TOK_LINE: + next(); + if (tok != TOK_CINT) + error("#line"); + file->line_num = tokc.i - 1; /* the line number will be incremented after */ + next(); + if (tok != TOK_LINEFEED) { + if (tok != TOK_STR) + error("#line"); + pstrcpy(file->filename, sizeof(file->filename), + (char *)tokc.cstr->data); + } + break; + case TOK_ERROR: + case TOK_WARNING: + c = tok; + ch = file->buf_ptr[0]; + skip_spaces(); + q = buf; + while (ch != '\n' && ch != CH_EOF) { + if ((q - buf) < sizeof(buf) - 1) + *q++ = ch; + if (ch == '\\') { + if (handle_stray_noerror() == 0) + --q; + } else + inp(); + } + *q = '\0'; + if (c == TOK_ERROR) + error("#error %s", buf); + else + warning("#warning %s", buf); + break; + case TOK_PRAGMA: + pragma_parse(s1); + break; + default: + if (tok == TOK_LINEFEED || tok == '!' || tok == TOK_CINT) { + /* '!' is ignored to allow C scripts. numbers are ignored + to emulate cpp behaviour */ + } else { + if (!(saved_parse_flags & PARSE_FLAG_ASM_COMMENTS)) + warning("Ignoring unknown preprocessing directive #%s", get_tok_str(tok, &tokc)); + } + break; + } + /* ignore other preprocess commands or #! for C scripts */ + while (tok != TOK_LINEFEED) + next_nomacro(); + the_end: + parse_flags = saved_parse_flags; +} + +/* evaluate escape codes in a string. */ +static void parse_escape_string(CString *outstr, const uint8_t *buf, int is_long) +{ + int c, n; + const uint8_t *p; + + p = buf; + for(;;) { + c = *p; + if (c == '\0') + break; + if (c == '\\') { + p++; + /* escape */ + c = *p; + switch(c) { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + /* at most three octal digits */ + n = c - '0'; + p++; + c = *p; + if (isoct(c)) { + n = n * 8 + c - '0'; + p++; + c = *p; + if (isoct(c)) { + n = n * 8 + c - '0'; + p++; + } + } + c = n; + goto add_char_nonext; + case 'x': + case 'u': + case 'U': + p++; + n = 0; + for(;;) { + c = *p; + if (c >= 'a' && c <= 'f') + c = c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + c = c - 'A' + 10; + else if (isnum(c)) + c = c - '0'; + else + break; + n = n * 16 + c; + p++; + } + c = n; + goto add_char_nonext; + case 'a': + c = '\a'; + break; + case 'b': + c = '\b'; + break; + case 'f': + c = '\f'; + break; + case 'n': + c = '\n'; + break; + case 'r': + c = '\r'; + break; + case 't': + c = '\t'; + break; + case 'v': + c = '\v'; + break; + case 'e': + if (!gnu_ext) + goto invalid_escape; + c = 27; + break; + case '\'': + case '\"': + case '\\': + case '?': + break; + default: + invalid_escape: + if (c >= '!' && c <= '~') + warning("unknown escape sequence: \'\\%c\'", c); + else + warning("unknown escape sequence: \'\\x%x\'", c); + break; + } + } + p++; + add_char_nonext: + if (!is_long) + cstr_ccat(outstr, c); + else + cstr_wccat(outstr, c); + } + /* add a trailing '\0' */ + if (!is_long) + cstr_ccat(outstr, '\0'); + else + cstr_wccat(outstr, '\0'); +} + +/* we use 64 bit numbers */ +#define BN_SIZE 2 + +/* bn = (bn << shift) | or_val */ +void bn_lshift(unsigned int *bn, int shift, int or_val) +{ + int i; + unsigned int v; + for(i=0;i> (32 - shift); + } +} + +void bn_zero(unsigned int *bn) +{ + int i; + for(i=0;i= 'a' && ch <= 'f') + t = ch - 'a' + 10; + else if (ch >= 'A' && ch <= 'F') + t = ch - 'A' + 10; + else if (isnum(ch)) + t = ch - '0'; + else + break; + if (t >= b) + break; + if (q >= token_buf + STRING_MAX_SIZE) { + num_too_long: + error("number too long"); + } + *q++ = ch; + ch = *p++; + } + if (ch == '.' || + ((ch == 'e' || ch == 'E') && b == 10) || + ((ch == 'p' || ch == 'P') && (b == 16 || b == 2))) { + if (b != 10) { + /* NOTE: strtox should support that for hexa numbers, but + non ISOC99 libcs do not support it, so we prefer to do + it by hand */ + /* hexadecimal or binary floats */ + /* XXX: handle overflows */ + *q = '\0'; + if (b == 16) + shift = 4; + else + shift = 2; + bn_zero(bn); + q = token_buf; + while (1) { + t = *q++; + if (t == '\0') { + break; + } else if (t >= 'a') { + t = t - 'a' + 10; + } else if (t >= 'A') { + t = t - 'A' + 10; + } else { + t = t - '0'; + } + bn_lshift(bn, shift, t); + } + frac_bits = 0; + if (ch == '.') { + ch = *p++; + while (1) { + t = ch; + if (t >= 'a' && t <= 'f') { + t = t - 'a' + 10; + } else if (t >= 'A' && t <= 'F') { + t = t - 'A' + 10; + } else if (t >= '0' && t <= '9') { + t = t - '0'; + } else { + break; + } + if (t >= b) + error("invalid digit"); + bn_lshift(bn, shift, t); + frac_bits += shift; + ch = *p++; + } + } + if (ch != 'p' && ch != 'P') + expect("exponent"); + ch = *p++; + s = 1; + exp_val = 0; + if (ch == '+') { + ch = *p++; + } else if (ch == '-') { + s = -1; + ch = *p++; + } + if (ch < '0' || ch > '9') + expect("exponent digits"); + while (ch >= '0' && ch <= '9') { + exp_val = exp_val * 10 + ch - '0'; + ch = *p++; + } + exp_val = exp_val * s; + + /* now we can generate the number */ + /* XXX: should patch directly float number */ + d = (double)bn[1] * 4294967296.0 + (double)bn[0]; + d = ldexp(d, exp_val - frac_bits); + t = toup(ch); + if (t == 'F') { + ch = *p++; + tok = TOK_CFLOAT; + /* float : should handle overflow */ + tokc.f = (float)d; + } else if (t == 'L') { + ch = *p++; + tok = TOK_CLDOUBLE; + /* XXX: not large enough */ + tokc.ld = (long double)d; + } else { + tok = TOK_CDOUBLE; + tokc.d = d; + } + } else { + /* decimal floats */ + if (ch == '.') { + if (q >= token_buf + STRING_MAX_SIZE) + goto num_too_long; + *q++ = ch; + ch = *p++; + float_frac_parse: + while (ch >= '0' && ch <= '9') { + if (q >= token_buf + STRING_MAX_SIZE) + goto num_too_long; + *q++ = ch; + ch = *p++; + } + } + if (ch == 'e' || ch == 'E') { + if (q >= token_buf + STRING_MAX_SIZE) + goto num_too_long; + *q++ = ch; + ch = *p++; + if (ch == '-' || ch == '+') { + if (q >= token_buf + STRING_MAX_SIZE) + goto num_too_long; + *q++ = ch; + ch = *p++; + } + if (ch < '0' || ch > '9') + expect("exponent digits"); + while (ch >= '0' && ch <= '9') { + if (q >= token_buf + STRING_MAX_SIZE) + goto num_too_long; + *q++ = ch; + ch = *p++; + } + } + *q = '\0'; + t = toup(ch); + errno = 0; + if (t == 'F') { + ch = *p++; + tok = TOK_CFLOAT; + tokc.f = strtof(token_buf, NULL); + } else if (t == 'L') { + ch = *p++; + tok = TOK_CLDOUBLE; + tokc.ld = strtold(token_buf, NULL); + } else { + tok = TOK_CDOUBLE; + tokc.d = strtod(token_buf, NULL); + } + } + } else { + unsigned long long n, n1; + int lcount, ucount; + + /* integer number */ + *q = '\0'; + q = token_buf; + if (b == 10 && *q == '0') { + b = 8; + q++; + } + n = 0; + while(1) { + t = *q++; + /* no need for checks except for base 10 / 8 errors */ + if (t == '\0') { + break; + } else if (t >= 'a') { + t = t - 'a' + 10; + } else if (t >= 'A') { + t = t - 'A' + 10; + } else { + t = t - '0'; + if (t >= b) + error("invalid digit"); + } + n1 = n; + n = n * b + t; + /* detect overflow */ + /* XXX: this test is not reliable */ + if (n < n1) + error("integer constant overflow"); + } + + /* XXX: not exactly ANSI compliant */ + if ((n & 0xffffffff00000000LL) != 0) { + if ((n >> 63) != 0) + tok = TOK_CULLONG; + else + tok = TOK_CLLONG; + } else if (n > 0x7fffffff) { + tok = TOK_CUINT; + } else { + tok = TOK_CINT; + } + lcount = 0; + ucount = 0; + for(;;) { + t = toup(ch); + if (t == 'L') { + if (lcount >= 2) + error("three 'l's in integer constant"); + lcount++; + if (lcount == 2) { + if (tok == TOK_CINT) + tok = TOK_CLLONG; + else if (tok == TOK_CUINT) + tok = TOK_CULLONG; + } + ch = *p++; + } else if (t == 'U') { + if (ucount >= 1) + error("two 'u's in integer constant"); + ucount++; + if (tok == TOK_CINT) + tok = TOK_CUINT; + else if (tok == TOK_CLLONG) + tok = TOK_CULLONG; + ch = *p++; + } else { + break; + } + } + if (tok == TOK_CINT || tok == TOK_CUINT) + tokc.ui = n; + else + tokc.ull = n; + } + if (ch) + error("invalid number\n"); +} + + +#define PARSE2(c1, tok1, c2, tok2) \ + case c1: \ + PEEKC(c, p); \ + if (c == c2) { \ + p++; \ + tok = tok2; \ + } else { \ + tok = tok1; \ + } \ + break; + +/* return next token without macro substitution */ +static inline void next_nomacro1(void) +{ + int t, c, is_long; + TokenSym *ts; + uint8_t *p, *p1; + unsigned int h; + + p = file->buf_ptr; + redo_no_start: + c = *p; + switch(c) { + case ' ': + case '\t': + tok = c; + p++; + goto keep_tok_flags; + case '\f': + case '\v': + case '\r': + p++; + goto redo_no_start; + case '\\': + /* first look if it is in fact an end of buffer */ + if (p >= file->buf_end) { + file->buf_ptr = p; + handle_eob(); + p = file->buf_ptr; + if (p >= file->buf_end) + goto parse_eof; + else + goto redo_no_start; + } else { + file->buf_ptr = p; + ch = *p; + handle_stray(); + p = file->buf_ptr; + goto redo_no_start; + } + parse_eof: + { + TCCState *s1 = tcc_state; + if ((parse_flags & PARSE_FLAG_LINEFEED) + && !(tok_flags & TOK_FLAG_EOF)) { + tok_flags |= TOK_FLAG_EOF; + tok = TOK_LINEFEED; + goto keep_tok_flags; + } else if (s1->include_stack_ptr == s1->include_stack || + !(parse_flags & PARSE_FLAG_PREPROCESS)) { + /* no include left : end of file. */ + tok = TOK_EOF; + } else { + tok_flags &= ~TOK_FLAG_EOF; + /* pop include file */ + + /* test if previous '#endif' was after a #ifdef at + start of file */ + if (tok_flags & TOK_FLAG_ENDIF) { +#ifdef INC_DEBUG + printf("#endif %s\n", get_tok_str(file->ifndef_macro_saved, NULL)); +#endif + add_cached_include(s1, file->inc_type, file->inc_filename, + file->ifndef_macro_saved); + } + + /* add end of include file debug info */ + if (tcc_state->do_debug) { + put_stabd(N_EINCL, 0, 0); + } + /* pop include stack */ + tcc_close(file); + s1->include_stack_ptr--; + file = *s1->include_stack_ptr; + p = file->buf_ptr; + goto redo_no_start; + } + } + break; + + case '\n': + file->line_num++; + tok_flags |= TOK_FLAG_BOL; + p++; + if (0 == (parse_flags & PARSE_FLAG_LINEFEED)) + goto redo_no_start; + tok = TOK_LINEFEED; + goto keep_tok_flags; + + case '#': + /* XXX: simplify */ + PEEKC(c, p); + if ((tok_flags & TOK_FLAG_BOL) && + (parse_flags & PARSE_FLAG_PREPROCESS)) { + file->buf_ptr = p; + preprocess(tok_flags & TOK_FLAG_BOF); + p = file->buf_ptr; + goto redo_no_start; + } else { + if (c == '#') { + p++; + tok = TOK_TWOSHARPS; + } else { + if (parse_flags & PARSE_FLAG_ASM_COMMENTS) { + p = parse_line_comment(p - 1); + goto redo_no_start; + } else { + tok = '#'; + } + } + } + break; + + case 'a': case 'b': case 'c': case 'd': + case 'e': case 'f': case 'g': case 'h': + case 'i': case 'j': case 'k': case 'l': + case 'm': case 'n': case 'o': case 'p': + case 'q': case 'r': case 's': case 't': + case 'u': case 'v': case 'w': case 'x': + case 'y': case 'z': + case 'A': case 'B': case 'C': case 'D': + case 'E': case 'F': case 'G': case 'H': + case 'I': case 'J': case 'K': + case 'M': case 'N': case 'O': case 'P': + case 'Q': case 'R': case 'S': case 'T': + case 'U': case 'V': case 'W': case 'X': + case 'Y': case 'Z': + case '_': + parse_ident_fast: + p1 = p; + h = TOK_HASH_INIT; + h = TOK_HASH_FUNC(h, c); + p++; + for(;;) { + c = *p; + if (!isidnum_table[c-CH_EOF]) + break; + h = TOK_HASH_FUNC(h, c); + p++; + } + if (c != '\\') { + TokenSym **pts; + int len; + + /* fast case : no stray found, so we have the full token + and we have already hashed it */ + len = p - p1; + h &= (TOK_HASH_SIZE - 1); + pts = &hash_ident[h]; + for(;;) { + ts = *pts; + if (!ts) + break; + if (ts->len == len && !memcmp(ts->str, p1, len)) + goto token_found; + pts = &(ts->hash_next); + } + ts = tok_alloc_new(pts, p1, len); + token_found: ; + } else { + /* slower case */ + cstr_reset(&tokcstr); + + while (p1 < p) { + cstr_ccat(&tokcstr, *p1); + p1++; + } + p--; + PEEKC(c, p); + parse_ident_slow: + while (isidnum_table[c-CH_EOF]) { + cstr_ccat(&tokcstr, c); + PEEKC(c, p); + } + ts = tok_alloc(tokcstr.data, tokcstr.size); + } + tok = ts->tok; + break; + case 'L': + t = p[1]; + if (t != '\\' && t != '\'' && t != '\"') { + /* fast case */ + goto parse_ident_fast; + } else { + PEEKC(c, p); + if (c == '\'' || c == '\"') { + is_long = 1; + goto str_const; + } else { + cstr_reset(&tokcstr); + cstr_ccat(&tokcstr, 'L'); + goto parse_ident_slow; + } + } + break; + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': + + cstr_reset(&tokcstr); + /* after the first digit, accept digits, alpha, '.' or sign if + prefixed by 'eEpP' */ + parse_num: + for(;;) { + t = c; + cstr_ccat(&tokcstr, c); + PEEKC(c, p); + if (!(isnum(c) || isid(c) || c == '.' || + ((c == '+' || c == '-') && + (t == 'e' || t == 'E' || t == 'p' || t == 'P')))) + break; + } + /* We add a trailing '\0' to ease parsing */ + cstr_ccat(&tokcstr, '\0'); + tokc.cstr = &tokcstr; + tok = TOK_PPNUM; + break; + case '.': + /* special dot handling because it can also start a number */ + PEEKC(c, p); + if (isnum(c)) { + cstr_reset(&tokcstr); + cstr_ccat(&tokcstr, '.'); + goto parse_num; + } else if (c == '.') { + PEEKC(c, p); + if (c != '.') + expect("'.'"); + PEEKC(c, p); + tok = TOK_DOTS; + } else { + tok = '.'; + } + break; + case '\'': + case '\"': + is_long = 0; + str_const: + { + CString str; + int sep; + + sep = c; + + /* parse the string */ + cstr_new(&str); + p = parse_pp_string(p, sep, &str); + cstr_ccat(&str, '\0'); + + /* eval the escape (should be done as TOK_PPNUM) */ + cstr_reset(&tokcstr); + parse_escape_string(&tokcstr, str.data, is_long); + cstr_free(&str); + + if (sep == '\'') { + int char_size; + /* XXX: make it portable */ + if (!is_long) + char_size = 1; + else + char_size = sizeof(nwchar_t); + if (tokcstr.size <= char_size) + error("empty character constant"); + if (tokcstr.size > 2 * char_size) + warning("multi-character character constant"); + if (!is_long) { + tokc.i = *(int8_t *)tokcstr.data; + tok = TOK_CCHAR; + } else { + tokc.i = *(nwchar_t *)tokcstr.data; + tok = TOK_LCHAR; + } + } else { + tokc.cstr = &tokcstr; + if (!is_long) + tok = TOK_STR; + else + tok = TOK_LSTR; + } + } + break; + + case '<': + PEEKC(c, p); + if (c == '=') { + p++; + tok = TOK_LE; + } else if (c == '<') { + PEEKC(c, p); + if (c == '=') { + p++; + tok = TOK_A_SHL; + } else { + tok = TOK_SHL; + } + } else { + tok = TOK_LT; + } + break; + + case '>': + PEEKC(c, p); + if (c == '=') { + p++; + tok = TOK_GE; + } else if (c == '>') { + PEEKC(c, p); + if (c == '=') { + p++; + tok = TOK_A_SAR; + } else { + tok = TOK_SAR; + } + } else { + tok = TOK_GT; + } + break; + + case '&': + PEEKC(c, p); + if (c == '&') { + p++; + tok = TOK_LAND; + } else if (c == '=') { + p++; + tok = TOK_A_AND; + } else { + tok = '&'; + } + break; + + case '|': + PEEKC(c, p); + if (c == '|') { + p++; + tok = TOK_LOR; + } else if (c == '=') { + p++; + tok = TOK_A_OR; + } else { + tok = '|'; + } + break; + + case '+': + PEEKC(c, p); + if (c == '+') { + p++; + tok = TOK_INC; + } else if (c == '=') { + p++; + tok = TOK_A_ADD; + } else { + tok = '+'; + } + break; + + case '-': + PEEKC(c, p); + if (c == '-') { + p++; + tok = TOK_DEC; + } else if (c == '=') { + p++; + tok = TOK_A_SUB; + } else if (c == '>') { + p++; + tok = TOK_ARROW; + } else { + tok = '-'; + } + break; + + PARSE2('!', '!', '=', TOK_NE) + PARSE2('=', '=', '=', TOK_EQ) + PARSE2('*', '*', '=', TOK_A_MUL) + PARSE2('%', '%', '=', TOK_A_MOD) + PARSE2('^', '^', '=', TOK_A_XOR) + + /* comments or operator */ + case '/': + PEEKC(c, p); + if (c == '*') { + p = parse_comment(p); + goto redo_no_start; + } else if (c == '/') { + p = parse_line_comment(p); + goto redo_no_start; + } else if (c == '=') { + p++; + tok = TOK_A_DIV; + } else { + tok = '/'; + } + break; + + /* simple tokens */ + case '(': + case ')': + case '[': + case ']': + case '{': + case '}': + case ',': + case ';': + case ':': + case '?': + case '~': + case '$': /* only used in assembler */ + case '@': /* dito */ + tok = c; + p++; + break; + default: + error("unrecognized character \\x%02x", c); + break; + } + tok_flags = 0; +keep_tok_flags: + file->buf_ptr = p; +#if defined(PARSE_DEBUG) + printf("token = %s\n", get_tok_str(tok, &tokc)); +#endif +} + +/* return next token without macro substitution. Can read input from + macro_ptr buffer */ +static void next_nomacro_spc(void) +{ + if (macro_ptr) { + redo: + tok = *macro_ptr; + if (tok) { + TOK_GET(tok, macro_ptr, tokc); + if (tok == TOK_LINENUM) { + file->line_num = tokc.i; + goto redo; + } + } + } else { + next_nomacro1(); + } +} + +static void next_nomacro(void) +{ + do { + next_nomacro_spc(); + } while (is_space(tok)); +} + +/* substitute args in macro_str and return allocated string */ +static int *macro_arg_subst(Sym **nested_list, int *macro_str, Sym *args) +{ + int *st, last_tok, t, spc; + Sym *s; + CValue cval; + TokenString str; + CString cstr; + + tok_str_new(&str); + last_tok = 0; + while(1) { + TOK_GET(t, macro_str, cval); + if (!t) + break; + if (t == '#') { + /* stringize */ + TOK_GET(t, macro_str, cval); + if (!t) + break; + s = sym_find2(args, t); + if (s) { + cstr_new(&cstr); + st = (int *)s->c; + spc = 0; + while (*st) { + TOK_GET(t, st, cval); + if (!check_space(t, &spc)) + cstr_cat(&cstr, get_tok_str(t, &cval)); + } + cstr.size -= spc; + cstr_ccat(&cstr, '\0'); +#ifdef PP_DEBUG + printf("stringize: %s\n", (char *)cstr.data); +#endif + /* add string */ + cval.cstr = &cstr; + tok_str_add2(&str, TOK_STR, &cval); + cstr_free(&cstr); + } else { + tok_str_add2(&str, t, &cval); + } + } else if (t >= TOK_IDENT) { + s = sym_find2(args, t); + if (s) { + st = (int *)s->c; + /* if '##' is present before or after, no arg substitution */ + if (*macro_str == TOK_TWOSHARPS || last_tok == TOK_TWOSHARPS) { + /* special case for var arg macros : ## eats the + ',' if empty VA_ARGS variable. */ + /* XXX: test of the ',' is not 100% + reliable. should fix it to avoid security + problems */ + if (gnu_ext && s->type.t && + last_tok == TOK_TWOSHARPS && + str.len >= 2 && str.str[str.len - 2] == ',') { + if (*st == 0) { + /* suppress ',' '##' */ + str.len -= 2; + } else { + /* suppress '##' and add variable */ + str.len--; + goto add_var; + } + } else { + int t1; + add_var: + for(;;) { + TOK_GET(t1, st, cval); + if (!t1) + break; + tok_str_add2(&str, t1, &cval); + } + } + } else { + /* NOTE: the stream cannot be read when macro + substituing an argument */ + macro_subst(&str, nested_list, st, NULL); + } + } else { + tok_str_add(&str, t); + } + } else { + tok_str_add2(&str, t, &cval); + } + last_tok = t; + } + tok_str_add(&str, 0); + return str.str; +} + +static char const ab_month_name[12][4] = +{ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +/* do macro substitution of current token with macro 's' and add + result to (tok_str,tok_len). 'nested_list' is the list of all + macros we got inside to avoid recursing. Return non zero if no + substitution needs to be done */ +static int macro_subst_tok(TokenString *tok_str, + Sym **nested_list, Sym *s, struct macro_level **can_read_stream) +{ + Sym *args, *sa, *sa1; + int mstr_allocated, parlevel, *mstr, t, t1, *p, spc; + TokenString str; + char *cstrval; + CValue cval; + CString cstr; + char buf[32]; + + /* if symbol is a macro, prepare substitution */ + /* special macros */ + if (tok == TOK___LINE__) { + snprintf(buf, sizeof(buf), "%d", file->line_num); + cstrval = buf; + t1 = TOK_PPNUM; + goto add_cstr1; + } else if (tok == TOK___FILE__) { + cstrval = file->filename; + goto add_cstr; + } else if (tok == TOK___DATE__ || tok == TOK___TIME__) { + time_t ti; + struct tm *tm; + + time(&ti); + tm = localtime(&ti); + if (tok == TOK___DATE__) { + snprintf(buf, sizeof(buf), "%s %2d %d", + ab_month_name[tm->tm_mon], tm->tm_mday, tm->tm_year + 1900); + } else { + snprintf(buf, sizeof(buf), "%02d:%02d:%02d", + tm->tm_hour, tm->tm_min, tm->tm_sec); + } + cstrval = buf; + add_cstr: + t1 = TOK_STR; + add_cstr1: + cstr_new(&cstr); + cstr_cat(&cstr, cstrval); + cstr_ccat(&cstr, '\0'); + cval.cstr = &cstr; + tok_str_add2(tok_str, t1, &cval); + cstr_free(&cstr); + } else { + mstr = (int *)s->c; + mstr_allocated = 0; + if (s->type.t == MACRO_FUNC) { + /* NOTE: we do not use next_nomacro to avoid eating the + next token. XXX: find better solution */ + redo: + if (macro_ptr) { + p = macro_ptr; + while (is_space(t = *p) || TOK_LINEFEED == t) + ++p; + if (t == 0 && can_read_stream) { + /* end of macro stream: we must look at the token + after in the file */ + struct macro_level *ml = *can_read_stream; + macro_ptr = NULL; + if (ml) + { + macro_ptr = ml->p; + ml->p = NULL; + *can_read_stream = ml -> prev; + } + goto redo; + } + } else { + /* XXX: incorrect with comments */ + ch = file->buf_ptr[0]; + while (is_space(ch) || ch == '\n') + cinp(); + t = ch; + } + if (t != '(') /* no macro subst */ + return -1; + + /* argument macro */ + next_nomacro(); + next_nomacro(); + args = NULL; + sa = s->next; + /* NOTE: empty args are allowed, except if no args */ + for(;;) { + /* handle '()' case */ + if (!args && !sa && tok == ')') + break; + if (!sa) + error("macro '%s' used with too many args", + get_tok_str(s->v, 0)); + tok_str_new(&str); + parlevel = spc = 0; + /* NOTE: non zero sa->t indicates VA_ARGS */ + while ((parlevel > 0 || + (tok != ')' && + (tok != ',' || sa->type.t))) && + tok != -1) { + if (tok == '(') + parlevel++; + else if (tok == ')') + parlevel--; + if (tok == TOK_LINEFEED) + tok = ' '; + if (!check_space(tok, &spc)) + tok_str_add2(&str, tok, &tokc); + next_nomacro_spc(); + } + str.len -= spc; + tok_str_add(&str, 0); + sym_push2(&args, sa->v & ~SYM_FIELD, sa->type.t, (long)str.str); + sa = sa->next; + if (tok == ')') { + /* special case for gcc var args: add an empty + var arg argument if it is omitted */ + if (sa && sa->type.t && gnu_ext) + continue; + else + break; + } + if (tok != ',') + expect(","); + next_nomacro(); + } + if (sa) { + error("macro '%s' used with too few args", + get_tok_str(s->v, 0)); + } + + /* now subst each arg */ + mstr = macro_arg_subst(nested_list, mstr, args); + /* free memory */ + sa = args; + while (sa) { + sa1 = sa->prev; + tok_str_free((int *)sa->c); + sym_free(sa); + sa = sa1; + } + mstr_allocated = 1; + } + sym_push2(nested_list, s->v, 0, 0); + macro_subst(tok_str, nested_list, mstr, can_read_stream); + /* pop nested defined symbol */ + sa1 = *nested_list; + *nested_list = sa1->prev; + sym_free(sa1); + if (mstr_allocated) + tok_str_free(mstr); + } + return 0; +} + +/* handle the '##' operator. Return NULL if no '##' seen. Otherwise + return the resulting string (which must be freed). */ +static inline int *macro_twosharps(const int *macro_str) +{ + TokenSym *ts; + const int *ptr, *saved_macro_ptr; + int t; + const char *p1, *p2; + CValue cval; + TokenString macro_str1; + CString cstr; + + /* we search the first '##' */ + for(ptr = macro_str;;) { + TOK_GET(t, ptr, cval); + if (t == TOK_TWOSHARPS) + break; + /* nothing more to do if end of string */ + if (t == 0) + return NULL; + } + + /* we saw '##', so we need more processing to handle it */ + cstr_new(&cstr); + tok_str_new(¯o_str1); + saved_macro_ptr = macro_ptr; + /* XXX: get rid of the use of macro_ptr here */ + macro_ptr = (int *)macro_str; + for(;;) { + next_nomacro_spc(); + if (tok == 0) + break; + if (tok == TOK_TWOSHARPS) + continue; + while (*macro_ptr == TOK_TWOSHARPS) { + t = *++macro_ptr; + if (t && t != TOK_TWOSHARPS) { + TOK_GET(t, macro_ptr, cval); + /* We concatenate the two tokens if we have an + identifier or a preprocessing number */ + cstr_reset(&cstr); + p1 = get_tok_str(tok, &tokc); + cstr_cat(&cstr, p1); + p2 = get_tok_str(t, &cval); + cstr_cat(&cstr, p2); + cstr_ccat(&cstr, '\0'); + + if ((tok >= TOK_IDENT || tok == TOK_PPNUM) && + (t >= TOK_IDENT || t == TOK_PPNUM)) { + if (tok == TOK_PPNUM) { + /* if number, then create a number token */ + /* NOTE: no need to allocate because + tok_str_add2() does it */ + cstr_reset(&tokcstr); + tokcstr = cstr; + cstr_new(&cstr); + tokc.cstr = &tokcstr; + } else { + /* if identifier, we must do a test to + validate we have a correct identifier */ + if (t == TOK_PPNUM) { + const char *p; + int c; + + p = p2; + for(;;) { + c = *p; + if (c == '\0') + break; + p++; + if (!isnum(c) && !isid(c)) + goto error_pasting; + } + } + ts = tok_alloc(cstr.data, strlen(cstr.data)); + tok = ts->tok; /* modify current token */ + } + } else { + const char *str = cstr.data; + const unsigned char *q; + + /* we look for a valid token */ + /* XXX: do more extensive checks */ + if (!strcmp(str, ">>=")) { + tok = TOK_A_SAR; + } else if (!strcmp(str, "<<=")) { + tok = TOK_A_SHL; + } else if (strlen(str) == 2) { + /* search in two bytes table */ + q = tok_two_chars; + for(;;) { + if (!*q) + goto error_pasting; + if (q[0] == str[0] && q[1] == str[1]) + break; + q += 3; + } + tok = q[2]; + } else { + error_pasting: + /* NOTE: because get_tok_str use a static buffer, + we must save it */ + cstr_reset(&cstr); + p1 = get_tok_str(tok, &tokc); + cstr_cat(&cstr, p1); + cstr_ccat(&cstr, '\0'); + p2 = get_tok_str(t, &cval); + warning("pasting \"%s\" and \"%s\" does not give a valid preprocessing token", cstr.data, p2); + /* cannot merge tokens: just add them separately */ + tok_str_add2(¯o_str1, tok, &tokc); + /* XXX: free associated memory ? */ + tok = t; + tokc = cval; + } + } + } + } + tok_str_add2(¯o_str1, tok, &tokc); + } + macro_ptr = (int *)saved_macro_ptr; + cstr_free(&cstr); + tok_str_add(¯o_str1, 0); + return macro_str1.str; +} + + +/* do macro substitution of macro_str and add result to + (tok_str,tok_len). 'nested_list' is the list of all macros we got + inside to avoid recursing. */ +static void macro_subst(TokenString *tok_str, Sym **nested_list, + const int *macro_str, struct macro_level ** can_read_stream) +{ + Sym *s; + int *macro_str1; + const int *ptr; + int t, ret, spc; + CValue cval; + struct macro_level ml; + + /* first scan for '##' operator handling */ + ptr = macro_str; + macro_str1 = macro_twosharps(ptr); + if (macro_str1) + ptr = macro_str1; + spc = 0; + while (1) { + /* NOTE: ptr == NULL can only happen if tokens are read from + file stream due to a macro function call */ + if (ptr == NULL) + break; + TOK_GET(t, ptr, cval); + if (t == 0) + break; + s = define_find(t); + if (s != NULL) { + /* if nested substitution, do nothing */ + if (sym_find2(*nested_list, t)) + goto no_subst; + ml.p = macro_ptr; + if (can_read_stream) + ml.prev = *can_read_stream, *can_read_stream = &ml; + macro_ptr = (int *)ptr; + tok = t; + ret = macro_subst_tok(tok_str, nested_list, s, can_read_stream); + ptr = (int *)macro_ptr; + macro_ptr = ml.p; + if (can_read_stream && *can_read_stream == &ml) + *can_read_stream = ml.prev; + if (ret != 0) + goto no_subst; + } else { + no_subst: + if (!check_space(t, &spc)) + tok_str_add2(tok_str, t, &cval); + } + } + if (macro_str1) + tok_str_free(macro_str1); +} + +/* return next token with macro substitution */ +static void next(void) +{ + Sym *nested_list, *s; + TokenString str; + struct macro_level *ml; + + redo: + if (parse_flags & PARSE_FLAG_SPACES) + next_nomacro_spc(); + else + next_nomacro(); + if (!macro_ptr) { + /* if not reading from macro substituted string, then try + to substitute macros */ + if (tok >= TOK_IDENT && + (parse_flags & PARSE_FLAG_PREPROCESS)) { + s = define_find(tok); + if (s) { + /* we have a macro: we try to substitute */ + tok_str_new(&str); + nested_list = NULL; + ml = NULL; + if (macro_subst_tok(&str, &nested_list, s, &ml) == 0) { + /* substitution done, NOTE: maybe empty */ + tok_str_add(&str, 0); + macro_ptr = str.str; + macro_ptr_allocated = str.str; + goto redo; + } + } + } + } else { + if (tok == 0) { + /* end of macro or end of unget buffer */ + if (unget_buffer_enabled) { + macro_ptr = unget_saved_macro_ptr; + unget_buffer_enabled = 0; + } else { + /* end of macro string: free it */ + tok_str_free(macro_ptr_allocated); + macro_ptr = NULL; + } + goto redo; + } + } + + /* convert preprocessor tokens into C tokens */ + if (tok == TOK_PPNUM && + (parse_flags & PARSE_FLAG_TOK_NUM)) { + parse_number((char *)tokc.cstr->data); + } +} + +/* push back current token and set current token to 'last_tok'. Only + identifier case handled for labels. */ +static inline void unget_tok(int last_tok) +{ + int i, n; + int *q; + unget_saved_macro_ptr = macro_ptr; + unget_buffer_enabled = 1; + q = unget_saved_buffer; + macro_ptr = q; + *q++ = tok; + n = tok_ext_size(tok) - 1; + for(i=0;iinclude_stack_ptr = s1->include_stack; + /* XXX: move that before to avoid having to initialize + file->ifdef_stack_ptr ? */ + s1->ifdef_stack_ptr = s1->ifdef_stack; + file->ifdef_stack_ptr = s1->ifdef_stack_ptr; + + /* XXX: not ANSI compliant: bound checking says error */ + vtop = vstack - 1; + s1->pack_stack[0] = 0; + s1->pack_stack_ptr = s1->pack_stack; +} + +void preprocess_new() +{ + int i, c; + const char *p, *r; + TokenSym *ts; + + /* init isid table */ + for(i=CH_EOF;i<256;i++) + isidnum_table[i-CH_EOF] = isid(i) || isnum(i); + + /* add all tokens */ + table_ident = NULL; + memset(hash_ident, 0, TOK_HASH_SIZE * sizeof(TokenSym *)); + + tok_ident = TOK_IDENT; + p = tcc_keywords; + while (*p) { + r = p; + for(;;) { + c = *r++; + if (c == '\0') + break; + } + ts = tok_alloc(p, r - p - 1); + p = r; + } +} + +/* Preprocess the current file */ +static int tcc_preprocess(TCCState *s1) +{ + Sym *define_start; + BufferedFile *file_ref; + int token_seen, line_ref; + + preprocess_init(s1); + define_start = define_stack; + ch = file->buf_ptr[0]; + tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF; + parse_flags = PARSE_FLAG_ASM_COMMENTS | PARSE_FLAG_PREPROCESS | + PARSE_FLAG_LINEFEED | PARSE_FLAG_SPACES; + token_seen = 0; + line_ref = 0; + file_ref = NULL; + + for (;;) { + next(); + if (tok == TOK_EOF) { + break; + } else if (tok == TOK_LINEFEED) { + if (!token_seen) + continue; + ++line_ref; + token_seen = 0; + } else if (!token_seen) { + int d = file->line_num - line_ref; + if (file != file_ref || d < 0 || d >= 8) + fprintf(s1->outfile, "# %d \"%s\"\n", file->line_num, file->filename); + else + while (d) + fputs("\n", s1->outfile), --d; + line_ref = (file_ref = file)->line_num; + token_seen = 1; + } + fputs(get_tok_str(tok, &tokc), s1->outfile); + } + free_defines(define_start); + return 0; +} + diff --git a/05/tcc-0.9.25/tcctok.h b/05/tcc-0.9.25/tcctok.h new file mode 100644 index 0000000..6dc4778 --- /dev/null +++ b/05/tcc-0.9.25/tcctok.h @@ -0,0 +1,469 @@ +/* keywords */ + DEF(TOK_INT, "int") + DEF(TOK_VOID, "void") + DEF(TOK_CHAR, "char") + DEF(TOK_IF, "if") + DEF(TOK_ELSE, "else") + DEF(TOK_WHILE, "while") + DEF(TOK_BREAK, "break") + DEF(TOK_RETURN, "return") + DEF(TOK_FOR, "for") + DEF(TOK_EXTERN, "extern") + DEF(TOK_STATIC, "static") + DEF(TOK_UNSIGNED, "unsigned") + DEF(TOK_GOTO, "goto") + DEF(TOK_DO, "do") + DEF(TOK_CONTINUE, "continue") + DEF(TOK_SWITCH, "switch") + DEF(TOK_CASE, "case") + + DEF(TOK_CONST1, "const") + DEF(TOK_CONST2, "__const") /* gcc keyword */ + DEF(TOK_CONST3, "__const__") /* gcc keyword */ + DEF(TOK_VOLATILE1, "volatile") + DEF(TOK_VOLATILE2, "__volatile") /* gcc keyword */ + DEF(TOK_VOLATILE3, "__volatile__") /* gcc keyword */ + DEF(TOK_LONG, "long") + DEF(TOK_REGISTER, "register") + DEF(TOK_SIGNED1, "signed") + DEF(TOK_SIGNED2, "__signed") /* gcc keyword */ + DEF(TOK_SIGNED3, "__signed__") /* gcc keyword */ + DEF(TOK_AUTO, "auto") + DEF(TOK_INLINE1, "inline") + DEF(TOK_INLINE2, "__inline") /* gcc keyword */ + DEF(TOK_INLINE3, "__inline__") /* gcc keyword */ + DEF(TOK_RESTRICT1, "restrict") + DEF(TOK_RESTRICT2, "__restrict") + DEF(TOK_RESTRICT3, "__restrict__") + DEF(TOK_EXTENSION, "__extension__") /* gcc keyword */ + + DEF(TOK_FLOAT, "float") + DEF(TOK_DOUBLE, "double") + DEF(TOK_BOOL, "_Bool") + DEF(TOK_SHORT, "short") + DEF(TOK_STRUCT, "struct") + DEF(TOK_UNION, "union") + DEF(TOK_TYPEDEF, "typedef") + DEF(TOK_DEFAULT, "default") + DEF(TOK_ENUM, "enum") + DEF(TOK_SIZEOF, "sizeof") + DEF(TOK_ATTRIBUTE1, "__attribute") + DEF(TOK_ATTRIBUTE2, "__attribute__") + DEF(TOK_ALIGNOF1, "__alignof") + DEF(TOK_ALIGNOF2, "__alignof__") + DEF(TOK_TYPEOF1, "typeof") + DEF(TOK_TYPEOF2, "__typeof") + DEF(TOK_TYPEOF3, "__typeof__") + DEF(TOK_LABEL, "__label__") + DEF(TOK_ASM1, "asm") + DEF(TOK_ASM2, "__asm") + DEF(TOK_ASM3, "__asm__") + +/*********************************************************************/ +/* the following are not keywords. They are included to ease parsing */ +/* preprocessor only */ + DEF(TOK_DEFINE, "define") + DEF(TOK_INCLUDE, "include") + DEF(TOK_INCLUDE_NEXT, "include_next") + DEF(TOK_IFDEF, "ifdef") + DEF(TOK_IFNDEF, "ifndef") + DEF(TOK_ELIF, "elif") + DEF(TOK_ENDIF, "endif") + DEF(TOK_DEFINED, "defined") + DEF(TOK_UNDEF, "undef") + DEF(TOK_ERROR, "error") + DEF(TOK_WARNING, "warning") + DEF(TOK_LINE, "line") + DEF(TOK_PRAGMA, "pragma") + DEF(TOK___LINE__, "__LINE__") + DEF(TOK___FILE__, "__FILE__") + DEF(TOK___DATE__, "__DATE__") + DEF(TOK___TIME__, "__TIME__") + DEF(TOK___FUNCTION__, "__FUNCTION__") + DEF(TOK___VA_ARGS__, "__VA_ARGS__") + +/* special identifiers */ + DEF(TOK___FUNC__, "__func__") + +/* attribute identifiers */ +/* XXX: handle all tokens generically since speed is not critical */ + DEF(TOK_SECTION1, "section") + DEF(TOK_SECTION2, "__section__") + DEF(TOK_ALIGNED1, "aligned") + DEF(TOK_ALIGNED2, "__aligned__") + DEF(TOK_PACKED1, "packed") + DEF(TOK_PACKED2, "__packed__") + DEF(TOK_UNUSED1, "unused") + DEF(TOK_UNUSED2, "__unused__") + DEF(TOK_CDECL1, "cdecl") + DEF(TOK_CDECL2, "__cdecl") + DEF(TOK_CDECL3, "__cdecl__") + DEF(TOK_STDCALL1, "stdcall") + DEF(TOK_STDCALL2, "__stdcall") + DEF(TOK_STDCALL3, "__stdcall__") + DEF(TOK_FASTCALL1, "fastcall") + DEF(TOK_FASTCALL2, "__fastcall") + DEF(TOK_FASTCALL3, "__fastcall__") + DEF(TOK_DLLEXPORT, "dllexport") + DEF(TOK_NORETURN1, "noreturn") + DEF(TOK_NORETURN2, "__noreturn__") + DEF(TOK_builtin_types_compatible_p, "__builtin_types_compatible_p") + DEF(TOK_builtin_constant_p, "__builtin_constant_p") + DEF(TOK_builtin_frame_address, "__builtin_frame_address") +#ifdef TCC_TARGET_X86_64 + DEF(TOK_builtin_malloc, "__builtin_malloc") + DEF(TOK_builtin_free, "__builtin_free") + DEF(TOK_malloc, "malloc") + DEF(TOK_free, "free") +#endif + DEF(TOK_REGPARM1, "regparm") + DEF(TOK_REGPARM2, "__regparm__") + +/* pragma */ + DEF(TOK_pack, "pack") +#if !defined(TCC_TARGET_I386) + /* already defined for assembler */ + DEF(TOK_ASM_push, "push") + DEF(TOK_ASM_pop, "pop") +#endif + +/* builtin functions or variables */ +#ifdef TCC_ARM_EABI + DEF(TOK_memcpy, "__aeabi_memcpy") + DEF(TOK_memcpy4, "__aeabi_memcpy4") + DEF(TOK_memcpy8, "__aeabi_memcpy8") + DEF(TOK_memset, "__aeabi_memset") + DEF(TOK___aeabi_ldivmod, "__aeabi_ldivmod") + DEF(TOK___aeabi_uldivmod, "__aeabi_uldivmod") +#else + DEF(TOK_memcpy, "memcpy") + DEF(TOK_memset, "memset") + DEF(TOK___divdi3, "__divdi3") + DEF(TOK___moddi3, "__moddi3") + DEF(TOK___udivdi3, "__udivdi3") + DEF(TOK___umoddi3, "__umoddi3") +#endif +#if defined(TCC_TARGET_ARM) +#ifdef TCC_ARM_EABI + DEF(TOK___aeabi_idivmod, "__aeabi_idivmod") + DEF(TOK___aeabi_uidivmod, "__aeabi_uidivmod") + DEF(TOK___divsi3, "__aeabi_idiv") + DEF(TOK___udivsi3, "__aeabi_uidiv") + DEF(TOK___floatdisf, "__aeabi_l2f") + DEF(TOK___floatdidf, "__aeabi_l2d") + DEF(TOK___fixsfdi, "__aeabi_f2lz") + DEF(TOK___fixdfdi, "__aeabi_d2lz") +#else + DEF(TOK___modsi3, "__modsi3") + DEF(TOK___umodsi3, "__umodsi3") + DEF(TOK___divsi3, "__divsi3") + DEF(TOK___udivsi3, "__udivsi3") + DEF(TOK___floatdisf, "__floatdisf") + DEF(TOK___floatdidf, "__floatdidf") +#ifndef TCC_ARM_VFP + DEF(TOK___floatdixf, "__floatdixf") + DEF(TOK___fixunssfsi, "__fixunssfsi") + DEF(TOK___fixunsdfsi, "__fixunsdfsi") + DEF(TOK___fixunsxfsi, "__fixunsxfsi") + DEF(TOK___fixxfdi, "__fixxfdi") +#endif + DEF(TOK___fixsfdi, "__fixsfdi") + DEF(TOK___fixdfdi, "__fixdfdi") +#endif +#elif defined(TCC_TARGET_C67) + DEF(TOK__divi, "_divi") + DEF(TOK__divu, "_divu") + DEF(TOK__divf, "_divf") + DEF(TOK__divd, "_divd") + DEF(TOK__remi, "_remi") + DEF(TOK__remu, "_remu") +#endif +#ifdef TCC_TARGET_I386 + DEF(TOK___tcc_int_fpu_control, "__tcc_int_fpu_control") + DEF(TOK___tcc_fpu_control, "__tcc_fpu_control") +#endif +#ifdef TCC_ARM_EABI + DEF(TOK___ashrdi3, "__aeabi_lasr") + DEF(TOK___lshrdi3, "__aeabi_llsr") + DEF(TOK___ashldi3, "__aeabi_llsl") + DEF(TOK___floatundisf, "__aeabi_ul2f") + DEF(TOK___floatundidf, "__aeabi_ul2d") + DEF(TOK___fixunssfdi, "__aeabi_f2ulz") + DEF(TOK___fixunsdfdi, "__aeabi_d2ulz") +#else + DEF(TOK___ashrdi3, "__ashrdi3") + DEF(TOK___lshrdi3, "__lshrdi3") + DEF(TOK___ashldi3, "__ashldi3") + DEF(TOK___floatundisf, "__floatundisf") + DEF(TOK___floatundidf, "__floatundidf") +#ifndef TCC_ARM_VFP + DEF(TOK___floatundixf, "__floatundixf") + DEF(TOK___fixunsxfdi, "__fixunsxfdi") +#endif + DEF(TOK___fixunssfdi, "__fixunssfdi") + DEF(TOK___fixunsdfdi, "__fixunsdfdi") +#endif +#ifdef TCC_TARGET_PE + DEF(TOK___chkstk, "__chkstk") +#endif + +/* bound checking symbols */ +#ifdef CONFIG_TCC_BCHECK + DEF(TOK___bound_ptr_add, "__bound_ptr_add") + DEF(TOK___bound_ptr_indir1, "__bound_ptr_indir1") + DEF(TOK___bound_ptr_indir2, "__bound_ptr_indir2") + DEF(TOK___bound_ptr_indir4, "__bound_ptr_indir4") + DEF(TOK___bound_ptr_indir8, "__bound_ptr_indir8") + DEF(TOK___bound_ptr_indir12, "__bound_ptr_indir12") + DEF(TOK___bound_ptr_indir16, "__bound_ptr_indir16") + DEF(TOK___bound_local_new, "__bound_local_new") + DEF(TOK___bound_local_delete, "__bound_local_delete") +#if 0 + DEF(TOK_malloc, "malloc") + DEF(TOK_free, "free") + DEF(TOK_realloc, "realloc") + DEF(TOK_memalign, "memalign") + DEF(TOK_calloc, "calloc") +#endif + DEF(TOK_memmove, "memmove") + DEF(TOK_strlen, "strlen") + DEF(TOK_strcpy, "strcpy") + DEF(TOK_alloca, "alloca") +#endif + +/* Tiny Assembler */ + + DEF_ASM(byte) + DEF_ASM(align) + DEF_ASM(skip) + DEF_ASM(space) + DEF_ASM(string) + DEF_ASM(asciz) + DEF_ASM(ascii) + DEF_ASM(globl) + DEF_ASM(global) + DEF_ASM(text) + DEF_ASM(data) + DEF_ASM(bss) + DEF_ASM(previous) + DEF_ASM(fill) + DEF_ASM(org) + DEF_ASM(quad) + +#ifdef TCC_TARGET_I386 + +/* WARNING: relative order of tokens is important. */ + DEF_ASM(al) + DEF_ASM(cl) + DEF_ASM(dl) + DEF_ASM(bl) + DEF_ASM(ah) + DEF_ASM(ch) + DEF_ASM(dh) + DEF_ASM(bh) + DEF_ASM(ax) + DEF_ASM(cx) + DEF_ASM(dx) + DEF_ASM(bx) + DEF_ASM(sp) + DEF_ASM(bp) + DEF_ASM(si) + DEF_ASM(di) + DEF_ASM(eax) + DEF_ASM(ecx) + DEF_ASM(edx) + DEF_ASM(ebx) + DEF_ASM(esp) + DEF_ASM(ebp) + DEF_ASM(esi) + DEF_ASM(edi) + DEF_ASM(mm0) + DEF_ASM(mm1) + DEF_ASM(mm2) + DEF_ASM(mm3) + DEF_ASM(mm4) + DEF_ASM(mm5) + DEF_ASM(mm6) + DEF_ASM(mm7) + DEF_ASM(xmm0) + DEF_ASM(xmm1) + DEF_ASM(xmm2) + DEF_ASM(xmm3) + DEF_ASM(xmm4) + DEF_ASM(xmm5) + DEF_ASM(xmm6) + DEF_ASM(xmm7) + DEF_ASM(cr0) + DEF_ASM(cr1) + DEF_ASM(cr2) + DEF_ASM(cr3) + DEF_ASM(cr4) + DEF_ASM(cr5) + DEF_ASM(cr6) + DEF_ASM(cr7) + DEF_ASM(tr0) + DEF_ASM(tr1) + DEF_ASM(tr2) + DEF_ASM(tr3) + DEF_ASM(tr4) + DEF_ASM(tr5) + DEF_ASM(tr6) + DEF_ASM(tr7) + DEF_ASM(db0) + DEF_ASM(db1) + DEF_ASM(db2) + DEF_ASM(db3) + DEF_ASM(db4) + DEF_ASM(db5) + DEF_ASM(db6) + DEF_ASM(db7) + DEF_ASM(dr0) + DEF_ASM(dr1) + DEF_ASM(dr2) + DEF_ASM(dr3) + DEF_ASM(dr4) + DEF_ASM(dr5) + DEF_ASM(dr6) + DEF_ASM(dr7) + DEF_ASM(es) + DEF_ASM(cs) + DEF_ASM(ss) + DEF_ASM(ds) + DEF_ASM(fs) + DEF_ASM(gs) + DEF_ASM(st) + + DEF_BWL(mov) + + /* generic two operands */ + DEF_BWL(add) + DEF_BWL(or) + DEF_BWL(adc) + DEF_BWL(sbb) + DEF_BWL(and) + DEF_BWL(sub) + DEF_BWL(xor) + DEF_BWL(cmp) + + /* unary ops */ + DEF_BWL(inc) + DEF_BWL(dec) + DEF_BWL(not) + DEF_BWL(neg) + DEF_BWL(mul) + DEF_BWL(imul) + DEF_BWL(div) + DEF_BWL(idiv) + + DEF_BWL(xchg) + DEF_BWL(test) + + /* shifts */ + DEF_BWL(rol) + DEF_BWL(ror) + DEF_BWL(rcl) + DEF_BWL(rcr) + DEF_BWL(shl) + DEF_BWL(shr) + DEF_BWL(sar) + + DEF_ASM(shldw) + DEF_ASM(shldl) + DEF_ASM(shld) + DEF_ASM(shrdw) + DEF_ASM(shrdl) + DEF_ASM(shrd) + + DEF_ASM(pushw) + DEF_ASM(pushl) + DEF_ASM(push) + DEF_ASM(popw) + DEF_ASM(popl) + DEF_ASM(pop) + DEF_BWL(in) + DEF_BWL(out) + + DEF_WL(movzb) + + DEF_ASM(movzwl) + DEF_ASM(movsbw) + DEF_ASM(movsbl) + DEF_ASM(movswl) + + DEF_WL(lea) + + DEF_ASM(les) + DEF_ASM(lds) + DEF_ASM(lss) + DEF_ASM(lfs) + DEF_ASM(lgs) + + DEF_ASM(call) + DEF_ASM(jmp) + DEF_ASM(lcall) + DEF_ASM(ljmp) + + DEF_ASMTEST(j) + + DEF_ASMTEST(set) + DEF_ASMTEST(cmov) + + DEF_WL(bsf) + DEF_WL(bsr) + DEF_WL(bt) + DEF_WL(bts) + DEF_WL(btr) + DEF_WL(btc) + + DEF_WL(lsl) + + /* generic FP ops */ + DEF_FP(add) + DEF_FP(mul) + + DEF_ASM(fcom) + DEF_ASM(fcom_1) /* non existant op, just to have a regular table */ + DEF_FP1(com) + + DEF_FP(comp) + DEF_FP(sub) + DEF_FP(subr) + DEF_FP(div) + DEF_FP(divr) + + DEF_BWL(xadd) + DEF_BWL(cmpxchg) + + /* string ops */ + DEF_BWL(cmps) + DEF_BWL(scmp) + DEF_BWL(ins) + DEF_BWL(outs) + DEF_BWL(lods) + DEF_BWL(slod) + DEF_BWL(movs) + DEF_BWL(smov) + DEF_BWL(scas) + DEF_BWL(ssca) + DEF_BWL(stos) + DEF_BWL(ssto) + + /* generic asm ops */ + +#define ALT(x) +#define DEF_ASM_OP0(name, opcode) DEF_ASM(name) +#define DEF_ASM_OP0L(name, opcode, group, instr_type) +#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) +#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) +#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) +#include "i386-asm.h" + +#define ALT(x) +#define DEF_ASM_OP0(name, opcode) +#define DEF_ASM_OP0L(name, opcode, group, instr_type) DEF_ASM(name) +#define DEF_ASM_OP1(name, opcode, group, instr_type, op0) DEF_ASM(name) +#define DEF_ASM_OP2(name, opcode, group, instr_type, op0, op1) DEF_ASM(name) +#define DEF_ASM_OP3(name, opcode, group, instr_type, op0, op1, op2) DEF_ASM(name) +#include "i386-asm.h" + +#endif diff --git a/05/tcc-0.9.25/texi2pod.pl b/05/tcc-0.9.25/texi2pod.pl new file mode 100755 index 0000000..d86e176 --- /dev/null +++ b/05/tcc-0.9.25/texi2pod.pl @@ -0,0 +1,427 @@ +#! /usr/bin/perl -w + +# Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc. + +# This file is part of GNU CC. + +# GNU CC is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# GNU CC is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with GNU CC; see the file COPYING. If not, write to +# the Free Software Foundation, 59 Temple Place - Suite 330, +# Boston MA 02111-1307, USA. + +# This does trivial (and I mean _trivial_) conversion of Texinfo +# markup to Perl POD format. It's intended to be used to extract +# something suitable for a manpage from a Texinfo document. + +$output = 0; +$skipping = 0; +%sects = (); +$section = ""; +@icstack = (); +@endwstack = (); +@skstack = (); +@instack = (); +$shift = ""; +%defs = (); +$fnno = 1; +$inf = ""; +$ibase = ""; + +while ($_ = shift) { + if (/^-D(.*)$/) { + if ($1 ne "") { + $flag = $1; + } else { + $flag = shift; + } + $value = ""; + ($flag, $value) = ($flag =~ /^([^=]+)(?:=(.+))?/); + die "no flag specified for -D\n" + unless $flag ne ""; + die "flags may only contain letters, digits, hyphens, dashes and underscores\n" + unless $flag =~ /^[a-zA-Z0-9_-]+$/; + $defs{$flag} = $value; + } elsif (/^-/) { + usage(); + } else { + $in = $_, next unless defined $in; + $out = $_, next unless defined $out; + usage(); + } +} + +if (defined $in) { + $inf = gensym(); + open($inf, "<$in") or die "opening \"$in\": $!\n"; + $ibase = $1 if $in =~ m|^(.+)/[^/]+$|; +} else { + $inf = \*STDIN; +} + +if (defined $out) { + open(STDOUT, ">$out") or die "opening \"$out\": $!\n"; +} + +while(defined $inf) { +while(<$inf>) { + # Certain commands are discarded without further processing. + /^\@(?: + [a-z]+index # @*index: useful only in complete manual + |need # @need: useful only in printed manual + |(?:end\s+)?group # @group .. @end group: ditto + |page # @page: ditto + |node # @node: useful only in .info file + |(?:end\s+)?ifnottex # @ifnottex .. @end ifnottex: use contents + )\b/x and next; + + chomp; + + # Look for filename and title markers. + /^\@setfilename\s+([^.]+)/ and $fn = $1, next; + /^\@settitle\s+([^.]+)/ and $tl = postprocess($1), next; + + # Identify a man title but keep only the one we are interested in. + /^\@c\s+man\s+title\s+([A-Za-z0-9-]+)\s+(.+)/ and do { + if (exists $defs{$1}) { + $fn = $1; + $tl = postprocess($2); + } + next; + }; + + # Look for blocks surrounded by @c man begin SECTION ... @c man end. + # This really oughta be @ifman ... @end ifman and the like, but such + # would require rev'ing all other Texinfo translators. + /^\@c\s+man\s+begin\s+([A-Z]+)\s+([A-Za-z0-9-]+)/ and do { + $output = 1 if exists $defs{$2}; + $sect = $1; + next; + }; + /^\@c\s+man\s+begin\s+([A-Z]+)/ and $sect = $1, $output = 1, next; + /^\@c\s+man\s+end/ and do { + $sects{$sect} = "" unless exists $sects{$sect}; + $sects{$sect} .= postprocess($section); + $section = ""; + $output = 0; + next; + }; + + # handle variables + /^\@set\s+([a-zA-Z0-9_-]+)\s*(.*)$/ and do { + $defs{$1} = $2; + next; + }; + /^\@clear\s+([a-zA-Z0-9_-]+)/ and do { + delete $defs{$1}; + next; + }; + + next unless $output; + + # Discard comments. (Can't do it above, because then we'd never see + # @c man lines.) + /^\@c\b/ and next; + + # End-block handler goes up here because it needs to operate even + # if we are skipping. + /^\@end\s+([a-z]+)/ and do { + # Ignore @end foo, where foo is not an operation which may + # cause us to skip, if we are presently skipping. + my $ended = $1; + next if $skipping && $ended !~ /^(?:ifset|ifclear|ignore|menu|iftex)$/; + + die "\@end $ended without \@$ended at line $.\n" unless defined $endw; + die "\@$endw ended by \@end $ended at line $.\n" unless $ended eq $endw; + + $endw = pop @endwstack; + + if ($ended =~ /^(?:ifset|ifclear|ignore|menu|iftex)$/) { + $skipping = pop @skstack; + next; + } elsif ($ended =~ /^(?:example|smallexample|display)$/) { + $shift = ""; + $_ = ""; # need a paragraph break + } elsif ($ended =~ /^(?:itemize|enumerate|[fv]?table)$/) { + $_ = "\n=back\n"; + $ic = pop @icstack; + } else { + die "unknown command \@end $ended at line $.\n"; + } + }; + + # We must handle commands which can cause skipping even while we + # are skipping, otherwise we will not process nested conditionals + # correctly. + /^\@ifset\s+([a-zA-Z0-9_-]+)/ and do { + push @endwstack, $endw; + push @skstack, $skipping; + $endw = "ifset"; + $skipping = 1 unless exists $defs{$1}; + next; + }; + + /^\@ifclear\s+([a-zA-Z0-9_-]+)/ and do { + push @endwstack, $endw; + push @skstack, $skipping; + $endw = "ifclear"; + $skipping = 1 if exists $defs{$1}; + next; + }; + + /^\@(ignore|menu|iftex)\b/ and do { + push @endwstack, $endw; + push @skstack, $skipping; + $endw = $1; + $skipping = 1; + next; + }; + + next if $skipping; + + # Character entities. First the ones that can be replaced by raw text + # or discarded outright: + s/\@copyright\{\}/(c)/g; + s/\@dots\{\}/.../g; + s/\@enddots\{\}/..../g; + s/\@([.!? ])/$1/g; + s/\@[:-]//g; + s/\@bullet(?:\{\})?/*/g; + s/\@TeX\{\}/TeX/g; + s/\@pounds\{\}/\#/g; + s/\@minus(?:\{\})?/-/g; + s/\\,/,/g; + + # Now the ones that have to be replaced by special escapes + # (which will be turned back into text by unmunge()) + s/&/&/g; + s/\@\{/{/g; + s/\@\}/}/g; + s/\@\@/&at;/g; + + # Inside a verbatim block, handle @var specially. + if ($shift ne "") { + s/\@var\{([^\}]*)\}/<$1>/g; + } + + # POD doesn't interpret E<> inside a verbatim block. + if ($shift eq "") { + s//>/g; + } else { + s//>/g; + } + + # Single line command handlers. + + /^\@include\s+(.+)$/ and do { + push @instack, $inf; + $inf = gensym(); + + # Try cwd and $ibase. + open($inf, "<" . $1) + or open($inf, "<" . $ibase . "/" . $1) + or die "cannot open $1 or $ibase/$1: $!\n"; + next; + }; + + /^\@(?:section|unnumbered|unnumberedsec|center)\s+(.+)$/ + and $_ = "\n=head2 $1\n"; + /^\@subsection\s+(.+)$/ + and $_ = "\n=head3 $1\n"; + + # Block command handlers: + /^\@itemize\s+(\@[a-z]+|\*|-)/ and do { + push @endwstack, $endw; + push @icstack, $ic; + $ic = $1; + $_ = "\n=over 4\n"; + $endw = "itemize"; + }; + + /^\@enumerate(?:\s+([a-zA-Z0-9]+))?/ and do { + push @endwstack, $endw; + push @icstack, $ic; + if (defined $1) { + $ic = $1 . "."; + } else { + $ic = "1."; + } + $_ = "\n=over 4\n"; + $endw = "enumerate"; + }; + + /^\@([fv]?table)\s+(\@[a-z]+)/ and do { + push @endwstack, $endw; + push @icstack, $ic; + $endw = $1; + $ic = $2; + $ic =~ s/\@(?:samp|strong|key|gcctabopt|option|env)/B/; + $ic =~ s/\@(?:code|kbd)/C/; + $ic =~ s/\@(?:dfn|var|emph|cite|i)/I/; + $ic =~ s/\@(?:file)/F/; + $_ = "\n=over 4\n"; + }; + + /^\@((?:small)?example|display)/ and do { + push @endwstack, $endw; + $endw = $1; + $shift = "\t"; + $_ = ""; # need a paragraph break + }; + + /^\@itemx?\s*(.+)?$/ and do { + if (defined $1) { + # Entity escapes prevent munging by the <> processing below. + $_ = "\n=item $ic\<$1\>\n"; + } else { + $_ = "\n=item $ic\n"; + $ic =~ y/A-Ya-y/B-Zb-z/; + $ic =~ s/(\d+)/$1 + 1/eg; + } + }; + + $section .= $shift.$_."\n"; +} +# End of current file. +close($inf); +$inf = pop @instack; +} + +die "No filename or title\n" unless defined $fn && defined $tl; + +$sects{NAME} = "$fn \- $tl\n"; +$sects{FOOTNOTES} .= "=back\n" if exists $sects{FOOTNOTES}; + +for $sect (qw(NAME SYNOPSIS DESCRIPTION OPTIONS ENVIRONMENT FILES + BUGS NOTES FOOTNOTES SEEALSO AUTHOR COPYRIGHT)) { + if(exists $sects{$sect}) { + $head = $sect; + $head =~ s/SEEALSO/SEE ALSO/; + print "=head1 $head\n\n"; + print scalar unmunge ($sects{$sect}); + print "\n"; + } +} + +sub usage +{ + die "usage: $0 [-D toggle...] [infile [outfile]]\n"; +} + +sub postprocess +{ + local $_ = $_[0]; + + # @value{foo} is replaced by whatever 'foo' is defined as. + while (m/(\@value\{([a-zA-Z0-9_-]+)\})/g) { + if (! exists $defs{$2}) { + print STDERR "Option $2 not defined\n"; + s/\Q$1\E//; + } else { + $value = $defs{$2}; + s/\Q$1\E/$value/; + } + } + + # Formatting commands. + # Temporary escape for @r. + s/\@r\{([^\}]*)\}/R<$1>/g; + s/\@(?:dfn|var|emph|cite|i)\{([^\}]*)\}/I<$1>/g; + s/\@(?:code|kbd)\{([^\}]*)\}/C<$1>/g; + s/\@(?:gccoptlist|samp|strong|key|option|env|command|b)\{([^\}]*)\}/B<$1>/g; + s/\@sc\{([^\}]*)\}/\U$1/g; + s/\@file\{([^\}]*)\}/F<$1>/g; + s/\@w\{([^\}]*)\}/S<$1>/g; + s/\@(?:dmn|math)\{([^\}]*)\}/$1/g; + + # Cross references are thrown away, as are @noindent and @refill. + # (@noindent is impossible in .pod, and @refill is unnecessary.) + # @* is also impossible in .pod; we discard it and any newline that + # follows it. Similarly, our macro @gol must be discarded. + + s/\(?\@xref\{(?:[^\}]*)\}(?:[^.<]|(?:<[^<>]*>))*\.\)?//g; + s/\s+\(\@pxref\{(?:[^\}]*)\}\)//g; + s/;\s+\@pxref\{(?:[^\}]*)\}//g; + s/\@noindent\s*//g; + s/\@refill//g; + s/\@gol//g; + s/\@\*\s*\n?//g; + + # @uref can take one, two, or three arguments, with different + # semantics each time. @url and @email are just like @uref with + # one argument, for our purposes. + s/\@(?:uref|url|email)\{([^\},]*)\}/<B<$1>>/g; + s/\@uref\{([^\},]*),([^\},]*)\}/$2 (C<$1>)/g; + s/\@uref\{([^\},]*),([^\},]*),([^\},]*)\}/$3/g; + + # Turn B blah> into B I B to + # match Texinfo semantics of @emph inside @samp. Also handle @r + # inside bold. + s/<//g; + 1 while s/B<((?:[^<>]|I<[^<>]*>)*)R<([^>]*)>/B<$1>${2}B]*)I<([^>]+)>/B<$1>I<$2>B]*)B<([^>]+)>/I<$1>B<$2>I//g; + s/([BI])<(\s+)([^>]+)>/$2$1<$3>/g; + s/([BI])<([^>]+?)(\s+)>/$1<$2>$3/g; + + # Extract footnotes. This has to be done after all other + # processing because otherwise the regexp will choke on formatting + # inside @footnote. + while (/\@footnote/g) { + s/\@footnote\{([^\}]+)\}/[$fnno]/; + add_footnote($1, $fnno); + $fnno++; + } + + return $_; +} + +sub unmunge +{ + # Replace escaped symbols with their equivalents. + local $_ = $_[0]; + + s/</E/g; + s/>/E/g; + s/{/\{/g; + s/}/\}/g; + s/&at;/\@/g; + s/&/&/g; + return $_; +} + +sub add_footnote +{ + unless (exists $sects{FOOTNOTES}) { + $sects{FOOTNOTES} = "\n=over 4\n\n"; + } + + $sects{FOOTNOTES} .= "=item $fnno.\n\n"; $fnno++; + $sects{FOOTNOTES} .= $_[0]; + $sects{FOOTNOTES} .= "\n\n"; +} + +# stolen from Symbol.pm +{ + my $genseq = 0; + sub gensym + { + my $name = "GEN" . $genseq++; + my $ref = \*{$name}; + delete $::{$name}; + return $ref; + } +} diff --git a/05/tcc-0.9.25/time.h b/05/tcc-0.9.25/time.h new file mode 100644 index 0000000..8207156 --- /dev/null +++ b/05/tcc-0.9.25/time.h @@ -0,0 +1,293 @@ +#ifndef _TIME_H +#define _TIME_H + +#include +#define CLK_TCK 1000000000 // doesnt matter; clock() will always fail. + +typedef long clock_t; + +clock_t clock(void) { + // "If the processor time used is not available or its value cannot be represented, the function returns the value (clock_t)-1." C89 § 4.12.2.1 + return -1; +} + +double difftime(time_t time1, time_t time0) { + return (double)(time1 - time0); +} + +time_t time(time_t *timer) { + struct timespec ts = {0}; + if (clock_gettime(CLOCK_REALTIME, &ts) != 0) return -1; + if (timer) *timer = ts.tv_sec; + return ts.tv_sec; +} + + +// @NONSTANDARD(except in UTC+0): we don't support local time in timezones other than UTC+0. + +struct tm { + int tm_sec; /* seconds after the minute --- [0, 60] */ + int tm_min; /* minutes after the hour --- [0, 59] */ + int tm_hour; /* hours since midnight --- [0, 23] */ + int tm_mday; /* day of the month --- [1, 31] */ + int tm_mon; /* months since January --- [0, 11] */ + int tm_year; /* years since 1900 */ + int tm_wday; /* days since Sunday --- [0, 6] */ + int tm_yday; /* days since January 1 --- [0, 365] */ + int tm_isdst; /* Daylight Saving Time flag */ +}; + + +void _gmtime_r(const time_t *timer, struct tm *tm) { + time_t t = *timer; + int year = 1970; + int days = t / 86400; + int leap_year; + int month; + + tm->tm_isdst = 0; + tm->tm_wday = (4 + days) % 7; // jan 1 1970 was a thursday + while (1) { + leap_year = year % 4 == 0 && (year % 100 != 0 || year % 400 == 0); + int ydays = leap_year ? 366 : 365; + if (days < ydays) break; + days -= ydays; + ++year; + } + tm->tm_year = year - 1900; + tm->tm_yday = days; + for (month = 0; month < 12; ++month) { + int mdays; + switch (month) { + case 0: case 2: case 4: case 6: case 7: case 9: case 11: + mdays = 31; + break; + case 3: case 5: case 8: case 10: + mdays = 30; + break; + case 1: + mdays = 28 + leap_year; + break; + } + if (days < mdays) break; + days -= mdays; + } + tm->tm_mday = days + 1; + tm->tm_mon = month; + t %= 86400; + tm->tm_hour = t / 3600; + t %= 3600; + tm->tm_min = t / 60; + tm->tm_sec = t % 60; +} + +time_t mktime(struct tm *tm) { + // @NONSTANDARD-ish. + // not implementing this -- note that the implementation has to support tm_* values + // outside of their proper ranges. + return (time_t)-1; + +} + +struct tm *gmtime(const time_t *timer) { + static struct tm result; + _gmtime_r(timer, &result); + return &result; +} + +struct tm *localtime(const time_t *timer) { + static struct tm result; + _gmtime_r(timer, &result); + return &result; +} + +static const char _wday_name[7][4] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; +static const char _weekday_name[7][16] = { + "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" +}; +static const char _mon_name[12][4] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; +static const char _month_name[12][16] = { + "January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December" +}; + +char *asctime(const struct tm *timeptr) { + // lifted from the (draft of the) C standard + static char result[32]; + sprintf(result, "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n", + _wday_name[timeptr->tm_wday], + _mon_name[timeptr->tm_mon], + timeptr->tm_mday, timeptr->tm_hour, + timeptr->tm_min, timeptr->tm_sec, + 1900 + timeptr->tm_year); + return result; +} + +char *ctime(const time_t *timer) { + return asctime(localtime(timer)); +} + +size_t strftime(char *s, size_t maxsize, const char *format, const struct tm *tm) { + size_t n = 0, l; + char *name; + + while (*format) { + if (*format == '%') { + ++format; + int c = *format++; + switch (c) { + case 'a': + if (n+4 >= maxsize) return 0; + strcpy(s, _wday_name[tm->tm_wday]); + s += 3; + n += 3; + break; + case 'A': + name = _weekday_name[tm->tm_wday]; + l = strlen(name); + if (n+l+1 >= maxsize) return 0; + strcpy(s, name); + s += l; + n += l; + break; + case 'b': + if (n+4 >= maxsize) return 0; + strcpy(s, _mon_name[tm->tm_mon]); + s += 3; + n += 3; + break; + case 'B': + name = _month_name[tm->tm_mon]; + l = strlen(name); + if (n+l+1 >= maxsize) return 0; + strcpy(s, name); + s += l; + n += l; + break; + case 'c': + if (n+32 >= maxsize) return 0; + sprintf(s, "%s %02d %s %d %02d:%02d:%02d %s UTC", + _wday_name[tm->tm_wday], tm->tm_mday, _mon_name[tm->tm_mon], + 1900+tm->tm_year, (tm->tm_hour + 11) % 12 + 1, tm->tm_min, tm->tm_sec, + tm->tm_hour >= 12 ? "PM" : "AM"); + s += 31; + n += 31; + break; + case 'd': + if (n+3 >= maxsize) return 0; + sprintf(s, "%02d", tm->tm_mday); + s += 2; + n += 2; + break; + case 'H': + if (n+3 >= maxsize) return 0; + sprintf(s, "%02d", tm->tm_hour); + s += 2; + n += 2; + break; + case 'I': + if (n+3 >= maxsize) return 0; + sprintf(s, "%02d", (tm->tm_hour + 11) % 12 + 1); + s += 2; + n += 2; + break; + case 'j': + if (n+4 >= maxsize) return 0; + sprintf(s, "%03d", tm->tm_yday + 1); + s += 3; + n += 3; + break; + case 'm': + if (n+3 >= maxsize) return 0; + sprintf(s, "%02d", tm->tm_mon + 1); + s += 2; + n += 2; + break; + case 'M': + if (n+3 >= maxsize) return 0; + sprintf(s, "%02d", tm->tm_min); + s += 2; + n += 2; + break; + case 'p': + if (n+3 >= maxsize) return 0; + sprintf(s, "%s", tm->tm_hour >= 12 ? "PM" : "AM"); + s += 2; + n += 2; + break; + case 'S': + if (n+3 >= maxsize) return 0; + sprintf(s, "%02d", tm->tm_sec); + s += 2; + n += 2; + break; + case 'w': + if (n+2 >= maxsize) return 0; + sprintf(s, "%d", tm->tm_wday); + s += 1; + n += 1; + break; + case 'x': + if (n+16 >= maxsize) return 0; + sprintf(s, "%s %02d %s %d", + _wday_name[tm->tm_wday], tm->tm_mday, _mon_name[tm->tm_mon], + 1900+tm->tm_year); + s += 15; + n += 15; + break; + case 'X': + if (n+16 >= maxsize) return 0; + sprintf(s, "%02d:%02d:%02d %s UTC", + (tm->tm_hour + 11) % 12 + 1, tm->tm_min, tm->tm_sec, + tm->tm_hour >= 12 ? "PM" : "AM"); + s += 15; + n += 15; + break; + case 'y': + if (n+3 >= maxsize) return 0; + sprintf(s, "%02d", tm->tm_year % 100); + s += 2; + n += 2; + break; + case 'Y': + if (n+5 >= maxsize) return 0; + sprintf(s, "%d", tm->tm_year + 1900); + s += 4; + n += 4; + break; + case 'Z': + if (n+4 >= maxsize) return 0; + strcpy(s, "UTC"); + s += 3; + n += 3; + break; + case '%': + if (n >= maxsize) return 0; + *s++ = '%'; + n += 1; + break; + case 'U': // WEEK NUMBER OF THE YEAR? WHO GIVES A SHIT? + case 'W': // WEEK NUMBER OF THE YEAR MONDAY-BASED EDITION. IF YOU DIDNT ALREADY GET ENOUGH WEEK NUMBERS. FUCK YOU + default: + fprintf(stderr, "Bad strftime format.\n"); + abort(); + + } + } else { + if (n >= maxsize) return 0; + *s++ = *format++; + n += 1; + } + } + if (n >= maxsize) return 0; + *s = 0; + #undef _Push_str + return n; +} + +#endif // _TIME_H diff --git a/05/tcc-0.9.25/x86_64-gen.c b/05/tcc-0.9.25/x86_64-gen.c new file mode 100644 index 0000000..6b6e2f1 --- /dev/null +++ b/05/tcc-0.9.25/x86_64-gen.c @@ -0,0 +1,1419 @@ +/* + * x86-64 code generator for TCC + * + * Copyright (c) 2008 Shinichiro Hamaji + * + * Based on i386-gen.c by Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +/* number of available registers */ +#define NB_REGS 5 + +/* a register can belong to several classes. The classes must be + sorted from more general to more precise (see gv2() code which does + assumptions on it). */ +#define RC_INT 0x0001 /* generic integer register */ +#define RC_FLOAT 0x0002 /* generic float register */ +#define RC_RAX 0x0004 +#define RC_RCX 0x0008 +#define RC_RDX 0x0010 +#define RC_XMM0 0x0020 +#define RC_ST0 0x0040 /* only for long double */ +#define RC_IRET RC_RAX /* function return: integer register */ +#define RC_LRET RC_RDX /* function return: second integer register */ +#define RC_FRET RC_XMM0 /* function return: float register */ + +/* pretty names for the registers */ +enum { + TREG_RAX = 0, + TREG_RCX = 1, + TREG_RDX = 2, + TREG_RSI = 6, + TREG_RDI = 7, + TREG_R8 = 8, + TREG_R9 = 9, + TREG_R10 = 10, + TREG_R11 = 11, + + TREG_XMM0 = 3, + TREG_ST0 = 4, + + TREG_MEM = 0x10, +}; + +#define REX_BASE(reg) (((reg) >> 3) & 1) +#define REG_VALUE(reg) ((reg) & 7) + +int reg_classes[NB_REGS] = { + /* eax */ RC_INT | RC_RAX, + /* ecx */ RC_INT | RC_RCX, + /* edx */ RC_INT | RC_RDX, + /* xmm0 */ RC_FLOAT | RC_XMM0, + /* st0 */ RC_ST0 +}; + +/* return registers for function */ +#define REG_IRET TREG_RAX /* single word int return register */ +#define REG_LRET TREG_RDX /* second word return register (for long long) */ +#define REG_FRET TREG_XMM0 /* float return register */ + +/* defined if function parameters must be evaluated in reverse order */ +#define INVERT_FUNC_PARAMS + +/* pointer size, in bytes */ +#define PTR_SIZE 8 + +/* long double size and alignment, in bytes */ +#define LDOUBLE_SIZE 16 +#define LDOUBLE_ALIGN 8 +/* maximum alignment (for aligned attribute support) */ +#define MAX_ALIGN 8 + +/******************************************************/ +/* ELF defines */ + +#define EM_TCC_TARGET EM_X86_64 + +/* relocation type for 32 bit data relocation */ +#define R_DATA_32 R_X86_64_64 +#define R_JMP_SLOT R_X86_64_JUMP_SLOT +#define R_COPY R_X86_64_COPY + +#define ELF_START_ADDR 0x08048000 +#define ELF_PAGE_SIZE 0x1000 + +/******************************************************/ + +static unsigned long func_sub_sp_offset; +static int func_ret_sub; + +/* XXX: make it faster ? */ +void g(int c) +{ + int ind1; + ind1 = ind + 1; + if (ind1 > cur_text_section->data_allocated) + section_realloc(cur_text_section, ind1); + cur_text_section->data[ind] = c; + ind = ind1; +} + +void o(unsigned int c) +{ + while (c) { + g(c); + c = c >> 8; + } +} + +void gen_le32(int c) +{ + g(c); + g(c >> 8); + g(c >> 16); + g(c >> 24); +} + +void gen_le64(int64_t c) +{ + g(c); + g(c >> 8); + g(c >> 16); + g(c >> 24); + g(c >> 32); + g(c >> 40); + g(c >> 48); + g(c >> 56); +} + +/* output a symbol and patch all calls to it */ +void gsym_addr(int t, int a) +{ + int n, *ptr; + while (t) { + ptr = (int *)(cur_text_section->data + t); + n = *ptr; /* next value */ + *ptr = a - t - 4; + t = n; + } +} + +void gsym(int t) +{ + gsym_addr(t, ind); +} + +/* psym is used to put an instruction with a data field which is a + reference to a symbol. It is in fact the same as oad ! */ +#define psym oad + +static int is64_type(int t) +{ + return ((t & VT_BTYPE) == VT_PTR || + (t & VT_BTYPE) == VT_FUNC || + (t & VT_BTYPE) == VT_LLONG); +} + +static int is_sse_float(int t) { + int bt; + bt = t & VT_BTYPE; + return bt == VT_DOUBLE || bt == VT_FLOAT; +} + +/* instruction + 4 bytes data. Return the address of the data */ +static int oad(int c, int s) +{ + int ind1; + + o(c); + ind1 = ind + 4; + if (ind1 > cur_text_section->data_allocated) + section_realloc(cur_text_section, ind1); + *(int *)(cur_text_section->data + ind) = s; + s = ind; + ind = ind1; + return s; +} + +/* output constant with relocation if 'r & VT_SYM' is true */ +static void gen_addr64(int r, Sym *sym, int64_t c) +{ + if (r & VT_SYM) + greloc(cur_text_section, sym, ind, R_X86_64_64); + gen_le64(c); +} + +/* output constant with relocation if 'r & VT_SYM' is true */ +static void gen_addrpc32(int r, Sym *sym, int c) +{ + if (r & VT_SYM) + greloc(cur_text_section, sym, ind, R_X86_64_PC32); + gen_le32(c-4); +} + +/* output got address with relocation */ +static void gen_gotpcrel(int r, Sym *sym, int c) +{ + Section *sr; + ElfW(Rela) *rel; + greloc(cur_text_section, sym, ind, R_X86_64_GOTPCREL); + sr = cur_text_section->reloc; + rel = (ElfW(Rela) *)(sr->data + sr->data_offset - sizeof(ElfW(Rela))); + rel->r_addend = -4; + gen_le32(0); + + if (c) { + /* we use add c, %xxx for displacement */ + o(0x48 + REX_BASE(r)); + o(0x81); + o(0xc0 + REG_VALUE(r)); + gen_le32(c); + } +} + +static void gen_modrm_impl(int op_reg, int r, Sym *sym, int c, int is_got) +{ + op_reg = REG_VALUE(op_reg) << 3; + if ((r & VT_VALMASK) == VT_CONST) { + /* constant memory reference */ + o(0x05 | op_reg); + if (is_got) { + gen_gotpcrel(r, sym, c); + } else { + gen_addrpc32(r, sym, c); + } + } else if ((r & VT_VALMASK) == VT_LOCAL) { + /* currently, we use only ebp as base */ + if (c == (char)c) { + /* short reference */ + o(0x45 | op_reg); + g(c); + } else { + oad(0x85 | op_reg, c); + } + } else if ((r & VT_VALMASK) >= TREG_MEM) { + if (c) { + g(0x80 | op_reg | REG_VALUE(r)); + gen_le32(c); + } else { + g(0x00 | op_reg | REG_VALUE(r)); + } + } else { + g(0x00 | op_reg | (r & VT_VALMASK)); + } +} + +/* generate a modrm reference. 'op_reg' contains the addtionnal 3 + opcode bits */ +static void gen_modrm(int op_reg, int r, Sym *sym, int c) +{ + gen_modrm_impl(op_reg, r, sym, c, 0); +} + +/* generate a modrm reference. 'op_reg' contains the addtionnal 3 + opcode bits */ +static void gen_modrm64(int opcode, int op_reg, int r, Sym *sym, int c) +{ + int is_got; + int rex = 0x48 | (REX_BASE(op_reg) << 2); + if ((r & VT_VALMASK) != VT_CONST && + (r & VT_VALMASK) != VT_LOCAL) { + rex |= REX_BASE(VT_VALMASK & r); + } + o(rex); + o(opcode); + is_got = (op_reg & TREG_MEM) && !(sym->type.t & VT_STATIC); + gen_modrm_impl(op_reg, r, sym, c, is_got); +} + + +/* load 'r' from value 'sv' */ +void load(int r, SValue *sv) +{ + int v, t, ft, fc, fr; + SValue v1; + + fr = sv->r; + ft = sv->type.t; + fc = sv->c.ul; + + /* we use indirect access via got */ + if ((fr & VT_VALMASK) == VT_CONST && (fr & VT_SYM) && + (fr & VT_LVAL) && !(sv->sym->type.t & VT_STATIC)) { + /* use the result register as a temporal register */ + int tr = r | TREG_MEM; + if (is_float(ft)) { + /* we cannot use float registers as a temporal register */ + tr = get_reg(RC_INT) | TREG_MEM; + } + gen_modrm64(0x8b, tr, fr, sv->sym, 0); + + /* load from the temporal register */ + fr = tr | VT_LVAL; + } + + v = fr & VT_VALMASK; + if (fr & VT_LVAL) { + if (v == VT_LLOCAL) { + v1.type.t = VT_PTR; + v1.r = VT_LOCAL | VT_LVAL; + v1.c.ul = fc; + load(r, &v1); + fr = r; + } + if ((ft & VT_BTYPE) == VT_FLOAT) { + o(0x6e0f66); /* movd */ + r = 0; + } else if ((ft & VT_BTYPE) == VT_DOUBLE) { + o(0x7e0ff3); /* movq */ + r = 0; + } else if ((ft & VT_BTYPE) == VT_LDOUBLE) { + o(0xdb); /* fldt */ + r = 5; + } else if ((ft & VT_TYPE) == VT_BYTE) { + o(0xbe0f); /* movsbl */ + } else if ((ft & VT_TYPE) == (VT_BYTE | VT_UNSIGNED)) { + o(0xb60f); /* movzbl */ + } else if ((ft & VT_TYPE) == VT_SHORT) { + o(0xbf0f); /* movswl */ + } else if ((ft & VT_TYPE) == (VT_SHORT | VT_UNSIGNED)) { + o(0xb70f); /* movzwl */ + } else if (is64_type(ft)) { + gen_modrm64(0x8b, r, fr, sv->sym, fc); + return; + } else { + o(0x8b); /* movl */ + } + gen_modrm(r, fr, sv->sym, fc); + } else { + if (v == VT_CONST) { + if ((ft & VT_BTYPE) == VT_LLONG) { + assert(!(fr & VT_SYM)); + o(0x48); + o(0xb8 + REG_VALUE(r)); /* mov $xx, r */ + gen_addr64(fr, sv->sym, sv->c.ull); + } else { + if (fr & VT_SYM) { + if (sv->sym->type.t & VT_STATIC) { + o(0x8d48); + o(0x05 + REG_VALUE(r) * 8); /* lea xx(%rip), r */ + gen_addrpc32(fr, sv->sym, fc); + } else { + o(0x8b48); + o(0x05 + REG_VALUE(r) * 8); /* mov xx(%rip), r */ + gen_gotpcrel(r, sv->sym, fc); + } + } else { + o(0xb8 + REG_VALUE(r)); /* mov $xx, r */ + gen_le32(fc); + } + } + } else if (v == VT_LOCAL) { + o(0x48 | REX_BASE(r)); + o(0x8d); /* lea xxx(%ebp), r */ + gen_modrm(r, VT_LOCAL, sv->sym, fc); + } else if (v == VT_CMP) { + oad(0xb8 + r, 0); /* mov $0, r */ + o(0x0f); /* setxx %br */ + o(fc); + o(0xc0 + r); + } else if (v == VT_JMP || v == VT_JMPI) { + t = v & 1; + oad(0xb8 + r, t); /* mov $1, r */ + o(0x05eb); /* jmp after */ + gsym(fc); + oad(0xb8 + r, t ^ 1); /* mov $0, r */ + } else if (v != r) { + if (r == TREG_XMM0) { + assert(v == TREG_ST0); + /* gen_cvt_ftof(VT_DOUBLE); */ + o(0xf0245cdd); /* fstpl -0x10(%rsp) */ + /* movsd -0x10(%rsp),%xmm0 */ + o(0x44100ff2); + o(0xf024); + } else if (r == TREG_ST0) { + assert(v == TREG_XMM0); + /* gen_cvt_ftof(VT_LDOUBLE); */ + /* movsd %xmm0,-0x10(%rsp) */ + o(0x44110ff2); + o(0xf024); + o(0xf02444dd); /* fldl -0x10(%rsp) */ + } else { + o(0x48 | REX_BASE(r) | (REX_BASE(v) << 2)); + o(0x89); + o(0xc0 + r + v * 8); /* mov v, r */ + } + } + } +} + +/* store register 'r' in lvalue 'v' */ +void store(int r, SValue *v) +{ + int fr, bt, ft, fc; + int op64 = 0; + /* store the REX prefix in this variable when PIC is enabled */ + int pic = 0; + + ft = v->type.t; + fc = v->c.ul; + fr = v->r & VT_VALMASK; + bt = ft & VT_BTYPE; + + /* we need to access the variable via got */ + if (fr == VT_CONST && (v->r & VT_SYM)) { + /* mov xx(%rip), %r11 */ + o(0x1d8b4c); + gen_gotpcrel(TREG_R11, v->sym, v->c.ul); + pic = is64_type(bt) ? 0x49 : 0x41; + } + + /* XXX: incorrect if float reg to reg */ + if (bt == VT_FLOAT) { + o(0x66); + o(pic); + o(0x7e0f); /* movd */ + r = 0; + } else if (bt == VT_DOUBLE) { + o(0x66); + o(pic); + o(0xd60f); /* movq */ + r = 0; + } else if (bt == VT_LDOUBLE) { + o(0xc0d9); /* fld %st(0) */ + o(pic); + o(0xdb); /* fstpt */ + r = 7; + } else { + if (bt == VT_SHORT) + o(0x66); + o(pic); + if (bt == VT_BYTE || bt == VT_BOOL) + o(0x88); + else if (is64_type(bt)) + op64 = 0x89; + else + o(0x89); + } + if (pic) { + /* xxx r, (%r11) where xxx is mov, movq, fld, or etc */ + if (op64) + o(op64); + o(3 + (r << 3)); + } else if (op64) { + if (fr == VT_CONST || + fr == VT_LOCAL || + (v->r & VT_LVAL)) { + gen_modrm64(op64, r, v->r, v->sym, fc); + } else if (fr != r) { + /* XXX: don't we really come here? */ + abort(); + o(0xc0 + fr + r * 8); /* mov r, fr */ + } + } else { + if (fr == VT_CONST || + fr == VT_LOCAL || + (v->r & VT_LVAL)) { + gen_modrm(r, v->r, v->sym, fc); + } else if (fr != r) { + /* XXX: don't we really come here? */ + abort(); + o(0xc0 + fr + r * 8); /* mov r, fr */ + } + } +} + +static void gadd_sp(int val) +{ + if (val == (char)val) { + o(0xc48348); + g(val); + } else { + oad(0xc48148, val); /* add $xxx, %rsp */ + } +} + +/* 'is_jmp' is '1' if it is a jump */ +static void gcall_or_jmp(int is_jmp) +{ + int r; + if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { + /* constant case */ + if (vtop->r & VT_SYM) { + /* relocation case */ + greloc(cur_text_section, vtop->sym, + ind + 1, R_X86_64_PC32); + } else { + /* put an empty PC32 relocation */ + put_elf_reloc(symtab_section, cur_text_section, + ind + 1, R_X86_64_PC32, 0); + } + oad(0xe8 + is_jmp, vtop->c.ul - 4); /* call/jmp im */ + } else { + /* otherwise, indirect call */ + r = TREG_R11; + load(r, vtop); + o(0x41); /* REX */ + o(0xff); /* call/jmp *r */ + o(0xd0 + REG_VALUE(r) + (is_jmp << 4)); + } +} + +static uint8_t arg_regs[6] = { + TREG_RDI, TREG_RSI, TREG_RDX, TREG_RCX, TREG_R8, TREG_R9 +}; +/* Generate function call. The function address is pushed first, then + all the parameters in call order. This functions pops all the + parameters and the function address. */ +void gfunc_call(int nb_args) +{ + int size, align, r, args_size, i, func_call; + Sym *func_sym; + SValue *orig_vtop; + int nb_reg_args = 0; + int nb_sse_args = 0; + int sse_reg, gen_reg; + + /* calculate the number of integer/float arguments */ + args_size = 0; + for(i = 0; i < nb_args; i++) { + if ((vtop[-i].type.t & VT_BTYPE) == VT_STRUCT) { + args_size += type_size(&vtop->type, &align); + } else if ((vtop[-i].type.t & VT_BTYPE) == VT_LDOUBLE) { + args_size += 16; + } else if (is_sse_float(vtop[-i].type.t)) { + nb_sse_args++; + if (nb_sse_args > 8) args_size += 8; + } else { + nb_reg_args++; + if (nb_reg_args > 6) args_size += 8; + } + } + + /* for struct arguments, we need to call memcpy and the function + call breaks register passing arguments we are preparing. + So, we process arguments which will be passed by stack first. */ + orig_vtop = vtop; + gen_reg = nb_reg_args; + sse_reg = nb_sse_args; + /* adjust stack to align SSE boundary */ + if (args_size &= 8) { + o(0x50); /* push $rax */ + } + for(i = 0; i < nb_args; i++) { + if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { + size = type_size(&vtop->type, &align); + /* align to stack align size */ + size = (size + 3) & ~3; + /* allocate the necessary size on stack */ + o(0x48); + oad(0xec81, size); /* sub $xxx, %rsp */ + /* generate structure store */ + r = get_reg(RC_INT); + o(0x48 + REX_BASE(r)); + o(0x89); /* mov %rsp, r */ + o(0xe0 + r); + { + /* following code breaks vtop[1] */ + SValue tmp = vtop[1]; + vset(&vtop->type, r | VT_LVAL, 0); + vswap(); + vstore(); + vtop[1] = tmp; + } + args_size += size; + } else if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { + gv(RC_ST0); + size = LDOUBLE_SIZE; + oad(0xec8148, size); /* sub $xxx, %rsp */ + o(0x7cdb); /* fstpt 0(%rsp) */ + g(0x24); + g(0x00); + args_size += size; + } else if (is_sse_float(vtop->type.t)) { + int j = --sse_reg; + if (j >= 8) { + gv(RC_FLOAT); + o(0x50); /* push $rax */ + /* movq %xmm0, (%rsp) */ + o(0x04d60f66); + o(0x24); + args_size += 8; + } + } else { + int j = --gen_reg; + /* simple type */ + /* XXX: implicit cast ? */ + if (j >= 6) { + r = gv(RC_INT); + o(0x50 + r); /* push r */ + args_size += 8; + } + } + vtop--; + } + vtop = orig_vtop; + + /* then, we prepare register passing arguments. + Note that we cannot set RDX and RCX in this loop because gv() + may break these temporary registers. Let's use R10 and R11 + instead of them */ + gen_reg = nb_reg_args; + sse_reg = nb_sse_args; + for(i = 0; i < nb_args; i++) { + if ((vtop->type.t & VT_BTYPE) == VT_STRUCT || + (vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { + } else if (is_sse_float(vtop->type.t)) { + int j = --sse_reg; + if (j < 8) { + gv(RC_FLOAT); /* only one float register */ + /* movaps %xmm0, %xmmN */ + o(0x280f); + o(0xc0 + (sse_reg << 3)); + } + } else { + int j = --gen_reg; + /* simple type */ + /* XXX: implicit cast ? */ + if (j < 6) { + r = gv(RC_INT); + if (j < 2) { + o(0x8948); /* mov */ + o(0xc0 + r * 8 + arg_regs[j]); + } else if (j < 4) { + o(0x8949); /* mov */ + /* j=2: r10, j=3: r11 */ + o(0xc0 + r * 8 + j); + } else { + o(0x8949); /* mov */ + /* j=4: r8, j=5: r9 */ + o(0xc0 + r * 8 + j - 4); + } + } + } + vtop--; + } + + save_regs(0); /* save used temporary registers */ + + /* Copy R10 and R11 into RDX and RCX, respectively */ + if (nb_reg_args > 2) { + o(0xd2894c); /* mov %r10, %rdx */ + if (nb_reg_args > 3) { + o(0xd9894c); /* mov %r11, %rcx */ + } + } + + func_sym = vtop->type.ref; + func_call = FUNC_CALL(func_sym->r); + oad(0xb8, nb_sse_args < 8 ? nb_sse_args : 8); /* mov nb_sse_args, %eax */ + gcall_or_jmp(0); + if (args_size) + gadd_sp(args_size); + vtop--; +} + +#ifdef TCC_TARGET_PE +/* XXX: support PE? */ +#warning "PE isn't tested at all" +#define FUNC_PROLOG_SIZE 12 +#else +#define FUNC_PROLOG_SIZE 11 +#endif + +static void push_arg_reg(int i) { + loc -= 8; + gen_modrm64(0x89, arg_regs[i], VT_LOCAL, NULL, loc); +} + +/* generate function prolog of type 't' */ +void gfunc_prolog(CType *func_type) +{ + int i, addr, align, size, func_call; + int param_index, param_addr, reg_param_index, sse_param_index; + Sym *sym; + CType *type; + + func_ret_sub = 0; + + sym = func_type->ref; + func_call = FUNC_CALL(sym->r); + addr = PTR_SIZE * 2; + loc = 0; + ind += FUNC_PROLOG_SIZE; + func_sub_sp_offset = ind; + + if (func_type->ref->c == FUNC_ELLIPSIS) { + int seen_reg_num, seen_sse_num, seen_stack_size; + seen_reg_num = seen_sse_num = 0; + /* frame pointer and return address */ + seen_stack_size = PTR_SIZE * 2; + /* count the number of seen parameters */ + sym = func_type->ref; + while ((sym = sym->next) != NULL) { + type = &sym->type; + if (is_sse_float(type->t)) { + if (seen_sse_num < 8) { + seen_sse_num++; + } else { + seen_stack_size += 8; + } + } else if ((type->t & VT_BTYPE) == VT_STRUCT) { + size = type_size(type, &align); + size = (size + 3) & ~3; + seen_stack_size += size; + } else if ((type->t & VT_BTYPE) == VT_LDOUBLE) { + seen_stack_size += LDOUBLE_SIZE; + } else { + if (seen_reg_num < 6) { + seen_reg_num++; + } else { + seen_stack_size += 8; + } + } + } + + loc -= 16; + /* movl $0x????????, -0x10(%rbp) */ + o(0xf045c7); + gen_le32(seen_reg_num * 8); + /* movl $0x????????, -0xc(%rbp) */ + o(0xf445c7); + gen_le32(seen_sse_num * 16 + 48); + /* movl $0x????????, -0x8(%rbp) */ + o(0xf845c7); + gen_le32(seen_stack_size); + + /* save all register passing arguments */ + for (i = 0; i < 8; i++) { + loc -= 16; + o(0xd60f66); /* movq */ + gen_modrm(7 - i, VT_LOCAL, NULL, loc); + /* movq $0, loc+8(%rbp) */ + o(0x85c748); + gen_le32(loc + 8); + gen_le32(0); + } + for (i = 0; i < 6; i++) { + push_arg_reg(5 - i); + } + } + + sym = func_type->ref; + param_index = 0; + reg_param_index = 0; + sse_param_index = 0; + + /* if the function returns a structure, then add an + implicit pointer parameter */ + func_vt = sym->type; + if ((func_vt.t & VT_BTYPE) == VT_STRUCT) { + push_arg_reg(reg_param_index); + param_addr = loc; + + func_vc = loc; + param_index++; + reg_param_index++; + } + /* define parameters */ + while ((sym = sym->next) != NULL) { + type = &sym->type; + size = type_size(type, &align); + size = (size + 3) & ~3; + if (is_sse_float(type->t)) { + if (sse_param_index < 8) { + /* save arguments passed by register */ + loc -= 8; + o(0xd60f66); /* movq */ + gen_modrm(sse_param_index, VT_LOCAL, NULL, loc); + param_addr = loc; + } else { + param_addr = addr; + addr += size; + } + sse_param_index++; + } else if ((type->t & VT_BTYPE) == VT_STRUCT || + (type->t & VT_BTYPE) == VT_LDOUBLE) { + param_addr = addr; + addr += size; + } else { + if (reg_param_index < 6) { + /* save arguments passed by register */ + push_arg_reg(reg_param_index); + param_addr = loc; + } else { + param_addr = addr; + addr += 8; + } + reg_param_index++; + } + sym_push(sym->v & ~SYM_FIELD, type, + VT_LOCAL | VT_LVAL, param_addr); + param_index++; + } +} + +/* generate function epilog */ +void gfunc_epilog(void) +{ + int v, saved_ind; + + o(0xc9); /* leave */ + if (func_ret_sub == 0) { + o(0xc3); /* ret */ + } else { + o(0xc2); /* ret n */ + g(func_ret_sub); + g(func_ret_sub >> 8); + } + /* align local size to word & save local variables */ + v = (-loc + 15) & -16; + saved_ind = ind; + ind = func_sub_sp_offset - FUNC_PROLOG_SIZE; +#ifdef TCC_TARGET_PE + if (v >= 4096) { + Sym *sym = external_global_sym(TOK___chkstk, &func_old_type, 0); + oad(0xb8, v); /* mov stacksize, %eax */ + oad(0xe8, -4); /* call __chkstk, (does the stackframe too) */ + greloc(cur_text_section, sym, ind-4, R_X86_64_PC32); + } else +#endif + { + o(0xe5894855); /* push %rbp, mov %rsp, %rbp */ + o(0xec8148); /* sub rsp, stacksize */ + gen_le32(v); +#if FUNC_PROLOG_SIZE == 12 + o(0x90); /* adjust to FUNC_PROLOG_SIZE */ +#endif + } + ind = saved_ind; +} + +/* generate a jump to a label */ +int gjmp(int t) +{ + return psym(0xe9, t); +} + +/* generate a jump to a fixed address */ +void gjmp_addr(int a) +{ + int r; + r = a - ind - 2; + if (r == (char)r) { + g(0xeb); + g(r); + } else { + oad(0xe9, a - ind - 5); + } +} + +/* generate a test. set 'inv' to invert test. Stack entry is popped */ +int gtst(int inv, int t) +{ + int v, *p; + + v = vtop->r & VT_VALMASK; + if (v == VT_CMP) { + /* fast case : can jump directly since flags are set */ + g(0x0f); + t = psym((vtop->c.i - 16) ^ inv, t); + } else if (v == VT_JMP || v == VT_JMPI) { + /* && or || optimization */ + if ((v & 1) == inv) { + /* insert vtop->c jump list in t */ + p = &vtop->c.i; + while (*p != 0) + p = (int *)(cur_text_section->data + *p); + *p = t; + t = vtop->c.i; + } else { + t = gjmp(t); + gsym(vtop->c.i); + } + } else { + if (is_float(vtop->type.t) || + (vtop->type.t & VT_BTYPE) == VT_LLONG) { + vpushi(0); + gen_op(TOK_NE); + } + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + /* constant jmp optimization */ + if ((vtop->c.i != 0) != inv) + t = gjmp(t); + } else { + v = gv(RC_INT); + o(0x85); + o(0xc0 + v * 9); + g(0x0f); + t = psym(0x85 ^ inv, t); + } + } + vtop--; + return t; +} + +/* generate an integer binary operation */ +void gen_opi(int op) +{ + int r, fr, opc, c; + + switch(op) { + case '+': + case TOK_ADDC1: /* add with carry generation */ + opc = 0; + gen_op8: + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST && + !is64_type(vtop->type.t)) { + /* constant case */ + vswap(); + r = gv(RC_INT); + if (is64_type(vtop->type.t)) { + o(0x48 | REX_BASE(r)); + } + vswap(); + c = vtop->c.i; + if (c == (char)c) { + /* XXX: generate inc and dec for smaller code ? */ + o(0x83); + o(0xc0 | (opc << 3) | REG_VALUE(r)); + g(c); + } else { + o(0x81); + oad(0xc0 | (opc << 3) | REG_VALUE(r), c); + } + } else { + gv2(RC_INT, RC_INT); + r = vtop[-1].r; + fr = vtop[0].r; + if (opc != 7 || + is64_type(vtop[0].type.t) || (vtop[0].type.t & VT_UNSIGNED) || + is64_type(vtop[-1].type.t) || (vtop[-1].type.t & VT_UNSIGNED)) { + o(0x48 | REX_BASE(r) | (REX_BASE(fr) << 2)); + } + o((opc << 3) | 0x01); + o(0xc0 + REG_VALUE(r) + REG_VALUE(fr) * 8); + } + vtop--; + if (op >= TOK_ULT && op <= TOK_GT) { + vtop->r = VT_CMP; + vtop->c.i = op; + } + break; + case '-': + case TOK_SUBC1: /* sub with carry generation */ + opc = 5; + goto gen_op8; + case TOK_ADDC2: /* add with carry use */ + opc = 2; + goto gen_op8; + case TOK_SUBC2: /* sub with carry use */ + opc = 3; + goto gen_op8; + case '&': + opc = 4; + goto gen_op8; + case '^': + opc = 6; + goto gen_op8; + case '|': + opc = 1; + goto gen_op8; + case '*': + gv2(RC_INT, RC_INT); + r = vtop[-1].r; + fr = vtop[0].r; + if (is64_type(vtop[0].type.t) || (vtop[0].type.t & VT_UNSIGNED) || + is64_type(vtop[-1].type.t) || (vtop[-1].type.t & VT_UNSIGNED)) { + o(0x48 | REX_BASE(fr) | (REX_BASE(r) << 2)); + } + vtop--; + o(0xaf0f); /* imul fr, r */ + o(0xc0 + fr + r * 8); + break; + case TOK_SHL: + opc = 4; + goto gen_shift; + case TOK_SHR: + opc = 5; + goto gen_shift; + case TOK_SAR: + opc = 7; + gen_shift: + opc = 0xc0 | (opc << 3); + if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) { + /* constant case */ + vswap(); + r = gv(RC_INT); + if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { + o(0x48 | REX_BASE(r)); + c = 0x3f; + } else { + c = 0x1f; + } + vswap(); + c &= vtop->c.i; + o(0xc1); /* shl/shr/sar $xxx, r */ + o(opc | r); + g(c); + } else { + /* we generate the shift in ecx */ + gv2(RC_INT, RC_RCX); + r = vtop[-1].r; + if ((vtop[-1].type.t & VT_BTYPE) == VT_LLONG) { + o(0x48 | REX_BASE(r)); + } + o(0xd3); /* shl/shr/sar %cl, r */ + o(opc | r); + } + vtop--; + break; + case '/': + case TOK_UDIV: + case TOK_PDIV: + case '%': + case TOK_UMOD: + case TOK_UMULL: + /* first operand must be in eax */ + /* XXX: need better constraint for second operand */ + gv2(RC_RAX, RC_RCX); + r = vtop[-1].r; + fr = vtop[0].r; + vtop--; + save_reg(TREG_RDX); + if (op == TOK_UMULL) { + o(0xf7); /* mul fr */ + o(0xe0 + fr); + vtop->r2 = TREG_RDX; + r = TREG_RAX; + } else { + if (op == TOK_UDIV || op == TOK_UMOD) { + o(0xf7d231); /* xor %edx, %edx, div fr, %eax */ + o(0xf0 + fr); + } else { + if ((vtop->type.t & VT_BTYPE) & VT_LLONG) { + o(0x9948); /* cqto */ + o(0x48 + REX_BASE(fr)); + } else { + o(0x99); /* cltd */ + } + o(0xf7); /* idiv fr, %eax */ + o(0xf8 + fr); + } + if (op == '%' || op == TOK_UMOD) + r = TREG_RDX; + else + r = TREG_RAX; + } + vtop->r = r; + break; + default: + opc = 7; + goto gen_op8; + } +} + +void gen_opl(int op) +{ + gen_opi(op); +} + +/* generate a floating point operation 'v = t1 op t2' instruction. The + two operands are guaranted to have the same floating point type */ +/* XXX: need to use ST1 too */ +void gen_opf(int op) +{ + int a, ft, fc, swapped, r; + int float_type = + (vtop->type.t & VT_BTYPE) == VT_LDOUBLE ? RC_ST0 : RC_FLOAT; + + /* convert constants to memory references */ + if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { + vswap(); + gv(float_type); + vswap(); + } + if ((vtop[0].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) + gv(float_type); + + /* must put at least one value in the floating point register */ + if ((vtop[-1].r & VT_LVAL) && + (vtop[0].r & VT_LVAL)) { + vswap(); + gv(float_type); + vswap(); + } + swapped = 0; + /* swap the stack if needed so that t1 is the register and t2 is + the memory reference */ + if (vtop[-1].r & VT_LVAL) { + vswap(); + swapped = 1; + } + if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { + if (op >= TOK_ULT && op <= TOK_GT) { + /* load on stack second operand */ + load(TREG_ST0, vtop); + save_reg(TREG_RAX); /* eax is used by FP comparison code */ + if (op == TOK_GE || op == TOK_GT) + swapped = !swapped; + else if (op == TOK_EQ || op == TOK_NE) + swapped = 0; + if (swapped) + o(0xc9d9); /* fxch %st(1) */ + o(0xe9da); /* fucompp */ + o(0xe0df); /* fnstsw %ax */ + if (op == TOK_EQ) { + o(0x45e480); /* and $0x45, %ah */ + o(0x40fC80); /* cmp $0x40, %ah */ + } else if (op == TOK_NE) { + o(0x45e480); /* and $0x45, %ah */ + o(0x40f480); /* xor $0x40, %ah */ + op = TOK_NE; + } else if (op == TOK_GE || op == TOK_LE) { + o(0x05c4f6); /* test $0x05, %ah */ + op = TOK_EQ; + } else { + o(0x45c4f6); /* test $0x45, %ah */ + op = TOK_EQ; + } + vtop--; + vtop->r = VT_CMP; + vtop->c.i = op; + } else { + /* no memory reference possible for long double operations */ + load(TREG_ST0, vtop); + swapped = !swapped; + + switch(op) { + case '-': + a = 4; + if (swapped) + a++; + break; + case '*': + a = 1; + break; + case '/': + a = 6; + if (swapped) + a++; + break; + case '+': + default: + a = 0; + break; + } + ft = vtop->type.t; + fc = vtop->c.ul; + o(0xde); /* fxxxp %st, %st(1) */ + o(0xc1 + (a << 3)); + vtop--; + } + } else { + if (op >= TOK_ULT && op <= TOK_GT) { + /* if saved lvalue, then we must reload it */ + r = vtop->r; + fc = vtop->c.ul; + if ((r & VT_VALMASK) == VT_LLOCAL) { + SValue v1; + r = get_reg(RC_INT); + v1.type.t = VT_INT; + v1.r = VT_LOCAL | VT_LVAL; + v1.c.ul = fc; + load(r, &v1); + fc = 0; + } + + if (op == TOK_EQ || op == TOK_NE) { + swapped = 0; + } else { + if (op == TOK_LE || op == TOK_LT) + swapped = !swapped; + if (op == TOK_LE || op == TOK_GE) { + op = 0x93; /* setae */ + } else { + op = 0x97; /* seta */ + } + } + + if (swapped) { + o(0x7e0ff3); /* movq */ + gen_modrm(1, r, vtop->sym, fc); + + if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) { + o(0x66); + } + o(0x2e0f); /* ucomisd %xmm0, %xmm1 */ + o(0xc8); + } else { + if ((vtop->type.t & VT_BTYPE) == VT_DOUBLE) { + o(0x66); + } + o(0x2e0f); /* ucomisd */ + gen_modrm(0, r, vtop->sym, fc); + } + + vtop--; + vtop->r = VT_CMP; + vtop->c.i = op; + } else { + /* no memory reference possible for long double operations */ + if ((vtop->type.t & VT_BTYPE) == VT_LDOUBLE) { + load(TREG_XMM0, vtop); + swapped = !swapped; + } + switch(op) { + case '-': + a = 4; + break; + case '*': + a = 1; + break; + case '/': + a = 6; + break; + case '+': + default: + a = 0; + break; + } + ft = vtop->type.t; + fc = vtop->c.ul; + if ((ft & VT_BTYPE) == VT_LDOUBLE) { + o(0xde); /* fxxxp %st, %st(1) */ + o(0xc1 + (a << 3)); + } else { + /* if saved lvalue, then we must reload it */ + r = vtop->r; + if ((r & VT_VALMASK) == VT_LLOCAL) { + SValue v1; + r = get_reg(RC_INT); + v1.type.t = VT_INT; + v1.r = VT_LOCAL | VT_LVAL; + v1.c.ul = fc; + load(r, &v1); + fc = 0; + } + if (swapped) { + /* movq %xmm0,%xmm1 */ + o(0x7e0ff3); + o(0xc8); + load(TREG_XMM0, vtop); + /* subsd %xmm1,%xmm0 (f2 0f 5c c1) */ + if ((ft & VT_BTYPE) == VT_DOUBLE) { + o(0xf2); + } else { + o(0xf3); + } + o(0x0f); + o(0x58 + a); + o(0xc1); + } else { + if ((ft & VT_BTYPE) == VT_DOUBLE) { + o(0xf2); + } else { + o(0xf3); + } + o(0x0f); + o(0x58 + a); + gen_modrm(0, r, vtop->sym, fc); + } + } + vtop--; + } + } +} + +/* convert integers to fp 't' type. Must handle 'int', 'unsigned int' + and 'long long' cases. */ +void gen_cvt_itof(int t) +{ + if ((t & VT_BTYPE) == VT_LDOUBLE) { + save_reg(TREG_ST0); + gv(RC_INT); + if ((vtop->type.t & VT_BTYPE) == VT_LLONG) { + /* signed long long to float/double/long double (unsigned case + is handled generically) */ + o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ + o(0x242cdf); /* fildll (%rsp) */ + o(0x08c48348); /* add $8, %rsp */ + } else if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == + (VT_INT | VT_UNSIGNED)) { + /* unsigned int to float/double/long double */ + o(0x6a); /* push $0 */ + g(0x00); + o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ + o(0x242cdf); /* fildll (%rsp) */ + o(0x10c48348); /* add $16, %rsp */ + } else { + /* int to float/double/long double */ + o(0x50 + (vtop->r & VT_VALMASK)); /* push r */ + o(0x2404db); /* fildl (%rsp) */ + o(0x08c48348); /* add $8, %rsp */ + } + vtop->r = TREG_ST0; + } else { + save_reg(TREG_XMM0); + gv(RC_INT); + o(0xf2 + ((t & VT_BTYPE) == VT_FLOAT)); + if ((vtop->type.t & (VT_BTYPE | VT_UNSIGNED)) == + (VT_INT | VT_UNSIGNED) || + (vtop->type.t & VT_BTYPE) == VT_LLONG) { + o(0x48); /* REX */ + } + o(0x2a0f); + o(0xc0 + (vtop->r & VT_VALMASK)); /* cvtsi2sd */ + vtop->r = TREG_XMM0; + } +} + +/* convert from one floating point type to another */ +void gen_cvt_ftof(int t) +{ + int ft, bt, tbt; + + ft = vtop->type.t; + bt = ft & VT_BTYPE; + tbt = t & VT_BTYPE; + + if (bt == VT_FLOAT) { + gv(RC_FLOAT); + if (tbt == VT_DOUBLE) { + o(0xc0140f); /* unpcklps */ + o(0xc05a0f); /* cvtps2pd */ + } else if (tbt == VT_LDOUBLE) { + /* movss %xmm0,-0x10(%rsp) */ + o(0x44110ff3); + o(0xf024); + o(0xf02444d9); /* flds -0x10(%rsp) */ + vtop->r = TREG_ST0; + } + } else if (bt == VT_DOUBLE) { + gv(RC_FLOAT); + if (tbt == VT_FLOAT) { + o(0xc0140f66); /* unpcklpd */ + o(0xc05a0f66); /* cvtpd2ps */ + } else if (tbt == VT_LDOUBLE) { + /* movsd %xmm0,-0x10(%rsp) */ + o(0x44110ff2); + o(0xf024); + o(0xf02444dd); /* fldl -0x10(%rsp) */ + vtop->r = TREG_ST0; + } + } else { + gv(RC_ST0); + if (tbt == VT_DOUBLE) { + o(0xf0245cdd); /* fstpl -0x10(%rsp) */ + /* movsd -0x10(%rsp),%xmm0 */ + o(0x44100ff2); + o(0xf024); + vtop->r = TREG_XMM0; + } else if (tbt == VT_FLOAT) { + o(0xf0245cd9); /* fstps -0x10(%rsp) */ + /* movss -0x10(%rsp),%xmm0 */ + o(0x44100ff3); + o(0xf024); + vtop->r = TREG_XMM0; + } + } +} + +/* convert fp to int 't' type */ +void gen_cvt_ftoi(int t) +{ + int ft, bt, size, r; + ft = vtop->type.t; + bt = ft & VT_BTYPE; + if (bt == VT_LDOUBLE) { + gen_cvt_ftof(VT_DOUBLE); + bt = VT_DOUBLE; + } + + gv(RC_FLOAT); + if (t != VT_INT) + size = 8; + else + size = 4; + + r = get_reg(RC_INT); + if (bt == VT_FLOAT) { + o(0xf3); + } else if (bt == VT_DOUBLE) { + o(0xf2); + } else { + assert(0); + } + if (size == 8) { + o(0x48 + REX_BASE(r)); + } + o(0x2c0f); /* cvttss2si or cvttsd2si */ + o(0xc0 + (REG_VALUE(r) << 3)); + vtop->r = r; +} + +/* computed goto support */ +void ggoto(void) +{ + gcall_or_jmp(1); + vtop--; +} + +/* end of x86-64 code generator */ +/*************************************************************/ -- cgit v1.2.3