primitive fastcall functions support - -fno-common option - -Ttext linker option - bit field fixes - section alignment fixes
This commit is contained in:
124
tcc.c
124
tcc.c
@ -205,12 +205,12 @@ typedef struct DLLReference {
|
|||||||
typedef struct AttributeDef {
|
typedef struct AttributeDef {
|
||||||
int aligned;
|
int aligned;
|
||||||
Section *section;
|
Section *section;
|
||||||
unsigned char func_call; /* FUNC_CDECL or FUNC_STDCALL */
|
unsigned char func_call; /* FUNC_CDECL, FUNC_STDCALL, FUNC_FASTCALLx */
|
||||||
} AttributeDef;
|
} AttributeDef;
|
||||||
|
|
||||||
#define SYM_STRUCT 0x40000000 /* struct/union/enum symbol space */
|
#define SYM_STRUCT 0x40000000 /* struct/union/enum symbol space */
|
||||||
#define SYM_FIELD 0x20000000 /* struct/union field symbol space */
|
#define SYM_FIELD 0x20000000 /* struct/union field symbol space */
|
||||||
#define SYM_FIRST_ANOM (1 << (31 - VT_STRUCT_SHIFT)) /* first anonymous sym */
|
#define SYM_FIRST_ANOM 0x10000000 /* first anonymous sym */
|
||||||
|
|
||||||
/* stored in 'Sym.c' field */
|
/* stored in 'Sym.c' field */
|
||||||
#define FUNC_NEW 1 /* ansi function prototype */
|
#define FUNC_NEW 1 /* ansi function prototype */
|
||||||
@ -220,6 +220,9 @@ typedef struct AttributeDef {
|
|||||||
/* stored in 'Sym.r' field */
|
/* stored in 'Sym.r' field */
|
||||||
#define FUNC_CDECL 0 /* standard c call */
|
#define FUNC_CDECL 0 /* standard c call */
|
||||||
#define FUNC_STDCALL 1 /* pascal 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 */
|
||||||
|
|
||||||
/* field 'Sym.t' for macros */
|
/* field 'Sym.t' for macros */
|
||||||
#define MACRO_OBJ 0 /* object like macro */
|
#define MACRO_OBJ 0 /* object like macro */
|
||||||
@ -299,10 +302,12 @@ static int parse_flags;
|
|||||||
#define PARSE_FLAG_LINEFEED 0x0004 /* line feed is returned as a
|
#define PARSE_FLAG_LINEFEED 0x0004 /* line feed is returned as a
|
||||||
token. line feed is also
|
token. line feed is also
|
||||||
returned at eof */
|
returned at eof */
|
||||||
|
#define PARSE_FLAG_ASM_COMMENTS 0x0008 /* '#' can be used for line comment */
|
||||||
|
|
||||||
static Section *text_section, *data_section, *bss_section; /* predefined sections */
|
static Section *text_section, *data_section, *bss_section; /* predefined sections */
|
||||||
static Section *cur_text_section; /* current section where function code is
|
static Section *cur_text_section; /* current section where function code is
|
||||||
generated */
|
generated */
|
||||||
|
static Section *last_text_section; /* to handle .previous asm directive */
|
||||||
/* bound check related sections */
|
/* bound check related sections */
|
||||||
static Section *bounds_section; /* contains global data bound description */
|
static Section *bounds_section; /* contains global data bound description */
|
||||||
static Section *lbounds_section; /* contains local data bound description */
|
static Section *lbounds_section; /* contains local data bound description */
|
||||||
@ -413,6 +418,8 @@ struct TCCState {
|
|||||||
int nostdinc; /* if true, no standard headers are added */
|
int nostdinc; /* if true, no standard headers are added */
|
||||||
int nostdlib; /* if true, no standard libraries 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 */
|
/* if true, static linking is performed */
|
||||||
int static_link;
|
int static_link;
|
||||||
|
|
||||||
@ -422,6 +429,10 @@ struct TCCState {
|
|||||||
/* if true, only link in referenced objects from archive */
|
/* if true, only link in referenced objects from archive */
|
||||||
int alacarte_link;
|
int alacarte_link;
|
||||||
|
|
||||||
|
/* address of text section */
|
||||||
|
unsigned long text_addr;
|
||||||
|
int has_text_addr;
|
||||||
|
|
||||||
/* C language options */
|
/* C language options */
|
||||||
int char_is_unsigned;
|
int char_is_unsigned;
|
||||||
|
|
||||||
@ -789,9 +800,12 @@ typedef struct ASMOperand {
|
|||||||
char asm_str[16]; /* computed asm string for operand */
|
char asm_str[16]; /* computed asm string for operand */
|
||||||
SValue *vt; /* C value of the expression */
|
SValue *vt; /* C value of the expression */
|
||||||
int ref_index; /* if >= 0, gives reference to a output constraint */
|
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 priority; /* priority, used to assign registers */
|
||||||
int reg; /* if >= 0, register number used for this operand */
|
int reg; /* if >= 0, register number used for this operand */
|
||||||
int is_llong; /* true if double register value */
|
int is_llong; /* true if double register value */
|
||||||
|
int is_memory; /* true if memory operand */
|
||||||
|
int is_rw; /* for '+' modifier */
|
||||||
} ASMOperand;
|
} ASMOperand;
|
||||||
|
|
||||||
static void asm_expr(TCCState *s1, ExprValue *pe);
|
static void asm_expr(TCCState *s1, ExprValue *pe);
|
||||||
@ -804,6 +818,7 @@ static int tcc_assemble(TCCState *s1, int do_preprocess);
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void asm_instr(void);
|
static void asm_instr(void);
|
||||||
|
static void asm_global_instr(void);
|
||||||
|
|
||||||
/* true if float/double/long double type */
|
/* true if float/double/long double type */
|
||||||
static inline int is_float(int t)
|
static inline int is_float(int t)
|
||||||
@ -914,6 +929,22 @@ static char *pstrcat(char *buf, int buf_size, const char *s)
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/* memory management */
|
/* memory management */
|
||||||
#ifdef MEM_DEBUG
|
#ifdef MEM_DEBUG
|
||||||
int mem_cur_size;
|
int mem_cur_size;
|
||||||
@ -1086,6 +1117,8 @@ Section *find_section(TCCState *s1, const char *name)
|
|||||||
return new_section(s1, name, SHT_PROGBITS, SHF_ALLOC);
|
return new_section(s1, name, SHT_PROGBITS, SHF_ALLOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define SECTION_ABS ((void *)1)
|
||||||
|
|
||||||
/* update sym->c so that it points to an external symbol in section
|
/* update sym->c so that it points to an external symbol in section
|
||||||
'section' with value 'value' */
|
'section' with value 'value' */
|
||||||
static void put_extern_sym(Sym *sym, Section *section,
|
static void put_extern_sym(Sym *sym, Section *section,
|
||||||
@ -1095,10 +1128,12 @@ static void put_extern_sym(Sym *sym, Section *section,
|
|||||||
Elf32_Sym *esym;
|
Elf32_Sym *esym;
|
||||||
const char *name;
|
const char *name;
|
||||||
|
|
||||||
if (section)
|
if (section == NULL)
|
||||||
sh_num = section->sh_num;
|
|
||||||
else
|
|
||||||
sh_num = SHN_UNDEF;
|
sh_num = SHN_UNDEF;
|
||||||
|
else if (section == SECTION_ABS)
|
||||||
|
sh_num = SHN_ABS;
|
||||||
|
else
|
||||||
|
sh_num = section->sh_num;
|
||||||
if (!sym->c) {
|
if (!sym->c) {
|
||||||
if ((sym->type.t & VT_BTYPE) == VT_FUNC)
|
if ((sym->type.t & VT_BTYPE) == VT_FUNC)
|
||||||
sym_type = STT_FUNC;
|
sym_type = STT_FUNC;
|
||||||
@ -2850,6 +2885,7 @@ static void preprocess(int is_bof)
|
|||||||
/* '!' is ignored to allow C scripts. numbers are ignored
|
/* '!' is ignored to allow C scripts. numbers are ignored
|
||||||
to emulate cpp behaviour */
|
to emulate cpp behaviour */
|
||||||
} else {
|
} else {
|
||||||
|
if (!(saved_parse_flags & PARSE_FLAG_ASM_COMMENTS))
|
||||||
error("invalid preprocessing directive #%s", get_tok_str(tok, &tokc));
|
error("invalid preprocessing directive #%s", get_tok_str(tok, &tokc));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -3366,10 +3402,15 @@ static inline void next_nomacro1(void)
|
|||||||
if (c == '#') {
|
if (c == '#') {
|
||||||
p++;
|
p++;
|
||||||
tok = TOK_TWOSHARPS;
|
tok = TOK_TWOSHARPS;
|
||||||
|
} else {
|
||||||
|
if (parse_flags & PARSE_FLAG_ASM_COMMENTS) {
|
||||||
|
p = parse_line_comment(p - 1);
|
||||||
|
goto redo_no_start;
|
||||||
} else {
|
} else {
|
||||||
tok = '#';
|
tok = '#';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'a': case 'b': case 'c': case 'd':
|
case 'a': case 'b': case 'c': case 'd':
|
||||||
@ -3815,7 +3856,6 @@ static int macro_subst_tok(TokenString *tok_str,
|
|||||||
char buf[32];
|
char buf[32];
|
||||||
|
|
||||||
/* if symbol is a macro, prepare substitution */
|
/* if symbol is a macro, prepare substitution */
|
||||||
|
|
||||||
/* special macros */
|
/* special macros */
|
||||||
if (tok == TOK___LINE__) {
|
if (tok == TOK___LINE__) {
|
||||||
snprintf(buf, sizeof(buf), "%d", file->line_num);
|
snprintf(buf, sizeof(buf), "%d", file->line_num);
|
||||||
@ -5745,6 +5785,9 @@ static int is_compatible_func(CType *type1, CType *type2)
|
|||||||
s2 = type2->ref;
|
s2 = type2->ref;
|
||||||
if (!is_compatible_types(&s1->type, &s2->type))
|
if (!is_compatible_types(&s1->type, &s2->type))
|
||||||
return 0;
|
return 0;
|
||||||
|
/* check func_call */
|
||||||
|
if (s1->r != s2->r)
|
||||||
|
return 0;
|
||||||
/* XXX: not complete */
|
/* XXX: not complete */
|
||||||
if (s1->c == FUNC_OLD || s2->c == FUNC_OLD)
|
if (s1->c == FUNC_OLD || s2->c == FUNC_OLD)
|
||||||
return 1;
|
return 1;
|
||||||
@ -5985,6 +6028,7 @@ void vstore(void)
|
|||||||
warning("assignment of read-only location");
|
warning("assignment of read-only location");
|
||||||
} else {
|
} else {
|
||||||
delayed_cast = 0;
|
delayed_cast = 0;
|
||||||
|
if (!(ft & VT_BITFIELD))
|
||||||
gen_assign_cast(&vtop[-1].type);
|
gen_assign_cast(&vtop[-1].type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6164,6 +6208,20 @@ static void parse_attribute(AttributeDef *ad)
|
|||||||
case TOK_STDCALL3:
|
case TOK_STDCALL3:
|
||||||
ad->func_call = FUNC_STDCALL;
|
ad->func_call = FUNC_STDCALL;
|
||||||
break;
|
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)
|
||||||
|
ad->func_call = FUNC_FASTCALL1 + n - 1;
|
||||||
|
skip(')');
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
if (tcc_state->warn_unsupported)
|
if (tcc_state->warn_unsupported)
|
||||||
warning("'%s' attribute ignored", get_tok_str(t, NULL));
|
warning("'%s' attribute ignored", get_tok_str(t, NULL));
|
||||||
@ -6280,6 +6338,8 @@ static void struct_decl(CType *type, int u)
|
|||||||
get_tok_str(v, NULL));
|
get_tok_str(v, NULL));
|
||||||
}
|
}
|
||||||
size = type_size(&type1, &align);
|
size = type_size(&type1, &align);
|
||||||
|
if (align < ad.aligned)
|
||||||
|
align = ad.aligned;
|
||||||
lbit_pos = 0;
|
lbit_pos = 0;
|
||||||
if (bit_size >= 0) {
|
if (bit_size >= 0) {
|
||||||
bt = type1.t & VT_BTYPE;
|
bt = type1.t & VT_BTYPE;
|
||||||
@ -8418,6 +8478,12 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
|
|||||||
if (sym->type.t & VT_EXTERN) {
|
if (sym->type.t & VT_EXTERN) {
|
||||||
/* if the variable is extern, it was not allocated */
|
/* if the variable is extern, it was not allocated */
|
||||||
sym->type.t &= ~VT_EXTERN;
|
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 {
|
} else {
|
||||||
/* we accept several definitions of the same
|
/* we accept several definitions of the same
|
||||||
global variable. this is tricky, because we
|
global variable. this is tricky, because we
|
||||||
@ -8437,6 +8503,8 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
|
|||||||
if (!sec) {
|
if (!sec) {
|
||||||
if (has_init)
|
if (has_init)
|
||||||
sec = data_section;
|
sec = data_section;
|
||||||
|
else if (tcc_state->nocommon)
|
||||||
|
sec = bss_section;
|
||||||
}
|
}
|
||||||
if (sec) {
|
if (sec) {
|
||||||
data_offset = sec->data_offset;
|
data_offset = sec->data_offset;
|
||||||
@ -8453,6 +8521,9 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
|
|||||||
if (sec->sh_type != SHT_NOBITS &&
|
if (sec->sh_type != SHT_NOBITS &&
|
||||||
data_offset > sec->data_allocated)
|
data_offset > sec->data_allocated)
|
||||||
section_realloc(sec, data_offset);
|
section_realloc(sec, data_offset);
|
||||||
|
/* align section if needed */
|
||||||
|
if (align > sec->sh_addralign)
|
||||||
|
sec->sh_addralign = align;
|
||||||
} else {
|
} else {
|
||||||
addr = 0; /* avoid warning */
|
addr = 0; /* avoid warning */
|
||||||
}
|
}
|
||||||
@ -8646,6 +8717,12 @@ static void decl(int l)
|
|||||||
next();
|
next();
|
||||||
continue;
|
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
|
/* special test for old K&R protos without explicit int
|
||||||
type. Only accepted when defining global data */
|
type. Only accepted when defining global data */
|
||||||
if (l == VT_LOCAL || tok < TOK_DEFINE)
|
if (l == VT_LOCAL || tok < TOK_DEFINE)
|
||||||
@ -8683,7 +8760,6 @@ static void decl(int l)
|
|||||||
ParseState saved_parse_state;
|
ParseState saved_parse_state;
|
||||||
int block_level;
|
int block_level;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (l == VT_LOCAL)
|
if (l == VT_LOCAL)
|
||||||
error("cannot use local functions");
|
error("cannot use local functions");
|
||||||
if (!(type.t & VT_FUNC))
|
if (!(type.t & VT_FUNC))
|
||||||
@ -8738,6 +8814,19 @@ static void decl(int l)
|
|||||||
funcname = get_tok_str(v, NULL);
|
funcname = get_tok_str(v, NULL);
|
||||||
sym = sym_find(v);
|
sym = sym_find(v);
|
||||||
if (sym) {
|
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 */
|
||||||
|
if (sym->type.ref->r != FUNC_CDECL &&
|
||||||
|
type.ref->r == FUNC_CDECL)
|
||||||
|
type.ref->r = sym->type.ref->r;
|
||||||
|
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 */
|
/* if symbol is already defined, then put complete type */
|
||||||
sym->type = type;
|
sym->type = type;
|
||||||
} else {
|
} else {
|
||||||
@ -8790,6 +8879,9 @@ static void decl(int l)
|
|||||||
sym->type.t |= VT_TYPEDEF;
|
sym->type.t |= VT_TYPEDEF;
|
||||||
} else if ((type.t & VT_BTYPE) == VT_FUNC) {
|
} else if ((type.t & VT_BTYPE) == VT_FUNC) {
|
||||||
/* external function definition */
|
/* external function definition */
|
||||||
|
/* specific case for func_call attribute */
|
||||||
|
if (ad.func_call)
|
||||||
|
type.ref->r = ad.func_call;
|
||||||
external_sym(v, &type, 0);
|
external_sym(v, &type, 0);
|
||||||
} else {
|
} else {
|
||||||
/* not lvalue if array */
|
/* not lvalue if array */
|
||||||
@ -9013,6 +9105,10 @@ static void asm_instr(void)
|
|||||||
{
|
{
|
||||||
error("inline asm() not supported");
|
error("inline asm() not supported");
|
||||||
}
|
}
|
||||||
|
static void asm_global_instr(void)
|
||||||
|
{
|
||||||
|
error("inline asm() not supported");
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "tccelf.c"
|
#include "tccelf.c"
|
||||||
@ -9794,6 +9890,7 @@ int tcc_set_warning(TCCState *s, const char *warning_name, int value)
|
|||||||
static const FlagDef flag_defs[] = {
|
static const FlagDef flag_defs[] = {
|
||||||
{ offsetof(TCCState, char_is_unsigned), 0, "unsigned-char" },
|
{ offsetof(TCCState, char_is_unsigned), 0, "unsigned-char" },
|
||||||
{ offsetof(TCCState, char_is_unsigned), FD_INVERT, "signed-char" },
|
{ offsetof(TCCState, char_is_unsigned), FD_INVERT, "signed-char" },
|
||||||
|
{ offsetof(TCCState, nocommon), FD_INVERT, "common" },
|
||||||
};
|
};
|
||||||
|
|
||||||
/* set/reset a flag */
|
/* set/reset a flag */
|
||||||
@ -9897,6 +9994,7 @@ enum {
|
|||||||
TCC_OPTION_shared,
|
TCC_OPTION_shared,
|
||||||
TCC_OPTION_o,
|
TCC_OPTION_o,
|
||||||
TCC_OPTION_r,
|
TCC_OPTION_r,
|
||||||
|
TCC_OPTION_Wl,
|
||||||
TCC_OPTION_W,
|
TCC_OPTION_W,
|
||||||
TCC_OPTION_O,
|
TCC_OPTION_O,
|
||||||
TCC_OPTION_m,
|
TCC_OPTION_m,
|
||||||
@ -9932,6 +10030,7 @@ static const TCCOption tcc_options[] = {
|
|||||||
{ "run", TCC_OPTION_run, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
|
{ "run", TCC_OPTION_run, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
|
||||||
{ "rdynamic", TCC_OPTION_rdynamic, 0 },
|
{ "rdynamic", TCC_OPTION_rdynamic, 0 },
|
||||||
{ "r", TCC_OPTION_r, 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 },
|
{ "W", TCC_OPTION_W, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP },
|
||||||
{ "O", TCC_OPTION_O, 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 },
|
{ "m", TCC_OPTION_m, TCC_OPTION_HAS_ARG },
|
||||||
@ -10144,6 +10243,17 @@ int parse_args(TCCState *s, int argc, char **argv)
|
|||||||
case TCC_OPTION_rdynamic:
|
case TCC_OPTION_rdynamic:
|
||||||
s->rdynamic = 1;
|
s->rdynamic = 1;
|
||||||
break;
|
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 {
|
||||||
|
error("unsupported ld option '%s'", optarg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
if (s->warn_unsupported) {
|
if (s->warn_unsupported) {
|
||||||
unsupported_option:
|
unsupported_option:
|
||||||
|
|||||||
Reference in New Issue
Block a user