summary refs log tree commit diff stats
path: root/tinyc/tccasm.c
diff options
context:
space:
mode:
Diffstat (limited to 'tinyc/tccasm.c')
-rw-r--r--tinyc/tccasm.c1303
1 files changed, 0 insertions, 1303 deletions
diff --git a/tinyc/tccasm.c b/tinyc/tccasm.c
deleted file mode 100644
index ac1618a52..000000000
--- a/tinyc/tccasm.c
+++ /dev/null
@@ -1,1303 +0,0 @@
-/*
- *  GAS like assembler for TCC
- * 
- *  Copyright (c) 2001-2004 Fabrice Bellard
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#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;
-
-    snprintf(buf, sizeof(buf), "L..%u", n);
-    ts = tok_alloc(buf, strlen(buf));
-    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;
-
-/* 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. */
-
-static void asm_expr_unary(TCCState *s1, ExprValue *pe)
-{
-    Sym *sym;
-    int op, label;
-    uint64_t n;
-    const char *p;
-
-    switch(tok) {
-    case TOK_PPNUM:
-        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);
-            sym = label_find(label);
-            if (*p == 'b') {
-                /* backward : find the last corresponding defined label */
-                if (sym && sym->r == 0)
-                    sym = sym->prev_tok;
-                if (!sym)
-                    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 | VT_EXTERN;
-                }
-            }
-	    pe->v = 0;
-	    pe->sym = sym;
-	    pe->pcrel = 0;
-        } else if (*p == '\0') {
-            pe->v = n;
-            pe->sym = NULL;
-	    pe->pcrel = 0;
-        } else {
-            tcc_error("invalid number syntax");
-        }
-        next();
-        break;
-    case '+':
-        next();
-        asm_expr_unary(s1, pe);
-        break;
-    case '-':
-    case '~':
-        op = tok;
-        next();
-        asm_expr_unary(s1, pe);
-        if (pe->sym)
-            tcc_error("invalid operation with label");
-        if (op == '-')
-            pe->v = -pe->v;
-        else
-            pe->v = ~pe->v;
-        break;
-    case TOK_CCHAR:
-    case TOK_LCHAR:
-	pe->v = tokc.i;
-	pe->sym = NULL;
-	pe->pcrel = 0;
-	next();
-	break;
-    case '(':
-        next();
-        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 = get_asm_sym(tok, NULL);
-            if (sym->r == SHN_ABS) {
-                /* if absolute symbol, no need to put a symbol value */
-                pe->v = sym->jnext;
-                pe->sym = NULL;
-		pe->pcrel = 0;
-            } else {
-                pe->v = 0;
-                pe->sym = sym;
-		pe->pcrel = 0;
-            }
-            next();
-        } else {
-            tcc_error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
-        }
-        break;
-    }
-}
-    
-static void asm_expr_prod(TCCState *s1, ExprValue *pe)
-{
-    int op;
-    ExprValue e2;
-
-    asm_expr_unary(s1, pe);
-    for(;;) {
-        op = tok;
-        if (op != '*' && op != '/' && op != '%' && 
-            op != TOK_SHL && op != TOK_SAR)
-            break;
-        next();
-        asm_expr_unary(s1, &e2);
-        if (pe->sym || e2.sym)
-            tcc_error("invalid operation with label");
-        switch(op) {
-        case '*':
-            pe->v *= e2.v;
-            break;
-        case '/':  
-            if (e2.v == 0) {
-            div_error:
-                tcc_error("division by zero");
-            }
-            pe->v /= e2.v;
-            break;
-        case '%':  
-            if (e2.v == 0)
-                goto div_error;
-            pe->v %= e2.v;
-            break;
-        case TOK_SHL:
-            pe->v <<= e2.v;
-            break;
-        default:
-        case TOK_SAR:
-            pe->v >>= e2.v;
-            break;
-        }
-    }
-}
-
-static void asm_expr_logic(TCCState *s1, ExprValue *pe)
-{
-    int op;
-    ExprValue e2;
-
-    asm_expr_prod(s1, pe);
-    for(;;) {
-        op = tok;
-        if (op != '&' && op != '|' && op != '^')
-            break;
-        next();
-        asm_expr_prod(s1, &e2);
-        if (pe->sym || e2.sym)
-            tcc_error("invalid operation with label");
-        switch(op) {
-        case '&':
-            pe->v &= e2.v;
-            break;
-        case '|':  
-            pe->v |= e2.v;
-            break;
-        default:
-        case '^':
-            pe->v ^= e2.v;
-            break;
-        }
-    }
-}
-
-static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
-{
-    int op;
-    ExprValue e2;
-
-    asm_expr_logic(s1, pe);
-    for(;;) {
-        op = tok;
-        if (op != '+' && op != '-')
-            break;
-        next();
-        asm_expr_logic(s1, &e2);
-        if (op == '+') {
-            if (pe->sym != NULL && e2.sym != NULL)
-                goto cannot_relocate;
-            pe->v += e2.v;
-            if (pe->sym == NULL && e2.sym != NULL)
-                pe->sym = e2.sym;
-        } else {
-            pe->v -= e2.v;
-            /* NOTE: we are less powerful than gas in that case
-               because we store only one symbol in the expression */
-	    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:
-                tcc_error("invalid operation with label");
-            }
-        }
-    }
-}
-
-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;
-    }
-}
-
-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);
-    if (e.sym)
-        expect("constant");
-    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);
-    if (sym) {
-	/* 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) {
-                tcc_error("assembler label '%s' already defined", 
-                      get_tok_str(label, NULL));
-            } else {
-                /* redefinition of local labels is possible */
-                goto new_label;
-            }
-        }
-    } else {
-    new_label:
-        sym = label_push(&s1->asm_labels, label, 0);
-	/* 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->jnext = value;
-    return sym;
-}
-
-static Sym* asm_new_label(TCCState *s1, int label, int is_local)
-{
-    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 */
-	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, s->jnext, 0, 0);
-        }
-        /* remove label */
-        table_ident[s->v - TOK_IDENT]->sym_label = NULL;
-        sym_free(s);
-    }
-    st->asm_labels = NULL;
-}
-
-static void use_section1(TCCState *s1, Section *sec)
-{
-    cur_text_section->data_offset = ind;
-    cur_text_section = sec;
-    ind = cur_text_section->data_offset;
-}
-
-static void use_section(TCCState *s1, const char *name)
-{
-    Section *sec;
-    sec = find_section(s1, name);
-    use_section1(s1, sec);
-}
-
-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 */
-    sec = cur_text_section;
-    switch(tok) {
-    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_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)
-                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;
-        if (tok == ',') {
-            next();
-            v = asm_int_expr(s1);
-        }
-    zero_pad:
-        if (sec->sh_type != SHT_NOBITS) {
-            sec->data_offset = ind;
-            ptr = section_ptr_add(sec, size);
-            memset(ptr, v, size);
-        }
-        ind += size;
-        break;
-    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.str.data;
-            if (tok != TOK_PPNUM) {
-            error_constant:
-                tcc_error("64 bit constant");
-            }
-            vl = strtoll(p, (char **)&p, 0);
-            if (*p != '\0')
-                goto error_constant;
-            next();
-            if (sec->sh_type != SHT_NOBITS) {
-                /* XXX: endianness */
-                gen_le32(vl);
-                gen_le32(vl >> 32);
-            } else {
-                ind += 8;
-            }
-            if (tok != ',')
-                break;
-            next();
-        }
-        break;
-#endif
-    case TOK_ASMDIR_byte:
-        size = 1;
-        goto asm_data;
-    case TOK_ASMDIR_word:
-    case TOK_ASMDIR_short:
-        size = 2;
-        goto asm_data;
-    case TOK_ASMDIR_long:
-    case TOK_ASMDIR_int:
-        size = 4;
-    asm_data:
-        next();
-        for(;;) {
-            ExprValue e;
-            asm_expr(s1, &e);
-            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");
-                    if (size == 1)
-                        g(e.v);
-                    else
-                        gen_le16(e.v);
-                }
-            } else {
-                ind += size;
-            }
-            if (tok != ',')
-                break;
-            next();
-        }
-        break;
-    case TOK_ASMDIR_fill:
-        {
-            int repeat, size, val, i, j;
-            uint8_t repeat_buf[8];
-            next();
-            repeat = asm_int_expr(s1);
-            if (repeat < 0) {
-                tcc_error("repeat < 0; .fill ignored");
-                break;
-            }
-            size = 1;
-            val = 0;
-            if (tok == ',') {
-                next();
-                size = asm_int_expr(s1);
-                if (size < 0) {
-                    tcc_error("size < 0; .fill ignored");
-                    break;
-                }
-                if (size > 8)
-                    size = 8;
-                if (tok == ',') {
-                    next();
-                    val = asm_int_expr(s1);
-                }
-            }
-            /* XXX: endianness */
-            repeat_buf[0] = val;
-            repeat_buf[1] = val >> 8;
-            repeat_buf[2] = val >> 16;
-            repeat_buf[3] = val >> 24;
-            repeat_buf[4] = 0;
-            repeat_buf[5] = 0;
-            repeat_buf[6] = 0;
-            repeat_buf[7] = 0;
-            for(i = 0; i < repeat; i++) {
-                for(j = 0; j < size; j++) {
-                    g(repeat_buf[j]);
-                }
-            }
-        }
-        break;
-    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();
-	    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)
-                tcc_error("attempt to .org backwards");
-            v = 0;
-            size = n - ind;
-            goto zero_pad;
-        }
-        break;
-    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 = 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_ASMDIR_string:
-    case TOK_ASMDIR_ascii:
-    case TOK_ASMDIR_asciz:
-        {
-            const uint8_t *p;
-            int i, size, t;
-
-            t = tok;
-            next();
-            for(;;) {
-                if (tok != TOK_STR)
-                    expect("string constant");
-                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]);
-                next();
-                if (tok == ',') {
-                    next();
-                } else if (tok != TOK_STR) {
-                    break;
-                }
-            }
-	}
-	break;
-    case TOK_ASMDIR_text:
-    case TOK_ASMDIR_data:
-    case TOK_ASMDIR_bss:
-	{ 
-            char sname[64];
-            tok1 = tok;
-            n = 0;
-            next();
-            if (tok != ';' && tok != TOK_LINEFEED) {
-		n = asm_int_expr(s1);
-		next();
-            }
-            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_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.str.data);
-                else
-                    pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
-                next();
-            }
-            if (tok == ',') {
-                /* skip section options */
-                next();
-                if (tok != TOK_STR)
-                    expect("string constant");
-                next();
-                if (tok == ',') {
-                    next();
-                    if (tok == '@' || tok == '%')
-                        next();
-                    next();
-                }
-            }
-            last_text_section = cur_text_section;
-	    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_ASMDIR_previous:
-        { 
-            Section *sec;
-            next();
-            if (!last_text_section)
-                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:
-        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, int global)
-{
-    int opcode;
-    int saved_parse_flags = parse_flags;
-
-    /* XXX: undefine C labels */
-    parse_flags = PARSE_FLAG_ASM_FILE | PARSE_FLAG_TOK_STR;
-    if (do_preprocess)
-        parse_flags |= PARSE_FLAG_PREPROCESS;
-    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 >= 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.str.data;
-            n = strtoul(p, (char **)&p, 10);
-            if (*p != '\0')
-                expect("':'");
-            /* new local label */
-            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;
-        } else if (tok >= TOK_IDENT) {
-            /* instruction or label */
-            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 */
-                sym = asm_new_label(s1, opcode, 0);
-		sym->type.t &= ~VT_EXTERN;
-                next();
-                goto redo;
-            } else if (tok == '=') {
-		set_symbol(s1, opcode);
-                goto redo;
-            } else {
-                asm_opcode(s1, opcode);
-            }
-        }
-        /* end of line */
-        if (tok != ';' && tok != TOK_LINEFEED)
-            expect("end of line");
-        parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
-    }
-
-    asm_free_labels(s1);
-    parse_flags = saved_parse_flags;
-    return 0;
-}
-
-/* Assemble the current file */
-ST_FUNC int tcc_assemble(TCCState *s1, int do_preprocess)
-{
-    int ret;
-    tcc_debug_start(s1);
-    /* default section is text */
-    cur_text_section = text_section;
-    ind = cur_text_section->data_offset;
-    nocode_wanted = 0;
-    ret = tcc_assemble_internal(s1, do_preprocess, 1);
-    cur_text_section->data_offset = ind;
-    tcc_debug_end(s1);
-    return ret;
-}
-
-/********************************************************************/
-/* GCC inline asm support */
-
-/* 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, int global)
-{
-    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, global);
-    tcc_close();
-
-    set_idnum('.', dotid);
-    macro_ptr = saved_macro_ptr;
-}
-
-/* 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 */
-ST_FUNC int find_constraint(ASMOperand *operands, int nb_operands, 
-                           const char *name, const char **pp)
-{
-    int index;
-    TokenSym *ts;
-    const char *p;
-
-    if (isnum(*name)) {
-        index = 0;
-        while (isnum(*name)) {
-            index = (index * 10) + (*name) - '0';
-            name++;
-        }
-        if ((unsigned)index >= nb_operands)
-            index = -1;
-    } else if (*name == '[') {
-        name++;
-        p = strchr(name, ']');
-        if (p) {
-            ts = tok_alloc(name, p - name);
-            for(index = 0; index < nb_operands; index++) {
-                if (operands[index].id == ts->tok)
-                    goto found;
-            }
-            index = -1;
-        found:
-            name = p + 1;
-        } else {
-            index = -1;
-        }
-    } else {
-        index = -1;
-    }
-    if (pp)
-        *pp = name;
-    return index;
-}
-
-static void subst_asm_operands(ASMOperand *operands, int nb_operands, 
-                               CString *out_str, CString *in_str)
-{
-    int c, index, modifier;
-    const char *str;
-    ASMOperand *op;
-    SValue sv;
-
-    cstr_new(out_str);
-    str = in_str->data;
-    for(;;) {
-        c = *str++;
-        if (c == '%') {
-            if (*str == '%') {
-                str++;
-                goto add_char;
-            }
-            modifier = 0;
-            if (*str == 'c' || *str == 'n' ||
-                *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)
-                tcc_error("invalid operand reference after %%");
-            op = &operands[index];
-            sv = *op->vt;
-            if (op->reg >= 0) {
-                sv.r = op->reg;
-                if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory)
-                    sv.r |= VT_LVAL;
-            }
-            subst_asm_operand(out_str, &sv, modifier);
-        } else {
-        add_char:
-            cstr_ccat(out_str, c);
-            if (c == '\0')
-                break;
-        }
-    }
-}
-
-
-static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
-                               int is_output)
-{
-    ASMOperand *op;
-    int nb_operands;
-
-    if (tok != ':') {
-        nb_operands = *nb_operands_ptr;
-        for(;;) {
-	    CString astr;
-            if (nb_operands >= MAX_ASM_OPERANDS)
-                tcc_error("too many asm operands");
-            op = &operands[nb_operands++];
-            op->id = 0;
-            if (tok == '[') {
-                next();
-                if (tok < TOK_IDENT)
-                    expect("identifier");
-                op->id = tok;
-                next();
-                skip(']');
-            }
-	    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) {
-                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
-                   register storage, so we need to convert (reg)
-                   case */
-                if ((vtop->r & VT_LVAL) &&
-                    ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
-                     (vtop->r & VT_VALMASK) < VT_CONST) &&
-                    !strchr(op->constraint, 'm')) {
-                    gv(RC_INT);
-                }
-            }
-            op->vt = vtop;
-            skip(')');
-            if (tok == ',') {
-                next();
-            } else {
-                break;
-            }
-        }
-        *nb_operands_ptr = nb_operands;
-    }
-}
-
-/* parse the GCC asm() instruction */
-ST_FUNC void asm_instr(void)
-{
-    CString astr, astr1;
-    ASMOperand operands[MAX_ASM_OPERANDS];
-    int nb_outputs, nb_operands, i, must_subst, out_reg;
-    uint8_t clobber_regs[NB_ASM_REGS];
-
-    next();
-    /* since we always generate the asm() instruction, we can ignore
-       volatile */
-    if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
-        next();
-    }
-    parse_asm_str(&astr);
-    nb_operands = 0;
-    nb_outputs = 0;
-    must_subst = 0;
-    memset(clobber_regs, 0, sizeof(clobber_regs));
-    if (tok == ':') {
-        next();
-        must_subst = 1;
-        /* output args */
-        parse_asm_operands(operands, &nb_operands, 1);
-        nb_outputs = nb_operands;
-        if (tok == ':') {
-            next();
-            if (tok != ')') {
-                /* input args */
-                parse_asm_operands(operands, &nb_operands, 0);
-                if (tok == ':') {
-                    /* clobber list */
-                    /* XXX: handle registers */
-                    next();
-                    for(;;) {
-                        if (tok != TOK_STR)
-                            expect("string constant");
-                        asm_clobber(clobber_regs, tokc.str.data);
-                        next();
-                        if (tok == ',') {
-                            next();
-                        } else {
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-    }
-    skip(')');
-    /* NOTE: we do not eat the ';' so that we can restore the current
-       token after the assembler parsing */
-    if (tok != ';')
-        expect("';'");
-    
-    /* save all values in the memory */
-    save_regs(0);
-
-    /* compute constraints */
-    asm_compute_constraints(operands, nb_operands, nb_outputs, 
-                            clobber_regs, &out_reg);
-
-    /* substitute the operands in the asm string. No substitution is
-       done if no operands (GCC behaviour) */
-#ifdef ASM_DEBUG
-    printf("asm: \"%s\"\n", (char *)astr.data);
-#endif
-    if (must_subst) {
-        subst_asm_operands(operands, nb_operands, &astr1, &astr);
-        cstr_free(&astr);
-    } else {
-        astr1 = astr;
-    }
-#ifdef ASM_DEBUG
-    printf("subst_asm: \"%s\"\n", (char *)astr1.data);
-#endif
-
-    /* generate loads */
-    asm_gen_code(operands, nb_operands, nb_outputs, 0, 
-                 clobber_regs, out_reg);    
-
-    /* assemble the string with tcc internal assembler */
-    tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1, 0);
-
-    /* restore the current C token */
-    next();
-
-    /* store the output values if needed */
-    asm_gen_code(operands, nb_operands, nb_outputs, 1, 
-                 clobber_regs, out_reg);
-    
-    /* free everything */
-    for(i=0;i<nb_operands;i++) {
-        ASMOperand *op;
-        op = &operands[i];
-        tcc_free(op->constraint);
-        vpop();
-    }
-    cstr_free(&astr1);
-}
-
-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(')');
-    /* NOTE: we do not eat the ';' so that we can restore the current
-       token after the assembler parsing */
-    if (tok != ';')
-        expect("';'");
-    
-#ifdef ASM_DEBUG
-    printf("asm_global: \"%s\"\n", (char *)astr.data);
-#endif
-    cur_text_section = text_section;
-    ind = cur_text_section->data_offset;
-
-    /* assemble the string with tcc internal assembler */
-    tcc_assemble_inline(tcc_state, astr.data, astr.size - 1, 1);
-    
-    cur_text_section->data_offset = ind;
-
-    /* restore the current C token */
-    next();
-
-    cstr_free(&astr);
-    nocode_wanted = saved_nocode_wanted;
-}
-#endif /* CONFIG_TCC_ASM */