summary refs log tree commit diff stats
path: root/tinyc/tccasm.c
diff options
context:
space:
mode:
authorDmitry Atamanov <data-man@users.noreply.github.com>2017-10-28 10:25:56 +0300
committerAndreas Rumpf <rumpf_a@web.de>2017-10-28 09:25:56 +0200
commitd2c7d391c8b69a6a590a2f702ed58bea033f6325 (patch)
treec74a1b46e1166ddb87453ddc49cea84e1baaa5ab /tinyc/tccasm.c
parent9c00f6decd4453a4233450a60ccef05b20e9f24a (diff)
downloadNim-d2c7d391c8b69a6a590a2f702ed58bea033f6325.tar.gz
TinyC upgrade (#6593)
Diffstat (limited to 'tinyc/tccasm.c')
-rw-r--r--tinyc/tccasm.c718
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 */