mirror of
https://github.com/mirror/tinycc.git
synced 2024-12-26 03:50:07 +08:00
do not generate code for unused inline functions - fixed long long code gen bug - added --oformat linker option
This commit is contained in:
parent
45466d2df6
commit
59c3563827
333
tcc.c
333
tcc.c
@ -20,6 +20,13 @@
|
||||
#define _GNU_SOURCE
|
||||
#include "config.h"
|
||||
|
||||
#ifdef CONFIG_TCCBOOT
|
||||
|
||||
#include "tccboot.h"
|
||||
#define CONFIG_TCC_STATIC
|
||||
|
||||
#else
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
@ -40,11 +47,17 @@
|
||||
#include <sys/time.h>
|
||||
#include <sys/ucontext.h>
|
||||
#endif
|
||||
|
||||
#endif /* !CONFIG_TCCBOOT */
|
||||
|
||||
#include "elf.h"
|
||||
#include "stab.h"
|
||||
#ifndef CONFIG_TCC_STATIC
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
|
||||
#include "libtcc.h"
|
||||
|
||||
@ -101,10 +114,8 @@ typedef int BOOL;
|
||||
#define VSTACK_SIZE 64
|
||||
#define STRING_MAX_SIZE 1024
|
||||
|
||||
#define TOK_HASH_SIZE 2048 /* must be a power of two */
|
||||
#define TOK_HASH_SIZE 8192 /* must be a power of two */
|
||||
#define TOK_ALLOC_INCR 512 /* must be a power of two */
|
||||
#define TOK_STR_ALLOC_INCR_BITS 6
|
||||
#define TOK_STR_ALLOC_INCR (1 << TOK_STR_ALLOC_INCR_BITS)
|
||||
#define TOK_MAX_SIZE 4 /* token max size in int unit when stored in string */
|
||||
|
||||
/* token symbol management */
|
||||
@ -432,6 +443,9 @@ struct TCCState {
|
||||
/* 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;
|
||||
@ -704,6 +718,7 @@ static void block(int *bsym, int *csym, int *case_sym, int *def_sym,
|
||||
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);
|
||||
@ -722,7 +737,6 @@ int get_reg_ex(int rc,int rc2);
|
||||
|
||||
static void macro_subst(TokenString *tok_str, Sym **nested_list,
|
||||
const int *macro_str, int can_read_stream);
|
||||
int save_reg_forced(int r);
|
||||
void gen_op(int op);
|
||||
void force_charshort_cast(int t);
|
||||
static void gen_cast(CType *type);
|
||||
@ -781,7 +795,7 @@ static int tcc_add_dll(TCCState *s, const char *filename, int flags);
|
||||
static int tcc_add_file_internal(TCCState *s, const char *filename, int flags);
|
||||
|
||||
/* tcccoff.c */
|
||||
int tcc_output_coff(TCCState *s1, const char *OutFile);
|
||||
int tcc_output_coff(TCCState *s1, FILE *f);
|
||||
|
||||
/* tccasm.c */
|
||||
|
||||
@ -867,10 +881,12 @@ typedef struct TCCSyms {
|
||||
|
||||
/* add the symbol you want here if no dynamic linking is done */
|
||||
static TCCSyms tcc_syms[] = {
|
||||
#if !defined(CONFIG_TCCBOOT)
|
||||
TCCSYM(printf)
|
||||
TCCSYM(fprintf)
|
||||
TCCSYM(fopen)
|
||||
TCCSYM(fclose)
|
||||
#endif
|
||||
{ NULL, NULL },
|
||||
};
|
||||
|
||||
@ -1722,7 +1738,7 @@ BufferedFile *tcc_open(TCCState *s1, const char *filename)
|
||||
int fd;
|
||||
BufferedFile *bf;
|
||||
|
||||
fd = open(filename, O_RDONLY);
|
||||
fd = open(filename, O_RDONLY | O_BINARY);
|
||||
if (fd < 0)
|
||||
return NULL;
|
||||
bf = tcc_malloc(sizeof(BufferedFile));
|
||||
@ -2254,7 +2270,11 @@ static int *tok_str_realloc(TokenString *s)
|
||||
{
|
||||
int *str, len;
|
||||
|
||||
len = s->allocated_len + TOK_STR_ALLOC_INCR;
|
||||
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");
|
||||
@ -4630,8 +4650,13 @@ int gv(int rc)
|
||||
load(r, vtop);
|
||||
vtop->r = r; /* save register value */
|
||||
vpushi(ll >> 32); /* second word */
|
||||
} else if (r >= VT_CONST ||
|
||||
} 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();
|
||||
@ -7119,7 +7144,20 @@ static void unary(void)
|
||||
get_tok_str(t, NULL));
|
||||
s = external_global_sym(t, &func_old_type, 0);
|
||||
}
|
||||
vset(&s->type, s->r, s->c);
|
||||
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;
|
||||
@ -8594,65 +8632,6 @@ void put_func_debug(Sym *sym)
|
||||
last_line_num = 0;
|
||||
}
|
||||
|
||||
/* not finished : try to put some local vars in registers */
|
||||
//#define CONFIG_REG_VARS
|
||||
|
||||
#ifdef CONFIG_REG_VARS
|
||||
void add_var_ref(int t)
|
||||
{
|
||||
printf("%s:%d: &%s\n",
|
||||
file->filename, file->line_num,
|
||||
get_tok_str(t, NULL));
|
||||
}
|
||||
|
||||
/* first pass on a function with heuristic to extract variable usage
|
||||
and pointer references to local variables for register allocation */
|
||||
void analyse_function(void)
|
||||
{
|
||||
int level, t;
|
||||
|
||||
for(;;) {
|
||||
if (tok == -1)
|
||||
break;
|
||||
/* any symbol coming after '&' is considered as being a
|
||||
variable whose reference is taken. It is highly unaccurate
|
||||
but it is difficult to do better without a complete parse */
|
||||
if (tok == '&') {
|
||||
next();
|
||||
/* if '& number', then no need to examine next tokens */
|
||||
if (tok == TOK_CINT ||
|
||||
tok == TOK_CUINT ||
|
||||
tok == TOK_CLLONG ||
|
||||
tok == TOK_CULLONG) {
|
||||
continue;
|
||||
} else if (tok >= TOK_UIDENT) {
|
||||
/* if '& ident [' or '& ident ->', then ident address
|
||||
is not needed */
|
||||
t = tok;
|
||||
next();
|
||||
if (tok != '[' && tok != TOK_ARROW)
|
||||
add_var_ref(t);
|
||||
} else {
|
||||
level = 0;
|
||||
while (tok != '}' && tok != ';' &&
|
||||
!((tok == ',' || tok == ')') && level == 0)) {
|
||||
if (tok >= TOK_UIDENT) {
|
||||
add_var_ref(tok);
|
||||
} else if (tok == '(') {
|
||||
level++;
|
||||
} else if (tok == ')') {
|
||||
level--;
|
||||
}
|
||||
next();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* parse an old style function declaration list */
|
||||
/* XXX: check multiple parameter */
|
||||
static void func_decl_list(Sym *func_sym)
|
||||
@ -8701,6 +8680,88 @@ static void func_decl_list(Sym *func_sym)
|
||||
}
|
||||
}
|
||||
|
||||
/* parse a function defined by symbol 'sym' and generate its code in
|
||||
'cur_text_section' */
|
||||
static void gen_function(Sym *sym)
|
||||
{
|
||||
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 (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 */
|
||||
((Elf32_Sym *)symtab_section->data)[sym->c].st_size =
|
||||
ind - func_ind;
|
||||
if (do_debug) {
|
||||
put_stabn(N_FUN, 0, 0, ind - func_ind);
|
||||
}
|
||||
funcname = ""; /* for safety */
|
||||
func_vt.t = VT_VOID; /* for safety */
|
||||
ind = 0; /* for safety */
|
||||
}
|
||||
|
||||
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 = (int *)sym->r;
|
||||
sym->r = VT_SYM | VT_CONST;
|
||||
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)) {
|
||||
str = (int *)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)
|
||||
{
|
||||
@ -8755,11 +8816,6 @@ static void decl(int l)
|
||||
}
|
||||
|
||||
if (tok == '{') {
|
||||
#ifdef CONFIG_REG_VARS
|
||||
TokenString func_str;
|
||||
ParseState saved_parse_state;
|
||||
int block_level;
|
||||
#endif
|
||||
if (l == VT_LOCAL)
|
||||
error("cannot use local functions");
|
||||
if (!(type.t & VT_FUNC))
|
||||
@ -8774,44 +8830,7 @@ static void decl(int l)
|
||||
/* 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;
|
||||
|
||||
#ifdef CONFIG_REG_VARS
|
||||
/* parse all function code and record it */
|
||||
|
||||
tok_str_new(&func_str);
|
||||
|
||||
block_level = 0;
|
||||
for(;;) {
|
||||
int t;
|
||||
if (tok == -1)
|
||||
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);
|
||||
|
||||
save_parse_state(&saved_parse_state);
|
||||
|
||||
macro_ptr = func_str.str;
|
||||
next();
|
||||
analyse_function();
|
||||
#endif
|
||||
|
||||
/* compute text section */
|
||||
cur_text_section = ad.section;
|
||||
if (!cur_text_section)
|
||||
cur_text_section = text_section;
|
||||
ind = cur_text_section->data_offset;
|
||||
funcname = get_tok_str(v, NULL);
|
||||
sym = sym_find(v);
|
||||
if (sym) {
|
||||
if ((sym->type.t & VT_BTYPE) != VT_FUNC)
|
||||
@ -8834,42 +8853,44 @@ static void decl(int l)
|
||||
sym = global_identifier_push(v, type.t, 0);
|
||||
sym->type.ref = type.ref;
|
||||
}
|
||||
/* NOTE: we patch the symbol size later */
|
||||
put_extern_sym(sym, cur_text_section, ind, 0);
|
||||
func_ind = ind;
|
||||
sym->r = VT_SYM | VT_CONST;
|
||||
/* put debug symbol */
|
||||
if (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(&type);
|
||||
rsym = 0;
|
||||
#ifdef CONFIG_REG_VARS
|
||||
macro_ptr = func_str.str;
|
||||
next();
|
||||
#endif
|
||||
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 */
|
||||
((Elf32_Sym *)symtab_section->data)[sym->c].st_size =
|
||||
ind - func_ind;
|
||||
if (do_debug) {
|
||||
put_stabn(N_FUN, 0, 0, ind - func_ind);
|
||||
}
|
||||
funcname = ""; /* for safety */
|
||||
func_vt.t = VT_VOID; /* for safety */
|
||||
ind = 0; /* for safety */
|
||||
|
||||
#ifdef CONFIG_REG_VARS
|
||||
tok_str_free(func_str.str);
|
||||
restore_parse_state(&saved_parse_state);
|
||||
#endif
|
||||
/* 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);
|
||||
sym->r = (int)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) {
|
||||
@ -9015,6 +9036,8 @@ static int tcc_compile(TCCState *s1)
|
||||
they are undefined) */
|
||||
free_defines(define_start);
|
||||
|
||||
gen_inline_functions();
|
||||
|
||||
sym_pop(&global_stack, NULL);
|
||||
|
||||
return s1->nb_errors != 0 ? -1 : 0;
|
||||
@ -9239,7 +9262,7 @@ static void rt_printline(unsigned long wanted_pc)
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
#if !defined(WIN32) && !defined(CONFIG_TCCBOOT)
|
||||
|
||||
#ifdef __i386__
|
||||
|
||||
@ -9406,7 +9429,7 @@ int tcc_run(TCCState *s1, int argc, char **argv)
|
||||
prog_main = tcc_get_symbol_err(s1, "main");
|
||||
|
||||
if (do_debug) {
|
||||
#ifdef WIN32
|
||||
#if defined(WIN32) || defined(CONFIG_TCCBOOT)
|
||||
error("debug mode currently not available for Windows");
|
||||
#else
|
||||
struct sigaction sigact;
|
||||
@ -10249,8 +10272,22 @@ int parse_args(TCCState *s, int argc, char **argv)
|
||||
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 ld option '%s'", optarg);
|
||||
error("unsupported linker option '%s'", optarg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user