From e7c71e24730ae07241980812c8b747963f216260 Mon Sep 17 00:00:00 2001 From: Michael Matz Date: Wed, 22 Nov 2017 17:57:43 +0100 Subject: [PATCH] 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. --- tcc.h | 2 + tccasm.c | 85 ++++++++++++++++++++++++++++++----------- tccgen.c | 5 +++ tccpp.c | 1 + tests/Makefile | 11 +++++- tests/asm-c-connect-1.c | 31 +++++++++++++++ tests/asm-c-connect-2.c | 7 ++++ 7 files changed, 119 insertions(+), 23 deletions(-) create mode 100644 tests/asm-c-connect-1.c create mode 100644 tests/asm-c-connect-2.c diff --git a/tcc.h b/tcc.h index da26298..1efd046 100644 --- a/tcc.h +++ b/tcc.h @@ -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 diff --git a/tccasm.c b/tccasm.c index 848f976..7761c39 100644 --- a/tccasm.c +++ b/tccasm.c @@ -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; diff --git a/tccgen.c b/tccgen.c index 7d554b5..a86103c 100644 --- a/tccgen.c +++ b/tccgen.c @@ -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; diff --git a/tccpp.c b/tccpp.c index 76f9e42..d8e7f53 100644 --- a/tccpp.c +++ b/tccpp.c @@ -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); diff --git a/tests/Makefile b/tests/Makefile index 6a61717..61b585e 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -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 $@ diff --git a/tests/asm-c-connect-1.c b/tests/asm-c-connect-1.c new file mode 100644 index 0000000..3f7b010 --- /dev/null +++ b/tests/asm-c-connect-1.c @@ -0,0 +1,31 @@ +#include + +#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); diff --git a/tests/asm-c-connect-2.c b/tests/asm-c-connect-2.c new file mode 100644 index 0000000..3a8cff2 --- /dev/null +++ b/tests/asm-c-connect-2.c @@ -0,0 +1,7 @@ +#include + +int x3(void) +{ + printf("x3\n"); + return 3; +}