tccasm: synch C and asm symtab tighter

See testcase.  The C and asm symtab are still separate,
but integrated tighter: the asm labels are only synched at file
end, not after each asm snippet (this fixes references from one
to another asm block), the C and asm syms are synched both ways,
so defining things in asm and refering from C, or the other way
around works.  In effect this model reflects what happens with
GCC better.

For this the asm labels aren't using the C label namespace anymore,
but their own, which increases the size of each TokenSym by a pointer.
This commit is contained in:
Michael Matz 2017-11-22 17:57:43 +01:00
parent 330c01bfc6
commit e7c71e2473
7 changed files with 119 additions and 23 deletions

2
tcc.h
View File

@ -383,6 +383,7 @@ typedef struct TokenSym {
struct Sym *sym_label; /* direct pointer to label */
struct Sym *sym_struct; /* direct pointer to structure */
struct Sym *sym_identifier; /* direct pointer to identifier */
struct Sym *sym_asm_label; /* direct pointer to asm label */
int tok; /* token number */
int len;
char str[1];
@ -1584,6 +1585,7 @@ ST_FUNC Sym* get_asm_sym(int name, Sym *csym);
ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
ST_FUNC int asm_int_expr(TCCState *s1);
ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess);
ST_FUNC void asm_free_labels(TCCState *st);
/* ------------ i386-asm.c ------------ */
ST_FUNC void gen_expr32(ExprValue *pe);
#ifdef TCC_TARGET_X86_64

View File

@ -31,10 +31,27 @@ ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n)
return ts->tok;
}
ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe);
static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global);
static Sym sym_dot;
static Sym *asm_label_find(int v)
{
v -= TOK_IDENT;
if ((unsigned)v >= (unsigned)(tok_ident - TOK_IDENT))
return NULL;
return table_ident[v]->sym_asm_label;
}
static Sym *asm_label_push(Sym **ptop, int v)
{
Sym *s, **ps;
s = sym_push2(ptop, v, 0, 0);
ps = &table_ident[v - TOK_IDENT]->sym_asm_label;
s->prev_tok = *ps;
*ps = s;
return s;
}
/* Return a symbol we can use inside the assembler, having name NAME.
The assembler symbol table is different from the C symbol table
(and the Sym members are used differently). But we must be able
@ -49,10 +66,10 @@ static Sym sym_dot;
we look up NAME in the C symbol table and use that. */
ST_FUNC Sym* get_asm_sym(int name, Sym *csym)
{
Sym *sym = label_find(name);
Sym *sym = asm_label_find(name);
if (!sym) {
sym = label_push(&tcc_state->asm_labels, name, 0);
sym->type.t = VT_VOID | VT_STATIC | VT_EXTERN;
sym = asm_label_push(&tcc_state->asm_labels, name);
sym->type.t = VT_VOID | VT_EXTERN;
if (!csym) {
csym = sym_find(name);
/* We might be called for an asm block from inside a C routine
@ -75,6 +92,7 @@ ST_FUNC Sym* get_asm_sym(int name, Sym *csym)
sym->type.t = VT_VOID | (csym->type.t & VT_STATIC);
/* Mark that this asm symbol doesn't need to be fed back. */
sym->a.dllimport = 1;
sym->a.dllexport = !(csym->type.t & VT_STATIC);
}
}
return sym;
@ -97,7 +115,7 @@ static void asm_expr_unary(TCCState *s1, ExprValue *pe)
if (*p == 'b' || *p == 'f') {
/* backward or forward label */
label = asm_get_local_label_name(s1, n);
sym = label_find(label);
sym = asm_label_find(label);
if (*p == 'b') {
/* backward : find the last corresponding defined label */
if (sym && sym->r == 0)
@ -108,7 +126,7 @@ static void asm_expr_unary(TCCState *s1, ExprValue *pe)
/* 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 = asm_label_push(&s1->asm_labels, label);
sym->type.t = VT_STATIC | VT_VOID | VT_EXTERN;
}
}
@ -355,14 +373,12 @@ ST_FUNC int asm_int_expr(TCCState *s1)
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 Sym* asm_new_label1(TCCState *s1, int label, int is_local,
int sh_num, int value)
{
Sym *sym;
sym = label_find(label);
sym = asm_label_find(label);
if (sym) {
/* A VT_EXTERN symbol, even if it has a section is considered
overridable. This is how we "define" .set targets. Real
@ -379,11 +395,11 @@ static Sym* asm_new_label1(TCCState *s1, int label, int is_local,
}
} else {
new_label:
sym = label_push(&s1->asm_labels, label, 0);
sym = asm_label_push(&s1->asm_labels, label);
/* If we need a symbol to hold a value, mark it as
tentative only (for .set). If this is for a real label
we'll remove VT_EXTERN. */
sym->type.t = VT_STATIC | VT_VOID | VT_EXTERN;
sym->type.t = VT_VOID | (is_local ? VT_STATIC : 0) | VT_EXTERN;
}
sym->r = sh_num;
sym->jnext = value;
@ -427,28 +443,53 @@ static void patch_binding(Sym *sym)
ELFW(ST_TYPE)(esym->st_info));
}
static void asm_free_labels(TCCState *st)
ST_FUNC void asm_free_labels(TCCState *st)
{
Sym *s, *s1;
Section *sec;
for(s = st->asm_labels; s != NULL; s = s1) {
int was_ext = s->type.t & VT_EXTERN;
s1 = s->prev;
/* define symbol value in object file */
/* define symbol value in object file and care for updating
the C and asm symbols */
s->type.t &= ~VT_EXTERN;
if (!s->a.dllimport) {
Sym *csym = sym_find(s->v);
ElfW(Sym) *esym = NULL;
if (csym) {
s->a.dllexport |= !(csym->type.t & VT_STATIC);
if (csym->c) {
esym = &((ElfW(Sym) *)symtab_section->data)[csym->c];
if (s->c) {
/* We have generated code and possibly relocs
referencing the symtab entry s->c (the asm
ELF symbol). If that's undefined but the C
symbol is defined, copy over the info. */
ElfW(Sym) *asm_esym = &((ElfW(Sym) *)symtab_section->data)[s->c];
if (asm_esym->st_shndx == SHN_UNDEF) {
*asm_esym = *esym;
}
}
}
}
if (!s->a.dllexport)
s->type.t |= VT_STATIC;
if (s->r) {
if (s->r == SHN_ABS)
sec = SECTION_ABS;
else
sec = st->sections[s->r];
put_extern_sym2(s, sec, s->jnext, 0, 0);
/* !was_ext so that we run into an multi-def error if
we defined it in C and in asm (non-tentatively) */
if (!esym || esym->st_shndx == SHN_UNDEF || !was_ext)
put_extern_sym2(s, sec, s->jnext, 0, 0);
} else /* undefined symbols are global */
s->type.t &= ~VT_STATIC;
s->type.t &= ~VT_STATIC, s->a.dllexport = 1;
patch_binding(s);
}
patch_binding(s);
/* remove label */
table_ident[s->v - TOK_IDENT]->sym_label = NULL;
table_ident[s->v - TOK_IDENT]->sym_asm_label = NULL;
sym_free(s);
}
st->asm_labels = NULL;
@ -709,7 +750,7 @@ static void asm_parse_directive(TCCState *s1, int global)
next();
sym = get_asm_sym(tok, NULL);
if (tok1 != TOK_ASMDIR_hidden)
sym->type.t &= ~VT_STATIC;
sym->type.t &= ~VT_STATIC, sym->a.dllexport = 1;
if (tok1 == TOK_ASMDIR_weak)
sym->a.weak = 1;
else if (tok1 == TOK_ASMDIR_hidden)
@ -804,7 +845,7 @@ static void asm_parse_directive(TCCState *s1, int global)
Sym *sym;
next();
sym = label_find(tok);
sym = asm_label_find(tok);
if (!sym) {
tcc_error("label not found: %s", get_tok_str(tok, NULL));
}
@ -980,9 +1021,9 @@ static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global)
"__asm__("globl vide\nvide: ret");" */
Sym *sym = sym_find(opcode);
if (sym && (sym->type.t & VT_EXTERN) && global) {
sym = label_find(opcode);
sym = asm_label_find(opcode);
if (!sym) {
sym = label_push(&s1->asm_labels, opcode, 0);
sym = asm_label_push(&s1->asm_labels, opcode);
sym->type.t = VT_VOID | VT_EXTERN;
}
}
@ -1004,7 +1045,6 @@ static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global)
parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
}
asm_free_labels(s1);
parse_flags = saved_parse_flags;
return 0;
}
@ -1019,6 +1059,7 @@ ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
ind = cur_text_section->data_offset;
nocode_wanted = 0;
ret = tcc_assemble_internal(s1, do_preprocess, 1);
asm_free_labels(s1);
cur_text_section->data_offset = ind;
tcc_debug_end(s1);
return ret;

View File

@ -275,6 +275,11 @@ ST_FUNC int tccgen_compile(TCCState *s1)
decl(VT_CONST);
gen_inline_functions(s1);
check_vstack();
#ifdef CONFIG_TCC_ASM
asm_free_labels(s1);
#endif
/* end of translation unit info */
tcc_debug_end(s1);
return 0;

View File

@ -432,6 +432,7 @@ static TokenSym *tok_alloc_new(TokenSym **pts, const char *str, int len)
ts->sym_label = NULL;
ts->sym_struct = NULL;
ts->sym_identifier = NULL;
ts->sym_asm_label = NULL;
ts->len = len;
ts->hash_next = NULL;
memcpy(ts->str, str, len);

View File

@ -16,6 +16,7 @@ TESTS = \
memtest \
dlltest \
abitest \
asm-c-connect-test \
vla_test-run \
cross-test \
tests2-dir \
@ -44,7 +45,7 @@ ifeq ($(CONFIG_arm_eabi),yes)
TESTS := $(filter-out test3,$(TESTS))
endif
ifeq (,$(filter i386 x86_64,$(ARCH)))
TESTS := $(filter-out dlltest,$(TESTS))
TESTS := $(filter-out dlltest asm-c-connect-test,$(TESTS))
endif
ifndef CONFIG_cross
TESTS := $(filter-out cross-%,$(TESTS))
@ -231,6 +232,13 @@ vla_test-run: vla_test$(EXESUF)
@echo ------------ $@ ------------
./vla_test$(EXESUF)
asm-c-connect$(EXESUF): asm-c-connect-1.c asm-c-connect-2.c
$(TCC) -o $@ $^
asm-c-connect-test: asm-c-connect$(EXESUF)
@echo ------------ $@ ------------
./asm-c-connect$(EXESUF)
cross-test :
@echo ------------ $@ ------------
$(TOP)/i386-tcc$(EXESUF) $(TCCFLAGS-unx) -c $(TOPSRC)/examples/ex3.c && echo "ok"
@ -264,6 +272,7 @@ cache: tcc_g
clean:
rm -f *~ *.o *.a *.bin *.i *.ref *.out *.out? *.out?b *.cc *.gcc
rm -f *-cc *-gcc *-tcc *.exe hello libtcc_test vla_test tcctest[1234]
rm -f asm-c-connect$(EXESUF)
rm -f ex? tcc_g weaktest.*.txt *.def
@$(MAKE) -C tests2 $@
@$(MAKE) -C pp $@

31
tests/asm-c-connect-1.c Normal file
View File

@ -0,0 +1,31 @@
#include <stdio.h>
#if defined _WIN32 && !defined __TINYC__
# define U "_"
#else
# define U
#endif
const char str[] = "x1\n";
#ifdef __x86_64__
asm(U"x1: push %rbp; mov $"U"str, %rdi; call "U"printf; pop %rbp; ret");
#elif defined (__i386__)
asm(U"x1: push $"U"str; call "U"printf; pop %eax; ret");
#endif
int main(int argc, char *argv[])
{
asm("call "U"x1");
asm("call "U"x2");
asm("call "U"x3");
return 0;
}
static
int x2(void)
{
printf("x2\n");
return 2;
}
extern int x3(void);

7
tests/asm-c-connect-2.c Normal file
View File

@ -0,0 +1,7 @@
#include <stdio.h>
int x3(void)
{
printf("x3\n");
return 3;
}