diff options
author | Dmitry Atamanov <data-man@users.noreply.github.com> | 2017-10-28 10:25:56 +0300 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2017-10-28 09:25:56 +0200 |
commit | d2c7d391c8b69a6a590a2f702ed58bea033f6325 (patch) | |
tree | c74a1b46e1166ddb87453ddc49cea84e1baaa5ab /tinyc/tccasm.c | |
parent | 9c00f6decd4453a4233450a60ccef05b20e9f24a (diff) | |
download | Nim-d2c7d391c8b69a6a590a2f702ed58bea033f6325.tar.gz |
TinyC upgrade (#6593)
Diffstat (limited to 'tinyc/tccasm.c')
-rw-r--r-- | tinyc/tccasm.c | 718 |
1 files changed, 500 insertions, 218 deletions
diff --git a/tinyc/tccasm.c b/tinyc/tccasm.c index 9b5289f77..ac1618a52 100644 --- a/tinyc/tccasm.c +++ b/tinyc/tccasm.c @@ -18,7 +18,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -static int asm_get_local_label_name(TCCState *s1, unsigned int n) +#include "tcc.h" +#ifdef CONFIG_TCC_ASM + +ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n) { char buf[64]; TokenSym *ts; @@ -28,7 +31,56 @@ static int asm_get_local_label_name(TCCState *s1, unsigned int n) return ts->tok; } -static void asm_expr(TCCState *s1, ExprValue *pe); +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; + +/* 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 + to look up file-global C symbols from inside the assembler, e.g. + for global asm blocks to be able to refer to defined C symbols. + + This routine gives back either an existing asm-internal + symbol, or a new one. In the latter case the new asm-internal + symbol is initialized with info from the C symbol table. + + If CSYM is non-null we take symbol info from it, otherwise + 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); + if (!sym) { + sym = label_push(&tcc_state->asm_labels, name, 0); + 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 + and so might have local decls on the identifier stack. Search + for the first global one. */ + while (csym && csym->sym_scope) + csym = csym->prev_tok; + } + /* Now, if we have a defined global symbol copy over + section and offset. */ + if (csym && + ((csym->r & (VT_SYM|VT_CONST)) == (VT_SYM|VT_CONST)) && + csym->c) { + ElfW(Sym) *esym; + esym = &((ElfW(Sym) *)symtab_section->data)[csym->c]; + sym->c = csym->c; + sym->r = esym->st_shndx; + sym->jnext = esym->st_value; + /* XXX can't yet store st_size anywhere. */ + sym->type.t &= ~VT_EXTERN; + /* Mark that this asm symbol doesn't need to be fed back. */ + sym->a.dllimport = 1; + } else { + sym->type.t |= VT_STATIC; + } + } + return sym; +} /* We do not use the C expression parser to handle symbols. Maybe the C expression parser could be tweaked to do so. */ @@ -36,13 +88,14 @@ static void asm_expr(TCCState *s1, ExprValue *pe); static void asm_expr_unary(TCCState *s1, ExprValue *pe) { Sym *sym; - int op, n, label; + int op, label; + uint64_t n; const char *p; switch(tok) { case TOK_PPNUM: - p = tokc.cstr->data; - n = strtoul(p, (char **)&p, 0); + p = tokc.str.data; + n = strtoull(p, (char **)&p, 0); if (*p == 'b' || *p == 'f') { /* backward or forward label */ label = asm_get_local_label_name(s1, n); @@ -52,22 +105,24 @@ static void asm_expr_unary(TCCState *s1, ExprValue *pe) if (sym && sym->r == 0) sym = sym->prev_tok; if (!sym) - error("local label '%d' not found backward", n); + tcc_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; + sym->type.t = VT_STATIC | VT_VOID | VT_EXTERN; } } - pe->v = 0; - pe->sym = sym; + pe->v = 0; + pe->sym = sym; + pe->pcrel = 0; } else if (*p == '\0') { pe->v = n; pe->sym = NULL; + pe->pcrel = 0; } else { - error("invalid number syntax"); + tcc_error("invalid number syntax"); } next(); break; @@ -81,7 +136,7 @@ static void asm_expr_unary(TCCState *s1, ExprValue *pe) next(); asm_expr_unary(s1, pe); if (pe->sym) - error("invalid operation with label"); + tcc_error("invalid operation with label"); if (op == '-') pe->v = -pe->v; else @@ -91,6 +146,7 @@ static void asm_expr_unary(TCCState *s1, ExprValue *pe) case TOK_LCHAR: pe->v = tokc.i; pe->sym = NULL; + pe->pcrel = 0; next(); break; case '(': @@ -98,26 +154,32 @@ static void asm_expr_unary(TCCState *s1, ExprValue *pe) asm_expr(s1, pe); skip(')'); break; + case '.': + pe->v = 0; + pe->sym = &sym_dot; + pe->pcrel = 0; + sym_dot.type.t = VT_VOID | VT_STATIC; + sym_dot.r = cur_text_section->sh_num; + sym_dot.jnext = ind; + next(); + 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; - } + sym = get_asm_sym(tok, NULL); if (sym->r == SHN_ABS) { /* if absolute symbol, no need to put a symbol value */ - pe->v = (long)sym->next; + pe->v = sym->jnext; pe->sym = NULL; + pe->pcrel = 0; } else { pe->v = 0; pe->sym = sym; + pe->pcrel = 0; } next(); } else { - error("bad expression syntax [%s]", get_tok_str(tok, &tokc)); + tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc)); } break; } @@ -137,7 +199,7 @@ static void asm_expr_prod(TCCState *s1, ExprValue *pe) next(); asm_expr_unary(s1, &e2); if (pe->sym || e2.sym) - error("invalid operation with label"); + tcc_error("invalid operation with label"); switch(op) { case '*': pe->v *= e2.v; @@ -145,7 +207,7 @@ static void asm_expr_prod(TCCState *s1, ExprValue *pe) case '/': if (e2.v == 0) { div_error: - error("division by zero"); + tcc_error("division by zero"); } pe->v /= e2.v; break; @@ -178,7 +240,7 @@ static void asm_expr_logic(TCCState *s1, ExprValue *pe) next(); asm_expr_prod(s1, &e2); if (pe->sym || e2.sym) - error("invalid operation with label"); + tcc_error("invalid operation with label"); switch(op) { case '&': pe->v &= e2.v; @@ -216,34 +278,77 @@ static inline void asm_expr_sum(TCCState *s1, ExprValue *pe) 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 subtracted to NULL */ + if (!e2.sym) { + /* OK */ + } else if (pe->sym == e2.sym) { + /* OK */ + pe->sym = NULL; /* same symbols can be subtracted to NULL */ + } else if (pe->sym && pe->sym->r == e2.sym->r && pe->sym->r != 0) { + /* we also accept defined symbols in the same section */ + pe->v += pe->sym->jnext - e2.sym->jnext; + pe->sym = NULL; + } else if (e2.sym->r == cur_text_section->sh_num) { + /* When subtracting a defined symbol in current section + this actually makes the value PC-relative. */ + pe->v -= e2.sym->jnext - ind - 4; + pe->pcrel = 1; + e2.sym = NULL; } else { cannot_relocate: - error("invalid operation with label"); + tcc_error("invalid operation with label"); } } } } -static void asm_expr(TCCState *s1, ExprValue *pe) +static inline void asm_expr_cmp(TCCState *s1, ExprValue *pe) { + int op; + ExprValue e2; + asm_expr_sum(s1, pe); + for(;;) { + op = tok; + if (op != TOK_EQ && op != TOK_NE + && (op > TOK_GT || op < TOK_ULE)) + break; + next(); + asm_expr_sum(s1, &e2); + if (pe->sym || e2.sym) + tcc_error("invalid operation with label"); + switch(op) { + case TOK_EQ: + pe->v = pe->v == e2.v; + break; + case TOK_NE: + pe->v = pe->v != e2.v; + break; + case TOK_LT: + pe->v = (int64_t)pe->v < (int64_t)e2.v; + break; + case TOK_GE: + pe->v = (int64_t)pe->v >= (int64_t)e2.v; + break; + case TOK_LE: + pe->v = (int64_t)pe->v <= (int64_t)e2.v; + break; + case TOK_GT: + pe->v = (int64_t)pe->v > (int64_t)e2.v; + break; + default: + break; + } + /* GAS compare results are -1/0 not 1/0. */ + pe->v = -(int64_t)pe->v; + } } -static int asm_int_expr(TCCState *s1) +ST_FUNC void asm_expr(TCCState *s1, ExprValue *pe) +{ + asm_expr_cmp(s1, pe); +} + +ST_FUNC int asm_int_expr(TCCState *s1) { ExprValue e; asm_expr(s1, &e); @@ -254,17 +359,20 @@ static int asm_int_expr(TCCState *s1) /* 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, +static Sym* 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) { + /* A VT_EXTERN symbol, even if it has a section is considered + overridable. This is how we "define" .set targets. Real + definitions won't have VT_EXTERN set. */ + if (sym->r && !(sym->type.t & VT_EXTERN)) { /* the label is already defined */ if (!is_local) { - error("assembler label '%s' already defined", + tcc_error("assembler label '%s' already defined", get_tok_str(label, NULL)); } else { /* redefinition of local labels is possible */ @@ -274,31 +382,50 @@ static void asm_new_label1(TCCState *s1, int label, int is_local, } else { new_label: sym = label_push(&s1->asm_labels, label, 0); - sym->type.t = VT_STATIC | VT_VOID; + /* 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->r = sh_num; - sym->next = (void *)value; + sym->jnext = value; + return sym; } -static void asm_new_label(TCCState *s1, int label, int is_local) +static Sym* asm_new_label(TCCState *s1, int label, int is_local) { - asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind); + return asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind); +} + +/* Set the value of LABEL to that of some expression (possibly + involving other symbols). LABEL can be overwritten later still. */ +static Sym* set_symbol(TCCState *s1, int label) +{ + long n; + ExprValue e; + next(); + asm_expr(s1, &e); + n = e.v; + if (e.sym) + n += e.sym->jnext; + return asm_new_label1(s1, label, 0, e.sym ? e.sym->r : SHN_ABS, n); } 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) { + s->type.t &= ~VT_EXTERN; + if (s->r && !s->a.dllimport) { if (s->r == SHN_ABS) sec = SECTION_ABS; else sec = st->sections[s->r]; - put_extern_sym2(s, sec, (long)s->next, 0, 0); + put_extern_sym2(s, sec, s->jnext, 0, 0); } /* remove label */ table_ident[s->v - TOK_IDENT]->sym_label = NULL; @@ -321,31 +448,57 @@ static void use_section(TCCState *s1, const char *name) use_section1(s1, sec); } -static void asm_parse_directive(TCCState *s1) +static void push_section(TCCState *s1, const char *name) +{ + Section *sec = find_section(s1, name); + sec->prev = cur_text_section; + use_section1(s1, sec); +} + +static void pop_section(TCCState *s1) +{ + Section *prev = cur_text_section->prev; + if (!prev) + tcc_error(".popsection without .pushsection"); + cur_text_section->prev = NULL; + use_section1(s1, prev); +} + +static void asm_parse_directive(TCCState *s1, int global) { 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: + case TOK_ASMDIR_align: + case TOK_ASMDIR_balign: + case TOK_ASMDIR_p2align: + case TOK_ASMDIR_skip: + case TOK_ASMDIR_space: tok1 = tok; next(); n = asm_int_expr(s1); - if (tok1 == TOK_ASM_align) { + if (tok1 == TOK_ASMDIR_p2align) + { + if (n < 0 || n > 30) + tcc_error("invalid p2align, must be between 0 and 30"); + n = 1 << n; + tok1 = TOK_ASMDIR_align; + } + if (tok1 == TOK_ASMDIR_align || tok1 == TOK_ASMDIR_balign) { if (n < 0 || (n & (n-1)) != 0) - error("alignment must be a positive power of two"); + tcc_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 { + if (n < 0) + n = 0; size = n; } v = 0; @@ -361,16 +514,20 @@ static void asm_parse_directive(TCCState *s1) } ind += size; break; - case TOK_ASM_quad: + case TOK_ASMDIR_quad: +#ifdef TCC_TARGET_X86_64 + size = 8; + goto asm_data; +#else next(); for(;;) { uint64_t vl; const char *p; - p = tokc.cstr->data; + p = tokc.str.data; if (tok != TOK_PPNUM) { error_constant: - error("64 bit constant"); + tcc_error("64 bit constant"); } vl = strtoll(p, (char **)&p, 0); if (*p != '\0') @@ -388,15 +545,16 @@ static void asm_parse_directive(TCCState *s1) next(); } break; - case TOK_ASM_byte: +#endif + case TOK_ASMDIR_byte: size = 1; goto asm_data; - case TOK_ASM_word: - case TOK_SHORT: + case TOK_ASMDIR_word: + case TOK_ASMDIR_short: size = 2; goto asm_data; - case TOK_LONG: - case TOK_INT: + case TOK_ASMDIR_long: + case TOK_ASMDIR_int: size = 4; asm_data: next(); @@ -406,6 +564,10 @@ static void asm_parse_directive(TCCState *s1) if (sec->sh_type != SHT_NOBITS) { if (size == 4) { gen_expr32(&e); +#ifdef TCC_TARGET_X86_64 + } else if (size == 8) { + gen_expr64(&e); +#endif } else { if (e.sym) expect("constant"); @@ -422,14 +584,14 @@ static void asm_parse_directive(TCCState *s1) next(); } break; - case TOK_ASM_fill: + case TOK_ASMDIR_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"); + tcc_error("repeat < 0; .fill ignored"); break; } size = 1; @@ -438,7 +600,7 @@ static void asm_parse_directive(TCCState *s1) next(); size = asm_int_expr(s1); if (size < 0) { - error("size < 0; .fill ignored"); + tcc_error("size < 0; .fill ignored"); break; } if (size > 8) @@ -464,37 +626,80 @@ static void asm_parse_directive(TCCState *s1) } } break; - case TOK_ASM_org: + case TOK_ASMDIR_rept: + { + int repeat; + TokenString *init_str; + next(); + repeat = asm_int_expr(s1); + init_str = tok_str_alloc(); + while (next(), tok != TOK_ASMDIR_endr) { + if (tok == CH_EOF) + tcc_error("we at end of file, .endr not found"); + tok_str_add_tok(init_str); + } + tok_str_add(init_str, -1); + tok_str_add(init_str, 0); + begin_macro(init_str, 1); + while (repeat-- > 0) { + tcc_assemble_internal(s1, (parse_flags & PARSE_FLAG_PREPROCESS), + global); + macro_ptr = init_str->str; + } + end_macro(); + next(); + break; + } + case TOK_ASMDIR_org: { unsigned long n; + ExprValue e; next(); - /* XXX: handle section symbols too */ - n = asm_int_expr(s1); + asm_expr(s1, &e); + n = e.v; + if (e.sym) { + if (e.sym->r != cur_text_section->sh_num) + expect("constant or same-section symbol"); + n += e.sym->jnext; + } if (n < ind) - error("attempt to .org backwards"); + tcc_error("attempt to .org backwards"); v = 0; size = n - ind; goto zero_pad; } break; - case TOK_ASM_globl: - case TOK_ASM_global: - { + case TOK_ASMDIR_set: + next(); + tok1 = tok; + next(); + /* Also accept '.set stuff', but don't do anything with this. + It's used in GAS to set various features like '.set mips16'. */ + if (tok == ',') + set_symbol(s1, tok1); + break; + case TOK_ASMDIR_globl: + case TOK_ASMDIR_global: + case TOK_ASMDIR_weak: + case TOK_ASMDIR_hidden: + tok1 = tok; + do { 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; + sym = get_asm_sym(tok, NULL); + if (tok1 != TOK_ASMDIR_hidden) + sym->type.t &= ~VT_STATIC; + if (tok1 == TOK_ASMDIR_weak) + sym->a.weak = 1; + else if (tok1 == TOK_ASMDIR_hidden) + sym->a.visibility = STV_HIDDEN; next(); - } + } while (tok == ','); break; - case TOK_ASM_string: - case TOK_ASM_ascii: - case TOK_ASM_asciz: + case TOK_ASMDIR_string: + case TOK_ASMDIR_ascii: + case TOK_ASMDIR_asciz: { const uint8_t *p; int i, size, t; @@ -504,9 +709,9 @@ static void asm_parse_directive(TCCState *s1) for(;;) { if (tok != TOK_STR) expect("string constant"); - p = tokc.cstr->data; - size = tokc.cstr->size; - if (t == TOK_ASM_ascii && size > 0) + p = tokc.str.data; + size = tokc.str.size; + if (t == TOK_ASMDIR_ascii && size > 0) size--; for(i = 0; i < size; i++) g(p[i]); @@ -519,9 +724,9 @@ static void asm_parse_directive(TCCState *s1) } } break; - case TOK_ASM_text: - case TOK_ASM_data: - case TOK_ASM_bss: + case TOK_ASMDIR_text: + case TOK_ASMDIR_data: + case TOK_ASMDIR_bss: { char sname[64]; tok1 = tok; @@ -531,20 +736,110 @@ static void asm_parse_directive(TCCState *s1) n = asm_int_expr(s1); next(); } - sprintf(sname, (n?".%s%d":".%s"), get_tok_str(tok1, NULL), n); + if (n) + sprintf(sname, "%s%d", get_tok_str(tok1, NULL), n); + else + sprintf(sname, "%s", get_tok_str(tok1, NULL)); use_section(s1, sname); } break; - case TOK_SECTION1: + case TOK_ASMDIR_file: + { + char filename[512]; + + filename[0] = '\0'; + next(); + + if (tok == TOK_STR) + pstrcat(filename, sizeof(filename), tokc.str.data); + else + pstrcat(filename, sizeof(filename), get_tok_str(tok, NULL)); + + if (s1->warn_unsupported) + tcc_warning("ignoring .file %s", filename); + + next(); + } + break; + case TOK_ASMDIR_ident: + { + char ident[256]; + + ident[0] = '\0'; + next(); + + if (tok == TOK_STR) + pstrcat(ident, sizeof(ident), tokc.str.data); + else + pstrcat(ident, sizeof(ident), get_tok_str(tok, NULL)); + + if (s1->warn_unsupported) + tcc_warning("ignoring .ident %s", ident); + + next(); + } + break; + case TOK_ASMDIR_size: + { + Sym *sym; + + next(); + sym = label_find(tok); + if (!sym) { + tcc_error("label not found: %s", get_tok_str(tok, NULL)); + } + + /* XXX .size name,label2-label1 */ + if (s1->warn_unsupported) + tcc_warning("ignoring .size %s,*", get_tok_str(tok, NULL)); + + next(); + skip(','); + while (tok != TOK_LINEFEED && tok != ';' && tok != CH_EOF) { + next(); + } + } + break; + case TOK_ASMDIR_type: + { + Sym *sym; + const char *newtype; + + next(); + sym = get_asm_sym(tok, NULL); + next(); + skip(','); + if (tok == TOK_STR) { + newtype = tokc.str.data; + } else { + if (tok == '@' || tok == '%') + next(); + newtype = get_tok_str(tok, NULL); + } + + if (!strcmp(newtype, "function") || !strcmp(newtype, "STT_FUNC")) { + sym->type.t = (sym->type.t & ~VT_BTYPE) | VT_FUNC; + } + else if (s1->warn_unsupported) + tcc_warning("change type of '%s' from 0x%x to '%s' ignored", + get_tok_str(sym->v, NULL), sym->type.t, newtype); + + next(); + } + break; + case TOK_ASMDIR_pushsection: + case TOK_ASMDIR_section: { char sname[256]; + int old_nb_section = s1->nb_sections; + tok1 = tok; /* 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); + pstrcat(sname, sizeof(sname), tokc.str.data); else pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL)); next(); @@ -555,94 +850,104 @@ static void asm_parse_directive(TCCState *s1) if (tok != TOK_STR) expect("string constant"); next(); + if (tok == ',') { + next(); + if (tok == '@' || tok == '%') + next(); + next(); + } } last_text_section = cur_text_section; - use_section(s1, sname); + if (tok1 == TOK_ASMDIR_section) + use_section(s1, sname); + else + push_section(s1, sname); + /* If we just allocated a new section reset its alignment to + 1. new_section normally acts for GCC compatibility and + sets alignment to PTR_SIZE. The assembler behaves different. */ + if (old_nb_section != s1->nb_sections) + cur_text_section->sh_addralign = 1; } break; - case TOK_ASM_previous: + case TOK_ASMDIR_previous: { Section *sec; next(); if (!last_text_section) - error("no previous section referenced"); + tcc_error("no previous section referenced"); sec = cur_text_section; use_section1(s1, last_text_section); last_text_section = sec; } break; + case TOK_ASMDIR_popsection: + next(); + pop_section(s1); + break; +#ifdef TCC_TARGET_I386 + case TOK_ASMDIR_code16: + { + next(); + s1->seg_size = 16; + } + break; + case TOK_ASMDIR_code32: + { + next(); + s1->seg_size = 32; + } + break; +#endif +#ifdef TCC_TARGET_X86_64 + /* added for compatibility with GAS */ + case TOK_ASMDIR_code64: + next(); + break; +#endif default: - error("unknown assembler directive '.%s'", get_tok_str(tok, NULL)); + tcc_error("unknown assembler directive '.%s'", get_tok_str(tok, NULL)); break; } } /* assemble a file */ -static int tcc_assemble_internal(TCCState *s1, int do_preprocess) +static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global) { 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;i<pa->nb_ops;i++) { - for(j=0;j<nb_op_vals;j++) { - if (pa->op_type[i] == op_vals[j]) - goto found; - } - op_vals[nb_op_vals++] = pa->op_type[i]; - found: ; - } - } - for(i=0;i<nb_op_vals;i++) { - int v = op_vals[i]; - if ((v & (v - 1)) != 0) - printf("%3d: %08x\n", i, v); - } - printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n", - sizeof(asm_instrs), sizeof(asm_instrs) / sizeof(ASMInstr), - freq[0], freq[1], freq[2], freq[3]); - } -#endif + int saved_parse_flags = parse_flags; /* XXX: undefine C labels */ - - ch = file->buf_ptr[0]; - tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF; - parse_flags = PARSE_FLAG_ASM_COMMENTS; + parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR; if (do_preprocess) parse_flags |= PARSE_FLAG_PREPROCESS; - next(); for(;;) { + next(); if (tok == TOK_EOF) break; + /* generate line number info */ + if (global && s1->do_debug) + tcc_debug_line(s1); 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_ASMDIR_FIRST && tok <= TOK_ASMDIR_LAST) { + asm_parse_directive(s1, global); } else if (tok == TOK_PPNUM) { + Sym *sym; const char *p; int n; - p = tokc.cstr->data; + p = tokc.str.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); + sym = asm_new_label(s1, asm_get_local_label_name(s1, n), 1); + /* Remove the marker for tentative definitions. */ + sym->type.t &= ~VT_EXTERN; next(); skip(':'); goto redo; @@ -651,53 +956,51 @@ static int tcc_assemble_internal(TCCState *s1, int do_preprocess) opcode = tok; next(); if (tok == ':') { + /* handle "extern void vide(void); __asm__("vide: ret");" as + "__asm__("globl vide\nvide: ret");" */ + Sym *sym = sym_find(opcode); + if (sym && (sym->type.t & VT_EXTERN) && global) { + sym = label_find(opcode); + if (!sym) { + sym = label_push(&s1->asm_labels, opcode, 0); + sym->type.t = VT_VOID | VT_EXTERN; + } + } /* new label */ - asm_new_label(s1, opcode, 0); + sym = asm_new_label(s1, opcode, 0); + sym->type.t &= ~VT_EXTERN; next(); goto redo; } else if (tok == '=') { - int n; - next(); - n = asm_int_expr(s1); - asm_new_label1(s1, opcode, 0, SHN_ABS, n); + set_symbol(s1, opcode); goto redo; } else { asm_opcode(s1, opcode); } } /* end of line */ - if (tok != ';' && tok != TOK_LINEFEED){ + if (tok != ';' && tok != TOK_LINEFEED) expect("end of line"); - } parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */ - next(); } asm_free_labels(s1); - + parse_flags = saved_parse_flags; return 0; } /* Assemble the current file */ -static int tcc_assemble(TCCState *s1, int do_preprocess) +ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess) { - Sym *define_start; int ret; - - preprocess_init(s1); - + tcc_debug_start(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); - + nocode_wanted = 0; + ret = tcc_assemble_internal(s1, do_preprocess, 1); cur_text_section->data_offset = ind; - - free_defines(define_start); - + tcc_debug_end(s1); return ret; } @@ -707,39 +1010,25 @@ static int tcc_assemble(TCCState *s1, int do_preprocess) /* 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) +static void tcc_assemble_inline(TCCState *s1, char *str, int len, int global) { - 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; + const int *saved_macro_ptr = macro_ptr; + int dotid = set_idnum('.', IS_ID); + + tcc_open_bf(s1, ":asm:", len); + memcpy(file->buffer, str, len); macro_ptr = NULL; - - tcc_assemble_internal(s1, 0); + tcc_assemble_internal(s1, 0, global); + tcc_close(); - parse_flags = saved_parse_flags; + set_idnum('.', dotid); 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, +ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands, const char *name, const char **pp) { int index; @@ -778,7 +1067,6 @@ static int find_constraint(ASMOperand *operands, int nb_operands, } static void subst_asm_operands(ASMOperand *operands, int nb_operands, - int nb_outputs, CString *out_str, CString *in_str) { int c, index, modifier; @@ -797,11 +1085,15 @@ static void subst_asm_operands(ASMOperand *operands, int nb_operands, } modifier = 0; if (*str == 'c' || *str == 'n' || - *str == 'b' || *str == 'w' || *str == 'h') + *str == 'b' || *str == 'w' || *str == 'h' || *str == 'k' || + *str == 'q' || + /* P in GCC would add "@PLT" to symbol refs in PIC mode, + and make literal operands not be decorated with '$'. */ + *str == 'P') modifier = *str++; index = find_constraint(operands, nb_operands, str, &str); if (index < 0) - error("invalid operand reference after %%"); + tcc_error("invalid operand reference after %%"); op = &operands[index]; sv = *op->vt; if (op->reg >= 0) { @@ -829,8 +1121,9 @@ static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr, if (tok != ':') { nb_operands = *nb_operands_ptr; for(;;) { + CString astr; if (nb_operands >= MAX_ASM_OPERANDS) - error("too many asm operands"); + tcc_error("too many asm operands"); op = &operands[nb_operands++]; op->id = 0; if (tok == '[') { @@ -841,15 +1134,15 @@ static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr, next(); skip(']'); } - if (tok != TOK_STR) - expect("string constant"); - op->constraint = tcc_malloc(tokc.cstr->size); - strcpy(op->constraint, tokc.cstr->data); - next(); + parse_mult_str(&astr, "string constant"); + op->constraint = tcc_malloc(astr.size); + strcpy(op->constraint, astr.data); + cstr_free(&astr); skip('('); gexpr(); if (is_output) { - test_lvalue(); + if (!(vtop->type.t & VT_ARRAY)) + test_lvalue(); } else { /* we want to avoid LLOCAL case, except when the 'm' constraint is used. Note that it may come from @@ -874,27 +1167,12 @@ static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr, } } -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) +ST_FUNC void asm_instr(void) { CString astr, astr1; ASMOperand operands[MAX_ASM_OPERANDS]; - int nb_inputs, nb_outputs, nb_operands, i, must_subst, out_reg; + int nb_outputs, nb_operands, i, must_subst, out_reg; uint8_t clobber_regs[NB_ASM_REGS]; next(); @@ -926,7 +1204,7 @@ static void asm_instr(void) for(;;) { if (tok != TOK_STR) expect("string constant"); - asm_clobber(clobber_regs, tokc.cstr->data); + asm_clobber(clobber_regs, tokc.str.data); next(); if (tok == ',') { next(); @@ -943,7 +1221,6 @@ static void asm_instr(void) token after the assembler parsing */ if (tok != ';') expect("';'"); - nb_inputs = nb_operands - nb_outputs; /* save all values in the memory */ save_regs(0); @@ -958,7 +1235,7 @@ static void asm_instr(void) printf("asm: \"%s\"\n", (char *)astr.data); #endif if (must_subst) { - subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr); + subst_asm_operands(operands, nb_operands, &astr1, &astr); cstr_free(&astr); } else { astr1 = astr; @@ -972,7 +1249,7 @@ static void asm_instr(void) clobber_regs, out_reg); /* assemble the string with tcc internal assembler */ - tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1); + tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1, 0); /* restore the current C token */ next(); @@ -991,10 +1268,13 @@ static void asm_instr(void) cstr_free(&astr1); } -static void asm_global_instr(void) +ST_FUNC void asm_global_instr(void) { CString astr; + int saved_nocode_wanted = nocode_wanted; + /* Global asm blocks are always emitted. */ + nocode_wanted = 0; next(); parse_asm_str(&astr); skip(')'); @@ -1010,7 +1290,7 @@ static void asm_global_instr(void) ind = cur_text_section->data_offset; /* assemble the string with tcc internal assembler */ - tcc_assemble_inline(tcc_state, astr.data, astr.size - 1); + tcc_assemble_inline(tcc_state, astr.data, astr.size - 1, 1); cur_text_section->data_offset = ind; @@ -1018,4 +1298,6 @@ static void asm_global_instr(void) next(); cstr_free(&astr); + nocode_wanted = saved_nocode_wanted; } +#endif /* CONFIG_TCC_ASM */ |